mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 13:05:09 +00:00
feat(dl): allow --tvdb as an --enrich metadata source
This commit is contained in:
@@ -394,7 +394,7 @@ Create a download job. Requires `service` and `title_id`; every other field is a
|
|||||||
| `tag` | string | `null` | Release group tag. |
|
| `tag` | string | `null` | Release group tag. |
|
||||||
| `tmdb_id` / `imdb_id` / `tvdb_id` / `animeapi_id` | - | `null` | External ID overrides for tagging. |
|
| `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. |
|
| `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. |
|
| `output_dir` | string | `null` | Override output directory. |
|
||||||
| `no_cache` / `reset_cache` | boolean | `false` | Title cache controls. |
|
| `no_cache` / `reset_cache` | boolean | `false` | Title cache controls. |
|
||||||
|
|
||||||
|
|||||||
@@ -152,9 +152,9 @@ Keep only certain track types, or skip certain track types. Attachments are alwa
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `--tmdb` | TMDB ID (integer). |
|
| `--tmdb` | TMDB ID (integer). |
|
||||||
| `--imdb` | IMDb ID, e.g. `tt1375666`. |
|
| `--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. |
|
| `--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`. |
|
| `--tvdb-order` | Renumber episodes to a TVDB season order: `official` (aired), `dvd`, `absolute`, `alternate`, or `regional`. Needs `tvdb_api_key`. |
|
||||||
|
|
||||||
#### Episode ordering
|
#### Episode ordering
|
||||||
|
|||||||
@@ -583,7 +583,7 @@ tagging and naming:
|
|||||||
| `--imdb` | `--imdb tt1375666` | Use this IMDb ID. |
|
| `--imdb` | `--imdb tt1375666` | Use this IMDb ID. |
|
||||||
| `--tvdb` | `--tvdb 73871` | Use this TVDB ID instead of looking the series up. |
|
| `--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). |
|
| `--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`. |
|
| `--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"
|
```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.
|
- `--tmdb` reads TMDB.
|
||||||
- `--imdb` tries IMDxAPI, then falls back to OMDb. Since `imdb_api_enabled` is `false` by
|
- `--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.
|
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
|
- `--tvdb` reads TVDB. It needs `tvdb_api_key`.
|
||||||
the year and language.
|
- `--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
|
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
|
original language keeps it, because the platform knows its own catalogue better than a
|
||||||
|
|||||||
@@ -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. |
|
| `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. |
|
| `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. |
|
| `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_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. |
|
| `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. |
|
| `metadata_providers` | list | *(see below)* | Metadata providers to use, in the order they are tried. |
|
||||||
|
|||||||
@@ -157,6 +157,27 @@ def test_cached_tvdb_result_keeps_enriched_ids() -> None:
|
|||||||
assert result.external_ids.tmdb_id == 615
|
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:
|
def test_get_episodes_fails_closed_on_a_truncated_listing(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
p = TVDBProvider()
|
p = TVDBProvider()
|
||||||
pages: dict[int, object] = {0: {"episodes": [{"id": n} for n in range(500)]}, 1: None}
|
pages: dict[int, object] = {0: {"episodes": [{"id": n} for n in range(500)]}, 1: None}
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ class dl:
|
|||||||
"tvdb_id",
|
"tvdb_id",
|
||||||
type=int,
|
type=int,
|
||||||
default=None,
|
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(
|
@click.option(
|
||||||
"--tvdb-order",
|
"--tvdb-order",
|
||||||
@@ -638,7 +638,7 @@ class dl:
|
|||||||
default=False,
|
default=False,
|
||||||
help=(
|
help=(
|
||||||
"Override show title and year from external source, and fill in the original language "
|
"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(
|
@click.option(
|
||||||
@@ -871,8 +871,10 @@ class dl:
|
|||||||
if not self.tvdb_id and anime_ids.tvdb_id:
|
if not self.tvdb_id and anime_ids.tvdb_id:
|
||||||
self.tvdb_id = 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):
|
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, or --animeapi to provide a metadata source.")
|
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
|
# Initialize debug logger with service name if debug logging is enabled
|
||||||
if config.debug or logging.root.level == logging.DEBUG:
|
if config.debug or logging.root.level == logging.DEBUG:
|
||||||
@@ -1552,9 +1554,19 @@ class dl:
|
|||||||
enrich_year = imdb_result.year
|
enrich_year = imdb_result.year
|
||||||
enrich_lang = parse_language(imdb_result.original_language)
|
enrich_lang = parse_language(imdb_result.original_language)
|
||||||
break
|
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:
|
if enrich_lang:
|
||||||
self.log.info(f"Original language from metadata: {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:
|
if enrich_title or enrich_year or enrich_lang:
|
||||||
for t in titles if isinstance(titles, (Series, Movies)) else [titles]:
|
for t in titles if isinstance(titles, (Series, Movies)) else [titles]:
|
||||||
|
|||||||
@@ -764,7 +764,7 @@ async def download(request: web.Request) -> web.Response:
|
|||||||
description: Anime database ID via AnimeAPI, e.g. mal:12345 (default - None)
|
description: Anime database ID via AnimeAPI, e.g. mal:12345 (default - None)
|
||||||
enrich:
|
enrich:
|
||||||
type: boolean
|
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:
|
no_folder:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Disable folder creation for TV shows (default - false)
|
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)
|
description: Use this IMDB ID (e.g. tt1375666) for tagging (default - None)
|
||||||
tvdb_id:
|
tvdb_id:
|
||||||
type: integer
|
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:
|
tvdb_order:
|
||||||
type: string
|
type: string
|
||||||
enum: [official, dvd, absolute, alternate, regional]
|
enum: [official, dvd, absolute, alternate, regional]
|
||||||
|
|||||||
Reference in New Issue
Block a user