jellyfin and kodi prebuilt tv
This commit is contained in:
parent
7aab2315c0
commit
78d9236e5f
6 changed files with 163 additions and 8 deletions
|
|
@ -22,6 +22,7 @@ from ytdl_sub.downloaders.downloader import DownloaderValidator
|
|||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.plugins.plugin import Plugin
|
||||
from ytdl_sub.plugins.plugin import PluginOptions
|
||||
from ytdl_sub.utils.yaml import dump_yaml
|
||||
from ytdl_sub.utils.yaml import load_yaml
|
||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
|
||||
|
|
@ -328,8 +329,8 @@ class Preset(StrictDictValidator):
|
|||
)
|
||||
|
||||
# Merge all presets
|
||||
self._value = mergedeep.merge(
|
||||
{}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE
|
||||
self._value = dict(
|
||||
mergedeep.merge({}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE)
|
||||
)
|
||||
|
||||
def __init__(self, config: ConfigFile, name: str, value: Any):
|
||||
|
|
@ -411,3 +412,7 @@ class Preset(StrictDictValidator):
|
|||
)
|
||||
|
||||
return subscriptions
|
||||
|
||||
@property
|
||||
def yaml(self) -> str:
|
||||
return dump_yaml({"presets": {self._name: self._value}})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from typing import Dict
|
|||
|
||||
import mergedeep
|
||||
|
||||
from ytdl_sub.prebuilt_presets.tv_show.out import PrebuiltJellyfinTVShowPresets
|
||||
from ytdl_sub.prebuilt_presets.tv_show.out import PrebuiltKodiTVShowPresets
|
||||
from ytdl_sub.utils.yaml import load_yaml
|
||||
|
||||
|
|
@ -18,7 +19,11 @@ def _merge_presets() -> Dict[str, Any]:
|
|||
mergedeep.merge(merged_configs, load_yaml(file))
|
||||
|
||||
# Get all presets from published preset configs
|
||||
mergedeep.merge(merged_configs, *PrebuiltKodiTVShowPresets.get_presets())
|
||||
mergedeep.merge(
|
||||
merged_configs,
|
||||
*PrebuiltKodiTVShowPresets.get_presets(),
|
||||
*PrebuiltJellyfinTVShowPresets.get_presets()
|
||||
)
|
||||
|
||||
return merged_configs["presets"]
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,28 @@ presets:
|
|||
season_number: "{collection_season_number}"
|
||||
season_number_padded: "{collection_season_number_padded}"
|
||||
|
||||
tv-show-collection-season-1-youtube-channel:
|
||||
generic:
|
||||
urls:
|
||||
- url: "{collection_season_1_url}"
|
||||
variables:
|
||||
collection_season_number: "1"
|
||||
collection_season_number_padded: "01"
|
||||
playlist_thumbnails:
|
||||
- name: "{season_poster_file_name}"
|
||||
uid: "latest_entry"
|
||||
- name: "{tv_show_poster_file_name}"
|
||||
uid: "avatar_uncropped"
|
||||
- name: "{tv_show_fanart_file_name}"
|
||||
uid: "banner_uncropped"
|
||||
|
||||
output_directory_nfo_tags:
|
||||
tags:
|
||||
namedseason:
|
||||
- tag: "{collection_season_1_name}"
|
||||
attributes:
|
||||
number: "1"
|
||||
|
||||
tv-show-collection-season-1:
|
||||
generic:
|
||||
urls:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
Preset = Dict[str, Any]
|
||||
|
||||
|
|
@ -40,8 +41,19 @@ class PrebuiltPresets:
|
|||
return preset_names
|
||||
|
||||
@classmethod
|
||||
def get_presets(cls) -> List[Preset]:
|
||||
return [getattr(cls(), preset_name) for preset_name in cls.get_preset_names()]
|
||||
def get_collection_preset_names(cls) -> List[str]:
|
||||
return [name for name in cls.get_preset_names() if "collection" in name]
|
||||
|
||||
@classmethod
|
||||
def get_non_collection_preset_names(cls) -> List[str]:
|
||||
return [name for name in cls.get_preset_names() if "collection" not in name]
|
||||
|
||||
@classmethod
|
||||
def get_presets(cls, preset_names: Optional[List[str]] = None) -> List[Preset]:
|
||||
if preset_names is None:
|
||||
preset_names = cls.get_preset_names()
|
||||
|
||||
return [getattr(cls(), preset_name) for preset_name in preset_names]
|
||||
|
||||
|
||||
class PrebuiltKodiTVShowPresets(PrebuiltPresets):
|
||||
|
|
@ -90,3 +102,53 @@ class PrebuiltKodiTVShowPresets(PrebuiltPresets):
|
|||
Kodi TV Show from a collection of multiple URLs. TODO: finish docstring
|
||||
"""
|
||||
return self._tv_show_collection_reversed(name="kodi_tv_show_collection_reversed")
|
||||
|
||||
|
||||
class PrebuiltJellyfinTVShowPresets(PrebuiltPresets):
|
||||
BASE_PRESET = "jellyfin-tv-show"
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_url(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show with seasons as years, episodes ordered by upload date
|
||||
"""
|
||||
return self._tv_show_url(name="jellyfin_tv_show_url")
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_url_reversed(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show with seasons as years, episodes ordered by upload date in descending order
|
||||
(more recent uploads have smaller episode number)
|
||||
"""
|
||||
return self._tv_show_url_reversed(name="jellyfin_tv_show_url_reversed")
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_youtube_channel(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show from a YouTube channel with seasons as years, episodes ordered by upload date
|
||||
"""
|
||||
return self._tv_show_youtube_channel(name="jellyfin_tv_show_youtube_channel")
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_youtube_channel_reversed(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show from a YouTube channel with seasons as years, episodes ordered by upload date
|
||||
in descending order (more recent uploads have smaller episode number)
|
||||
"""
|
||||
return self._tv_show_youtube_channel_reversed(
|
||||
name="jellyfin_tv_show_youtube_channel_reversed"
|
||||
)
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_collection(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show from a collection of multiple URLs. TODO: finish docstring
|
||||
"""
|
||||
return self._tv_show_collection(name="jellyfin_tv_show_collection")
|
||||
|
||||
@property
|
||||
def jellyfin_tv_show_collection_reversed(self) -> Preset:
|
||||
"""
|
||||
Kodi TV Show from a collection of multiple URLs. TODO: finish docstring
|
||||
"""
|
||||
return self._tv_show_collection_reversed(name="jellyfin_tv_show_collection_reversed")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import os.path
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
|
|
@ -37,3 +38,9 @@ def load_yaml(file_path: str | Path) -> Dict:
|
|||
raise InvalidYamlException(
|
||||
f"'{file_path}' has invalid YAML, copy-paste it into a YAML checker to find the issue."
|
||||
) from yaml_exception
|
||||
|
||||
|
||||
def dump_yaml(to_dump: Dict) -> str:
|
||||
string_io = StringIO()
|
||||
yaml.safe_dump(to_dump, string_io, indent=2, allow_unicode=True, sort_keys=True)
|
||||
return string_io.getvalue()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from typing import Dict
|
||||
from typing import List
|
||||
|
||||
import pytest
|
||||
|
|
@ -5,6 +6,7 @@ import pytest
|
|||
from ytdl_sub.config.config_file import ConfigFile
|
||||
from ytdl_sub.config.preset import Preset
|
||||
from ytdl_sub.prebuilt_presets import PrebuiltKodiTVShowPresets
|
||||
from ytdl_sub.prebuilt_presets.tv_show.out import PrebuiltJellyfinTVShowPresets
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -15,9 +17,15 @@ def config() -> ConfigFile:
|
|||
|
||||
|
||||
class TestPrebuiltTVShowPresets:
|
||||
@pytest.mark.parametrize("preset_name", PrebuiltKodiTVShowPresets.get_preset_names())
|
||||
def test_presets_compile(self, config, preset_name: str):
|
||||
Preset.from_dict(
|
||||
@pytest.mark.parametrize(
|
||||
"preset_name",
|
||||
[
|
||||
*PrebuiltKodiTVShowPresets.get_non_collection_preset_names(),
|
||||
*PrebuiltJellyfinTVShowPresets.get_non_collection_preset_names(),
|
||||
],
|
||||
)
|
||||
def test_non_collection_presets_compile(self, config, preset_name: str):
|
||||
preset = Preset.from_dict(
|
||||
config=config,
|
||||
preset_name=f"{preset_name}-test",
|
||||
preset_dict={
|
||||
|
|
@ -29,3 +37,49 @@ class TestPrebuiltTVShowPresets:
|
|||
},
|
||||
},
|
||||
)
|
||||
assert preset
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"preset_name",
|
||||
[
|
||||
*PrebuiltKodiTVShowPresets.get_collection_preset_names(),
|
||||
*PrebuiltJellyfinTVShowPresets.get_collection_preset_names(),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"season_indices", [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5], [4, 1]]
|
||||
)
|
||||
@pytest.mark.parametrize("is_season_1_youtube_channel", [True, False])
|
||||
def test_collection_presets_compile(
|
||||
self, config, preset_name: str, season_indices: List[int], is_season_1_youtube_channel: bool
|
||||
):
|
||||
parent_presets: List[str] = [preset_name]
|
||||
overrides: Dict[str, str] = {}
|
||||
for season_index in season_indices:
|
||||
parent_presets.append(f"tv-show-collection-season-{season_index}")
|
||||
if season_index == 1 and is_season_1_youtube_channel:
|
||||
parent_presets[-1] += "-youtube-channel"
|
||||
|
||||
overrides = dict(
|
||||
overrides,
|
||||
**{
|
||||
f"collection_season_{season_index}_name": f"Season {season_index}",
|
||||
f"collection_season_{season_index}_url": f"https://season.{season_index}.com",
|
||||
},
|
||||
)
|
||||
|
||||
preset = Preset.from_dict(
|
||||
config=config,
|
||||
preset_name=f"{preset_name}-test",
|
||||
preset_dict={
|
||||
"preset": parent_presets,
|
||||
"overrides": dict(
|
||||
overrides,
|
||||
**{
|
||||
"tv_show_name": "test tv show",
|
||||
"tv_show_directory": "output_path",
|
||||
},
|
||||
),
|
||||
},
|
||||
)
|
||||
assert preset
|
||||
|
|
|
|||
Loading…
Reference in a new issue