mirror of
https://github.com/Luchexx/claude-video.git
synced 2026-11-02 09:19:48 +00:00
Add opt-in local transcription with managed installation, cache warm-up, and a one-time backend choice while preserving native-caption priority. Correct frame timing and coverage, caption provenance and download ownership, configuration parsing, provider pairing, and partial reports. Update cross-host instructions, packaging, and test-gated release CI. Validate 198 isolated tests, real local English and Spanish inference, network-denied warm-cache inference, live captions/media, and packaging. Record platform and account-dependent acceptance limits separately.
150 lines
7.4 KiB
Python
150 lines
7.4 KiB
Python
"""Bounded caption selection, argv inheritance, and download ownership."""
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import download
|
|
|
|
URL = 'https://example.com/video'
|
|
|
|
|
|
def _tracks(*keys):
|
|
return {k: [{'url': 'https://example.com/sub.vtt', 'ext': 'vtt'}] for k in keys}
|
|
|
|
|
|
@pytest.mark.parametrize('language', ['en', 'ko', 'pt-BR'])
|
|
def test_original_track_overrides_dubbed_metadata(language):
|
|
info = {'extractor_key': 'Youtube', 'language': 'en', 'subtitles': _tracks(language),
|
|
'automatic_captions': _tracks(f'{language}-orig', 'en', 'ko')}
|
|
track = download.select_caption(info)
|
|
assert track['key'] == language and track['kind'] == 'manual' and track['provenance'] == 'original'
|
|
info['subtitles'] = _tracks('fr')
|
|
assert download.select_caption(info)['key'] == f'{language}-orig'
|
|
|
|
|
|
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'
|
|
assert download.select_caption({'subtitles': _tracks('es')})['provenance'] == 'unknown'
|
|
assert download.select_caption(info, 'de') is None
|
|
|
|
|
|
def test_cookie_modes_and_conflict():
|
|
assert download.auth_args(cookies_from_browser='firefox:profile with spaces') == ['--no-cookies', '--cookies-from-browser', 'firefox:profile with spaces']
|
|
assert download.auth_args(cookies_file='jar.txt')[:2] == ['--no-cookies-from-browser', '--cookies']
|
|
assert download.auth_args() == []
|
|
with pytest.raises(SystemExit, match='either'):
|
|
download.auth_args('jar', 'browser')
|
|
|
|
|
|
def _fake_stages(monkeypatch, *, fail_media=False, media_path=None):
|
|
calls = []
|
|
def run(cmd):
|
|
calls.append(cmd)
|
|
template = cmd[cmd.index('-o') + 1]
|
|
directory = Path(template.rsplit('/video.', 1)[0].replace('%%', '%'))
|
|
if '--load-info-json' not in cmd and '--skip-download' in cmd:
|
|
(directory / 'video.info.json').write_text(json.dumps({'id': 'test', 'language': 'ko', 'subtitles': _tracks('ko')}))
|
|
elif '--sub-langs' in cmd:
|
|
(directory / 'video.ko.vtt').write_text('WEBVTT\n\n00:00.000 --> 00:01.000\nhello\n')
|
|
else:
|
|
if fail_media:
|
|
raise SystemExit('network failure')
|
|
path = Path(media_path) if media_path else directory / 'video.mp4'
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_bytes(b'completed')
|
|
record = Path(cmd[cmd.index('--print-to-file') + 2].replace('%%', '%'))
|
|
record.write_text(json.dumps(str(path)) + '\n')
|
|
return subprocess.CompletedProcess(cmd, 0, '', '')
|
|
monkeypatch.setattr(download, '_run', run)
|
|
return calls
|
|
|
|
|
|
def test_fetch_captions_requests_one_exact_track(monkeypatch, tmp_path):
|
|
calls = _fake_stages(monkeypatch)
|
|
result = download.fetch_captions(URL, tmp_path / '100% 한 글', cookies_from_browser='firefox:profile')
|
|
download.download_url(URL, tmp_path, context=result, cookies_from_browser='firefox:profile')
|
|
metadata, captions, media = calls
|
|
assert '--no-write-subs' in metadata and '--no-write-auto-subs' in metadata
|
|
assert metadata[-2:] == ['--', URL]
|
|
assert captions[captions.index('--sub-langs') + 1] == '-all,^ko$'
|
|
assert '--load-info-json' in captions and URL not in captions
|
|
assert '--no-write-subs' in media and '--no-write-auto-subs' in media and '--sub-langs' not in media
|
|
assert all('--cookies-from-browser' in cmd and '--no-cookies' in cmd for cmd in calls)
|
|
assert Path(result['subtitle_path']).exists()
|
|
|
|
|
|
def test_failed_download_never_returns_old_media(monkeypatch, tmp_path):
|
|
(tmp_path / 'video.mp4').write_bytes(b'old source')
|
|
(tmp_path / 'video.f137.mp4').write_bytes(b'partial')
|
|
_fake_stages(monkeypatch, fail_media=True)
|
|
with pytest.raises(SystemExit, match='network'):
|
|
download.download_url(URL, tmp_path)
|
|
assert (tmp_path / 'video.mp4').read_bytes() == b'old source'
|
|
|
|
|
|
@pytest.mark.parametrize('name', ['video.f137.mp4', 'video.part'])
|
|
def test_rejects_partial_final_paths(monkeypatch, tmp_path, name):
|
|
run = tmp_path / 'run'
|
|
run.mkdir()
|
|
_fake_stages(monkeypatch, media_path=run / name)
|
|
with pytest.raises(SystemExit, match='completed'):
|
|
download.download_url(URL, tmp_path, context={'run_dir': str(run), 'info': {'title': 'test'}})
|
|
|
|
|
|
def test_rejects_outside_final_path(monkeypatch, tmp_path):
|
|
_fake_stages(monkeypatch, media_path=tmp_path / 'outside.mp4')
|
|
with pytest.raises(SystemExit, match='inside'):
|
|
download.download_url(URL, tmp_path)
|
|
|
|
|
|
@pytest.mark.parametrize('message,hint', [
|
|
('HTTP 403 forbidden', 'original error'), ('HTTP 429', 'rate limiting'),
|
|
('Sign in to continue', 'authentication'), ('No JavaScript runtime', 'Deno/EJS'),
|
|
('egress denied', 'network settings'), ('certificate verify failed', 'trusted CA'),
|
|
])
|
|
def test_network_diagnostics(message, hint):
|
|
assert hint in download.network_diagnostic(message)
|
|
|
|
|
|
def test_actual_ytdlp_exact_track_with_inherited_all(monkeypatch, tmp_path, static_clip):
|
|
"""Use the installed stable CLI offline for caption selection and final media paths."""
|
|
import shutil
|
|
if not shutil.which('yt-dlp'):
|
|
pytest.skip('yt-dlp required for offline CLI acceptance')
|
|
subs = tmp_path / 'source.vtt'
|
|
subs.write_text('WEBVTT\n\n00:00.000 --> 00:01.000\n안녕\n', encoding='utf-8')
|
|
user_cfg = Path.home() / '.config' / 'yt-dlp'
|
|
user_cfg.mkdir(parents=True)
|
|
# Redirecting typed templates must not divert watch's output.
|
|
user_cfg.joinpath('config').write_text('--enable-file-urls\n--sub-langs all\n-o "subtitle:elsewhere/sub.%(ext)s"\n', encoding='utf-8')
|
|
raw = {'id': 'offline', 'title': 'Offline', 'extractor': 'generic', 'extractor_key': 'Generic',
|
|
'webpage_url': URL, 'url': static_clip.as_uri(), 'ext': 'mp4', 'duration': 3,
|
|
'language': 'ko', 'subtitles': {'ko': [{'url': subs.as_uri(), 'ext': 'vtt'}],
|
|
'en': [{'url': (tmp_path / 'must-not-read.vtt').as_uri(), 'ext': 'vtt'}]}}
|
|
real_run = download._run
|
|
def stages(cmd):
|
|
if '--load-info-json' not in cmd and '--skip-download' in cmd:
|
|
input_info = tmp_path / 'input.info.json'
|
|
input_info.write_text(json.dumps(raw))
|
|
# Retain the actual metadata-stage flags/templates, only substitute
|
|
# an offline source for the network extractor.
|
|
return real_run([*cmd[:-2], '--load-info-json', str(input_info)])
|
|
return real_run(cmd)
|
|
monkeypatch.setattr(download, '_run', stages)
|
|
result = download.fetch_captions(URL, tmp_path / '100% 한 글')
|
|
assert not result['errors'], result['errors']
|
|
assert Path(result['subtitle_path']).read_text(encoding='utf-8').endswith('안녕\n')
|
|
assert len(list(Path(result['run_dir']).glob('*.vtt'))) == 1
|
|
assert Path(result['info_path']).name == 'video.info.json'
|
|
assert 'requested_subtitles' not in json.loads(Path(result['info_path']).read_text())
|
|
result = download.download_url(URL, tmp_path, context=result)
|
|
assert Path(result['video_path']).read_bytes() == static_clip.read_bytes()
|
|
assert not Path('elsewhere').exists()
|
|
|
|
|
|
def test_explicit_regional_language_is_not_silently_replaced():
|
|
assert download.select_caption({'subtitles': _tracks('en-US')}, 'en-GB') is None
|
|
assert download.select_caption({'subtitles': _tracks('en-US')}, 'en')['key'] == 'en-US'
|