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.
This commit is contained in:
imSp4rky
2026-08-11 16:42:05 -06:00
parent ebb4e74318
commit 72581ba5d2
24 changed files with 794 additions and 196 deletions
+2 -2
View File
@@ -392,9 +392,9 @@ Create a download job. Requires `service` and `title_id`; every other field is a
| `best_available` | boolean | `false` | Fall back to best available. |
| `repack` | boolean | `false` | Add REPACK tag. |
| `tag` | string | `null` | Release group tag. |
| `tmdb_id` / `imdb_id` / `tvdb_id` / `animeapi_id` | - | `null` | External ID overrides. Each resolves its metadata directly instead of by a title search, and is used for tagging. Set `enrich` to also take the title, year and original language. Give at most one of `tmdb_id`, `imdb_id` and `tvdb_id`, since unshackle resolves the others from it. Sending two returns `400`. `animeapi_id` still combines with one of them. `tmdb_id` and `tvdb_id` must be positive integers and `imdb_id` must look like `tt1375666`, or the request returns `400`. An ID whose provider is unconfigured, such as `tmdb_id` with no `tmdb_api_key`, fails the job rather than returning `400`. |
| `tmdb_id` / `imdb_id` / `tvdb_id` / `anilist_id` | - | `null` | External ID overrides. Each resolves its metadata directly instead of by a title search, and is used for tagging. Set `enrich` to also take the title, year and original language. Give at most one of `tmdb_id`, `imdb_id` and `tvdb_id`, since unshackle resolves the others from it. Sending two returns `400`. `anilist_id` still combines with one of them. `tmdb_id` and `tvdb_id` must be positive integers, `imdb_id` must look like `tt1375666`, and `anilist_id` must be a positive integer or a string like `mal:12345`, or the request returns `400`. An ID whose provider is unconfigured, such as `tmdb_id` with no `tmdb_api_key`, fails the job rather than returning `400`. `anilist_id` needs no key. |
| `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` | Overwrite title, year and original language with the external source's. Needs one of `tmdb_id`, `imdb_id`, `tvdb_id` or `animeapi_id`. Without one the job fails instead of returning `400`. |
| `enrich` | boolean | `false` | Overwrite title, year and original language with the external source's. Needs one of `tmdb_id`, `imdb_id`, `tvdb_id` or `anilist_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. |
+5 -4
View File
@@ -153,15 +153,16 @@ Keep only certain track types, or skip certain track types. Attachments are alwa
| `--tmdb` | TMDB ID (integer). Used for the tags. Skips the title search. `--enrich` reads it too. Needs `tmdb_api_key`. |
| `--imdb` | IMDb ID, e.g. `tt1375666`. Used for the tags. Skips the title search. `--enrich` reads it too. Needs no key. |
| `--tvdb` | TVDB ID (integer). Used for the tags. Skips the series lookup that `--tvdb-order` would otherwise do. `--enrich` reads it too. Needs `tvdb_api_key`. |
| `--animeapi` | AnimeAPI ID, e.g. `mal:12345` or `anilist:98765` (defaults to MAL). Back-fills TMDB/IMDb/TVDB. |
| `--enrich` | Overwrite show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--animeapi`. |
| `--anilist` | AniList ID (integer), e.g. `--anilist 21`. A MyAnimeList ID is accepted as `mal:12345` and resolved to the AniList entry. Used for the tags. Skips the title search. `--enrich` reads it too. Needs no key. |
| `--enrich` | Overwrite show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
| `--tvdb-order` | Renumber episodes to a TVDB season order: `official` (aired), `dvd`, `absolute`, `alternate`, or `regional`. Needs `tvdb_api_key`. |
!!! note "One ID at a time"
`--tmdb`, `--imdb` and `--tvdb` cannot be combined. Give one and unshackle resolves the
others from it, writing all three to the tags. `--animeapi` still pairs with one of them.
others from it, writing all three to the tags. `--anilist` is outside that rule and pairs
with one of them, which is how you attach a western ID to an anime title.
An ID with no provider that can resolve it, such as `--tmdb` with no `tmdb_api_key`, fails
before the download starts.
before the download starts. `--anilist` needs no key, so it never fails this check.
#### Episode ordering
+19 -9
View File
@@ -633,8 +633,8 @@ lookups but keep the IDs you give, set
| `--tmdb` | `--tmdb 27205` | Use this TMDB ID instead of an automatic search. Needs `tmdb_api_key`. |
| `--imdb` | `--imdb tt1375666` | Use this IMDb ID instead of an automatic search. Needs no key. |
| `--tvdb` | `--tvdb 73871` | Use this TVDB ID instead of looking the series up. Needs `tvdb_api_key`. |
| `--animeapi` | `--animeapi mal:12345` | Resolve via AnimeAPI (`mal:`/`anilist:` prefix; defaults to MAL). |
| `--enrich` | - | Overwrite the show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--animeapi`. |
| `--anilist` | `--anilist 21` | Use this AniList ID instead of an automatic search. `mal:12345` is also accepted. Needs no key. |
| `--enrich` | - | Overwrite the show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
| `--tvdb-order` | `--tvdb-order dvd` | Renumber episodes to a TVDB season order. Needs `tvdb_api_key`. |
```shell title="Force the right IMDb match, and keep the service's own naming"
@@ -657,10 +657,21 @@ which ID you gave. Every provider except `imdb` only runs when its key is config
| OMDb | `omdb_api_key` | yes | yes, as an English name such as `Korean` | IMDb |
| IMDb | *(no key needed)* | yes | yes, alpha-2 such as `ko` | IMDb |
| SIMKL | `simkl_client_id` | yes | no, it publishes a country and no language | IMDb, TMDB, TVDB |
| AniList | *(no key needed)* | yes | yes, worked out from the country of origin, such as `ja` | AniList |
Whatever the tag looks like, unshackle normalises it before use, so `ko`, `kor` and `Korean`
all end up as the same language.
!!! note "AniList only answers for anime"
It is last in the default provider order and returns nothing for a title that is not
anime. The only cost is one search that misses. Put `anilist` earlier in
[`metadata_providers`](../reference/configuration/misc.md#external-api-keys) if you mostly
download anime. Which of the three AniList title variants is used is set by
[`anilist_title_language`](../reference/configuration/misc.md#external-api-keys).
A service can also mark its titles as anime. For those titles unshackle tries AniList
first. When AniList has no match, it falls through to the normal order.
Each ID goes to the providers that read that kind of ID, in
[`metadata_providers`](../reference/configuration/misc.md#external-api-keys) order:
@@ -668,15 +679,14 @@ Each ID goes to the providers that read that kind of ID, in
- `--imdb` reads IMDb, then falls back to OMDb. IMDb needs no key, so this path works out of
the box, and an OMDb key only matters when IMDb has no answer.
- `--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`.
- `--anilist` reads AniList, which needs no key. It supplies the title, the year and the
original language, and writes an AniList tag. It knows no TMDB, IMDB or TVDB ID, so pair it
with `--tmdb`, `--imdb` or `--tvdb` when you want those tags as well.
Give **one** of `--tmdb`, `--imdb` and `--tvdb`. They cannot be combined, because one ID does
the job on its own: unshackle resolves the other two from it and writes all three to the tags.
Passing two is an error rather than a silent choice between them. `--animeapi` is the exception
and still pairs with one of the three, which is what to do when AnimeAPI knows no IDs for the
show.
Passing two is an error rather than a silent choice between them. `--anilist` is the exception
and still pairs with one of the three, which is how you tag an anime title with a western ID.
The ID you give must have a provider that can actually resolve it, so unshackle checks that
before it downloads anything. `--tmdb` without `tmdb_api_key`, or an ID whose providers your
@@ -760,5 +770,5 @@ authoritative list.
| `--list` / `--list-titles` / `--skip-dl` | | Dry runs. |
| `--cdm-only` / `--vaults-only` | | Key source control. |
| `--export` | | Export track info and keys to JSON. |
| `--tmdb` / `--imdb` / `--tvdb` / `--animeapi` / `--enrich` | | Metadata overrides. |
| `--tmdb` / `--imdb` / `--tvdb` / `--anilist` / `--enrich` | | Metadata overrides. |
| `--tvdb-order` | | Renumber episodes to a TVDB season order. |
+1 -1
View File
@@ -18,7 +18,7 @@ after the flag's internal destination rather than its visible name; set these ex
| `--list` | `list` |
| `--tmdb` | `tmdb_id` |
| `--imdb` | `imdb_id` |
| `--animeapi` | `animeapi_id` |
| `--anilist` | `anilist_id` |
| `-naa` / `--noatmos` | `no_atmos` |
| `-o` / `--output` | `output_dir` |
| `--cdm-only` / `--vaults-only` | `cdm_only` |
+1 -1
View File
@@ -36,7 +36,7 @@ Where the file lives, how unshackle discovers it, and how to edit it:
| [DRM & CDM](drm.md) | `cdm`, `remote_cdm`, `decryption` |
| [Network & proxy](network.md) | `network`, `headers`, `proxy_providers` |
| [Key vaults](vaults.md) | `key_vaults`, `vault_timeout` |
| [External API keys](misc.md#external-api-keys) | `omdb_api_key`, `tmdb_api_key`, `tvdb_api_key`, `tvdb_pin`, `tvdb_order`, `simkl_client_id`, `metadata_providers`, `disable_metadata`, `decrypt_labs_api_key`, `ipinfo_api_key` |
| [External API keys](misc.md#external-api-keys) | `omdb_api_key`, `tmdb_api_key`, `tvdb_api_key`, `tvdb_pin`, `tvdb_order`, `simkl_client_id`, `metadata_providers`, `anilist_title_language`, `disable_metadata`, `decrypt_labs_api_key`, `ipinfo_api_key` |
| [Caching & updates](misc.md#caching-updates) | `title_cache_enabled`, `title_cache_time`, `title_cache_max_retention`, `update_checks`, `update_check_interval` |
| [Logging, privacy & debug](misc.md#logging-privacy-debug) | `redact_paths`, `debug`, `debug_keys`, `debug_requests`, `set_terminal_bg` |
| [Deprecated & removed](misc.md#deprecated-removed-keys) | `curl_impersonate`, `downloader`, `scene_naming` |
+27 -10
View File
@@ -17,6 +17,7 @@ each metadata provider can actually answer with, see
| `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 or map | *(see below)* | Metadata providers to use, in the order they are tried. |
| `anilist_title_language` | str | `english` | Which AniList title variant to use: `english`, `romaji`, or `native`. |
| `disable_metadata` | bool | `false` | Set to `true` to stop all automatic metadata lookups. An ID you give yourself is still looked up. |
| `decrypt_labs_api_key` | str | `""` | Global Decrypt Labs API key (used by remote CDM / vault). |
| `ipinfo_api_key` | str | `""` | ipinfo.io API key for IP/region lookups. |
@@ -33,16 +34,32 @@ each metadata provider can actually answer with, see
ignored with a warning. Leave it unset for the default order:
```yaml
metadata_providers: [imdb, omdb, simkl, tmdb, tvdb]
metadata_providers: [imdb, omdb, simkl, tmdb, tvdb, anilist]
```
Whatever the order, a provider is skipped when its API key is missing. `imdb` needs no
key, so it is always available. Available names are `imdb`, `omdb`, `simkl`, `tmdb`, and
`tvdb`. The old name `imdbapi` still works and is read as `imdb`.
Whatever the order, unshackle skips a provider that has no API key. `imdb` and
`anilist` need no key, so they are always available. Available names are `imdb`, `omdb`,
`simkl`, `tmdb`, `tvdb`, and `anilist`. The old name `imdbapi` still works and is read as
`imdb`.
`anilist` is last in the default order. It only answers for anime and returns nothing for
anything else, so it is harmless there. Move it earlier if you mostly download anime.
The order applies to a title search and to a direct lookup of an ID you give with
`--tmdb`, `--imdb`, or `--tvdb`. A supplied ID always goes to the providers that read
that kind of ID, and it never falls back to a title search.
`--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. A supplied ID always goes to the providers
that read that kind of ID, and it never falls back to a title search.
!!! note "Which AniList title `anilist_title_language` picks"
AniList holds up to three titles for a work: the English one, the romaji transliteration,
and the native one. This option says which of them unshackle uses:
```yaml
anilist_title_language: romaji
```
A work does not always have all three. When the one you asked for is unset on AniList's
side, unshackle falls back in the order english → romaji → native. It takes the first one
that is set, so you always get a title.
!!! note "What `disable_metadata` turns off"
With `disable_metadata: true`, unshackle never contacts a metadata provider on its own.
@@ -53,10 +70,10 @@ each metadata provider can actually answer with, see
disable_metadata: true
```
An ID you give with `--tmdb`, `--imdb`, `--tvdb`, or `--animeapi` is your permission to use
that ID, so it still works: the ID is looked up directly, the other IDs are cross
referenced from it, and `--enrich` still reads that source. Only the lookups you did not
ask for are stopped.
An ID you give with `--tmdb`, `--imdb`, `--tvdb`, or `--anilist` is your permission to use
that ID. That lookup still works. unshackle looks the ID up directly, cross references the other
IDs from it, and `--enrich` still reads that source. Only the lookups you did not ask for
are stopped.
!!! note "The `imdb` provider needs no key"
It replaces the earlier `imdbapi` provider, which read a third-party mirror