5c7116557d
- Integrated HTMX for server-driven UI updates and fragments - Adopted Alpine.js for global reactive state and tab management - Replaced legacy player with Plyr.io for premium streaming experience - Implemented real-time download polling via HTMX - Added server-sent Toast notification system - Fixed navigation and authentication scoping issues
185 lines
6.4 KiB
Python
185 lines
6.4 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
|
|
|
|
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 main 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("", response_model=List[WatchlistItem])
|
|
async def get_watchlist(
|
|
request: Request,
|
|
status: Optional[WatchlistStatus] = None,
|
|
html: bool = Query(False),
|
|
current_user: User = Depends(get_current_user_from_token),
|
|
):
|
|
"""Get user's watchlist (supports HTML for HTMX)"""
|
|
from main import watchlist_manager
|
|
items = watchlist_manager.get_all(user_id=current_user.id, status=status)
|
|
|
|
if html or request.headers.get("HX-Request"):
|
|
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 main 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 main import auto_download_scheduler, 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 main 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 main 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 main 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", response_model=List)
|
|
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 main 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 main import watchlist_manager
|
|
return watchlist_manager.get_stats(current_user.id)
|