Optional ranking of forced/normal/sdh per language, mirroring audio.codec_priority; first track gets the default flag, so this also picks the default subtitle. Unset = existing behavior.
Bind sessions and download jobs to the API key (not source IP), reject path-traversal cache keys, bound decompression of peer-supplied blobs, gate the CDM device before the vault fallback, and compare API keys in constant time.
Persistent job history (capped by serve.history_limit, stored under cache), profiles/config reads, maintenance + env-check endpoints, and 409 conflict code.
Optional Episode.air_date names daily/sports content by date instead of SxxExx (no template change). Presentation-only; `{year}` dropped only when it duplicates the date's year. Round-trips via export/import.
Short-circuit merge() to os.replace() when joining one already-merged file with no init prefix; saves a full track-size copy, falls back to streamed copy across devices.
Expose DV via a plain elementary-stream demux instead of a dovi_tool RPU round-trip, and skip normalize_vui when the VUI already matches the target range. Both avoid a full bitstream pass; stale-VUI streams are still rewritten.
When a service calls `request_input` (e.g., during OTP verification in the `authenticate` phase), the terminal's active `Live` or `status` spinner contexts (handled by `rich`) would continuously refresh and redraw in the background. This caused the input prompt to disappear or become hidden immediately after rendering, although keystrokes were still registered.
This commit resolves the issue by overriding `Console.input` in `ComfyConsole` to:
1. Temporarily pause (`stop()`) all active `Live` displays before requesting input.
2. Read the user input safely without interference from the refresh loop.
3. Resume (`start()`) all previously active displays in the reverse order afterwards.
Split folder templates on both separators and format each segment separately so sanitize_filename never rewrites the divider; validate folder.* templates per-segment.
Allow directories.services entries to be git repo specs (URL/owner-repo/ @branch) alongside local paths: clone + TTL-pull as cache, list-order priority with a duplicate-ignored summary, a refresh-services command that force-syncs and reports the per-service diff, and a dirty-clone guard that protects local edits on the automated path. Adds binaries.Git and a redact_paths config toggle.
cdm lookups used the raw service key while decryption upper-cases its keys, so a case-mismatched cdm entry silently used the default. Add a shared ci_get helper, route cdm/decryption through resolvers, drop a redundant decryption mutation and dead downloader writes, and add a regression bench.
Default 10s on API/HTTP vault requests, overridable per-vault (`timeout:`) or globally (`vault_timeout`); previously they could hang indefinitely on an unreachable host.
Derive both setup_routes and setup_swagger from one ROUTES table, and move the repeated trailing APIError->response block into an @api_handler decorator.
Replace InputBridge's poll loop with Event.wait(timeout) and collapse the three download_manager enum maps into one to_enum() helper. No behaviour change.
Delete code paths that never executed in the serve/API/remote stack:
- BridgeCancelledError: raised but never caught; replace raises with RuntimeError and drop the class.
- InputBridge.error setter and _error field: written at the handler layer, never read (session.auth_error is the surfaced channel).
- cancel_event param and _check_cancel inner fn on _perform_download: the only caller always passed None (the live job.cancel_event is untouched).
- result_with_progress monkeypatch: its status pings were redundant with the file-polled progress_sink, which is now the sole path.
- session param on _resolve_manifest_data: unused in the body.
Reconcile SessionStore's hardcoded 600s auth-state TTL into a shared AUTH_INPUT_TIMEOUT constant referenced by both the InputBridge input timeout and the store, removing the magic number without changing the longer grace window.
Update input_bridge/session_store tests for the new channels and add an assertion that auth-state entries expire at the single rule.
The job progress already reports a weighted percentage, phase and track counts.
The per-track tee in build_job_progress_callables already tracks each track's
segment counts and sees the downloader's speed string, so also surface the
currently-downloading track's segments_done / segments_total and speed in the
emitted progress (and on the job + its to_dict). Lets a client show
"197/663 segments, 3.2 MB/s" instead of only a percentage.
Surface optional display metadata a service can provide: the synopsis
(title.description) and a release/air date or poster image URL stashed in
title.data ("date" / "cover_url"). Purely additive, all fields default to
null when the service does not set them, so existing clients are unaffected.
Move the supported interpreter range to >=3.11,<3.15, dropping the EOL 3.10 floor and unblocking installs on 3.13/3.14.
- pyproject: requires-python ">=3.10,<3.13" -> ">=3.11,<3.15"
- update_checker: replace deprecated asyncio.get_event_loop() with asyncio.to_thread() (warns on 3.12+, slated for removal)
- test_compression: simplify _run() to plain asyncio.run()
- README: bump documented Python range to 3.11 - 3.14
- uv.lock: regenerate for the new interpreter floor