Commit Graph
59 Commits
Author SHA1 Message Date
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 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 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 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 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 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 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 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 79ad70a9cf feat(dl): exclude languages with a - prefix on -l, -vl, -al, -sl and -fsl 2026-08-10 12:18:30 -06:00
imSp4rky 8a7022a091 feat(dl): allow --tvdb as an --enrich metadata source 2026-08-09 11:55:06 -06:00
imSp4rky 8d3785b460 feat(enrich): fill in the original language from metadata providers
--enrich now sets Title.language when the service left it unset, so video tracks stop falling back to und. Adds original_language to MetadataResult (TMDB, TVDB, OMDb, IMDxAPI) and falls back to OMDb for --imdb, which previously did nothing while imdb_api_enabled defaults to false.
2026-08-08 13:52:55 -06:00
imSp4rky cfae8f8882 Merge branch 'dev' into feat/downloader-speed
Catch the branch up with dev (45 commits) so testers can run the perf work
against current dev.

Conflict resolutions:
- downloaders/requests.py: keep the branch's racing-gated read1, adaptive
  controller speed reporting and byte-range slice guards; take dev's
  content-encoding checks (raw reads are skipped on an encoded body) and its
  rolling SpeedWindow for the no-controller speed fallback. The single-URL
  ranged-parallel gate now requires no speed limit and no caller Range.
- service.py / session.py: keep the branch's TimeoutHTTPAdapter and retry
  config, raise POOL_MAX_SIZE to 64 so dev's pool-starvation fix survives
  (pool_block=True stalls threads on a pool smaller than the in-flight count).
- docs: dev split configuration.md into per-area pages, so the branch's rnet
  network option rows moved to reference/configuration/network.md.
2026-08-07 10:45:47 -06:00
imSp4rky 041ba5293b feat(titles): add optional Episode.part, selected with -w s1e1.2
Opt-in and additive: an episode without a part behaves exactly as before.
2026-08-04 17:38:30 -06:00
imSp4rky 3e0d7b5f75 feat(dl): add tag_rules to conditionally swap the group tag 2026-08-04 09:42:42 -06:00
imSp4rky 330e274da6 feat(temp): give each download task its own temp directory
Each dl run works in temp/task_<id>/, removed on every exit path. Orphans from hard-killed processes are swept via lock liveness, and env clear temp skips directories whose task is still running.
2026-08-02 13:28:23 -06:00
imSp4rky 4ac34069ec feat(providers): add TheTVDB v4 as a metadata provider
TVDB covers TV far better than the existing sources, so add it as a
provider and let users decide where it sits in the lookup chain.

- new TVDB v4 provider: search, get_by_id, get_external_ids and
  find_by_imdb_id, with a process-level token cache and 401 re-auth
- metadata_providers config both orders and filters the providers used,
  so TVDB can be primary rather than a TMDB fallback. Cross-validation
  keeps its own fixed trust ranking, unchanged by search order
- --tvdb-order renumbers episodes into a TVDB season order. A service
  does not always use aired order, so the source order is detected
  rather than assumed, and the renumber is refused when an order that
  omits episodes would give two of them the same filename
- --tvdb sets the series ID directly, skipping the lookup
- the Matroska TVDB2 tag now populates on every lookup path, including
  --imdb, which previously produced no ID tags when IMDxAPI was off

