- Route GET /api/downloads/video/{task_id} pour streamer les videos
- Route POST /api/downloads/{task_id}/retry pour relancer les failed
- Route POST /api/downloads/cancel-all pour annuler tous les actifs
- Barre de progression animee (shimmer + pulse)
- Indicateurs visuels par status (bordures colorees)
- Bouton Retry pour telechargements echoues/annules
- Actions groupees (Nettoyer termines, Tout annuler)
- Compteur de telechargements actifs
- hx-on::after-request pour refresh auto
Closes #17, Closes #8
This commit is contained in:
@@ -2,13 +2,15 @@
|
||||
Download management routes for Ohm Stream Downloader API.
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, Request, Response
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.responses import HTMLResponse, FileResponse
|
||||
|
||||
from app.download_manager import DownloadManager
|
||||
from app.models import DownloadRequest
|
||||
from app.models import DownloadRequest, DownloadStatus
|
||||
from app.models.auth import User
|
||||
from app.routers.router_auth import get_current_user_from_token, get_optional_user
|
||||
|
||||
@@ -120,6 +122,73 @@ async def cancel_download(
|
||||
raise HTTPException(status_code=400, detail="Failed to cancel download")
|
||||
|
||||
|
||||
@router.get("/video/{task_id}")
|
||||
async def stream_video(
|
||||
task_id: str,
|
||||
download_manager: DownloadManager = Depends(get_download_manager),
|
||||
current_user: Optional[User] = Depends(get_optional_user),
|
||||
):
|
||||
"""Stream a completed download as video"""
|
||||
if current_user is None:
|
||||
raise HTTPException(status_code=401, detail="Authentication required")
|
||||
task = download_manager.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
if task.status != DownloadStatus.COMPLETED or not task.file_path:
|
||||
raise HTTPException(status_code=400, detail="Download not completed")
|
||||
file_path = Path(task.file_path)
|
||||
if not file_path.exists():
|
||||
raise HTTPException(status_code=404, detail="File not found on disk")
|
||||
media_types = {
|
||||
".mp4": "video/mp4", ".mkv": "video/x-matroska", ".avi": "video/x-msvideo",
|
||||
".webm": "video/webm", ".flv": "video/x-flv",
|
||||
}
|
||||
media_type = media_types.get(file_path.suffix.lower(), "video/mp4")
|
||||
return FileResponse(str(file_path), media_type=media_type)
|
||||
|
||||
|
||||
@router.post("/{task_id}/retry")
|
||||
async def retry_download(
|
||||
task_id: str,
|
||||
background_tasks: BackgroundTasks,
|
||||
response: Response,
|
||||
download_manager: DownloadManager = Depends(get_download_manager),
|
||||
current_user: User = Depends(get_current_user_from_token),
|
||||
):
|
||||
"""Retry a failed or cancelled download"""
|
||||
task = download_manager.get_task(task_id)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
if task.status not in ("failed", "cancelled"):
|
||||
raise HTTPException(status_code=400, detail="Can only retry failed or cancelled downloads")
|
||||
task.status = DownloadStatus.PENDING
|
||||
task.progress = 0.0
|
||||
if hasattr(download_manager, "_process_download"):
|
||||
background_tasks.add_task(download_manager._process_download, task_id)
|
||||
response.headers["HX-Trigger"] = json.dumps(
|
||||
{"show-toast": {"message": "Telechargement relance", "type": "success"}}
|
||||
)
|
||||
return {"status": "retrying"}
|
||||
|
||||
|
||||
@router.post("/cancel-all")
|
||||
async def cancel_all_downloads(
|
||||
response: Response,
|
||||
download_manager: DownloadManager = Depends(get_download_manager),
|
||||
current_user: User = Depends(get_current_user_from_token),
|
||||
):
|
||||
"""Cancel all active downloads"""
|
||||
count = 0
|
||||
for tid, task in list(download_manager.tasks.items()):
|
||||
if task.status in ("downloading", "pending"):
|
||||
download_manager.cancel_download(tid) if hasattr(download_manager, "cancel_download") else download_manager.tasks.pop(tid, None)
|
||||
count += 1
|
||||
response.headers["HX-Trigger"] = json.dumps(
|
||||
{"show-toast": {"message": f"{count} telechargement(s) annule(s)", "type": "info"}}
|
||||
)
|
||||
return {"status": "cancelled", "count": count}
|
||||
|
||||
|
||||
@router.post("/cleanup")
|
||||
async def cleanup_completed(
|
||||
download_manager: DownloadManager = Depends(get_download_manager),
|
||||
|
||||
Reference in New Issue
Block a user