Files
wordarr/wordarr/api/requests.py
T

163 lines
5.2 KiB
Python

from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session
from .. import config
from ..db import BookRequest, Library, get_session
from ..importer import tagger
router = APIRouter(prefix="/api/requests", tags=["requests"])
class RequestIn(BaseModel):
library_id: int
title: str
authors: str = ""
narrator: str = ""
external_id: str = ""
year: int | None = None
series: str = ""
volume: int | None = None
cover_url: str = ""
class RequestOut(RequestIn):
id: int
media_type: str
status: str
imported_path: str
library_name: str = ""
class Config:
from_attributes = True
def _out(r: BookRequest) -> RequestOut:
out = RequestOut.model_validate(r)
out.library_name = r.library.name if r.library else ""
return out
@router.get("", response_model=list[RequestOut])
def list_requests(status: str | None = None, media_type: str | None = None,
library_id: int | None = None, session: Session = Depends(get_session)):
stmt = select(BookRequest).order_by(BookRequest.created_at.desc())
if status:
stmt = stmt.where(BookRequest.status == status)
if media_type:
stmt = stmt.where(BookRequest.media_type == media_type)
if library_id:
stmt = stmt.where(BookRequest.library_id == library_id)
return [_out(r) for r in session.scalars(stmt).all()]
@router.post("", response_model=RequestOut)
def create_request(data: RequestIn, session: Session = Depends(get_session)):
lib = session.get(Library, data.library_id)
if not lib:
raise HTTPException(404, "library not found")
req = BookRequest(media_type=lib.media_type, **data.model_dump())
session.add(req)
session.commit()
session.refresh(req)
return _out(req)
class BulkRequestIn(BaseModel):
library_id: int
series: str
authors: str = ""
narrator: str = ""
volume_from: int
volume_to: int
year: int | None = None
cover_url: str = ""
@router.post("/bulk", response_model=list[RequestOut])
def create_bulk(data: BulkRequestIn, session: Session = Depends(get_session)):
lib = session.get(Library, data.library_id)
if not lib:
raise HTTPException(404, "library not found")
if data.volume_from > data.volume_to:
raise HTTPException(400, "volume_from must be <= volume_to")
if data.volume_to - data.volume_from > 999:
raise HTTPException(400, "at most 1000 requests per bulk call")
width = max(2, len(str(data.volume_to)))
reqs = []
for n in range(data.volume_from, data.volume_to + 1):
req = BookRequest(
media_type=lib.media_type,
library_id=lib.id,
title=f"{data.series} Folge {n:0{width}d}",
authors=data.authors,
narrator=data.narrator,
series=data.series,
volume=n,
year=data.year,
cover_url=data.cover_url,
)
session.add(req)
reqs.append(req)
session.commit()
for r in reqs:
session.refresh(r)
return [_out(r) for r in reqs]
@router.put("/{request_id}", response_model=RequestOut)
def update_request(request_id: int, data: RequestIn, session: Session = Depends(get_session)):
req = session.get(BookRequest, request_id)
if not req:
raise HTTPException(404, "request not found")
lib = session.get(Library, data.library_id)
if not lib:
raise HTTPException(404, "library not found")
for field, value in data.model_dump().items():
setattr(req, field, value)
req.media_type = lib.media_type
session.commit()
session.refresh(req)
return _out(req)
@router.post("/{request_id}/retag")
def retag_request(request_id: int, session: Session = Depends(get_session)):
"""Rewrite the audio tags of an already imported request from its current
metadata (e.g. after adding series/volume/narrator later)."""
req = session.get(BookRequest, request_id)
if not req:
raise HTTPException(404, "request not found")
if req.media_type != "audiobook":
raise HTTPException(400, "retag is only supported for audiobooks")
if req.status != "imported" or not req.imported_path:
raise HTTPException(400, "request is not imported yet")
path = Path(req.imported_path)
if path.is_dir():
files = sorted(
f for f in path.iterdir()
if f.is_file() and f.suffix.lower() in config.AUDIOBOOK_EXTENSIONS
)
if not files:
raise HTTPException(404, f"no audio files in {path}")
for i, f in enumerate(files, 1):
tagger.tag_audio(f, req, track=i, total=len(files))
return {"ok": True, "files": len(files)}
if path.is_file():
tagger.tag_audio(path, req)
return {"ok": True, "files": 1}
raise HTTPException(404, f"imported path no longer exists: {path}")
@router.delete("/{request_id}")
def delete_request(request_id: int, session: Session = Depends(get_session)):
req = session.get(BookRequest, request_id)
if not req:
raise HTTPException(404, "request not found")
session.delete(req)
session.commit()
return {"ok": True}