Plex interprets # as a URL fragment delimiter, causing files with # in
the name to be silently skipped during library scans.
- Added # to FORBIDDEN_CHARS in file-organizer.ts sanitizer
- Bulk-renamed 100 existing files on disk
- Updated 100 filePath records in DB to match
Generic Platform:
- New 'generic' platform type — catch-all for any URL yt-dlp supports
- GenericSource resolves channel metadata from any URL via yt-dlp extractors
- Content type auto-detection (video/audio/livestream) from yt-dlp metadata
- Works with Vimeo, Twitch, Bandcamp, Dailymotion, and 1000+ other sites
- Registered in both scheduler registry and channel route registry
- Frontend: indigo badge, URL detection fallback, AddChannelModal support
YouTube Enhancements:
- embedChapters: --embed-chapters flag on FormatProfile
- embedThumbnail: --embed-thumbnail flag on FormatProfile
- sponsorBlockRemove: --sponsorblock-remove with configurable categories
(sponsor, selfpromo, interaction, intro, outro, preview, music_offtopic, filler)
- Migration 0011: adds columns to format_profiles table
- All three configurable per format profile via API and (future) Settings UI
- 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)
S01 — Server-Side Pagination:
- Added getChannelContentPaginated() to content repository with search, filter, sort
- Channel content API now supports ?page, ?pageSize, ?search, ?status, ?contentType, ?sortBy, ?sortDirection
- Backwards-compatible: no params returns all items (legacy mode)
- Frontend useChannelContentPaginated hook with keepPreviousData
- ChannelDetail page: search bar, status/type filter dropdowns, pagination controls
- Sorting delegated to server (removed client-side sortedContent)
- Item count shown in Content header (e.g. '121 items')
S04 — Download Engine Hardening:
- yt-dlp auto-update on production startup (native -U with pip fallback)
- Error classification: rate_limit, format_unavailable, geo_blocked, age_restricted, private, network
- Format fallback chains: preferred res → best under res → single best → any
- Improved parseFinalPath: explicit non-path prefix detection, extension validation
- Error category included in download:failed events
- classifyYtDlpError() exported from yt-dlp module for downstream use
Verified and checked off 4 tasks that were already implemented in prior commits:
- Channel Detail page download progress integration
- Sidebar WebSocket connection status indicator
- Backend progress event emission verification
- Query invalidation on WebSocket events
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verified DownloadProgressProvider is already wrapping the app inside
QueryClientProvider in main.tsx (lines 25-29) from commit 0541a5f.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a download completes or fails, the UI now immediately refreshes the
Activity page, channel content counts, and Library page in addition to
the existing content and queue invalidations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Confirmed all event bus wiring between DownloadService, WebSocket route,
and server builder is correctly connected. No changes needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show a colored dot (green=connected, grey=disconnected) at the bottom
of the sidebar with a text label that hides when the sidebar is collapsed.
Uses the existing useDownloadProgressConnection hook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added QueueItemProgress wrapper component that shows a live progress bar
(percent, speed, ETA) for items with status 'downloading' when WebSocket
progress data is available, falling back to the existing StatusBadge otherwise.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Provider was already correctly wired in main.tsx from commit 0541a5f,
wrapping the entire app inside QueryClientProvider.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Snapshot of active development by separate Claude instance.
Includes: event bus, progress parser, WebSocket route,
download progress bar component, SSE contexts/hooks.
Not tested or validated — commit for migration to dev01.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>