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.
--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.
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.
--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.
Reading .raw skips urllib3's decoding, so brotli-served subtitles landed compressed and converted to empty files. Also drops a partial rather than Range-resuming an encoded body, since a Range indexes the encoded bytes.
sort_subtitles resolved only the all token, while the video and audio sorts
resolved all and best. by_language now asks matching_languages rather than
repeating the RFC 4647 preference, with no behaviour change.
subtitle.language_priority and audio.language_priority prepend to the CLI
languages, so the named languages sort first and nothing is filtered.
--exact-lang now applies to the sorts too, through matching_languages, which
follows the same RFC 4647 rule as Tracks.by_language and ignores invalid tags.
When ffmpeg runs with the terminal on stdin it turns off echo for its
interactive keys. If several run at once, or one gets killed, echo never
comes back and the terminal is left broken after dl exits.
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.
Document tvdb_api_key, tvdb_pin, tvdb_order and metadata_providers, the
--tvdb and --tvdb-order flags with their behaviour when an order does
not cover a whole series, the new REST API parameters, and the Matroska
TVDB2 tag prefixes.
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.
sort_subtitles applied the language sort before the type sort. Python sorts
are stable, so the last pass wins and type became the major key: every forced
track, then every normal, then every SDH, each block ordered by language. In a
title with many languages this put a track far from its own SDH version, for
example Finnish at track 5 and Finnish SDH at track 42.
Add a group_by parameter that selects the major key. "type" is the default and
keeps the current order byte for byte. "language" swaps the two passes so each
language stays next to its own variants. Read it from the new subtitle.group_by
config key.
Also correct the docs: type_priority was described as sorting the variants
within each language, which was never true. It ranks the types across all
languages.
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.
Error messages named the resolved language for a user who only ever defaulted to orig. Audio tracks now carry is_original, resolved through Tracks.by_language so it agrees with the downloader.
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.
- Clean up redundant spaces and separators when conditional tags (e.g. {year?}) are omitted.
- Strip leading and trailing spaces and dots in sanitize_filename and TemplateFormatter.
- Prevent FileNotFoundError [WinError 3] caused by trailing spaces in Windows directory paths.
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.
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.
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.
Stop remounting adapters on shared sessions (restores dropped 429/5xx retries, ends 16-conn pool starvation, pool sized to 64); defer DRM vault write-backs off the global lock; add mux output_path groundwork for future parallelism.