122 lines
4.0 KiB
Python
122 lines
4.0 KiB
Python
import imdb
|
|
import library_api
|
|
|
|
MEDIA_KINDS = {
|
|
"show": {"key": "shows", "label": "TV show", "omdb_type": "series"},
|
|
"movie": {"key": "movies", "label": "movie", "omdb_type": "movie"},
|
|
}
|
|
STATUSES = ["watched", "watching", "planning"]
|
|
STATUS_ORDER = {"watching": 0, "watched": 1, "planning": 2}
|
|
|
|
|
|
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 status_of(item: dict) -> str:
|
|
return item.get("status", "watched")
|
|
|
|
|
|
def with_details(item: dict, details: dict) -> dict:
|
|
return {**item, "imdbId": imdb.imdb_id(item["url"]), "details": details.get(item["url"]) or {}}
|
|
|
|
|
|
def list_items(kind: str) -> list[dict]:
|
|
library = library_api.fetch_library()
|
|
items = [with_details(item, library["mediaDetails"]) for item in library[MEDIA_KINDS[kind]["key"]]]
|
|
rating = lambda item: item["rating"] if item.get("rating") is not None else -1
|
|
return sorted(items, key=lambda item: (STATUS_ORDER[status_of(item)], -rating(item)))
|
|
|
|
|
|
def find_item(items: list[dict], text: str) -> dict | None:
|
|
identifier = imdb.imdb_id(text)
|
|
for item in items:
|
|
if identifier and item["imdbId"] == identifier:
|
|
return item
|
|
if item.get("name", "").lower() == text.lower():
|
|
return item
|
|
return None
|
|
|
|
|
|
def listed_identifier(kind: str, text: str) -> str:
|
|
item = find_item(list_items(kind), text)
|
|
if not item:
|
|
raise ValueError(f"{text} is not listed")
|
|
return item["imdbId"]
|
|
|
|
|
|
def lookup_details(identifier: str, name: str | None) -> dict:
|
|
try:
|
|
return imdb.details(identifier)
|
|
except ValueError:
|
|
if name:
|
|
return {}
|
|
raise ValueError("Could not look up the title on OMDb. Enter a name, or set OMDB_API_KEY in .env")
|
|
|
|
|
|
def add_item(kind: str, text: str, rating: int | None, name: str | None = None, status: str = "watched", collection: str | None = None) -> dict:
|
|
identifier = imdb.imdb_id(text)
|
|
if not identifier:
|
|
raise ValueError(f"{text} is not an IMDb link or ID")
|
|
check_rating(rating)
|
|
check_status(status)
|
|
if find_item(list_items(kind), identifier):
|
|
raise ValueError(f"{identifier} is already listed")
|
|
details = lookup_details(identifier, name)
|
|
fields = {
|
|
**details,
|
|
"kind": kind,
|
|
"name": name or details.get("name") or identifier,
|
|
"status": status,
|
|
"rating": rating,
|
|
"collection": collection or None,
|
|
}
|
|
return library_api.save_media(identifier, fields)["item"]
|
|
|
|
|
|
def update_item(kind: str, text: str, fields: dict) -> dict:
|
|
return library_api.save_media(listed_identifier(kind, text), fields)["item"]
|
|
|
|
|
|
def remove_item(kind: str, text: str) -> None:
|
|
library_api.delete_media(listed_identifier(kind, text))
|
|
|
|
|
|
def rate_item(kind: str, text: str, rating: int | None) -> dict:
|
|
check_rating(rating)
|
|
return update_item(kind, text, {"rating": rating})
|
|
|
|
|
|
def set_status(kind: str, text: str, status: str) -> dict:
|
|
check_status(status)
|
|
return update_item(kind, text, {"status": status})
|
|
|
|
|
|
def set_collection(kind: str, text: str, collection: str | None) -> dict:
|
|
return update_item(kind, text, {"collection": collection or None})
|
|
|
|
|
|
def set_dates(kind: str, text: str, started: str | None, finished: str | None) -> dict:
|
|
return update_item(kind, text, {"started": started, "finished": finished})
|
|
|
|
|
|
def has_details(item: dict) -> bool:
|
|
return any(item["details"].get(key) for key in ["year", "genre", "plot"])
|
|
|
|
|
|
def has_stored_poster(item: dict) -> bool:
|
|
return library_api.is_stored_image(item["details"].get("poster"))
|
|
|
|
|
|
def refresh_details(identifier: str) -> dict:
|
|
details = {key: value for key, value in imdb.details(identifier).items() if key != "name"}
|
|
return library_api.save_media(identifier, details)
|