From bd3590543689deeaedb73a0b97c5bdbb62eafb15 Mon Sep 17 00:00:00 2001 From: xpltd Date: Tue, 17 Mar 2026 22:56:37 -0500 Subject: [PATCH] chore(M001/S01/T03): auto-commit after state-rebuild --- .gsd/milestones/M001/slices/S01/S01-PLAN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gsd/milestones/M001/slices/S01/S01-PLAN.md b/.gsd/milestones/M001/slices/S01/S01-PLAN.md index 0124f8f..af1ab71 100644 --- a/.gsd/milestones/M001/slices/S01/S01-PLAN.md +++ b/.gsd/milestones/M001/slices/S01/S01-PLAN.md @@ -66,7 +66,7 @@ All tests run from `backend/`: - Verify: `cd backend && python -m pytest tests/test_config.py tests/test_database.py tests/test_sse_broker.py -v` - Done when: All three test files pass. `PRAGMA journal_mode` returns `wal`. Concurrent writes (3 simultaneous) complete without `SQLITE_BUSY`. Broker publish from a thread delivers event to subscriber queue. -- [ ] **T03: Implement download service with sync-to-async bridge** `est:1h` +- [x] **T03: Implement download service with sync-to-async bridge** `est:1h` - Why: This is the highest-risk component in the slice — the sync-to-async bridge between yt-dlp worker threads and asyncio queues. It must be built and proven separately before API routes wire it up. The output template resolver is a direct dependency. This task retires the primary risk identified in the roadmap: "proving yt-dlp progress events arrive in an asyncio.Queue via call_soon_threadsafe." - Files: `backend/app/services/download.py`, `backend/app/services/output_template.py`, `backend/app/services/__init__.py`, `backend/tests/test_download_service.py`, `backend/tests/test_output_template.py` - Do: Build `resolve_template(url, user_override, config)` — extract domain, lookup in `source_templates` config map, fallback to `*`. Build `DownloadService` class: accepts config, database, SSE broker, event loop in constructor. `ThreadPoolExecutor(max_workers=config.downloads.max_concurrent)`. `enqueue(job_create, session_id)` creates DB row then submits `_run_download` to executor. `_run_download` creates fresh `YoutubeDL` per job (never shared), registers progress hook that calls `loop.call_soon_threadsafe(broker.publish, session_id, ProgressEvent.from_yt_dlp(...))`, updates DB on completion/failure. `get_formats(url)` runs `extract_info(url, download=False)` in executor, returns list of `FormatInfo`. `cancel(job_id)` sets status=failed in DB. Handle `total_bytes: None` in progress hook. Throttle DB progress writes (≥1% change or status change). Write integration test: real yt-dlp download of a short Creative Commons video, assert progress events arrive in broker queue with `status=downloading` and valid percent. Write format extraction test. Write output template unit tests.