Files
ohm_streaming/app/routers/router_watchlist.py
T
root 87f245d3fc
CI / Test (Python 3.11) (pull_request) Has been cancelled
CI / Test (Python 3.12) (pull_request) Has been cancelled
CI / Lint (pull_request) Has been cancelled
CI / Type Check (pull_request) Has been cancelled
CI / Summary (pull_request) Has been cancelled
feat: flat design Sunset Glitch + download manager + settings + recommendations overhaul
- Sunset Glitch color palette applied to all templates
- Font Awesome icons throughout UI
- Download manager with parallel queue and progress tracking
- Settings page with dynamic configuration
- Recommendations router enhanced with scoring
- Local vendor libs (Alpine.js, HTMX) for offline support
- Auto test suite with screenshots
- Series releases list component
- New download model
2026-04-11 19:30:32 +00:00

246 lines
7.2 KiB
Python

"""
Watchlist management routes for Ohm Stream Downloader API.
"""
import re
import json
from typing import List, Optional
from fastapi import (
APIRouter,
BackgroundTasks,
Depends,
HTTPException,
Response,
Request,
Query,
)
from fastapi.templating import Jinja2Templates
from app.download_manager import DownloadManager
from app.downloaders import get_downloader
from app.models import DownloadRequest
from app.models.auth import User
from app.models.watchlist import (
WatchlistItem,
WatchlistItemCreate,
WatchlistItemUpdate,
WatchlistSettings,
WatchlistStatus,
)
from app.routers.router_auth import get_current_user_from_token, get_optional_user
router = APIRouter(prefix="/api/watchlist", tags=["watchlist"])
templates = Jinja2Templates(directory="templates")
def get_download_manager() -> DownloadManager:
from main import download_manager
return download_manager
@router.post("", response_model=WatchlistItem)
async def add_to_watchlist(
item_data: WatchlistItemCreate,
response: Response,
current_user: User = Depends(get_current_user_from_token),
):
"""Add an anime to the watchlist"""
from app.watchlist import watchlist_manager
try:
existing = watchlist_manager.get_by_anime_url(
item_data.anime_url, current_user.id
)
item = watchlist_manager.add(current_user.id, item_data)
msg = (
f"'{item.anime_title}' ajouté à la watchlist"
if not existing
else f"'{item.anime_title}' est déjà dans la watchlist"
)
toast_type = "success" if not existing else "info"
response.headers["HX-Trigger"] = json.dumps(
{"show-toast": {"message": msg, "type": toast_type}}
)
return item
except Exception as e:
from logging import getLogger
logger = getLogger(__name__)
logger.error(f"Error adding to watchlist: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("")
async def get_watchlist(
request: Request,
status: Optional[WatchlistStatus] = None,
html: bool = Query(False),
current_user: Optional[User] = Depends(get_optional_user),
):
from app.watchlist import watchlist_manager
is_htmx = request.headers.get("HX-Request")
if current_user is None and (html or is_htmx):
return templates.TemplateResponse(
"components/login_prompt.html", {"request": request}
)
if current_user is None:
raise HTTPException(status_code=401, detail="Authentication required")
items = watchlist_manager.get_all(user_id=current_user.id, status=status)
if html or is_htmx:
return templates.TemplateResponse(
"components/watchlist_items_list.html", {"request": request, "items": items}
)
return items
@router.get("/settings", response_model=WatchlistSettings)
async def get_watchlist_settings(
current_user: User = Depends(get_current_user_from_token),
):
"""Get global watchlist settings"""
from app.watchlist import watchlist_manager
return watchlist_manager.get_settings()
@router.put("/settings", response_model=WatchlistSettings)
async def update_watchlist_settings(
settings: WatchlistSettings,
response: Response,
current_user: User = Depends(get_current_user_from_token),
):
"""Update global watchlist settings"""
from app.auto_download_scheduler import auto_download_scheduler
from app.watchlist import watchlist_manager
try:
updated_settings = watchlist_manager.update_settings(settings)
if auto_download_scheduler.is_running():
auto_download_scheduler.update_interval(
updated_settings.check_interval_hours
)
response.headers["HX-Trigger"] = json.dumps(
{
"show-toast": {
"message": "Paramètres de la watchlist mis à jour",
"type": "success",
}
}
)
return updated_settings
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/{item_id}", response_model=WatchlistItem)
async def get_watchlist_item(
item_id: str,
current_user: User = Depends(get_current_user_from_token),
):
"""Get a specific watchlist item"""
from app.watchlist import watchlist_manager
item = watchlist_manager.get_by_id(item_id)
if not item or item.user_id != current_user.id:
raise HTTPException(status_code=404, detail="Item not found")
return item
@router.put("/{item_id}", response_model=WatchlistItem)
async def update_watchlist_item(
item_id: str,
update_data: WatchlistItemUpdate,
response: Response,
current_user: User = Depends(get_current_user_from_token),
):
"""Update a watchlist item"""
from app.watchlist import watchlist_manager
item = watchlist_manager.get_by_id(item_id)
if not item or item.user_id != current_user.id:
raise HTTPException(status_code=404, detail="Item not found")
updated_item = watchlist_manager.update(item_id, update_data)
response.headers["HX-Trigger"] = json.dumps(
{
"show-toast": {
"message": f"'{updated_item.anime_title}' mis à jour",
"type": "success",
}
}
)
return updated_item
@router.delete("/{item_id}")
async def delete_from_watchlist(
item_id: str,
response: Response,
current_user: User = Depends(get_current_user_from_token),
):
"""Remove an anime from the watchlist"""
from app.watchlist import watchlist_manager
item = watchlist_manager.get_by_id(item_id)
if not item or item.user_id != current_user.id:
raise HTTPException(status_code=404, detail="Item not found")
title = item.anime_title
if watchlist_manager.delete(item_id):
response.headers["HX-Trigger"] = json.dumps(
{
"show-toast": {
"message": f"'{title}' supprimé de la watchlist",
"type": "info",
}
}
)
# HTMX will handle removing the element if target is specified in the frontend
return Response(status_code=204)
raise HTTPException(status_code=500, detail="Failed to delete item")
@router.post("/check")
async def check_watchlist_now(
background_tasks: BackgroundTasks,
response: Response,
current_user: User = Depends(get_current_user_from_token),
):
"""Trigger an immediate check for new episodes"""
from app.auto_download_scheduler import auto_download_scheduler
background_tasks.add_task(auto_download_scheduler.trigger_check_now)
response.headers["HX-Trigger"] = json.dumps(
{
"show-toast": {
"message": "Vérification de la watchlist lancée en arrière-plan",
"type": "info",
}
}
)
return {"status": "success", "message": "Check triggered"}
@router.get("/stats/summary")
async def get_watchlist_stats(
current_user: User = Depends(get_current_user_from_token),
):
"""Get watchlist statistics for the user"""
from app.watchlist import watchlist_manager
return watchlist_manager.get_stats(current_user.id)