feat: fix auth, provider health checks, search, and redesign UI
- Fix register/login: dict-style access on UserTable ORM objects - Fix HTMX auth: inject JWT token in all HTMX request headers - Fix FS7 search: use DLE AJAX endpoint /engine/ajax/search.php - Fix ZT search: use ?p=series&search=QUERY (not DLE format) - Fix provider health: load hardcoded providers + domain manager - Add self.id to all anime/series providers - Redesign homepage: Netflix-style horizontal scroll cards (.hc) - Redesign search results: grouped by title, poster + synopsis + 3 buttons - Add Télécharger dropdown: season download + episode picker - Fix navbar CSS: restore .tabs flex layout, remove orphan rules - Fix HTMX spinner: remove inline display:none, use CSS indicator - Add AGENTS.md files across project for developer documentation
This commit is contained in:
@@ -6,7 +6,15 @@ import re
|
||||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Response, Request, Query
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
BackgroundTasks,
|
||||
Depends,
|
||||
HTTPException,
|
||||
Response,
|
||||
Request,
|
||||
Query,
|
||||
)
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from app.download_manager import DownloadManager
|
||||
@@ -20,7 +28,7 @@ from app.models.watchlist import (
|
||||
WatchlistSettings,
|
||||
WatchlistStatus,
|
||||
)
|
||||
from app.routers.router_auth import get_current_user_from_token
|
||||
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")
|
||||
@@ -28,6 +36,7 @@ templates = Jinja2Templates(directory="templates")
|
||||
|
||||
def get_download_manager() -> DownloadManager:
|
||||
from main import download_manager
|
||||
|
||||
return download_manager
|
||||
|
||||
|
||||
@@ -41,38 +50,56 @@ async def add_to_watchlist(
|
||||
from main import watchlist_manager
|
||||
|
||||
try:
|
||||
existing = watchlist_manager.get_by_anime_url(item_data.anime_url, current_user.id)
|
||||
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"
|
||||
|
||||
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}})
|
||||
|
||||
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])
|
||||
@router.get("")
|
||||
async def get_watchlist(
|
||||
request: Request,
|
||||
status: Optional[WatchlistStatus] = None,
|
||||
html: bool = Query(False),
|
||||
current_user: User = Depends(get_current_user_from_token),
|
||||
current_user: Optional[User] = Depends(get_optional_user),
|
||||
):
|
||||
"""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"):
|
||||
|
||||
is_htmx = request.headers.get("HX-Request")
|
||||
|
||||
if current_user is None and (html or is_htmx):
|
||||
return templates.TemplateResponse(
|
||||
"components/watchlist_items_list.html",
|
||||
{"request": request, "items": items}
|
||||
"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
|
||||
|
||||
|
||||
@@ -82,6 +109,7 @@ async def get_watchlist_settings(
|
||||
):
|
||||
"""Get global watchlist settings"""
|
||||
from main import watchlist_manager
|
||||
|
||||
return watchlist_manager.get_settings()
|
||||
|
||||
|
||||
@@ -97,9 +125,18 @@ async def update_watchlist_settings(
|
||||
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"}})
|
||||
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))
|
||||
@@ -112,6 +149,7 @@ async def get_watchlist_item(
|
||||
):
|
||||
"""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")
|
||||
@@ -133,8 +171,15 @@ async def update_watchlist_item(
|
||||
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"}})
|
||||
|
||||
response.headers["HX-Trigger"] = json.dumps(
|
||||
{
|
||||
"show-toast": {
|
||||
"message": f"'{updated_item.anime_title}' mis à jour",
|
||||
"type": "success",
|
||||
}
|
||||
}
|
||||
)
|
||||
return updated_item
|
||||
|
||||
|
||||
@@ -153,10 +198,17 @@ async def delete_from_watchlist(
|
||||
|
||||
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"}})
|
||||
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")
|
||||
|
||||
|
||||
@@ -168,10 +220,17 @@ async def check_watchlist_now(
|
||||
):
|
||||
"""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"}})
|
||||
|
||||
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"}
|
||||
|
||||
|
||||
@@ -181,4 +240,5 @@ async def get_watchlist_stats(
|
||||
):
|
||||
"""Get watchlist statistics for the user"""
|
||||
from main import watchlist_manager
|
||||
|
||||
return watchlist_manager.get_stats(current_user.id)
|
||||
|
||||
Reference in New Issue
Block a user