mirror of
https://github.com/markbeep/AudioBookRequest.git
synced 2026-05-08 01:09:28 -05:00
310 lines
9.4 KiB
Python
310 lines
9.4 KiB
Python
from typing import Annotated, Optional
|
|
|
|
import sqlalchemy as sa
|
|
from aiohttp import ClientSession
|
|
from fastapi import (
|
|
APIRouter,
|
|
BackgroundTasks,
|
|
Depends,
|
|
Form,
|
|
HTTPException,
|
|
Query,
|
|
Request,
|
|
)
|
|
from sqlmodel import Session, col, select
|
|
|
|
from app.internal import book_search
|
|
from app.internal.book_search import (
|
|
audible_region_type,
|
|
audible_regions,
|
|
get_audnexus_book,
|
|
list_audible_books,
|
|
)
|
|
from app.internal.models import (
|
|
BookRequest,
|
|
BookSearchResult,
|
|
EventEnum,
|
|
GroupEnum,
|
|
ManualBookRequest,
|
|
Notification,
|
|
)
|
|
from app.internal.notifications import (
|
|
send_all_notifications,
|
|
send_manual_notification,
|
|
)
|
|
from app.internal.prowlarr.prowlarr import prowlarr_config
|
|
from app.internal.query import query_sources
|
|
from app.internal.ranking.quality import quality_config
|
|
from app.routers.wishlist import get_wishlist_books
|
|
from app.internal.auth.auth import DetailedUser, get_authenticated_user
|
|
from app.util.connection import get_connection
|
|
from app.util.db import get_session, open_session
|
|
from app.util.templates import template_response
|
|
|
|
router = APIRouter(prefix="/search")
|
|
|
|
|
|
def get_already_requested(session: Session, results: list[BookRequest], username: str):
|
|
books: list[BookSearchResult] = []
|
|
if len(results) > 0:
|
|
# check what books are already requested by the user
|
|
asins = {book.asin for book in results}
|
|
requested_books = set(
|
|
session.exec(
|
|
select(BookRequest.asin).where(
|
|
col(BookRequest.asin).in_(asins),
|
|
BookRequest.user_username == username,
|
|
)
|
|
).all()
|
|
)
|
|
|
|
for book in results:
|
|
book_search = BookSearchResult.model_validate(book)
|
|
if book.asin in requested_books:
|
|
book_search.already_requested = True
|
|
books.append(book_search)
|
|
return books
|
|
|
|
|
|
@router.get("")
|
|
async def read_search(
|
|
request: Request,
|
|
user: Annotated[DetailedUser, Depends(get_authenticated_user())],
|
|
client_session: Annotated[ClientSession, Depends(get_connection)],
|
|
session: Annotated[Session, Depends(get_session)],
|
|
query: Annotated[Optional[str], Query(alias="q")] = None,
|
|
num_results: int = 20,
|
|
page: int = 0,
|
|
region: audible_region_type = "us",
|
|
):
|
|
if audible_regions.get(region) is None:
|
|
raise HTTPException(status_code=400, detail="Invalid region")
|
|
if query:
|
|
results = await list_audible_books(
|
|
session=session,
|
|
client_session=client_session,
|
|
query=query,
|
|
num_results=num_results,
|
|
page=page,
|
|
audible_region=region,
|
|
)
|
|
else:
|
|
results = []
|
|
|
|
books: list[BookSearchResult] = []
|
|
if len(results) > 0:
|
|
books = get_already_requested(session, results, user.username)
|
|
|
|
prowlarr_configured = prowlarr_config.is_valid(session)
|
|
|
|
return template_response(
|
|
"search.html",
|
|
request,
|
|
user,
|
|
{
|
|
"search_term": query or "",
|
|
"search_results": books,
|
|
"regions": audible_regions,
|
|
"selected_region": region,
|
|
"page": page,
|
|
"auto_start_download": quality_config.get_auto_download(session)
|
|
and user.is_above(GroupEnum.trusted),
|
|
"prowlarr_configured": prowlarr_configured,
|
|
},
|
|
)
|
|
|
|
|
|
@router.get("/suggestions")
|
|
async def search_suggestions(
|
|
request: Request,
|
|
user: Annotated[DetailedUser, Depends(get_authenticated_user())],
|
|
query: Annotated[str, Query(alias="q")],
|
|
region: audible_region_type = "us",
|
|
):
|
|
async with ClientSession() as client_session:
|
|
suggestions = await book_search.get_search_suggestions(
|
|
client_session, query, region
|
|
)
|
|
return template_response(
|
|
"search.html",
|
|
request,
|
|
user,
|
|
{"suggestions": suggestions},
|
|
block_name="search_suggestions",
|
|
)
|
|
|
|
|
|
async def background_start_query(asin: str, requester_username: str):
|
|
with open_session() as session:
|
|
async with ClientSession() as client_session:
|
|
await query_sources(
|
|
asin=asin,
|
|
session=session,
|
|
client_session=client_session,
|
|
start_auto_download=True,
|
|
requester_username=requester_username,
|
|
)
|
|
|
|
|
|
@router.post("/request/{asin}")
|
|
async def add_request(
|
|
request: Request,
|
|
asin: str,
|
|
user: Annotated[DetailedUser, Depends(get_authenticated_user())],
|
|
session: Annotated[Session, Depends(get_session)],
|
|
client_session: Annotated[ClientSession, Depends(get_connection)],
|
|
background_task: BackgroundTasks,
|
|
query: Annotated[Optional[str], Form()],
|
|
page: Annotated[int, Form()],
|
|
region: Annotated[audible_region_type, Form()],
|
|
num_results: Annotated[int, Form()] = 20,
|
|
):
|
|
book = await get_audnexus_book(client_session, asin, region)
|
|
if not book:
|
|
raise HTTPException(status_code=404, detail="Book not found")
|
|
|
|
book.user_username = user.username
|
|
try:
|
|
session.add(book)
|
|
session.commit()
|
|
except sa.exc.IntegrityError:
|
|
pass # ignore if already exists
|
|
|
|
if quality_config.get_auto_download(session) and user.is_above(GroupEnum.trusted):
|
|
# start querying and downloading if auto download is enabled
|
|
background_task.add_task(
|
|
background_start_query, asin=asin, requester_username=user.username
|
|
)
|
|
|
|
background_task.add_task(
|
|
send_all_notifications,
|
|
event_type=EventEnum.on_new_request,
|
|
requester_username=user.username,
|
|
book_asin=asin,
|
|
)
|
|
|
|
if audible_regions.get(region) is None:
|
|
raise HTTPException(status_code=400, detail="Invalid region")
|
|
if query:
|
|
results = await list_audible_books(
|
|
session=session,
|
|
client_session=client_session,
|
|
query=query,
|
|
num_results=num_results,
|
|
page=page,
|
|
audible_region=region,
|
|
)
|
|
else:
|
|
results = []
|
|
|
|
books: list[BookSearchResult] = []
|
|
if len(results) > 0:
|
|
books = get_already_requested(session, results, user.username)
|
|
|
|
prowlarr_configured = prowlarr_config.is_valid(session)
|
|
|
|
return template_response(
|
|
"search.html",
|
|
request,
|
|
user,
|
|
{
|
|
"search_term": query or "",
|
|
"search_results": books,
|
|
"regions": audible_regions,
|
|
"selected_region": region,
|
|
"page": page,
|
|
"auto_start_download": quality_config.get_auto_download(session)
|
|
and user.is_above(GroupEnum.trusted),
|
|
"prowlarr_configured": prowlarr_configured,
|
|
},
|
|
block_name="book_results",
|
|
)
|
|
|
|
|
|
@router.delete("/request/{asin}")
|
|
async def delete_request(
|
|
request: Request,
|
|
asin: str,
|
|
admin_user: Annotated[
|
|
DetailedUser, Depends(get_authenticated_user(GroupEnum.admin))
|
|
],
|
|
session: Annotated[Session, Depends(get_session)],
|
|
downloaded: Optional[bool] = None,
|
|
):
|
|
books = session.exec(select(BookRequest).where(BookRequest.asin == asin)).all()
|
|
if books:
|
|
[session.delete(b) for b in books]
|
|
session.commit()
|
|
|
|
books = get_wishlist_books(
|
|
session, None, "downloaded" if downloaded else "not_downloaded"
|
|
)
|
|
|
|
return template_response(
|
|
"wishlist_page/wishlist.html",
|
|
request,
|
|
admin_user,
|
|
{"books": books, "page": "downloaded" if downloaded else "wishlist"},
|
|
block_name="book_wishlist",
|
|
)
|
|
|
|
|
|
@router.get("/manual")
|
|
async def read_manual(
|
|
request: Request,
|
|
user: Annotated[DetailedUser, Depends(get_authenticated_user())],
|
|
session: Annotated[Session, Depends(get_session)],
|
|
):
|
|
auto_download = quality_config.get_auto_download(session)
|
|
return template_response(
|
|
"manual.html", request, user, {"auto_download": auto_download}
|
|
)
|
|
|
|
|
|
@router.post("/manual")
|
|
async def add_manual(
|
|
request: Request,
|
|
user: Annotated[DetailedUser, Depends(get_authenticated_user())],
|
|
session: Annotated[Session, Depends(get_session)],
|
|
background_task: BackgroundTasks,
|
|
title: Annotated[str, Form()],
|
|
author: Annotated[str, Form()],
|
|
narrator: Annotated[Optional[str], Form()] = None,
|
|
subtitle: Annotated[Optional[str], Form()] = None,
|
|
publish_date: Annotated[Optional[str], Form()] = None,
|
|
info: Annotated[Optional[str], Form()] = None,
|
|
):
|
|
book_request = ManualBookRequest(
|
|
user_username=user.username,
|
|
title=title,
|
|
authors=author.split(","),
|
|
narrators=narrator.split(",") if narrator else [],
|
|
subtitle=subtitle,
|
|
publish_date=publish_date,
|
|
additional_info=info,
|
|
)
|
|
session.add(book_request)
|
|
session.flush()
|
|
session.expunge_all() # so that we can pass down the object without the session
|
|
session.commit()
|
|
|
|
notifications = session.exec(
|
|
select(Notification).where(Notification.event == EventEnum.on_new_request)
|
|
).all()
|
|
for notif in notifications:
|
|
background_task.add_task(
|
|
send_manual_notification,
|
|
notification=notif,
|
|
book=book_request,
|
|
requester_username=user.username,
|
|
)
|
|
auto_download = quality_config.get_auto_download(session)
|
|
|
|
return template_response(
|
|
"manual.html",
|
|
request,
|
|
user,
|
|
{"success": "Successfully added request", "auto_download": auto_download},
|
|
block_name="form",
|
|
)
|