Commit Graph
857 Commits
Author SHA1 Message Date
imSp4rky 3312c218df perf(dl): overlap hybrid builds with muxing, tag in the mux thread, preload DRM in parallel
Submit standalone mux tasks largest-first as soon as they are enqueued and build hybrid tracks while they run, each muxed from its own pool slot when ready; run mkvpropedit inside the mux thread instead of a serial pass at the end; load per-track DRM from a thread pool; decode m3u8 playlists as UTF-8 instead of letting requests run chardet over them.
2026-08-20 12:41:04 -06:00
imSp4rky 537a1dc973 perf(dl): parallelise mux, licensing, and vault lookups; size the shared pool
Grow the service session pool to downloads * workers before track downloads, run mkvmerge jobs concurrently (muxing.concurrency, default 4), lock DRM preparation per key set instead of globally, and query local vaults first then remote vaults together.
2026-08-20 11:24:12 -06:00
imSp4rky 8b10b793e2 docs: phase 2 of the prose style across docs and docstrings 2026-08-19 20:59:09 -06:00
imSp4rky 66971230c3 docs: apply simplified technical english across docs and docstrings. 2026-08-19 16:31:06 -06:00
imSp4rky 4177cb2267 fix(hls): keep ad-filtered segments out of DRM key state 2026-08-19 14:39:09 -06:00
imSp4rky 29d0828c33 style: clean up prose 2026-08-19 13:45:58 -06:00
imSp4rky 54adb82746 fix(vaults): look tables up by the name the vault stores
get_services() normalised table names through Services.get_tag() and then handed the result back as an identifier, so kv copy and sync skipped any table whose name differed in case or matched an alias. Yield stored names, and resolve tables case-insensitively.
2026-08-19 12:11:44 -06:00
imSp4rky c4c49ea672 fix(cli): work around rich-click's patched click classes 2026-08-19 10:39:41 -06:00
imSp4rky 4aa217c206 refactor(console): style env listings like the rich-click help screens 2026-08-18 23:13:48 -06:00
imSp4rky ca543b3a14 docs(services): add MUSIC_EXAMPLE reference service 2026-08-18 15:07:23 -06:00
imSp4rky 68c7d95173 refactor(music)!: make Song an ordinary Title on the generic path
Delete the dead grouped-download block, the planner, and the music service hooks; Song now flows through the one download loop, gets tagged via tag_file, and answers -w with {disc}x{track} keys.

BREAKING CHANGE: GROUP_AUDIO_DOWNLOADS, get_music_track_options and get_music_collection_label are gone. A music service returns one Audio track per quality and lets the framework choose.
2026-08-18 15:05:30 -06:00
imSp4rky a4a385e5aa test(post-scripts): compare paths via Path so music hook tests pass on windows 2026-08-18 08:30:32 -06:00
imSp4rky 89acfab279 feat(dl): run user post-download scripts with metadata variables
New post_scripts config and repeatable --postscript flag run a command per output, season/album folder, or run, substituting filename-template variables into pre-tokenized argv. The REST API rejects hook commands.

