LVPWB/lvpwb/screenshots.py

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']