feat(dl): allow --tvdb as an --enrich metadata source

This commit is contained in:
imSp4rky
2026-08-09 11:55:06 -06:00
parent 8d3785b460
commit 8a7022a091
7 changed files with 48 additions and 13 deletions
+1 -1
View File
@@ -394,7 +394,7 @@ Create a download job. Requires `service` and `title_id`; every other field is a
| `tag` | string | `null` | Release group tag. |
| `tmdb_id` / `imdb_id` / `tvdb_id` / `animeapi_id` | - | `null` | External ID overrides for tagging. |
| `tvdb_order` | `official`, `dvd`, `absolute`, `alternate`, `regional` | `null` | Renumber episodes to a TVDB season order. Falls back to the `tvdb_order` config option. |
| `enrich` | boolean | `false` | Override title/year from external source, and fill in the original language. |
| `enrich` | boolean | `false` | Override title/year from external source, and fill in the original language. Needs one of `tmdb_id`, `imdb_id`, `tvdb_id` or `animeapi_id`; without one the job fails instead of returning `400`. |
| `output_dir` | string | `null` | Override output directory. |
| `no_cache` / `reset_cache` | boolean | `false` | Title cache controls. |
+2 -2
View File
@@ -152,9 +152,9 @@ Keep only certain track types, or skip certain track types. Attachments are alwa
|---|---|
| `--tmdb` | TMDB ID (integer). |
| `--imdb` | IMDb ID, e.g. `tt1375666`. |
| `--tvdb` | TVDB ID (integer). Skips the series lookup that `--tvdb-order` would otherwise do. |
| `--tvdb` | TVDB ID (integer). Skips the series lookup that `--tvdb-order` would otherwise do. `--enrich` reads it too. |
| `--animeapi` | AnimeAPI ID, e.g. `mal:12345` or `anilist:98765` (defaults to MAL). Back-fills TMDB/IMDb/TVDB. |
| `--enrich` | Override show title/year from the external source, and fill in the original language. **Requires** one of `--tmdb`, `--imdb`, or `--animeapi`. |
| `--enrich` | Override show title/year from the external source, and fill in the original language. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--animeapi`. |
| `--tvdb-order` | Renumber episodes to a TVDB season order: `official` (aired), `dvd`, `absolute`, `alternate`, or `regional`. Needs `tvdb_api_key`. |
#### Episode ordering
+5 -3
View File
@@ -583,7 +583,7 @@ tagging and naming:
| `--imdb` | `--imdb tt1375666` | Use this IMDb ID. |
| `--tvdb` | `--tvdb 73871` | Use this TVDB ID instead of looking the series up. |
| `--animeapi` | `--animeapi mal:12345` | Resolve via AnimeAPI (`mal:`/`anilist:` prefix; defaults to MAL). |
| `--enrich` | - | Override the show title and year from an external source, and fill in the original language. **Requires** one of `--tmdb`, `--imdb`, or `--animeapi`. |
| `--enrich` | - | Override the show title and year from an external source, and fill in the original language. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--animeapi`. |
| `--tvdb-order` | `--tvdb-order dvd` | Renumber episodes to a TVDB season order. Needs `tvdb_api_key`. |
```shell title="Force the right IMDb match and enrich the title"
@@ -611,8 +611,10 @@ all end up as the same language.
- `--tmdb` reads TMDB.
- `--imdb` tries IMDxAPI, then falls back to OMDb. Since `imdb_api_enabled` is `false` by
default, an OMDb key is what makes this path work for most people.
- `--animeapi` supplies a title only, so pair it with `--tmdb` or `--imdb` if you also want
the year and language.
- `--tvdb` reads TVDB. It needs `tvdb_api_key`.
- `--animeapi` supplies a title, plus whichever IDs AnimeAPI knows for the show. If that
includes a TMDB, IMDb or TVDB ID, the year and language come from that provider. If it
does not, pair it with `--tmdb`, `--imdb` or `--tvdb`.
Language is the one field `--enrich` will not overwrite. A service that already reported an
original language keeps it, because the platform knows its own catalogue better than a
+1 -1
View File
@@ -14,7 +14,7 @@ each metadata provider can actually answer with, see
| `omdb_api_key` | str | `""` | [OMDb](https://www.omdbapi.com/) API key for IMDb metadata lookups; a more reliable alternative to IMDxAPI. Free keys are available on the OMDb site. |
| `tmdb_api_key` | str | `""` | TMDB API key for metadata enrichment and external-ID tags. |
| `simkl_client_id` | str | `""` | SIMKL client ID for metadata lookups; an alternative/fallback source to TMDB. |
| `tvdb_api_key` | str | `""` | [TheTVDB v4](https://thetvdb.com/api-information) API key; a fallback source to TMDB, strongest on TV series. Free keys are available on the TVDB site. |
| `tvdb_api_key` | str | `""` | [TheTVDB v4](https://thetvdb.com/api-information) API key; a fallback source to TMDB, strongest on TV series. Also the source `--enrich --tvdb` reads. Free keys are available on the TVDB site. |
| `tvdb_pin` | str | `""` | Subscriber PIN, only needed for a user-supported TVDB key. Leave empty for a normal project key. |
| `tvdb_order` | str | `""` | Default for `--tvdb-order`: renumber episodes to a TVDB season order (`official`, `dvd`, `absolute`, `alternate`, `regional`). Empty keeps the numbering the service gives. |
| `metadata_providers` | list | *(see below)* | Metadata providers to use, in the order they are tried. |
+21
View File
@@ -157,6 +157,27 @@ def test_cached_tvdb_result_keeps_enriched_ids() -> None:
assert result.external_ids.tmdb_id == 615
@pytest.mark.parametrize(("kind", "path"), [("tv", "/series/73871/extended"), ("movie", "/movies/73871/extended")])
def test_get_by_id_supplies_what_enrich_needs(monkeypatch: pytest.MonkeyPatch, kind: str, path: str) -> None:
p = TVDBProvider()
seen: list[str] = []
def fake_get(request_path: str, params: dict) -> dict:
seen.append(request_path)
return {
"name": "Cowboy Bebop",
"year": "1998",
"originalLanguage": "jpn",
"remoteIds": [{"sourceName": "IMDB", "id": "tt0213338"}],
}
monkeypatch.setattr(p, "_get", fake_get)
result = p.get_by_id(73871, kind)
assert seen == [path]
assert (result.title, result.year, result.original_language) == ("Cowboy Bebop", 1998, "jpn")
assert result.external_ids.imdb_id == "tt0213338"
def test_get_episodes_fails_closed_on_a_truncated_listing(monkeypatch: pytest.MonkeyPatch) -> None:
p = TVDBProvider()
pages: dict[int, object] = {0: {"episodes": [{"id": n} for n in range(500)]}, 1: None}
+16 -4
View File
@@ -616,7 +616,7 @@ class dl:
"tvdb_id",
type=int,
default=None,
help="Use this TVDB ID for --tvdb-order instead of looking the series up automatically.",
help="Use this TVDB ID for --tvdb-order and --enrich instead of looking the series up automatically.",
)
@click.option(
"--tvdb-order",
@@ -638,7 +638,7 @@ class dl:
default=False,
help=(
"Override show title and year from external source, and fill in the original language "
"if the service did not provide one. Requires --tmdb, --imdb, or --animeapi."
"if the service did not provide one. Requires --tmdb, --imdb, --tvdb, or --animeapi."
),
)
@click.option(
@@ -871,8 +871,10 @@ class dl:
if not self.tvdb_id and anime_ids.tvdb_id:
self.tvdb_id = anime_ids.tvdb_id
if self.enrich and not (self.tmdb_id or self.imdb_id or self.animeapi_title):
raise click.UsageError("--enrich requires --tmdb, --imdb, or --animeapi to provide a metadata source.")
if self.enrich and not (self.tmdb_id or self.imdb_id or self.tvdb_id or self.animeapi_title):
raise click.UsageError(
"--enrich requires --tmdb, --imdb, --tvdb, or --animeapi to provide a metadata source."
)
# Initialize debug logger with service name if debug logging is enabled
if config.debug or logging.root.level == logging.DEBUG:
@@ -1552,9 +1554,19 @@ class dl:
enrich_year = imdb_result.year
enrich_lang = parse_language(imdb_result.original_language)
break
elif self.tvdb_id:
provider = providers.get_provider("tvdb")
tvdb_result = provider.get_by_id(self.tvdb_id, kind) if provider else None
if tvdb_result:
if not enrich_title:
enrich_title = tvdb_result.title
enrich_year = tvdb_result.year
enrich_lang = parse_language(tvdb_result.original_language)
if enrich_lang:
self.log.info(f"Original language from metadata: {enrich_lang}")
elif not (enrich_title or enrich_year):
self.log.warning("--enrich found no metadata; is the provider's API key configured?")
if enrich_title or enrich_year or enrich_lang:
for t in titles if isinstance(titles, (Series, Movies)) else [titles]:
+2 -2
View File
@@ -764,7 +764,7 @@ async def download(request: web.Request) -> web.Response:
description: Anime database ID via AnimeAPI, e.g. mal:12345 (default - None)
enrich:
type: boolean
description: Override show title and year from external source (default - false)
description: Override show title and year from external source, and fill in the original language. Requires one of tmdb_id, imdb_id, tvdb_id or animeapi_id (default - false)
no_folder:
type: boolean
description: Disable folder creation for TV shows (default - false)
@@ -794,7 +794,7 @@ async def download(request: web.Request) -> web.Response:
description: Use this IMDB ID (e.g. tt1375666) for tagging (default - None)
tvdb_id:
type: integer
description: Use this TVDB ID for tagging and episode ordering (default - None)
description: Use this TVDB ID for tagging, episode ordering and enrichment (default - None)
tvdb_order:
type: string
enum: [official, dvd, absolute, alternate, regional]