123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import lvpwb as app
|
|
|
|
SCREENSHOT_STAMP_RE = re.compile(
|
|
r"(?P<y>\d{4})-(?P<mo>\d{2})-(?P<d>\d{2})-(?P<h>\d{2})-(?P<mi>\d{2})-(?P<s>\d{2})"
|
|
)
|
|
|
|
|
|
def parse_screenshot_stamp(filename: str) -> datetime | None:
|
|
"""Parse 'GameTitle yyyy-mm-dd-hh-mm-ss.png' into an aware local datetime."""
|
|
match = app.SCREENSHOT_STAMP_RE.search(filename)
|
|
if not match:
|
|
return None
|
|
parts = {key: int(value) for key, value in match.groupdict().items()}
|
|
try:
|
|
return datetime(parts["y"], parts["mo"], parts["d"],
|
|
parts["h"], parts["mi"], parts["s"]).astimezone()
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def screenshot_taken_at(path: Path) -> datetime:
|
|
"""Screenshot time from the filename stamp, falling back to file mtime."""
|
|
stamp = app.parse_screenshot_stamp(path.name)
|
|
if stamp is not None:
|
|
return stamp
|
|
return datetime.fromtimestamp(path.stat().st_mtime).astimezone()
|
|
|
|
|
|
def session_screenshots(since: datetime | None = None,
|
|
directory: Path | None = None) -> list[Path]:
|
|
"""Emulator screenshots taken after `since` (default: app session start)."""
|
|
since = since or app.SESSION_START
|
|
directory = directory or app.DUCKSTATION_SCREENSHOT_DIR
|
|
if not directory.is_dir():
|
|
return []
|
|
found: list[tuple[datetime, Path]] = []
|
|
for path in directory.glob("*.png"):
|
|
try:
|
|
taken = app.screenshot_taken_at(path)
|
|
except OSError:
|
|
continue
|
|
if taken >= since:
|
|
found.append((taken, path))
|
|
found.sort(key=lambda item: (item[0], item[1].name), reverse=True)
|
|
return [path for _, path in found]
|
|
|
|
|
|
def choose_screenshot(directory: Path | None = None) -> Path | None:
|
|
"""Pick a screenshot from those captured since the app was opened."""
|
|
directory = directory or app.DUCKSTATION_SCREENSHOT_DIR
|
|
candidates = app.session_screenshots(directory=directory)
|
|
if candidates:
|
|
print(f"\nScreenshots taken since the app opened ({directory}):")
|
|
for index, path in enumerate(candidates, 1):
|
|
taken = app.screenshot_taken_at(path)
|
|
print(f" {index}. {taken:%Y-%m-%d %H:%M:%S} — {path.name}")
|
|
else:
|
|
print(f"\nNo screenshots found since the app opened in {directory}.")
|
|
while True:
|
|
raw = app.ask("Screenshot number or path (blank to cancel)").strip()
|
|
if not raw:
|
|
return None
|
|
if raw.isdigit() and candidates and 1 <= int(raw) <= len(candidates):
|
|
return candidates[int(raw) - 1]
|
|
path = Path(raw).expanduser()
|
|
if not path.is_absolute():
|
|
path = directory / path
|
|
if path.is_file():
|
|
return path
|
|
print(f"No such screenshot: {path}")
|
|
|
|
|
|
def attach_match_image(card: dict[str, Any]) -> None:
|
|
"""Attach or change a screenshot for any match on a supported game card."""
|
|
if not app.game_has_screenshots(card["game_id"]):
|
|
print("No screenshot directory is configured for this game.")
|
|
return
|
|
index = app.choose_match_index(card, list(range(len(card["matches"]))), "Choose a match")
|
|
match = card["matches"][index]
|
|
current = str(match.get("image") or "")
|
|
if current:
|
|
print(f"Current image: {current}")
|
|
chosen = app.choose_screenshot(app.screenshot_directory_for_game(card["game_id"]))
|
|
if chosen is None:
|
|
print("Keep or skip the image; card unchanged.")
|
|
return
|
|
match["image"] = str(chosen)
|
|
app.save_card(card)
|
|
print(f"Image saved for Match {index + 1}: {match['image']}")
|
|
|
|
|
|
def game_has_screenshots(game_id: str) -> bool:
|
|
return app.screenshot_directory_for_game(game_id) is not None
|
|
|
|
|
|
def screenshot_directory_for_game(game_id: str) -> Path | None:
|
|
"""Resolve a game-specific screenshot folder, including BYOG overrides."""
|
|
if game_id in app.PS1_GAMES:
|
|
return app.DUCKSTATION_SCREENSHOT_DIR
|
|
if game_id == "sd3":
|
|
return app.PCSX2_SCREENSHOT_DIR
|
|
|
|
config = app.CUSTOM_GAME_CONFIGS.get(game_id)
|
|
if not isinstance(config, dict):
|
|
return None
|
|
override = config.get("screenshot_directory")
|
|
if isinstance(override, str) and override.strip():
|
|
directory = Path(override.strip()).expanduser()
|
|
return directory if directory.is_absolute() else app.ROOT / directory
|
|
if config.get("platform") == "ps1":
|
|
return app.DUCKSTATION_SCREENSHOT_DIR
|
|
if config.get("platform") == "ps2":
|
|
return app.PCSX2_SCREENSHOT_DIR
|
|
return None
|
|
|
|
__all__ = ['SCREENSHOT_STAMP_RE', 'attach_match_image', 'choose_screenshot', 'game_has_screenshots', 'parse_screenshot_stamp', 'screenshot_directory_for_game', 'screenshot_taken_at', 'session_screenshots']
|