jellyfin and kodi prebuilt tv

This commit is contained in:
Jesse Bannon 2022-09-29 15:04:25 -07:00
parent 7aab2315c0
commit 78d9236e5f
6 changed files with 163 additions and 8 deletions

View file

@ -22,6 +22,7 @@ from ytdl_sub.downloaders.downloader import DownloaderValidator
from ytdl_sub.entries.entry import Entry from ytdl_sub.entries.entry import Entry
from ytdl_sub.plugins.plugin import Plugin from ytdl_sub.plugins.plugin import Plugin
from ytdl_sub.plugins.plugin import PluginOptions 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.utils.yaml import load_yaml
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
@ -328,8 +329,8 @@ class Preset(StrictDictValidator):
) )
# Merge all presets # Merge all presets
self._value = mergedeep.merge( self._value = dict(
{}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE mergedeep.merge({}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE)
) )
def __init__(self, config: ConfigFile, name: str, value: Any): def __init__(self, config: ConfigFile, name: str, value: Any):
@ -411,3 +412,7 @@ class Preset(StrictDictValidator):
) )
return subscriptions return subscriptions
@property
def yaml(self) -> str:
return dump_yaml({"presets": {self._name: self._value}})

View file

@ -5,6 +5,7 @@ from typing import Dict
import mergedeep 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.prebuilt_presets.tv_show.out import PrebuiltKodiTVShowPresets
from ytdl_sub.utils.yaml import load_yaml 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)) mergedeep.merge(merged_configs, load_yaml(file))
# Get all presets from published preset configs # 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"] return merged_configs["presets"]

View file

@ -32,6 +32,28 @@ presets:
season_number: "{collection_season_number}" season_number: "{collection_season_number}"
season_number_padded: "{collection_season_number_padded}" 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: tv-show-collection-season-1:
generic: generic:
urls: urls:

View file

@ -1,6 +1,7 @@
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Optional
Preset = Dict[str, Any] Preset = Dict[str, Any]
@ -40,8 +41,19 @@ class PrebuiltPresets:
return preset_names return preset_names
@classmethod @classmethod
def get_presets(cls) -> List[Preset]: def get_collection_preset_names(cls) -> List[str]:
return [getattr(cls(), preset_name) for preset_name in cls.get_preset_names()] 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): class PrebuiltKodiTVShowPresets(PrebuiltPresets):
@ -90,3 +102,53 @@ class PrebuiltKodiTVShowPresets(PrebuiltPresets):
Kodi TV Show from a collection of multiple URLs. TODO: finish docstring Kodi TV Show from a collection of multiple URLs. TODO: finish docstring
""" """
return self._tv_show_collection_reversed(name="kodi_tv_show_collection_reversed") 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")

View file

@ -1,4 +1,5 @@
import os.path import os.path
from io import StringIO
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
@ -37,3 +38,9 @@ def load_yaml(file_path: str | Path) -> Dict:
raise InvalidYamlException( raise InvalidYamlException(
f"'{file_path}' has invalid YAML, copy-paste it into a YAML checker to find the issue." f"'{file_path}' has invalid YAML, copy-paste it into a YAML checker to find the issue."
) from yaml_exception ) 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()

View file

@ -1,3 +1,4 @@
from typing import Dict
from typing import List from typing import List
import pytest import pytest
@ -5,6 +6,7 @@ import pytest
from ytdl_sub.config.config_file import ConfigFile from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.config.preset import Preset from ytdl_sub.config.preset import Preset
from ytdl_sub.prebuilt_presets import PrebuiltKodiTVShowPresets from ytdl_sub.prebuilt_presets import PrebuiltKodiTVShowPresets
from ytdl_sub.prebuilt_presets.tv_show.out import PrebuiltJellyfinTVShowPresets
@pytest.fixture @pytest.fixture
@ -15,9 +17,15 @@ def config() -> ConfigFile:
class TestPrebuiltTVShowPresets: class TestPrebuiltTVShowPresets:
@pytest.mark.parametrize("preset_name", PrebuiltKodiTVShowPresets.get_preset_names()) @pytest.mark.parametrize(
def test_presets_compile(self, config, preset_name: str): "preset_name",
Preset.from_dict( [
*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, config=config,
preset_name=f"{preset_name}-test", preset_name=f"{preset_name}-test",
preset_dict={ 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