From 285d7f46eb23a4e0dd2abab79d46cee40af6ddd3 Mon Sep 17 00:00:00 2001 From: bradautomates Date: Fri, 25 Sep 2026 09:37:07 +1000 Subject: [PATCH] fix: prefer English captions when the source language is unknown Without a reported language, selection fell back to alphabetical order, so an English video with de + en manual tracks got a German transcript. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 +- skills/watch/scripts/download.py | 6 ++++-- tests/test_download.py | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 654766d..80c6427 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ All notable changes to `/watch` are documented here. - Setup wizard asks for the engine first; a Gemini-only setup no longer requires `ffmpeg`/`yt-dlp` for YouTube URLs. - Managed, optional WhisperX 3.8.6 setup with uv, a separate Python 3.12 environment, cache warm-up, recoverable installation state, and local CPU transcription. Default model: small, int8, batch 8; language and timeout are configurable. Local failures never select a cloud provider. - One-time fallback selection in the skill wizard: WhisperX, Groq, OpenAI, or captions only. Existing installations retain the `auto` cloud-key preference. -- Original-language caption selection and provenance, `--sub-lang`, and explicit cookie-file/browser options shared across downloader stages. +- Original-language caption selection and provenance (English preferred when the source language is unknown), `--sub-lang`, and explicit cookie-file/browser options shared across downloader stages. - Reports for unavailable modalities, no speech, and missing cloud-transcription intervals. Offline dependency diagnostics and a cross-platform CI gate for release publication. ### Fixed diff --git a/skills/watch/scripts/download.py b/skills/watch/scripts/download.py index 158958a..fc8d0ff 100755 --- a/skills/watch/scripts/download.py +++ b/skills/watch/scripts/download.py @@ -106,8 +106,10 @@ def select_caption(info: dict, language: str = 'auto') -> dict | None: if language != 'auto': return key == target or base == target or ('-' not in language and base.split('-')[0] == target) return target and (base == target or base.split('-')[0] == str(target).split('-')[0]) + def english_first(key): # Unknown source language: prefer English over alphabetical order. + return key.removesuffix('-orig').split('-')[0] != 'en', key for kind, tracks in (('manual', manual), ('automatic', automatic)): - keys = sorted(tracks) + keys = sorted(tracks, key=english_first) if target: keys = sorted((k for k in keys if matches(k)), key=lambda k: (k != target, k != f'{target}-orig', k)) if keys: @@ -124,7 +126,7 @@ def select_caption(info: dict, language: str = 'auto') -> dict | None: if language == 'auto': for kind, tracks in (('manual', manual), ('automatic', automatic)): if tracks: - key = sorted(tracks)[0] + key = min(tracks, key=english_first) return {'key': key, 'language': key.removesuffix('-orig'), 'kind': kind, 'provenance': 'unknown'} return None diff --git a/tests/test_download.py b/tests/test_download.py index ea77391..545ccbc 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -23,6 +23,14 @@ def test_original_track_overrides_dubbed_metadata(language): assert download.select_caption(info)['key'] == f'{language}-orig' +def test_unknown_language_prefers_english(): + # "Me at the zoo" reports no language and has manual de + en tracks. + picked = download.select_caption({'extractor_key': 'Youtube', 'subtitles': _tracks('de', 'en')}) + assert (picked['key'], picked['provenance']) == ('en', 'unknown') + assert download.select_caption({'automatic_captions': _tracks('af', 'en-GB', 'zu')})['key'] == 'en-GB' + assert download.select_caption({'subtitles': _tracks('de', 'fr')})['key'] == 'de' + + def test_requested_translation_and_unknown_source(): info = {'extractor_key': 'Youtube', 'automatic_captions': _tracks('ko-orig', 'en')} assert download.select_caption(info, 'en')['provenance'] == 'requested translation'