mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-14 13:05:09 +00:00
feat(dl): add -ce/--continuous-episodes to number seasons on
Each season's episodes continue from the previous season's last episode (S02E05 after a four-episode S01); also on the REST API. A --remote get_tracks refresh no longer resets season, number and part.
This commit is contained in:
@@ -485,6 +485,7 @@ Make a download job. It requires `service` and `title_id`. Every other field is
|
|||||||
| `tag` | string | `null` | Release group tag. |
|
| `tag` | string | `null` | Release group tag. |
|
||||||
| `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 API key. |
|
| `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 API key. |
|
||||||
| `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. |
|
||||||
|
| `continuous_episodes` | boolean | `false` | Number each season on from the season before it, so season two starts at episode 5 after four episodes. Season 0 and episode 0 keep their numbers. `wanted` selects by the new numbers, but the list endpoints show the numbers of the service. |
|
||||||
| `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`. |
|
| `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`. |
|
||||||
| `daily` | boolean | `false` | Treat the title as daily/date-based content and fill missing episode air dates from TVDB. The fill needs `enrich` and a TVDB ID. An air date the service already set is kept. |
|
| `daily` | boolean | `false` | Treat the title as daily/date-based content and fill missing episode air dates from TVDB. The fill needs `enrich` and a TVDB ID. An air date the service already set is kept. |
|
||||||
| `output_dir` | string | `null` | Output directory, relative to the server's downloads directory. The server rejects a path that resolves outside it with `400`. |
|
| `output_dir` | string | `null` | Output directory, relative to the server's downloads directory. The server rejects a path that resolves outside it with `400`. |
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ Keep only certain track types, or skip certain track types.
|
|||||||
| `--enrich` | Overwrite show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
|
| `--enrich` | Overwrite show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
|
||||||
| `--daily` | Treat the title as daily/date-based content and fill missing episode air dates from TVDB. The fill needs `--enrich` and a TVDB ID. See [Daily and date-based content](downloading.md#daily-and-date-based-content). |
|
| `--daily` | Treat the title as daily/date-based content and fill missing episode air dates from TVDB. The fill needs `--enrich` and a TVDB ID. See [Daily and date-based content](downloading.md#daily-and-date-based-content). |
|
||||||
| `--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`. |
|
||||||
|
| `-ce` / `--continuous-episodes` | Number each season on from the season before it. After a season one of four episodes, season two starts at `S02E05`. Season 0 and episode 0 keep their numbers. |
|
||||||
|
|
||||||
!!! note "One ID at a time"
|
!!! note "One ID at a time"
|
||||||
You cannot combine `--tmdb`, `--imdb` and `--tvdb`. Give one and unshackle finds the
|
You cannot combine `--tmdb`, `--imdb` and `--tvdb`. Give one and unshackle finds the
|
||||||
|
|||||||
@@ -1047,6 +1047,7 @@ lookups but keep the IDs you give, set
|
|||||||
| `--anilist` | `--anilist 21` | Use this AniList ID instead of an automatic search. `mal:12345` is also accepted. Needs no API key. |
|
| `--anilist` | `--anilist 21` | Use this AniList ID instead of an automatic search. `mal:12345` is also accepted. Needs no API key. |
|
||||||
| `--enrich` | - | Overwrite the show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
|
| `--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`. |
|
| `--tvdb-order` | `--tvdb-order dvd` | Renumber episodes to a TVDB season order. Needs `tvdb_api_key`. |
|
||||||
|
| `-ce` / `--continuous-episodes` | - | Number each season on from the season before it, so season two starts at `S02E05` after four episodes. |
|
||||||
|
|
||||||
```shell title="Force the right IMDb match, and keep the service's own naming"
|
```shell title="Force the right IMDb match, and keep the service's own naming"
|
||||||
unshackle dl --imdb tt1375666 EXAMPLE 81234567
|
unshackle dl --imdb tt1375666 EXAMPLE 81234567
|
||||||
@@ -1148,6 +1149,23 @@ Available orders are `official` (aired), `dvd`, `absolute`, `alternate`, and `re
|
|||||||
two episodes the same season/episode slot, and so the same filename, unshackle logs an
|
two episodes the same season/episode slot, and so the same filename, unshackle logs an
|
||||||
error and leaves the numbering untouched. Pick an order that covers the whole series.
|
error and leaves the numbering untouched. Pick an order that covers the whole series.
|
||||||
|
|
||||||
|
### Continuous episode numbers
|
||||||
|
|
||||||
|
Some anime release groups number each season on from the season before it. With
|
||||||
|
`--continuous-episodes`, a season two that follows a four-episode season one starts at
|
||||||
|
`S02E05`:
|
||||||
|
|
||||||
|
```shell title="Number season two on from season one"
|
||||||
|
unshackle dl -ce EXAMPLE 81234567
|
||||||
|
```
|
||||||
|
|
||||||
|
The count comes from the episodes the service lists, so download from the series URL, not
|
||||||
|
from a single season. A season the service does not list in your region is not counted.
|
||||||
|
Season 0 and episode 0 keep their numbers, and a season that already starts past the
|
||||||
|
season before it is not changed. The renumbering runs before `--wanted`, so select episodes
|
||||||
|
by their new numbers, for example `-w S02E05`. A whole-season selection such as `-w S05`
|
||||||
|
matches episodes up to E999 only.
|
||||||
|
|
||||||
## Configuration defaults
|
## Configuration defaults
|
||||||
|
|
||||||
You can set every flag on this page as a default in your configuration file so you do not
|
You can set every flag on this page as a default in your configuration file so you do not
|
||||||
@@ -1202,3 +1220,4 @@ authoritative list.
|
|||||||
| `--export` | | Export track info and keys to JSON. |
|
| `--export` | | Export track info and keys to JSON. |
|
||||||
| `--tmdb` / `--imdb` / `--tvdb` / `--anilist` / `--enrich` | | Metadata overrides. |
|
| `--tmdb` / `--imdb` / `--tvdb` / `--anilist` / `--enrich` | | Metadata overrides. |
|
||||||
| `--tvdb-order` | | Renumber episodes to a TVDB season order. |
|
| `--tvdb-order` | | Renumber episodes to a TVDB season order. |
|
||||||
|
| `-ce` / `--continuous-episodes` | | Number each season on from the season before it. |
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ folder-kind keys.
|
|||||||
or `{episode}`. Any series with a TVDB absolute order can use it, anime most often.
|
or `{episode}`. Any series with a TVDB absolute order can use it, anime most often.
|
||||||
Services can supply it, and
|
Services can supply it, and
|
||||||
[`--enrich`](../../guide/downloading.md#metadata-and-tagging) fills it in from TVDB.
|
[`--enrich`](../../guide/downloading.md#metadata-and-tagging) fills it in from TVDB.
|
||||||
Use `{absolute?}` so the name stays clean when it is unknown.
|
Use `{absolute?}` so the name stays clean when it is unknown. To keep the season and
|
||||||
|
continue the episode number instead (`S02E05`), use
|
||||||
|
[`--continuous-episodes`](../../guide/downloading.md#continuous-episode-numbers).
|
||||||
|
|
||||||
!!! tip "`sports` names an event by year or date"
|
!!! tip "`sports` names an event by year or date"
|
||||||
A service marks an event episode as sport, and that episode then uses the `sports`
|
A service marks an event episode as sport, and that episode then uses the `sports`
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
"""--continuous-episodes: each season is numbered on from the season before it."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from unshackle.commands.dl import dl
|
||||||
|
from unshackle.core.remote_service import apply_title_fields
|
||||||
|
from unshackle.core.titles.episode import Episode, Series
|
||||||
|
|
||||||
|
|
||||||
|
class DummyService:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def episode(season: int, number: int, part: int | None = None) -> Episode:
|
||||||
|
return Episode(
|
||||||
|
id_=f"ep-{season:02}-{number:02}-{part}",
|
||||||
|
service=DummyService,
|
||||||
|
title="The Show",
|
||||||
|
season=season,
|
||||||
|
number=number,
|
||||||
|
part=part,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def renumber(series: Series) -> Series:
|
||||||
|
stub = SimpleNamespace(log=SimpleNamespace(info=lambda *a, **k: None))
|
||||||
|
return dl.apply_continuous_episodes(stub, series)
|
||||||
|
|
||||||
|
|
||||||
|
def numbers(slots: list[tuple[int, int]]) -> list[tuple[int, int]]:
|
||||||
|
return [(t.season, t.number) for t in renumber(Series(episode(s, n) for s, n in slots))]
|
||||||
|
|
||||||
|
|
||||||
|
def test_season_two_starts_after_season_one():
|
||||||
|
assert numbers([(1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (3, 1)]) == [
|
||||||
|
(1, 1),
|
||||||
|
(1, 2),
|
||||||
|
(1, 3),
|
||||||
|
(1, 4),
|
||||||
|
(2, 5),
|
||||||
|
(2, 6),
|
||||||
|
(3, 7),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_specials_keep_their_numbers():
|
||||||
|
assert numbers([(0, 1), (1, 1), (1, 2), (2, 1)]) == [(0, 1), (1, 1), (1, 2), (2, 3)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_already_continuous_season_is_kept():
|
||||||
|
assert numbers([(1, 1), (1, 2), (2, 3), (2, 4), (3, 1)]) == [(1, 1), (1, 2), (2, 3), (2, 4), (3, 5)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_episode_zero_keeps_its_number_and_a_second_run_changes_nothing():
|
||||||
|
once = numbers([(1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2)])
|
||||||
|
assert once == [(1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 5), (2, 6)]
|
||||||
|
assert numbers(once) == once
|
||||||
|
|
||||||
|
|
||||||
|
def test_episode_zero_does_not_make_a_continuous_season_move():
|
||||||
|
slots = [(1, 1), (1, 2), (2, 0), (2, 3), (2, 4)]
|
||||||
|
assert numbers(slots) == slots
|
||||||
|
|
||||||
|
|
||||||
|
def test_parts_of_one_episode_move_together():
|
||||||
|
series = renumber(Series([episode(1, 1), episode(1, 2), episode(2, 1, 1), episode(2, 1, 2)]))
|
||||||
|
assert [(t.season, t.number, t.part) for t in series] == [(1, 1, None), (1, 2, None), (2, 3, 1), (2, 3, 2)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_rebuilt_series_finds_a_renumbered_episode():
|
||||||
|
moved = episode(2, 1)
|
||||||
|
series = renumber(Series([episode(1, 1), moved]))
|
||||||
|
assert moved in series
|
||||||
|
assert series.index(moved) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_the_get_tracks_refresh_keeps_the_local_numbers():
|
||||||
|
title = episode(2, 3)
|
||||||
|
apply_title_fields(title, {"season": 2, "number": 1, "name": "Pilot"})
|
||||||
|
assert (title.season, title.number, title.name) == (2, 3, "Pilot")
|
||||||
@@ -20,7 +20,8 @@ from datetime import date, timedelta
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from http.cookiejar import CookieJar, MozillaCookieJar
|
from http.cookiejar import CookieJar, MozillaCookieJar
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from itertools import product
|
from itertools import groupby, product
|
||||||
|
from operator import attrgetter
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Collection, Optional, Sequence, TypedDict, Union, cast
|
from typing import TYPE_CHECKING, Any, Callable, Collection, Optional, Sequence, TypedDict, Union, cast
|
||||||
@@ -995,6 +996,13 @@ class dl:
|
|||||||
help="Renumber episodes to a TVDB season order. Services normally use 'official' (aired) order; "
|
help="Renumber episodes to a TVDB season order. Services normally use 'official' (aired) order; "
|
||||||
"a series' Streaming Order is 'alternate'. Defaults to the tvdb_order config option.",
|
"a series' Streaming Order is 'alternate'. Defaults to the tvdb_order config option.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"-ce",
|
||||||
|
"--continuous-episodes",
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help="Number each season on from the season before it, e.g. S02E05 after a four-episode season one.",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--anilist",
|
"--anilist",
|
||||||
"anilist_id",
|
"anilist_id",
|
||||||
@@ -1284,6 +1292,7 @@ class dl:
|
|||||||
imdb_id: Optional[str] = None,
|
imdb_id: Optional[str] = None,
|
||||||
tvdb_id: Optional[int] = None,
|
tvdb_id: Optional[int] = None,
|
||||||
tvdb_order: Optional[str] = None,
|
tvdb_order: Optional[str] = None,
|
||||||
|
continuous_episodes: bool = False,
|
||||||
anilist_id: Optional[Union[int, str]] = None,
|
anilist_id: Optional[Union[int, str]] = None,
|
||||||
enrich: bool = False,
|
enrich: bool = False,
|
||||||
daily: bool = False,
|
daily: bool = False,
|
||||||
@@ -1322,6 +1331,7 @@ class dl:
|
|||||||
imdb_id = ctx.params.get("imdb_id", imdb_id)
|
imdb_id = ctx.params.get("imdb_id", imdb_id)
|
||||||
tvdb_id = ctx.params.get("tvdb_id", tvdb_id)
|
tvdb_id = ctx.params.get("tvdb_id", tvdb_id)
|
||||||
tvdb_order = ctx.params.get("tvdb_order", tvdb_order)
|
tvdb_order = ctx.params.get("tvdb_order", tvdb_order)
|
||||||
|
continuous_episodes = ctx.params.get("continuous_episodes", continuous_episodes)
|
||||||
anilist_id = ctx.params.get("anilist_id", anilist_id)
|
anilist_id = ctx.params.get("anilist_id", anilist_id)
|
||||||
enrich = ctx.params.get("enrich", enrich)
|
enrich = ctx.params.get("enrich", enrich)
|
||||||
daily = ctx.params.get("daily", daily)
|
daily = ctx.params.get("daily", daily)
|
||||||
@@ -1337,6 +1347,7 @@ class dl:
|
|||||||
self.tvdb_order = (tvdb_order or config.tvdb_order or "").lower()
|
self.tvdb_order = (tvdb_order or config.tvdb_order or "").lower()
|
||||||
if self.tvdb_order and self.tvdb_order not in SEASON_TYPES:
|
if self.tvdb_order and self.tvdb_order not in SEASON_TYPES:
|
||||||
raise click.UsageError(f"tvdb_order must be one of {', '.join(SEASON_TYPES)}, not {self.tvdb_order!r}")
|
raise click.UsageError(f"tvdb_order must be one of {', '.join(SEASON_TYPES)}, not {self.tvdb_order!r}")
|
||||||
|
self.continuous_episodes = continuous_episodes
|
||||||
self.enrich = enrich
|
self.enrich = enrich
|
||||||
self.daily = daily
|
self.daily = daily
|
||||||
self.output_dir = output_dir
|
self.output_dir = output_dir
|
||||||
@@ -2063,6 +2074,9 @@ class dl:
|
|||||||
if self.tvdb_order and isinstance(titles, Series):
|
if self.tvdb_order and isinstance(titles, Series):
|
||||||
titles = self.apply_tvdb_order(titles, title_cacher, cache_title_id, cache_region, cache_account_hash)
|
titles = self.apply_tvdb_order(titles, title_cacher, cache_title_id, cache_region, cache_account_hash)
|
||||||
|
|
||||||
|
if self.continuous_episodes and isinstance(titles, Series):
|
||||||
|
titles = self.apply_continuous_episodes(titles)
|
||||||
|
|
||||||
console.print(Padding(Rule(f"[rule.text]{titles.__class__.__name__}: {titles}"), (1, 2)))
|
console.print(Padding(Rule(f"[rule.text]{titles.__class__.__name__}: {titles}"), (1, 2)))
|
||||||
console.print(Padding(titles.tree(verbose=list_titles), (0, 5)))
|
console.print(Padding(titles.tree(verbose=list_titles), (0, 5)))
|
||||||
|
|
||||||
@@ -4099,6 +4113,26 @@ class dl:
|
|||||||
if filled:
|
if filled:
|
||||||
self.log.info("Filled absolute numbers for %d episode(s) from TVDB (ID %s).", filled, tvdb_id)
|
self.log.info("Filled absolute numbers for %d episode(s) from TVDB (ID %s).", filled, tvdb_id)
|
||||||
|
|
||||||
|
def apply_continuous_episodes(self, titles: Series) -> Series:
|
||||||
|
"""Number each season on from the last episode of the season before it.
|
||||||
|
|
||||||
|
Season 0 and episode 0 keep their numbers, and a season that already starts past the season before it is kept.
|
||||||
|
The Series is rebuilt, as it does not re-sort an episode whose number changed.
|
||||||
|
"""
|
||||||
|
last = 0
|
||||||
|
renumbered = 0
|
||||||
|
for season, group in groupby(titles, key=attrgetter("season")):
|
||||||
|
episodes = [t for t in group if t.number]
|
||||||
|
if not season or not episodes:
|
||||||
|
continue
|
||||||
|
if min(t.number for t in episodes) <= last:
|
||||||
|
for t in episodes:
|
||||||
|
t.number += last
|
||||||
|
renumbered += len(episodes)
|
||||||
|
last = max(t.number for t in episodes)
|
||||||
|
self.log.info("Numbered %d episode(s) on from the season before.", renumbered)
|
||||||
|
return type(titles)(list(titles))
|
||||||
|
|
||||||
def apply_tvdb_order(
|
def apply_tvdb_order(
|
||||||
self,
|
self,
|
||||||
titles: Series,
|
titles: Series,
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ DEFAULT_DOWNLOAD_PARAMS = {
|
|||||||
"imdb_id": None,
|
"imdb_id": None,
|
||||||
"tvdb_id": None,
|
"tvdb_id": None,
|
||||||
"tvdb_order": None,
|
"tvdb_order": None,
|
||||||
|
"continuous_episodes": False,
|
||||||
"anilist_id": None,
|
"anilist_id": None,
|
||||||
"enrich": False,
|
"enrich": False,
|
||||||
"daily": False,
|
"daily": False,
|
||||||
|
|||||||
@@ -1184,6 +1184,9 @@ async def download(request: web.Request) -> web.Response:
|
|||||||
type: string
|
type: string
|
||||||
enum: [official, dvd, absolute, alternate, regional]
|
enum: [official, dvd, absolute, alternate, regional]
|
||||||
description: Renumber episodes to a TVDB season order (default - the tvdb_order config option)
|
description: Renumber episodes to a TVDB season order (default - the tvdb_order config option)
|
||||||
|
continuous_episodes:
|
||||||
|
type: boolean
|
||||||
|
description: Number each season on from the season before it, e.g. S02E05 after a four-episode season one (default - false)
|
||||||
output_dir:
|
output_dir:
|
||||||
type: string
|
type: string
|
||||||
description: >
|
description: >
|
||||||
|
|||||||
@@ -552,11 +552,12 @@ def parse_air_date(value: Any) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
def apply_title_fields(title: Title_T, info: Dict[str, Any]) -> None:
|
def apply_title_fields(title: Title_T, info: Dict[str, Any]) -> None:
|
||||||
"""Copy every serialized title field onto a local title.
|
"""Copy the serialized metadata fields onto a local title.
|
||||||
|
|
||||||
A service can change its title inside ``get_tracks`` (a corrected original language,
|
A service can change its title inside ``get_tracks`` (a corrected original language,
|
||||||
a real episode name, an air date), so the client re-applies the server's view of the
|
a real episode name, an air date), so the client re-applies the server's view of the
|
||||||
title after every call that returns one.
|
title after every call that returns one. Season, number and part are set only by
|
||||||
|
``build_title``, as dl can renumber them after the listing.
|
||||||
"""
|
"""
|
||||||
if info.get("language"):
|
if info.get("language"):
|
||||||
title.language = Language.get(info["language"])
|
title.language = Language.get(info["language"])
|
||||||
@@ -570,9 +571,8 @@ def apply_title_fields(title: Title_T, info: Dict[str, Any]) -> None:
|
|||||||
title.title = info["series_title"]
|
title.title = info["series_title"]
|
||||||
if "name" in info:
|
if "name" in info:
|
||||||
title.name = episode_name(info["name"], title.title)
|
title.name = episode_name(info["name"], title.title)
|
||||||
for key in ("season", "number", "part", "absolute"):
|
if info.get("absolute") is not None:
|
||||||
if info.get(key) is not None:
|
title.absolute = info["absolute"]
|
||||||
setattr(title, key, info[key])
|
|
||||||
if info.get("air_date") is not None:
|
if info.get("air_date") is not None:
|
||||||
title.air_date = parse_air_date(info["air_date"])
|
title.air_date = parse_air_date(info["air_date"])
|
||||||
elif info.get("name"):
|
elif info.get("name"):
|
||||||
@@ -602,6 +602,7 @@ def build_title(info: Dict[str, Any], service_tag: str, fallback_id: str) -> Tit
|
|||||||
title=info.get("series_title", "Unknown"),
|
title=info.get("series_title", "Unknown"),
|
||||||
season=info.get("season", 0),
|
season=info.get("season", 0),
|
||||||
number=info.get("number", 0),
|
number=info.get("number", 0),
|
||||||
|
part=info.get("part"),
|
||||||
language=lang,
|
language=lang,
|
||||||
)
|
)
|
||||||
elif title_type == "movie":
|
elif title_type == "movie":
|
||||||
|
|||||||
Reference in New Issue
Block a user