106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
import igdb
|
|
import library_api
|
|
|
|
STATUSES = ["played", "playing", "planning", "replayable"]
|
|
STATUS_ORDER = {"playing": 0, "replayable": 1, "played": 2, "planning": 3}
|
|
PROFILE_FLAGS = {"favorite": "favorite", "liked": "liked"}
|
|
SUGGESTED_TAGS = ["Casual", "Competitive", "Love it", "Open to play", "Looking to discuss"]
|
|
|
|
|
|
def check_rating(rating: int | None) -> int | None:
|
|
if rating is not None and not 0 <= rating <= 10:
|
|
raise ValueError("Rating must be between 0 and 10")
|
|
return rating
|
|
|
|
|
|
def check_status(status: str) -> str:
|
|
if status not in STATUSES:
|
|
raise ValueError(f"Status must be one of {', '.join(STATUSES)}")
|
|
return status
|
|
|
|
|
|
def list_games() -> list[dict]:
|
|
games = library_api.fetch_library()["games"]
|
|
rating = lambda game: game["rating"] if game.get("rating") is not None else -1
|
|
return sorted(games, key=lambda game: (STATUS_ORDER[game["status"]], -rating(game), game["name"].lower()))
|
|
|
|
|
|
def lookup(link: str | None, name: str | None) -> dict:
|
|
return igdb.details(igdb.find_game(link or None, name or None))
|
|
|
|
|
|
def add_game(link: str | None, name: str | None, status: str = "played", rating: int | None = None, image: str | None = None) -> dict:
|
|
if not link and not name:
|
|
raise ValueError("Enter a store link, an IGDB link or a name")
|
|
check_status(status)
|
|
check_rating(rating)
|
|
found = lookup(link, name)
|
|
identifier = found.pop("igdbId")
|
|
if any(game["igdbId"] == identifier for game in list_games()):
|
|
raise ValueError(f"{found['name']} is already listed")
|
|
is_store_link = bool(link) and not igdb.IGDB_SLUG_PATTERN.search(link)
|
|
fields = {
|
|
**found,
|
|
"name": name or found["name"],
|
|
"url": link if is_store_link else None,
|
|
"status": status,
|
|
"rating": rating,
|
|
}
|
|
if image:
|
|
fields["imageSource"] = image
|
|
return library_api.save_game(identifier, fields)
|
|
|
|
|
|
def update_game(identifier: str, fields: dict) -> dict:
|
|
return library_api.save_game(identifier, fields)
|
|
|
|
|
|
def rate_game(identifier: str, rating: int | None) -> dict:
|
|
return update_game(identifier, {"rating": check_rating(rating)})
|
|
|
|
|
|
def set_status(identifier: str, status: str) -> dict:
|
|
return update_game(identifier, {"status": check_status(status)})
|
|
|
|
|
|
def set_dates(identifier: str, started: str | None, finished: str | None) -> dict:
|
|
return update_game(identifier, {"started": started, "finished": finished})
|
|
|
|
|
|
def set_links(identifier: str, url: str | None, image: str | None) -> dict:
|
|
fields = {"url": url}
|
|
if image:
|
|
fields["imageSource"] = image
|
|
return update_game(identifier, fields)
|
|
|
|
|
|
def toggle_flag(game: dict, flag: str) -> dict:
|
|
if flag not in PROFILE_FLAGS:
|
|
raise ValueError(f"{flag} is not one of {', '.join(PROFILE_FLAGS)}")
|
|
return update_game(game["igdbId"], {flag: not game.get(flag, False)})
|
|
|
|
|
|
def set_tags(identifier: str, tags_text: str | None, note: str | None) -> dict:
|
|
tags = [tag.strip() for tag in (tags_text or "").split(",") if tag.strip()]
|
|
return update_game(identifier, {"tags": tags, "note": note or None})
|
|
|
|
|
|
def profile_text(game: dict) -> str:
|
|
return ", ".join(label for flag, label in PROFILE_FLAGS.items() if game.get(flag)) or "-"
|
|
|
|
|
|
def remove_game(identifier: str) -> None:
|
|
library_api.delete_game(identifier)
|
|
|
|
|
|
def refresh_game(game: dict) -> dict:
|
|
result = igdb.game_by_id(game["igdbId"])
|
|
if not result:
|
|
raise ValueError(f"{game['name']} is no longer on IGDB")
|
|
found = igdb.details(result)
|
|
found.pop("igdbId", None)
|
|
found.pop("name", None)
|
|
if game.get("image"):
|
|
found.pop("imageSource", None)
|
|
return update_game(game["igdbId"], found)
|