Téléchargements : suppression par épisode et regroupement par animé

- DELETE /api/downloads/{id} (?delete_file=true pour effacer aussi le fichier)
- Événement SSE « removed » → toutes les pages ouvertes se synchronisent
- Page Téléchargements groupée par animé : groupes repliables (dépliés si
  actifs), compteurs par statut, taille totale, tri par numéro d'épisode
- Actions par groupe : 🧹 retirer les terminés, ✖ annuler les actifs
- Limite de liste 200 → 2000 tâches
This commit is contained in:
Roman
2026-09-22 12:17:27 +00:00
parent affc97c527
commit 9500a84a5e
7 changed files with 283 additions and 58 deletions
+9 -2
View File
@@ -64,6 +64,13 @@ async def cancel(download_id: int) -> dict:
await download_manager.cancel(download_id)
return {"ok": True}
@router.delete("/{download_id}")
async def delete_download(download_id: int, delete_file: bool = False) -> dict:
"""Retire une tâche de la file ; delete_file=true efface aussi le fichier."""
if not await download_manager.delete(download_id, delete_file=delete_file):
raise HTTPException(404, detail="Téléchargement introuvable")
return {"ok": True}
@router.post("/cancel-all")
async def cancel_all() -> dict:
@@ -81,7 +88,7 @@ async def events() -> EventSourceResponse:
async def stream() -> AsyncIterator[dict]:
yield {"data": json.dumps({"type": "snapshot", "items": await download_manager.list_all()})}
async for update in download_manager.subscribe():
yield {"data": json.dumps({"type": "update", "item": update}, default=str)}
async for message in download_manager.subscribe():
yield {"data": json.dumps(message, default=str)}
return EventSourceResponse(stream())