mirror of
https://github.com/Luchexx/claude-video.git
synced 2026-11-02 09:19:48 +00:00
feat: setup wizard engine choice and Gemini-aware readiness
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
6dcc18a859
commit
afbc483356
@ -12,8 +12,8 @@ import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from config import (CONFIG_DIR, CONFIG_FILE, ConfigError, DETAILS, get_config,
|
||||
load_api_key, read_env_file, write_settings)
|
||||
from config import (CONFIG_DIR, CONFIG_FILE, ConfigError, DETAILS, ENGINES, get_config,
|
||||
load_api_key, load_gemini_key, read_env_file, resolve_engine, write_settings)
|
||||
from runtime import configure_stdio, diagnostic, run_text
|
||||
|
||||
REQUIRED_BINARIES = ['ffmpeg', 'ffprobe', 'yt-dlp']
|
||||
@ -23,6 +23,9 @@ ENV_TEMPLATE = '''# /watch configuration. No shell interpolation; last assignmen
|
||||
# Native captions always come first. Optional fallback: auto|whisperx|groq|openai|none.
|
||||
# auto preserves the Groq-then-OpenAI preference for existing installations.
|
||||
# The first-run skill wizard sets WATCH_DETAIL and WATCH_WHISPER_BACKEND.
|
||||
# WATCH_ENGINE=auto|gemini|local. auto uses Gemini when GEMINI_API_KEY is set.
|
||||
# With the gemini engine, local videos are uploaded to Google for analysis.
|
||||
GEMINI_API_KEY=
|
||||
GROQ_API_KEY=
|
||||
OPENAI_API_KEY=
|
||||
'''
|
||||
@ -142,12 +145,21 @@ def _status(detailed=False):
|
||||
missing = _check_binaries()
|
||||
_check_file_permissions(CONFIG_FILE)
|
||||
cfg = get_config()
|
||||
gemini_key = bool(load_gemini_key())
|
||||
try:
|
||||
engine = resolve_engine(cfg['engine'], gemini_key)
|
||||
except ConfigError:
|
||||
engine = 'gemini' # Explicitly chosen but the key is missing; gemini_key_present reports it.
|
||||
binaries_required = engine == 'local'
|
||||
has_key, detected = _have_api_key()
|
||||
chosen = cfg['whisper_backend']
|
||||
backend = detected if chosen == 'auto' else chosen
|
||||
local_ready = _whisperx_status(cfg, detailed)
|
||||
backend_ready = local_ready if chosen == 'whisperx' else (bool(load_api_key(backend)[1]) if backend in ('groq', 'openai') else chosen == 'none')
|
||||
result = {'status': 'needs_install' if missing else 'ready', 'can_proceed': not missing,
|
||||
blocked = bool(missing) and binaries_required
|
||||
result = {'status': 'needs_install' if blocked else 'ready', 'can_proceed': not blocked,
|
||||
'engine': engine, 'configured_engine': cfg['engine'], 'gemini_key_present': gemini_key,
|
||||
'gemini_model': cfg['gemini_model'], 'binaries_required': binaries_required,
|
||||
'first_run': is_first_run(), 'setup_complete': not is_first_run(),
|
||||
'missing_binaries': missing, 'whisper_backend': backend, 'configured_backend': chosen,
|
||||
'has_api_key': has_key, 'backend_ready': backend_ready,
|
||||
@ -184,7 +196,20 @@ def cmd_json():
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_install(backend=None, detail=None):
|
||||
def cmd_install(backend=None, detail=None, engine=None):
|
||||
if engine:
|
||||
_scaffold_env()
|
||||
write_settings({'WATCH_ENGINE': engine}, CONFIG_FILE)
|
||||
if engine == 'gemini':
|
||||
if not load_gemini_key():
|
||||
print(f'[setup] Add GEMINI_API_KEY privately to {CONFIG_FILE} (free key: https://aistudio.google.com/apikey), '
|
||||
'then rerun --engine gemini. Local videos will be uploaded to Google for analysis.', file=sys.stderr)
|
||||
return 3
|
||||
_write_setup_complete()
|
||||
missing = _check_binaries()
|
||||
note = (f" Optional: install {', '.join(_brew_pkg(missing))} for non-YouTube URLs and --engine local." if missing else '')
|
||||
print(f'[setup] Gemini engine is ready. Configuration: {CONFIG_FILE}.{note}')
|
||||
return 0
|
||||
missing = _check_binaries()
|
||||
if missing:
|
||||
system = platform.system()
|
||||
@ -295,13 +320,14 @@ def main():
|
||||
mode.add_argument('--install-whisperx', action='store_true')
|
||||
parser.add_argument('--backend', choices=['auto', 'whisperx', 'groq', 'openai', 'none'])
|
||||
parser.add_argument('--detail', choices=sorted(DETAILS))
|
||||
parser.add_argument('--engine', choices=sorted(ENGINES))
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.check:
|
||||
return cmd_check()
|
||||
if args.json:
|
||||
return cmd_json()
|
||||
return cmd_install('whisperx' if args.install_whisperx else args.backend, args.detail)
|
||||
return cmd_install('whisperx' if args.install_whisperx else args.backend, args.detail, args.engine)
|
||||
except (ConfigError, OSError, ValueError) as exc:
|
||||
print(f'[setup] {exc}', file=sys.stderr)
|
||||
return 2
|
||||
|
||||
@ -105,3 +105,43 @@ def test_none_backend_completion():
|
||||
assert not setup.is_first_run()
|
||||
assert config.get_config()['whisper_backend'] == 'none'
|
||||
assert config.get_config()['detail'] == 'efficient'
|
||||
|
||||
|
||||
def test_gemini_engine_is_ready_without_media_binaries(monkeypatch):
|
||||
monkeypatch.setenv('GEMINI_API_KEY', 'unit-key')
|
||||
monkeypatch.setattr(setup, '_check_binaries', lambda: list(setup.REQUIRED_BINARIES))
|
||||
status = setup._status()
|
||||
assert status['engine'] == 'gemini' and status['configured_engine'] == 'auto'
|
||||
assert status['gemini_key_present'] is True and status['gemini_model'] == 'gemini-3.7-flash'
|
||||
assert status['can_proceed'] is True and status['status'] == 'ready'
|
||||
assert status['binaries_required'] is False
|
||||
assert status['missing_binaries'] == ['ffmpeg', 'ffprobe', 'yt-dlp']
|
||||
assert 'unit-key' not in json.dumps(status)
|
||||
assert setup.cmd_check() == 0
|
||||
|
||||
|
||||
def test_local_engine_still_requires_binaries(monkeypatch):
|
||||
monkeypatch.setattr(setup, '_check_binaries', lambda: list(setup.REQUIRED_BINARIES))
|
||||
status = setup._status()
|
||||
assert status['engine'] == 'local' and status['gemini_key_present'] is False
|
||||
assert status['can_proceed'] is False and status['binaries_required'] is True
|
||||
assert setup.cmd_check() == 2
|
||||
|
||||
|
||||
def test_engine_gemini_without_key_writes_choice_and_asks_for_key(monkeypatch, capsys):
|
||||
monkeypatch.setattr(setup, '_check_binaries', lambda: list(setup.REQUIRED_BINARIES))
|
||||
assert setup.cmd_install(engine='gemini') == 3
|
||||
assert 'GEMINI_API_KEY' in capsys.readouterr().err
|
||||
values = config.read_env_file(config.CONFIG_FILE)
|
||||
assert values['WATCH_ENGINE'] == 'gemini' and values.get('SETUP_COMPLETE') != 'true'
|
||||
|
||||
|
||||
def test_engine_gemini_with_key_completes_setup_and_preserves_lines(monkeypatch):
|
||||
monkeypatch.setattr(setup, '_check_binaries', lambda: list(setup.REQUIRED_BINARIES))
|
||||
config.CONFIG_DIR.mkdir(parents=True)
|
||||
config.CONFIG_FILE.write_text('# my notes\nGEMINI_API_KEY=unit-key\nGROQ_API_KEY=keep-me\n')
|
||||
assert setup.cmd_install(engine='gemini') == 0
|
||||
text = config.CONFIG_FILE.read_text()
|
||||
assert '# my notes' in text and 'GROQ_API_KEY=keep-me' in text
|
||||
values = config.read_env_file(config.CONFIG_FILE)
|
||||
assert values['WATCH_ENGINE'] == 'gemini' and values['SETUP_COMPLETE'] == 'true'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user