Search matches on year as well as title so an identically named remake
cannot win on title alone.
2026-08-02 10:48:32 -06:00
imSp4rky e9a40a3af3 perf: speed up the code hash with sha1 and a plain file reader 2026-07-30 17:11:18 -06:00
imSp4rky c14277e6dc feat: identify the running code by source hash 2026-07-30 16:38:11 -06:00
imSp4rky 72b8ab7027 feat: show git commit hash alongside the version 2026-07-30 13:44:07 -06:00
imSp4rky 42d7fd8303 fix(import): scope the merge-back and detect ISM protection
Only merge back tracks from another manifest, so a drifted id no longer duplicates a rung or wipes the main manifest's own subtitles. track_is_encrypted now recognises an ISM ProtectionHeader.
2026-07-29 10:48:34 -06:00
imSp4rky b74b64825e fix(templates): use one separator style across a whole folder path
Detect the dot/space style from the full folder template instead of per segment, so `{artist}/{album} ({year})` no longer yields `Some.Artist/Some Album (2025)`. Also drops empty `[]` groups like the existing `()` handling and removes redundant cleanup passes.
2026-07-26 09:48:58 -06:00
imSp4rky d60d8abf53 fix(ism): repair synthesized init and verify fragments decrypted
UNITY_MATRIX emitted 11 int32s instead of 9, oversizing mvhd/tkhd so pymp4 desynced and fTTML subtitles yielded no cues. Also rewrites PIFF senc to CENC so shaka can decrypt Smooth, labels HEV1 as hev1, and raises when a fragment survives decryption still carrying a sample-encryption box.
2026-07-25 15:55:31 -06:00
imSp4rky b7029c343e fix(ism): emit two stsd entries so sample_description_index 2 resolves
Smooth fragments set tfhd sample_description_index=2 against a server init carrying a second, identical sample entry. A lone entry left that dangling: mp4decrypt skipped every such fragment silently and demuxers stopped after the first, yielding a file that looked fine but was almost entirely encrypted.
2026-07-25 12:59:55 -06:00
imSp4rky 8cf1700684 fix(import): stop losing side-loaded subtitles and blaming the manifest URL
Only fall back to Title.language when the manifest labels nothing, and reject und as firmly as a missing tag. Exported direct-URL subtitles now replace the re-parsed set, and fetch failures stay distinct from parse failures.
2026-07-25 11:32:17 -06:00
imSp4rky 95a754fa6c fix: harden ClearKey/CENC decryption correctness 2026-07-19 10:49:45 -06:00
imSp4rky 100857d9bb perf(dash): speed up segment collection and URL templating 2026-07-18 23:48:10 -06:00
imSp4rky b54aadea7a fix(downloader): honor Retry-After with cancel-safe backoff, add fault-injection bench and tests 2026-07-18 20:21:30 -06:00
imSp4rky 2c8a98f337 feat: original-language aware DUAL/MULTi tagging and new DUBBED variable
DUAL now requires the original language plus one dub (strict default);
dual_multi_mode: count restores the old pure-count behavior.
2026-07-18 11:02:02 -06:00
imSp4rky a856d14d25 fix(api): reject backslash traversal in safe_cache_key 2026-07-16 23:22:14 -06:00
imSp4rky e50c005106 fix(dash): license ClearKeyCENC before decrypt 2026-07-16 23:22:11 -06:00
imSp4rky 5de6fe9014 Merge branch 'dev' into feat/downloader-speed
# Conflicts:
#	unshackle/core/downloaders/requests.py
#	unshackle/core/manifests/dash.py
2026-07-16 12:47:59 -06:00
imSp4rky 7eee34014e perf(dash): collapse single-URL byte-range segments into one direct download
Also throttle hedge-median recompute to 0.5s, cache replace_fields format
patterns and short-circuit when no $ token, and stream HLS merge with
copyfileobj. Adds test_dash_collapse and live A/B tooling.
2026-07-16 12:42:16 -06:00
imSp4rky 4cf7a651a0 style: apply ruff format to branch files predating the format sweep 2026-07-16 00:00:19 -06:00
imSp4rky e31e60a418 style: run ruff format over the codebase 2026-07-15 14:49:27 -06:00
imSp4rky 59a7c76b2f feat(ism): harden Smooth parsing and derive fps/codec from spec fields
Regex-based UrlPattern substitution (handles {Bitrate}/{start_time} spellings), AudioTag/FourCC codec fallback, Width/Height fallbacks, integer fragment times, live-manifest and missing-language guards. Extract fps from the HEVC SPS VUI (HEVC-only; AVC timing untrusted).
2026-07-14 21:24:14 -06:00
imSp4rky 2ccae1a0ee feat(cdm): stamp remote CDM DRM type at load for reliable detection 2026-07-14 17:32:00 -06:00
imSp4rky bc8ebdfc67 perf(downloader): slow-start ramp for the adaptive worker controller
Warm up after half a tick and double the worker target while probes pay off, dropping to AIMD +2 on first plateau/error burst. 33% faster in shaped benchmarks; backoff semantics unchanged.
2026-07-09 23:17:00 -06:00
imSp4rky 2f8492ebaf feat(downloader): adaptive worker scaling, tail boost, session parity, resume fixes
Opt-in --adaptive-workers (AIMD scaling + tail range-split), request/rnet session parity, and fixes for resume poisoning, truncated-segment acceptance, 416 handling, and byte-range-slice corruption. Tests + docs.
2026-07-06 11:25:29 -06:00
imSp4rky 59f3cb4fbe feat(titles): add air_date for date-based episode naming
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.
2026-06-30 15:45:57 -06:00
imSp4rky 5d4a27b8ca style: apply pre-commit auto-fixes (isort, whitespace, eof) 2026-06-25 11:06:55 -06:00
imSp4rky 66638ead6b feat(services): load service plugins from git repos
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.
2026-06-24 08:59:03 -06:00
imSp4rky 6294566823 fix(config): match per-service cdm keys case-insensitively
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.
2026-06-22 16:50:59 -06:00
sp4rk.yandMrMovies-Dev 78a6a97fcf feat(music): native music core - shared helpers, album folder template, display cleanup (#125)
* Add native Music core workflow

* feat(music): add shared music core helpers, album folder template, and UX cleanup

Consolidate duplicated music-service logic into core and extend the native music workflow.

- add core/music/extract.py: shared stateless helpers (first_text, first_number, year/duration/name formatting, classify_release_kind, dedupe_track_options, build_music_from_songs) so services stop carrying their own copies
- add core/music/display.py: shared rich rendering (render_track_panel, render_album_header, render_artwork_preview) with TrackRow / MusicHeaderInfo data holders
- export the new helpers from core/music/__init__.py
- add dedicated `albums` folder template kind (output_template.folder.albums) resolving albums -> songs -> "{artist} - {album} ({year})"; whitelist music template variables (album_artist, track_total, disc_total, release_type, genre, explicit, isrc, upc, label)
- fix song filename crash: config.get_output_template(...) did not exist; use config.output_template.get("songs") with a sane default
- strip emojis from music output (renderer, dl.py music branch) to match unshackle UX; remove dead MusicSongPlan import and music_icon logic
- document the albums folder key in unshackle-example.yaml
- add tests for extract, display, and the folder template

---------

Co-authored-by: MrMovies-Dev <MrMovies-Dev@users.noreply.github.com>
2026-06-15 13:34:34 -06:00