Closes #150
2026-08-18 08:26:14 -06:00
imSp4rky 511f96d8db docs: strip non-essential comments, fold key facts into docstrings 2026-08-16 23:32:02 -06:00
imSp4rky 66bcbe9377 test(core): skip download integration tests when ffprobe is absent 2026-08-16 22:32:33 -06:00
imSp4rky 30b9cb06af Merge branch 'feat/downloader-speed' into dev 2026-08-16 22:19:35 -06:00
imSp4rky 42718cbd99 fix(titles): drop folder {year?} when it repeats the dated season label
Dated episodes turn the season folder into the air year, so a folder template's {year?} rendered the same year twice (Title.2025.2025).
2026-08-16 22:01:12 -06:00
imSp4rky 619c86cd10 feat(downloader): resume failed downloads from completed segments across runs
Fingerprint-gated reuse of atomically-published segment files (DASH/ISM/HLS) behind a continue_downloads config option and one-off dl --continue-downloads flag.
2026-08-16 19:38:06 -06:00
imSp4rky dd9dafef91 fix(downloader): harden ranged reads, teardown and multiprocess sessions
Validates the 206 range end, bounds the DASH collapse, and preserves cookie domains and pool size in spawned children.
2026-08-16 17:07:28 -06:00
imSp4rky 7f73d9c469 fix(downloader): validate Content-Range before placing 206 bytes
A 206 was trusted from its status alone, so a server answering a different range than requested silently corrupted output.
2026-08-16 14:23:39 -06:00
imSp4rky e3c1ce3511 refactor(tests): drop leading underscores from test helper functions
Completes the core underscore sweep for the test files this branch added; make_ctx/make_track/seg_urls avoid shadowing locals.
2026-08-16 12:52:36 -06:00
imSp4rky d52d24d933 fix(api): pass adaptive_workers and download_processes to dl.result
Both became required parameters of dl.result() when adaptive worker scaling
landed, but the API path calls it directly and never supplied them, so every
API-initiated download raised TypeError. Defaults match the click options.
2026-08-16 12:43:52 -06:00
imSp4rky ee9d3edfe3 Merge branch 'dev' into feat/downloader-speed
# Conflicts:
#	unshackle/core/api/download_manager.py
#	unshackle/core/downloaders/requests.py
#	unshackle/core/manifests/dash.py
#	unshackle/core/session.py
#	unshackle/core/utils/webvtt.py
2026-08-16 12:43:01 -06:00
imSp4rky 1af77d395d fix(downloaders): submit segments in a window so progress can drain
Submitting every segment up front convoyed on ThreadPoolExecutor's process-global lock, starving the event drain so per-track progress bars only advanced once the download had finished.
2026-08-16 11:06:17 -06:00
imSp4rky 21d5f1c5e1 refactor(core)!: drop leading underscores from internal definitions
Renames 314 defs and 41 module/class-level constants. Keeps 11 names underscored where the stripped form collides in scope, plus 5.5.0 shims for the three symbols external service repos call.
2026-08-15 22:07:41 -06:00
imSp4rky db4f0b39f9 refactor(console): route interactive prompts through a themed helper
Adds prompt_user() and uses it for local, remote, and Proton VPN input. Replaces click.prompt on the remote auth path, which ignored the theme and overlapped active Live displays. Remote auth now exits on EOF instead of POSTing an empty answer and spinning the poll loop.
2026-08-15 18:13:05 -06:00
imSp4rky a6cb0231d5 chore: bump dev version to 5.5.0 2026-08-15 16:00:14 -06:00
imSp4rky 0576d9d03c chore(release): 5.4.0 5.4.0 2026-08-15 15:54:07 -06:00
imSp4rky 9a2f7a6661 fix(kv): search remote vaults that cannot list their tables
Probe locally installed service tags in parallel when a vault has no table listing, and report every vault holding the KID instead of the first hit.
2026-08-15 15:09:42 -06:00
imSp4rky e1fbd11678 fix(dash): resolve representations by adaptation set, not id alone
Representation ids are only unique within an AdaptationSet, so re-finding a track by id could download a sibling set's stream (audio description in place of dialog). Applies to download_track and extract_dash.
2026-08-15 14:26:45 -06:00
imSp4rky 415a195957 test(serve): strip ANSI from CLI output before asserting error message 2026-08-13 16:33:08 -06:00
imSp4rky db9ec3e334 feat(cli): add named color themes and rich-formatted help
Render all click help through rich-click, themed by a new `theme` config key (9 palettes + aliases, previewable via `unshackle env theme`), and show service docstrings in local and remote `dl <TAG> -h`.
2026-08-13 16:25:14 -06:00
imSp4rky a248ae79bd feat(serve): gate server CDM licensing behind a per-key server_cdm opt-in
Keys configured under serve.users default to off, so they license with their own local CDM; session licensing and download jobs both refuse otherwise.
2026-08-13 11:40:30 -06:00
imSp4rky ccb4a69302 fix(services): re-raise ClickException for better error handling in remote services cache 2026-08-13 09:23:23 -06:00
imSp4rky 5796729bf3 feat(remote): accept serve.users keys in api-only mode and add client auth-header fallback
api-only/remote-only serve now authenticates every serve.users key (str-coerced), not just api_secret, so per-user service allowlists apply. RemoteClient tries X-Secret-Key then X-Api-Key on 401 (sticky winner); remote_services.auth_headers prepends extra header names to the defaults.
2026-08-12 17:58:55 -06:00
imSp4rky 274b2fc308 fix(subtitle): keep cue-less webvtt instead of failing the download
A rendition with headers but no cues raised CaptionReadNoCaptions when preserve_formatting is false, aborting the whole download.
2026-08-12 11:42:15 -06:00
imSp4rky 16ae73e3a0 feat(api): stream download job progress over server-sent events
Adds GET /api/download/jobs/{job_id}/events with snapshot, status, progress and terminal events, query-param auth for EventSource in both serve modes, atomic progress-file writes, and stream-safe CORS and compression middleware.
2026-08-12 10:35:43 -06:00
imSp4rky 581979c5a8 fix(dl): reset per-title language and range selection each iteration 2026-08-12 07:23:59 -06:00
imSp4rky c2f3577540 chore(changelog): update changelog 2026-08-11 21:46:54 -06:00
imSp4rky b5e1c3e6a7 fix(hls): take the key id from EXT-X-KEY when the PSSH carries none 2026-08-11 21:24:05 -06:00
imSp4rky 356d6fb984 fix(dl): treat audio muxed into the video as an available language
A muxed stream carries its audio in the video track, so -l orig no longer
fails when the only separate audio tracks are dubs.
2026-08-11 21:24:04 -06:00
imSp4rky 9016b526af chore(env): drop ML-Worker from the dependency check 2026-08-11 18:36:03 -06:00
imSp4rky ec594b0b9c fix(api): carry air_date, absolute, anime and daily across the title wire
serialize_title and title_to_meta now emit them as conditional keys,
_build_title restores them, and service-class flags stamp per title.
2026-08-11 17:50:38 -06:00
imSp4rky 4a21dbebb5 feat(dl): add daily date-based episodes with tvdb air-date fill
--daily flag plus Service.DAILY/Title.daily, -w selection by ISO air
date or colon date range, and a pre-1970 air-date guard.
2026-08-11 17:50:22 -06:00
imSp4rky e4f6ddec85 feat(dl): add absolute episode numbers filled from tvdb absolute order
Episode gains an additive absolute field services can set, an {absolute}
naming token (3-wide), and an --enrich fill from TVDB's absolute order
that never rewrites season or episode numbers.
2026-08-11 16:42:07 -06:00
imSp4rky 72581ba5d2 feat(metadata)!: replace animeapi with a first-class anilist provider
--anilist supersedes --animeapi (REST: anilist_id); the keyless AniList
GraphQL provider joins the registry with title search, mal: ID lookup,
an ANILIST mkv tag, and the anilist_title_language config option.
Services mark anime via Service.ANIME / Title.anime for anilist-first
metadata resolution with fall-through.
2026-08-11 16:42:05 -06:00
imSp4rky ebb4e74318 feat(metadata)!: authoritative id resolver and keyless imdb provider
Supplied IDs resolve directly via a shared resolve_by_ids and never fall back to title search; metadata_providers gains per-kind ordering and a disable_metadata switch stops all automatic lookups while explicit IDs keep working.
BREAKING: --tmdb/--imdb/--tvdb are now mutually exclusive and require a usable provider; --enrich also overwrites original language; the imdbapi provider and imdb_api_enabled are replaced by a keyless imdb provider.
2026-08-11 15:07:24 -06:00
imSp4rky 0cec6c9686 docs(api): note the - language exclusion prefix in the OpenAPI spec 2026-08-10 12:51:20 -06:00
imSp4rky d6f3958a16 fix(dl): warn when --enrich source is missing title, year, or language 2026-08-10 12:33:23 -06:00
imSp4rky 79ad70a9cf feat(dl): exclude languages with a - prefix on -l, -vl, -al, -sl and -fsl 2026-08-10 12:18:30 -06:00