Add popular fetch progress to Downloads page
- Track active background tasks in an in-memory dict with a lock - Expose GET /api/channels/tasks returning running task list - _fetch_popular_task updates done count as each video fetch completes - Downloads page polls /tasks every 2s and shows progress bars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import threading as _threading
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
@@ -14,6 +15,9 @@ from ..services import ytdlp
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
_tasks: dict = {}
|
||||
_tasks_lock = _threading.Lock()
|
||||
|
||||
|
||||
class ChannelOut(BaseModel):
|
||||
id: int
|
||||
@@ -286,6 +290,12 @@ def sync_all_channels(
|
||||
return {"indexing": len(channels)}
|
||||
|
||||
|
||||
@router.get("/tasks")
|
||||
def get_active_tasks(current_user: User = Depends(get_current_user)):
|
||||
with _tasks_lock:
|
||||
return list(_tasks.values())
|
||||
|
||||
|
||||
@router.post("/mark-seen", status_code=204)
|
||||
def mark_channels_seen(
|
||||
db: Session = Depends(get_db),
|
||||
@@ -652,11 +662,11 @@ def fetch_popular_videos(
|
||||
):
|
||||
"""Fetch the channel's most popular videos from YouTube and index them."""
|
||||
channel = _get_channel_or_404(db, channel_id)
|
||||
background_tasks.add_task(_fetch_popular_task, channel_id, channel.youtube_channel_id)
|
||||
background_tasks.add_task(_fetch_popular_task, channel_id, channel.youtube_channel_id, channel.name or "")
|
||||
return {"detail": "Fetching popular videos"}
|
||||
|
||||
|
||||
def _fetch_popular_task(channel_id: int, youtube_channel_id: str):
|
||||
def _fetch_popular_task(channel_id: int, youtube_channel_id: str, channel_name: str = ""):
|
||||
"""Half-and-half popular fetch.
|
||||
|
||||
Phase 1 (fast): flat-playlist crawl of the full channel → store any
|
||||
@@ -742,15 +752,32 @@ def _fetch_popular_task(channel_id: int, youtube_channel_id: str):
|
||||
if not video_ids:
|
||||
return
|
||||
|
||||
with ThreadPoolExecutor(max_workers=3) as pool:
|
||||
futures = {pool.submit(ytdlp.fetch_video_metadata, vid): vid for vid in video_ids}
|
||||
results = {}
|
||||
for future in as_completed(futures):
|
||||
vid = futures[future]
|
||||
try:
|
||||
results[vid] = future.result()
|
||||
except Exception:
|
||||
pass
|
||||
task_id = f"popular-{channel_id}"
|
||||
with _tasks_lock:
|
||||
_tasks[task_id] = {
|
||||
"id": task_id,
|
||||
"label": f"Popular fetch — {channel_name}" if channel_name else "Popular fetch",
|
||||
"total": len(video_ids),
|
||||
"done": 0,
|
||||
"started_at": datetime.utcnow().isoformat(),
|
||||
}
|
||||
|
||||
results = {}
|
||||
try:
|
||||
with ThreadPoolExecutor(max_workers=3) as pool:
|
||||
futures = {pool.submit(ytdlp.fetch_video_metadata, vid): vid for vid in video_ids}
|
||||
for future in as_completed(futures):
|
||||
vid = futures[future]
|
||||
try:
|
||||
results[vid] = future.result()
|
||||
except Exception:
|
||||
pass
|
||||
with _tasks_lock:
|
||||
if task_id in _tasks:
|
||||
_tasks[task_id]["done"] += 1
|
||||
finally:
|
||||
with _tasks_lock:
|
||||
_tasks.pop(task_id, None)
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user