mirror of
https://github.com/maxdorninger/MediaManager.git
synced 2026-04-22 16:55:36 +02:00
feat: implement season requests management with CRUD operations and enhance UI components
This commit is contained in:
@@ -3,6 +3,7 @@ from uuid import UUID
|
||||
from sqlalchemy import ForeignKey, PrimaryKeyConstraint, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from auth.db import User
|
||||
from backend.src.database import Base
|
||||
from torrent.models import Quality
|
||||
|
||||
@@ -36,6 +37,7 @@ class Season(Base):
|
||||
episodes: Mapped[list["Episode"]] = relationship(back_populates="season", cascade="all, delete")
|
||||
|
||||
season_files = relationship("SeasonFile", back_populates="season", cascade="all, delete")
|
||||
season_requests = relationship("SeasonRequest", back_populates="season", cascade="all, delete")
|
||||
|
||||
|
||||
class Episode(Base):
|
||||
@@ -74,6 +76,10 @@ class SeasonRequest(Base):
|
||||
season_id: Mapped[UUID] = mapped_column(ForeignKey(column="season.id", ondelete="CASCADE"), )
|
||||
wanted_quality: Mapped[Quality]
|
||||
min_quality: Mapped[Quality]
|
||||
requested_by: Mapped[UUID | None] = mapped_column(ForeignKey(column="user.id", ondelete="SET NULL"), )
|
||||
requested_by_id: Mapped[UUID | None] = mapped_column(ForeignKey(column="user.id", ondelete="SET NULL"), )
|
||||
authorized: Mapped[bool] = mapped_column(default=False)
|
||||
authorized_by: Mapped[UUID | None] = mapped_column(ForeignKey(column="user.id", ondelete="SET NULL"), )
|
||||
authorized_by_id: Mapped[UUID | None] = mapped_column(ForeignKey(column="user.id", ondelete="SET NULL"), )
|
||||
|
||||
requested_by: Mapped["User|None"] = relationship(foreign_keys=[requested_by_id], uselist=False)
|
||||
authorized_by: Mapped["User|None"] = relationship(foreign_keys=[authorized_by_id], uselist=False)
|
||||
season = relationship("Season", back_populates="season_requests", uselist=False)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import pprint
|
||||
|
||||
from sqlalchemy import select, delete, update
|
||||
from sqlalchemy import select, delete
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
@@ -121,12 +119,13 @@ def add_season_to_requested_list(season_request: SeasonRequestSchema, db: Sessio
|
||||
db.commit()
|
||||
|
||||
|
||||
def remove_season_from_requested_list(season_request: SeasonRequestSchema, db: Session) -> None:
|
||||
def delete_season_request(season_request_id: SeasonRequestId, db: Session) -> None:
|
||||
"""
|
||||
Removes a Season from the SeasonRequest table, which removes it from the 'requested' list.
|
||||
|
||||
"""
|
||||
db.delete(SeasonRequest(**season_request.model_dump()))
|
||||
stmt = delete(SeasonRequest).where(SeasonRequest.id == season_request_id)
|
||||
db.execute(stmt)
|
||||
db.commit()
|
||||
|
||||
|
||||
@@ -138,10 +137,16 @@ def get_season_by_number(db: Session, season_number: int, show_id: ShowId) -> Se
|
||||
|
||||
|
||||
def get_season_requests(db: Session) -> list[RichSeasonRequestSchema]:
|
||||
stmt = select(SeasonRequest).join(Season, Season.id == SeasonRequest.season_id).join(Show,
|
||||
Season.show_id == Show.id)
|
||||
result = db.execute(stmt).scalars().all()
|
||||
return [RichSeasonRequestSchema.model_validate(season) for season in result]
|
||||
stmt = select(SeasonRequest).options(joinedload(SeasonRequest.requested_by),
|
||||
joinedload(SeasonRequest.authorized_by),
|
||||
joinedload(SeasonRequest.season).joinedload(Season.show))
|
||||
result = db.execute(stmt).scalars().unique().all()
|
||||
return [RichSeasonRequestSchema(min_quality=x.min_quality,
|
||||
wanted_quality=x.wanted_quality, show=x.season.show, season=x.season,
|
||||
requested_by=x.requested_by, authorized_by=x.authorized_by, authorized=x.authorized,
|
||||
id=x.id)
|
||||
for x in result]
|
||||
|
||||
|
||||
def add_season_file(db: Session, season_file: SeasonFileSchema) -> SeasonFileSchema:
|
||||
db.add(SeasonFile(**season_file.model_dump()))
|
||||
@@ -209,5 +214,3 @@ def update_season_request(db: Session, season_request: SeasonRequestSchema) -> N
|
||||
db.delete(db.get(SeasonRequest, season_request.id))
|
||||
db.add(SeasonRequest(**season_request.model_dump()))
|
||||
db.commit()
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from fastapi.responses import JSONResponse
|
||||
import tv.repository
|
||||
import tv.service
|
||||
from auth.db import User
|
||||
from auth.schemas import UserRead
|
||||
from auth.users import current_active_user, current_superuser
|
||||
from backend.src.database import DbSessionDependency
|
||||
from indexer.schemas import PublicIndexerQueryResult, IndexerQueryResultId
|
||||
@@ -13,7 +14,7 @@ from metadataProvider.schemas import MetaDataProviderShowSearchResult
|
||||
from torrent.schemas import Torrent
|
||||
from tv.exceptions import MediaAlreadyExists
|
||||
from tv.schemas import Show, SeasonRequest, ShowId, RichShowTorrent, PublicShow, PublicSeasonFile, SeasonNumber, \
|
||||
CreateSeasonRequest, SeasonRequestId, UpdateSeasonRequest, RichSeasonRequest, SeasonId
|
||||
CreateSeasonRequest, SeasonRequestId, UpdateSeasonRequest, RichSeasonRequest
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -82,50 +83,51 @@ def get_season_files(db: DbSessionDependency, season_number: SeasonNumber, show_
|
||||
# MANAGE REQUESTS
|
||||
# --------------------------------
|
||||
|
||||
@router.post("/seasons/requests", status_code=status.HTTP_200_OK, response_model=RichSeasonRequest)
|
||||
@router.post("/seasons/requests", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def request_a_season(db: DbSessionDependency, user: Annotated[User, Depends(current_active_user)],
|
||||
season_request: CreateSeasonRequest):
|
||||
"""
|
||||
adds request flag to a season
|
||||
"""
|
||||
request: SeasonRequest = SeasonRequest.model_validate(season_request)
|
||||
request.requested_by = user.id
|
||||
request.requested_by = UserRead.model_validate(user)
|
||||
|
||||
tv.service.request_season(db=db, season_request=request)
|
||||
return request
|
||||
return
|
||||
|
||||
|
||||
@router.get("/seasons/requests", status_code=status.HTTP_200_OK, dependencies=[Depends(current_active_user)])
|
||||
def get_requested_seasons(db: DbSessionDependency) -> list[SeasonRequest]:
|
||||
@router.get("/seasons/requests", status_code=status.HTTP_200_OK, dependencies=[Depends(current_active_user)],
|
||||
response_model=list[RichSeasonRequest])
|
||||
def get_season_requests(db: DbSessionDependency) -> list[RichSeasonRequest]:
|
||||
return tv.service.get_all_season_requests(db=db)
|
||||
|
||||
|
||||
@router.patch("/seasons/requests/{season_request_id}", status_code=status.HTTP_200_OK, response_model=SeasonRequest)
|
||||
@router.patch("/seasons/requests/{season_request_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def authorize_request(db: DbSessionDependency, user: Annotated[User, Depends(current_superuser)],
|
||||
season_request_id: SeasonRequestId, authorized_status: bool = False):
|
||||
"""
|
||||
updates the request flag to true
|
||||
"""
|
||||
season_request: SeasonRequest = tv.repository.get_season_request(db=db, season_request_id=season_request_id)
|
||||
season_request.authorized_by = user.id
|
||||
season_request.authorized_by = UserRead.model_validate(user)
|
||||
season_request.authorized = authorized_status
|
||||
tv.service.update_season_request(db=db, season_request=season_request)
|
||||
return season_request
|
||||
return
|
||||
|
||||
|
||||
@router.put("/seasons/requests/{season_request_id}", status_code=status.HTTP_200_OK, response_model=SeasonRequest)
|
||||
@router.put("/seasons/requests/{season_request_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def update_request(db: DbSessionDependency, user: Annotated[User, Depends(current_superuser)],
|
||||
season_request: UpdateSeasonRequest):
|
||||
season_request: SeasonRequest = SeasonRequest.model_validate(season_request)
|
||||
season_request.requested_by = user.id
|
||||
season_request.requested_by = UserRead.model_validate(user)
|
||||
tv.service.update_season_request(db=db, season_request=season_request)
|
||||
return season_request
|
||||
return
|
||||
|
||||
|
||||
@router.delete("/seasons/requests/{season_request_id}", status_code=status.HTTP_204_NO_CONTENT,
|
||||
dependencies=[Depends(current_active_user)])
|
||||
def delete_season_request(db: DbSessionDependency, request: SeasonRequest):
|
||||
tv.service.unrequest_season(db=db, season_request=request)
|
||||
def delete_season_request(db: DbSessionDependency, request_id: SeasonRequestId):
|
||||
tv.service.delete_season_request(db=db, season_request_id=request_id)
|
||||
|
||||
|
||||
# --------------------------------
|
||||
|
||||
@@ -5,6 +5,7 @@ from uuid import UUID
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from tvdb_v4_official import Request
|
||||
|
||||
from auth.schemas import UserRead
|
||||
from torrent.models import Quality
|
||||
from torrent.schemas import TorrentId, TorrentStatus
|
||||
|
||||
@@ -55,12 +56,12 @@ class Show(BaseModel):
|
||||
|
||||
|
||||
class SeasonRequestBase(BaseModel):
|
||||
season_id: SeasonId
|
||||
min_quality: Quality
|
||||
wanted_quality: Quality
|
||||
|
||||
|
||||
class CreateSeasonRequest(SeasonRequestBase):
|
||||
season_id: SeasonId
|
||||
pass
|
||||
|
||||
|
||||
@@ -72,17 +73,15 @@ class SeasonRequest(SeasonRequestBase):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: SeasonRequestId = Field(default_factory=uuid.uuid4)
|
||||
season: Season
|
||||
|
||||
requested_by: UUID | None = None
|
||||
requested_by: UserRead | None = None
|
||||
authorized: bool = False
|
||||
authorized_by: UUID | None = None
|
||||
authorized_by: UserRead | None = None
|
||||
|
||||
|
||||
class RichSeasonRequest(SeasonRequest):
|
||||
show_id: ShowId
|
||||
show_name: str
|
||||
show_year: int | None
|
||||
season_number: SeasonNumber
|
||||
show: Show
|
||||
|
||||
class SeasonFile(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -15,7 +15,7 @@ from tv import log
|
||||
from tv.exceptions import MediaAlreadyExists
|
||||
from tv.repository import add_season_file, get_season_files_by_season_id
|
||||
from tv.schemas import Show, ShowId, SeasonRequest, SeasonFile, SeasonId, Season, RichShowTorrent, RichSeasonTorrent, \
|
||||
PublicSeason, PublicShow, PublicSeasonFile, SeasonNumber, SeasonRequestId
|
||||
PublicSeason, PublicShow, PublicSeasonFile, SeasonNumber, SeasonRequestId, RichSeasonRequest
|
||||
|
||||
|
||||
def add_show(db: Session, external_id: int, metadata_provider: str) -> Show | None:
|
||||
@@ -34,8 +34,9 @@ def request_season(db: Session, season_request: SeasonRequest) -> None:
|
||||
def update_season_request(db: Session, season_request: SeasonRequest) -> None:
|
||||
tv.repository.update_season_request(db=db, season_request=season_request)
|
||||
|
||||
def unrequest_season(db: Session, season_request: SeasonRequest) -> None:
|
||||
tv.repository.remove_season_from_requested_list(db=db, season_request=season_request)
|
||||
|
||||
def delete_season_request(db: Session, season_request_id: SeasonRequestId) -> None:
|
||||
tv.repository.delete_season_request(db=db, season_request_id=season_request_id)
|
||||
|
||||
|
||||
def get_public_season_files_by_season_id(db: Session, season_id: SeasonId) -> list[PublicSeasonFile]:
|
||||
@@ -148,7 +149,7 @@ def get_season(db: Session, season_id: SeasonId) -> Season:
|
||||
return tv.repository.get_season(season_id=season_id, db=db)
|
||||
|
||||
|
||||
def get_all_season_requests(db: Session) -> list[SeasonRequest]:
|
||||
def get_all_season_requests(db: Session) -> list[RichSeasonRequest]:
|
||||
return tv.repository.get_season_requests(db=db)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user