Two fixes for captions downloads:
1. Language variant fallback: When user requests 'en' but the video only
has 'en-GB' (or other regional variants), yt-dlp silently skips
subtitle extraction because it uses exact language matching. Now the
subtitleslangs list includes common regional variants (e.g. en-US,
en-GB, en-AU, etc.) so yt-dlp can match the first available variant.
2. Error on no files: When yt-dlp completes successfully (ret=0) but
produces no subtitle files (e.g. no matching language at all), the
download was incorrectly shown as 'completed'. Now it's marked as
'error' with a descriptive message like 'No subtitles found for
language "en"'. Same treatment for thumbnail downloads.
- Reset download_phase when transitioning to paused (pause(), update_status(),
_post_download_cleanup()) so UI shows 'Paused' instead of stale phase label
- Reset download_phase when starting/resuming download (start() preparing state)
- Count 'postprocessing' as active download in updateMetrics() so dashboard
shows accurate counts during ffmpeg post-processing
- Add missing completedDownload Subject to DownloadsServiceStub in app.spec.ts
- Add Download type import to app.spec.ts
Fixes: TypeError Cannot read properties of undefined (reading 'pipe')
at app.ts:300 (completedDownload not in test stub)
- _download_phase_from_status now falls back to checking requested_formats
when top-level vcodec/acodec are both 'none' (common when yt-dlp uses
separate downloaders for video+audio streams)
- Clear download_phase on download completion/error (was persisting stale
phase value in finished state)
- Add debug logging in put_status for phase detection
- Update docker-compose.local.yml to match docker-compose.yml structure
with local image build (adds TARGETARCH build arg)
- Removed shadow from URL entry wrap in app.html for a cleaner look.
- Updated various icon files to improve visual consistency across the application.
- Add suffix to identify different files of same video
- Fixed invisible download for short link
Co-authored-by: Copilot <copilot@github.com>
The on_prepare handler unconditionally reflected the Origin request
header into Access-Control-Allow-Origin, and Socket.IO was configured
with cors_allowed_origins='*'. This allowed any website to make
authenticated cross-origin requests to all API endpoints, enabling
cross-origin download initiation, cookie overwrite, and data deletion.
Replace the blanket origin reflection with an explicit allowlist via
the CORS_ALLOWED_ORIGINS environment variable. When unset, cross-origin
requests are denied by default. Users who need cross-origin access can
set CORS_ALLOWED_ORIGINS to a comma-separated list of trusted origins.
The playlist/channel processing loop now sets playlist_count,
playlist_autonumber, n_entries, and __last_playlist_index on each
video entry so that templates like %(playlist_autonumber)s,
%(playlist_count)s, and %(playlist_index&{} - |)s resolve correctly
instead of showing NA.
Also updates _compact_persisted_entry to preserve n_entries and
__last_playlist_index across restarts.
Fixes#692
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/b5aeb55a-3197-4a14-b8b4-96c9a67796e8
Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
Replace the hand-rolled _outtmpl_substitute_field() / _compile_outtmpl_pattern()
with a new _resolve_outtmpl_fields() that delegates to yt-dlp's
YoutubeDL.evaluate_outtmpl(). This gives playlist/channel output templates
access to yt-dlp's full template syntax: defaults (%(field|fallback)s),
conditional formatting (%(field&prefix {})s), math (%(field+N)d),
datetime formatting (%(field>%Y-%m-%d)s), and more.
Only field references whose root name matches the targeted prefix (e.g.
"playlist" or "channel") are resolved; all other references remain as
template placeholders for yt-dlp to fill during the actual download.
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/0ae5ff34-540f-4fc8-a81c-358fb92b7c15
Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
Allow users to prefer a specific video codec (H.264, H.265, AV1, VP9)
when adding downloads. The selector filters available formats via
yt-dlp format strings, falling back to best available if the preferred
codec is not found. The completed downloads table now shows Quality
and Codec columns.
When adding a playlist, deleting a video from the queue causes it to be
re-added on the next loop iteration because queue.exists() returns False
for the deleted key. Track canceled URLs in a set so __add_entry skips
them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>