- Rewrote scheduler enrichNewItems to fetch metadata per-video directly
instead of redundantly re-running Phase 1 discovery. Background enrichment
now calls yt-dlp --dump-json per video ID, updating publishedAt and
duration on DB records. Emits scan:complete when enrichment finishes
so clients know to refetch.
- Added missing --> statement-breakpoint markers to migration 0011.
Without them, Drizzle concatenated the three ALTER TABLE statements
and SQLite only executed the first one, leaving embed_thumbnail and
sponsor_block_remove columns missing from format_profiles.
- Default scanLimit increased to 500 (was 100, missing most channel content)
- First scan (lastCheckedAt === null) uses max(scanLimit, 999) for full catalog
- Discovery timeout scales with limit: 60s base + 30s per 500 items
- Updated platform-settings-repository defaults to match
- Scan endpoint returns 202 immediately, runs in background
- Items appear in real-time via WebSocket scan:item-discovered events
- Phase 1 (fast flat-playlist) runs first with discoveryOnly flag
- Phase 2 (slow enrichment) runs as background post-scan pass
- Added POST /api/v1/channel/:id/scan-cancel endpoint
- AbortController support in scheduler for scan cancellation
- Frontend: Scan button toggles to Stop button during scan
- Frontend: Live item count shown during scanning
- Frontend: useCancelScan hook for cancel functionality
- Moved tubearr config to local Docker volume (SQLite on CIFS fix)
Migrated git root from W:/programming/Projects/ to W:/programming/Projects/Tubearr/.
Previous history preserved in Tubearr-full-backup.bundle at parent directory.
Completed milestones: M001 through M005
Active: M006/S02 (Add Channel UX)