feat: auto-téléchargement par catégorie avec souscription persistante
Docker / docker (push) Has been cancelled
Docker / docker (push) Has been cancelled
- Bouton ⬇ sur chaque pill de catégorie pour activer/désactiver l'auto-DL
- Souscriptions sauvegardées en SQLite (table auto_dl_categories)
- Boucle background toutes les AUTO_DL_INTERVAL secondes (défaut 1h)
- Déduplication via already_enqueued() (évite re-queue si déjà queued/done)
- POST /api/auto-dl/check pour déclencher un check immédiat
- GET/POST/DELETE /api/auto-dl/{category} pour gérer les souscriptions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import BackgroundTasks, FastAPI, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse, StreamingResponse
|
||||
@@ -8,18 +11,61 @@ from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pydantic import BaseModel
|
||||
|
||||
from arte_api import fetch_concerts, invalidate_cache, CATEGORIES
|
||||
from arte_api import fetch_concerts, get_concerts_by_category, invalidate_cache, CATEGORIES
|
||||
from downloader import DownloadManager
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI(title="Arte-dl")
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
AUTO_DL_INTERVAL = int(os.getenv("AUTO_DL_INTERVAL", "3600"))
|
||||
|
||||
dm = DownloadManager()
|
||||
|
||||
|
||||
async def _run_auto_dl_check() -> int:
|
||||
cats = dm.get_watched_categories()
|
||||
if not cats:
|
||||
return 0
|
||||
total = 0
|
||||
for cat in cats:
|
||||
concerts = await get_concerts_by_category(cat)
|
||||
for c in concerts:
|
||||
if not dm.already_enqueued(c["url"]):
|
||||
m = re.search(r"\b(20\d{2})\b", c.get("subtitle", ""))
|
||||
year = int(m.group(1)) if m else None
|
||||
await dm.enqueue_direct(c["url"], c["title"], c.get("subtitle", ""), year, cat)
|
||||
total += 1
|
||||
return total
|
||||
|
||||
|
||||
async def _auto_dl_loop():
|
||||
await asyncio.sleep(60)
|
||||
while True:
|
||||
try:
|
||||
n = await _run_auto_dl_check()
|
||||
if n:
|
||||
logger.info("Auto-DL: enqueued %d new concert(s)", n)
|
||||
except Exception as e:
|
||||
logger.warning("Auto-DL check failed: %s", e)
|
||||
await asyncio.sleep(AUTO_DL_INTERVAL)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
task = asyncio.create_task(_auto_dl_loop())
|
||||
yield
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
app = FastAPI(title="Arte-dl", lifespan=lifespan)
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ pages
|
||||
|
||||
|
||||
@@ -47,6 +93,34 @@ async def api_refresh():
|
||||
return {"count": count}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ API: auto-download
|
||||
|
||||
|
||||
@app.get("/api/auto-dl")
|
||||
async def api_auto_dl_list():
|
||||
return dm.get_watched_categories()
|
||||
|
||||
|
||||
@app.post("/api/auto-dl/check")
|
||||
async def api_auto_dl_check():
|
||||
n = await _run_auto_dl_check()
|
||||
return {"enqueued": n}
|
||||
|
||||
|
||||
@app.post("/api/auto-dl/{category}")
|
||||
async def api_auto_dl_watch(category: str):
|
||||
if category not in CATEGORIES:
|
||||
raise HTTPException(status_code=404, detail="Unknown category")
|
||||
dm.watch_category(category)
|
||||
return {"watched": True}
|
||||
|
||||
|
||||
@app.delete("/api/auto-dl/{category}")
|
||||
async def api_auto_dl_unwatch(category: str):
|
||||
dm.unwatch_category(category)
|
||||
return {"watched": False}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ API: downloads
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user