import asyncio import json import logging from fastapi import BackgroundTasks, FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse, StreamingResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pydantic import BaseModel from arte_api import fetch_concerts, invalidate_cache, CATEGORIES from downloader import DownloadManager logging.basicConfig(level=logging.INFO) app = FastAPI(title="Arte-dl") app.mount("/static", StaticFiles(directory="static"), name="static") templates = Jinja2Templates(directory="templates") dm = DownloadManager() # ------------------------------------------------------------------ pages @app.get("/", response_class=HTMLResponse) async def index(request: Request): return templates.TemplateResponse("index.html", {"request": request}) # ------------------------------------------------------------------ API: concerts @app.get("/api/categories") async def api_categories(): return CATEGORIES @app.get("/api/concerts") async def api_concerts(page: int = 1, search: str = "", page_size: int = 24, category: str = ""): return await fetch_concerts(page=page, search=search, page_size=page_size, category=category) @app.post("/api/refresh") async def api_refresh(): count = await invalidate_cache() return {"count": count} # ------------------------------------------------------------------ API: downloads class DownloadRequest(BaseModel): url: str title: str subtitle: str = "" year: int | None = None category: str = "" @app.post("/api/download") async def api_download(req: DownloadRequest, bg: BackgroundTasks): if not req.url: raise HTTPException(status_code=400, detail="url required") dl_id = dm.enqueue(req.url, req.title, req.subtitle, req.year, req.category, bg) return {"id": dl_id} @app.get("/api/downloads") async def api_downloads(): return dm.history() @app.get("/api/progress/{dl_id}") async def api_progress(dl_id: str): async def stream(): while True: s = dm.status(dl_id) yield f"data: {json.dumps(s)}\n\n" if s.get("state") in ("done", "error", "unknown"): break await asyncio.sleep(0.8) return StreamingResponse(stream(), media_type="text/event-stream")