diff --git a/MediaManager/src/database/tv.py b/MediaManager/src/database/tv.py index d0cc4e3..006bdf8 100644 --- a/MediaManager/src/database/tv.py +++ b/MediaManager/src/database/tv.py @@ -8,7 +8,7 @@ from database.torrents import TorrentMixin class Show(SQLModel, table=True): - __table_args__ = (UniqueConstraint("external_id", "metadata_provider"),) + __table_args__ = (UniqueConstraint("external_id", "metadata_provider", "version"),) id: UUID = Field(primary_key=True, default_factory=uuid.uuid4) external_id: int metadata_provider: str @@ -16,6 +16,7 @@ class Show(SQLModel, table=True): overview: str # For some shows the first_air_date isn't known, therefore it needs to be nullable year: int | None + version: str = Field(default="") seasons: list["Season"] = Relationship(back_populates="show", cascade_delete=True) diff --git a/MediaManager/src/tv/__init__.py b/MediaManager/src/tv/__init__.py index b3b7e7f..3988bf1 100644 --- a/MediaManager/src/tv/__init__.py +++ b/MediaManager/src/tv/__init__.py @@ -1,10 +1,3 @@ import logging -import tmdbsimple as tmdb - -from config import TvConfig - -config = TvConfig() log = logging.getLogger(__name__) - -tmdb.API_KEY = config.api_key diff --git a/MediaManager/src/tv/router.py b/MediaManager/src/tv/router.py index 135e515..0d17e15 100644 --- a/MediaManager/src/tv/router.py +++ b/MediaManager/src/tv/router.py @@ -1,6 +1,5 @@ import json import pprint -from typing import List from uuid import UUID import psycopg.errors @@ -8,17 +7,17 @@ from fastapi import APIRouter, Depends, status from fastapi.responses import JSONResponse from pydantic import BaseModel from sqlmodel import select -from tmdbsimple import TV, TV_Seasons import auth import dowloadClients import indexer +import metadataProvider from database import SessionDependency from database.torrents import Torrent -from database.tv import Episode, Season, Show +from database.tv import Season, Show from indexer import IndexerQueryResult from routers.users import Message -from tv import log, tmdb +from tv import log router = APIRouter( prefix="/tv", @@ -35,48 +34,19 @@ class ShowDetails(BaseModel): status.HTTP_201_CREATED: {"model": Show, "description": "Successfully created show"}, status.HTTP_409_CONFLICT: {"model": Message, "description": "Show already exists"}, }) -def add_show(db: SessionDependency, show_id: int, metadata_provider: str = "tmdb"): - show_metadata = TV(show_id).info() +def add_show(db: SessionDependency, show_id: int, metadata_provider: str = "tmdb", version: str = ""): + res = db.exec(select(Show). + where(Show.external_id == show_id). + where(Show.metadata_provider == metadata_provider). + where(Show.version == version)).first() - # For some shows the first_air_date isn't known, therefore it needs to be nullable - year: str | None = show_metadata["first_air_date"] - if year: - year: int = int(year.split('-')[0]) - else: - year = None - - show = Show( - external_id=show_id, - metadata_provider=metadata_provider, - name=show_metadata["name"], - overview=show_metadata["overview"], - year=year, - ) + if res is not None: + return JSONResponse(status_code=status.HTTP_409_CONFLICT, content={"message": "Show already exists"}) + show = metadataProvider.get_show_metadata(id=show_id, provider=metadata_provider) + show.version = version log.info("Adding show: " + json.dumps(show.model_dump(), default=str)) db.add(show) - db.commit() - - for season in show_metadata["seasons"]: - season_metadata = TV_Seasons(tv_id=show_metadata["id"], season_number=season["season_number"]).info() - db.add(Season( - show_id=show.id, - number=int(season_metadata["season_number"]), - name=season_metadata["name"], - overview=season_metadata["overview"], - external_id=int(season_metadata["id"])) - ) - db.commit() - - for episode in season_metadata["episodes"]: - db.add(Episode( - show_id=show.id, - season_number=int(season_metadata["season_number"]), - title=episode["name"], - number=int(episode["episode_number"]), - external_id=int(episode["id"]), - )) - try: db.commit() db.refresh(show) @@ -151,8 +121,17 @@ def get_season_torrents(db: SessionDependency, show_id: UUID, season_id: UUID): @router.post("/{show_id}/torrent", status_code=status.HTTP_200_OK, dependencies=[Depends( auth.get_current_user)], response_model=list[Season]) -def download_seasons_torrent(db: SessionDependency, show_id: UUID, torrent: IndexerQueryResult, ): - seasons: list[Season] = [] +def download_seasons_torrent(db: SessionDependency, show_id: UUID, torrent_id: UUID): + """ + downloads torrents for a show season, links the torrent for all seasons the torrent contains + + """ + torrent = db.get(Torrent, torrent_id) + + if torrent is None: + return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": "Torrent not found"}) + + seasons = [] for season_number in torrent.season: seasons.append( db.exec(select(Season) @@ -172,13 +151,55 @@ def download_seasons_torrent(db: SessionDependency, show_id: UUID, torrent: Inde return seasons -@router.get("/", dependencies=[Depends(auth.get_current_user)], response_model=List[Show]) +@router.post("/{show_id}/{season_id}/torrent", status_code=status.HTTP_200_OK, dependencies=[Depends( + auth.get_current_user)], response_model=list[Season]) +def download_seasons_torrent(db: SessionDependency, show_id: UUID, season_id: UUID, torrent_id: UUID): + """ + downloads torrents for a season, links the torrent only to the specified season + this means that multiple torrents can contain a season but you can choose from one which the content should be + imported + + """ + torrent = db.get(Torrent, torrent_id) + + if torrent is None: + return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, content={"message": "Torrent not found"}) + + seasons = [] + for season_number in torrent.season: + seasons.append( + db.exec(select(Season) + .where(Season.show_id == show_id) + .where(Season.number == season_number) + ).first() + ) + + torrent = torrent.download() + + dowloadClients.client.download(Torrent) + + for season in seasons: + season.requested = True + season.torrent_id = torrent.id + + return seasons + + +@router.get("/", dependencies=[Depends(auth.get_current_user)], response_model=list[Show]) def get_shows(db: SessionDependency): + """""" return db.exec(select(Show)).unique().fetchall() @router.get("/{show_id}", dependencies=[Depends(auth.get_current_user)], response_model=ShowDetails) def get_show(db: SessionDependency, show_id: UUID): + """ + + :param show_id: + :type show_id: + :return: + :rtype: + """ shows = db.execute(select(Show, Season).where(Show.id == show_id).join(Season).order_by(Season.number)).fetchall() seasons = [] for show in shows: @@ -191,5 +212,11 @@ def get_show(db: SessionDependency, show_id: UUID): @router.get("/search") def search_show(query: str): + """ + Searches for shows by title in the metadata provider. + :param query: The search query string. + :param db: The database connection object. + :return: A list of show objects that match the query. + """ search = tmdb.Search() return search.tv(query=query)