tests WIP
This commit is contained in:
parent
bc839936a6
commit
220076d7d1
14 changed files with 320 additions and 278 deletions
|
|
@ -3,6 +3,9 @@ from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import mergedeep
|
||||||
|
|
||||||
|
from ytdl_sub.prebuilt_presets import PREBUILT_PRESETS
|
||||||
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.validators import LiteralDictValidator
|
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||||
|
|
@ -77,9 +80,21 @@ class ConfigFile(StrictDictValidator):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
self.config_options = self._validate_key("configuration", ConfigOptions)
|
self.config_options = self._validate_key("configuration", ConfigOptions)
|
||||||
|
|
||||||
|
prebuilt_presets = PREBUILT_PRESETS
|
||||||
|
|
||||||
# Make sure presets is a dictionary. Will be validated in `PresetValidator`
|
# Make sure presets is a dictionary. Will be validated in `PresetValidator`
|
||||||
self.presets = self._validate_key("presets", LiteralDictValidator)
|
self.presets = self._validate_key("presets", LiteralDictValidator)
|
||||||
|
|
||||||
|
# Ensure custom presets do not collide with prebuilt presets
|
||||||
|
for preset_name in self.presets.keys:
|
||||||
|
if preset_name in prebuilt_presets:
|
||||||
|
raise self._validation_exception(
|
||||||
|
f"preset name '{preset_name}' conflicts with a prebuilt preset"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Merge prebuilt presets into the config so custom presets can use them
|
||||||
|
mergedeep.merge(self.presets._value, prebuilt_presets)
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""
|
"""
|
||||||
Configures things (umask, pgid) prior to any downloading
|
Configures things (umask, pgid) prior to any downloading
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
import mergedeep
|
||||||
|
|
||||||
|
from ytdl_sub.prebuilt_presets.tv_show.out import PrebuiltKodiTVShowPresets
|
||||||
|
from ytdl_sub.utils.yaml import load_yaml
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_presets() -> Dict[str, Any]:
|
||||||
|
merged_configs: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
# Get all presets from the loose YAML files
|
||||||
|
for file in pathlib.Path(__file__).parent.resolve().rglob("*"):
|
||||||
|
if file.is_file() and file.name.endswith('yaml'):
|
||||||
|
mergedeep.merge(merged_configs, load_yaml(file))
|
||||||
|
|
||||||
|
# Get all presets from published preset configs
|
||||||
|
mergedeep.merge(merged_configs, *PrebuiltKodiTVShowPresets.get_presets())
|
||||||
|
|
||||||
|
return merged_configs['presets']
|
||||||
|
|
||||||
|
PREBUILT_PRESETS: Dict[str, Any] = _merge_presets()
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
presets:
|
presets:
|
||||||
|
|
||||||
kodi-safe:
|
kodi-safe-nfo:
|
||||||
nfo_tags:
|
nfo_tags:
|
||||||
kodi_safe: True
|
kodi_safe: True
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
kodi_safe: True
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
presets:
|
|
||||||
|
|
||||||
plex:
|
|
||||||
overrides:
|
|
||||||
season_poster_prefix: ""
|
|
||||||
|
|
||||||
jellyfin:
|
|
||||||
overrides:
|
|
||||||
poster_file_name: "poster"
|
|
||||||
season_poster_prefix: "-poster"
|
|
||||||
|
|
||||||
kodi:
|
|
||||||
presets:
|
|
||||||
- "jellyfin"
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
presets:
|
presets:
|
||||||
|
|
||||||
kodi-music-videos:
|
kodi-music-videos:
|
||||||
presets:
|
preset:
|
||||||
- "jellyfin-music-videos"
|
- "jellyfin-music-videos"
|
||||||
- "kodi-safe"
|
- "kodi-safe"
|
||||||
|
|
|
||||||
0
src/ytdl_sub/prebuilt_presets/tv_show/__init__.py
Normal file
0
src/ytdl_sub/prebuilt_presets/tv_show/__init__.py
Normal file
|
|
@ -1,5 +1,7 @@
|
||||||
presets:
|
presets:
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
tv-show-url:
|
tv-show-url:
|
||||||
generic:
|
generic:
|
||||||
download_strategy: "source"
|
download_strategy: "source"
|
||||||
|
|
@ -22,17 +24,100 @@ presets:
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
tv-show-collection-youtube-channel-season:
|
tv-show-collection:
|
||||||
generic:
|
generic:
|
||||||
download_strategy: "collection"
|
download_strategy: "collection"
|
||||||
urls:
|
|
||||||
- url: "{youtube_channel_url}"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "{season_number_padded}{season_poster_prefix}.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
- name: "{poster_file_name}"
|
|
||||||
uid: "avatar_uncropped"
|
|
||||||
- name: "{fanart_file_name}"
|
|
||||||
uid: "banner_uncropped"
|
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
|
season_number: "{collection_season_number}"
|
||||||
|
season_number_padded: "{collection_season_number_padded}"
|
||||||
|
|
||||||
|
tv-show-collection-season-1:
|
||||||
|
generic:
|
||||||
|
urls:
|
||||||
|
- url: "{collection_season_1_url}"
|
||||||
|
variables:
|
||||||
|
collection_season_number: "1"
|
||||||
|
collection_season_number_padded: "01"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "{season_poster_name}"
|
||||||
|
uid: "latest_entry"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
tags:
|
||||||
|
namedseason:
|
||||||
|
- tag: "{collection_season_1_name}"
|
||||||
|
attributes:
|
||||||
|
number: "1"
|
||||||
|
|
||||||
|
tv-show-collection-season-2:
|
||||||
|
generic:
|
||||||
|
urls:
|
||||||
|
- url: "{collection_season_2_url}"
|
||||||
|
variables:
|
||||||
|
collection_season_number: "2"
|
||||||
|
collection_season_number_padded: "02"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "{season_poster_name}"
|
||||||
|
uid: "latest_entry"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
tags:
|
||||||
|
namedseason:
|
||||||
|
- tag: "{collection_season_2_name}"
|
||||||
|
attributes:
|
||||||
|
number: "2"
|
||||||
|
|
||||||
|
tv-show-collection-season-3:
|
||||||
|
generic:
|
||||||
|
urls:
|
||||||
|
- url: "{collection_season_3_url}"
|
||||||
|
variables:
|
||||||
|
collection_season_number: "3"
|
||||||
|
collection_season_number_padded: "03"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "{season_poster_name}"
|
||||||
|
uid: "latest_entry"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
tags:
|
||||||
|
namedseason:
|
||||||
|
- tag: "{collection_season_3_name}"
|
||||||
|
attributes:
|
||||||
|
number: "3"
|
||||||
|
|
||||||
|
tv-show-collection-season-4:
|
||||||
|
generic:
|
||||||
|
urls:
|
||||||
|
- url: "{collection_season_4_url}"
|
||||||
|
variables:
|
||||||
|
collection_season_number: "4"
|
||||||
|
collection_season_number_padded: "04"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "{season_poster_name}"
|
||||||
|
uid: "latest_entry"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
tags:
|
||||||
|
namedseason:
|
||||||
|
- tag: "{collection_season_4_name}"
|
||||||
|
attributes:
|
||||||
|
number: "4"
|
||||||
|
|
||||||
|
tv-show-collection-season-5:
|
||||||
|
generic:
|
||||||
|
urls:
|
||||||
|
- url: "{collection_season_5_url}"
|
||||||
|
variables:
|
||||||
|
collection_season_number: "5"
|
||||||
|
collection_season_number_padded: "05"
|
||||||
|
playlist_thumbnails:
|
||||||
|
- name: "{season_poster_name}"
|
||||||
|
uid: "latest_entry"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
tags:
|
||||||
|
namedseason:
|
||||||
|
- tag: "{collection_season_5_name}"
|
||||||
|
attributes:
|
||||||
|
number: "5"
|
||||||
|
|
@ -22,36 +22,11 @@ presets:
|
||||||
# episode_number_padded
|
# episode_number_padded
|
||||||
season_title: "{season_number_padded}"
|
season_title: "{season_number_padded}"
|
||||||
episode_title: "{upload_date_standardized} - {title}"
|
episode_title: "{upload_date_standardized} - {title}"
|
||||||
episode_file_name: "s{season_number_padded}.e{episode_number_padded} - {uid_sanitized}"
|
|
||||||
episode_file_path: "Season {season_number_padded}/{episode_file_name}"
|
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
|
|
||||||
episode-nfo:
|
|
||||||
presets:
|
|
||||||
- "episode-base"
|
|
||||||
|
|
||||||
nfo_tags:
|
|
||||||
nfo_name: "{episode_file_path}.nfo"
|
|
||||||
nfo_root: "episodedetails"
|
|
||||||
tags:
|
|
||||||
season: "{season_number}"
|
|
||||||
episode: "{episode_number}"
|
|
||||||
title: "{episode_title}"
|
|
||||||
plot: "{episode_plot}"
|
|
||||||
year: "{episode_year}"
|
|
||||||
aired: "{episode_air_date_standardized}"
|
|
||||||
|
|
||||||
output_directory_nfo_tags:
|
|
||||||
nfo_name: "tvshow.nfo"
|
|
||||||
nfo_root: "tvshow"
|
|
||||||
tags:
|
|
||||||
title: "{tv_show_name}"
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
episode_plot: "{webpage_url}"
|
episode_plot: "{webpage_url}"
|
||||||
episode_year: "{upload_year}"
|
episode_year: "{upload_year}"
|
||||||
episode_air_date_standardized: "{upload_date_standardized}"
|
episode_air_date_standardized: "{upload_date_standardized}"
|
||||||
|
episode_file_name: "s{season_number_padded}.e{episode_number_padded} - {uid_sanitized}"
|
||||||
|
episode_file_path: "Season {season_number_padded}/{episode_file_name}"
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
|
@ -60,12 +35,26 @@ presets:
|
||||||
season_number: "{upload_year}"
|
season_number: "{upload_year}"
|
||||||
season_number_padded: "{upload_year}"
|
season_number_padded: "{upload_year}"
|
||||||
|
|
||||||
episode-by-day:
|
episode-by-month-day:
|
||||||
|
preset:
|
||||||
|
- "season-by-year"
|
||||||
overrides:
|
overrides:
|
||||||
episode_number: "{upload_month}{upload_day_padded}"
|
episode_number: "{upload_month}{upload_day_padded}"
|
||||||
episode_number_padded: "{upload_month_padded}{upload_day_padded}"
|
episode_number_padded: "{upload_month_padded}{upload_day_padded}"
|
||||||
|
|
||||||
episode-by-day-reversed:
|
episode-by-month-day-reversed:
|
||||||
|
preset:
|
||||||
|
- "season-by-year"
|
||||||
overrides:
|
overrides:
|
||||||
episode_number: "{upload_day_of_year_reversed}"
|
episode_number: "{upload_day_of_year_reversed}"
|
||||||
episode_number_padded: "{upload_day_of_year_reversed_padded}"
|
episode_number_padded: "{upload_day_of_year_reversed_padded}"
|
||||||
|
|
||||||
|
episode-by-year-month-day:
|
||||||
|
overrides:
|
||||||
|
episode_number: "{upload_year_truncated}{upload_month_padded}{upload_day_padded}"
|
||||||
|
episode_number_padded: "{episode_number}"
|
||||||
|
|
||||||
|
episode-by-year-month-day-reversed:
|
||||||
|
overrides:
|
||||||
|
episode_number: "{upload_year_truncated_reversed}{upload_month_reversed_padded}{upload_day_reversed_padded}"
|
||||||
|
episode_number_padded: "{episode_number}"
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
presets:
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Single video as an episode
|
|
||||||
jellyfin-episode:
|
|
||||||
output_options:
|
|
||||||
output_directory: "{tv_shows_directory}/{tv_show_name_sanitized}"
|
|
||||||
file_name: "{episode_file_path}.{ext}"
|
|
||||||
thumbnail_name: "{episode_file_path}-thumb.jpg"
|
|
||||||
info_json_name: "{episode_file_path}.{info_json_ext}"
|
|
||||||
maintain_download_archive: True
|
|
||||||
|
|
||||||
nfo_tags:
|
|
||||||
nfo_name: "{episode_file_path}.nfo"
|
|
||||||
nfo_root: "episodedetails"
|
|
||||||
kodi_safe: True
|
|
||||||
tags:
|
|
||||||
season: "{season_number}"
|
|
||||||
episode: "{episode_number}"
|
|
||||||
title: "{episode_title}"
|
|
||||||
year: "{episode_year}"
|
|
||||||
plot: "{episode_plot}"
|
|
||||||
aired: "{episode_air_date_standardized}"
|
|
||||||
|
|
||||||
chapters:
|
|
||||||
embed_chapters: True
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
# MUST DEFINE:
|
|
||||||
# tv_shows_directory
|
|
||||||
# tv_show_name
|
|
||||||
# season_number
|
|
||||||
# season_number_padded
|
|
||||||
# episode_number
|
|
||||||
# episode_number_padded
|
|
||||||
season_title: "{season_number_padded}"
|
|
||||||
episode_title: "{upload_date_standardized} - {title}"
|
|
||||||
episode_file_name: "s{season_number_padded}.e{episode_number_padded} - {uid_sanitized}"
|
|
||||||
episode_file_path: "Season {season_number_padded}/{episode_file_name}"
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Create a TV show from a youtube playlist or channel
|
|
||||||
yt_show:
|
|
||||||
preset: "yt_episode"
|
|
||||||
output_directory_nfo_tags:
|
|
||||||
nfo_name: "tvshow.nfo"
|
|
||||||
nfo_root: "tvshow"
|
|
||||||
kodi_safe: True
|
|
||||||
tags:
|
|
||||||
title: "{show_name}"
|
|
||||||
namedseason:
|
|
||||||
- tag: "{season_name}"
|
|
||||||
attributes:
|
|
||||||
number: "{season}"
|
|
||||||
genre: "{genre}"
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Entire channel as a TV show
|
|
||||||
yt_channel:
|
|
||||||
preset: "yt_show"
|
|
||||||
youtube:
|
|
||||||
download_strategy: "channel"
|
|
||||||
channel_avatar_path: "poster.jpg"
|
|
||||||
channel_banner_path: "fanart.jpg"
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Playlist as a TV show
|
|
||||||
yt_playlist:
|
|
||||||
preset: "yt_show"
|
|
||||||
youtube:
|
|
||||||
download_strategy: "playlist"
|
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Entire channel as a TV show
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Only store recent videos in the last {time_to_live} timespan.
|
|
||||||
# Defaults to 2 months
|
|
||||||
only_recent:
|
|
||||||
ytdl_options:
|
|
||||||
break_on_reject: True
|
|
||||||
output_options:
|
|
||||||
keep_files_after: "today-{time_to_live}"
|
|
||||||
date_range:
|
|
||||||
after: "today-{time_to_live}"
|
|
||||||
overrides:
|
|
||||||
time_to_live: "2months"
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# For channels/playlists/etc with known sponsor content.
|
|
||||||
# Wait 2 days after upload for sponsorblock submissions, then cut it out.
|
|
||||||
sponsor_block:
|
|
||||||
date_range:
|
|
||||||
before: "today-2days"
|
|
||||||
|
|
||||||
chapters:
|
|
||||||
sponsorblock_categories:
|
|
||||||
- "outro"
|
|
||||||
- "selfpromo"
|
|
||||||
- "preview"
|
|
||||||
- "interaction"
|
|
||||||
- "sponsor"
|
|
||||||
- "music_offtopic"
|
|
||||||
- "intro"
|
|
||||||
remove_sponsorblock_categories: "all"
|
|
||||||
force_key_frames: False
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Genres
|
|
||||||
genre_music:
|
|
||||||
overrides:
|
|
||||||
genre: "Music"
|
|
||||||
genre_news:
|
|
||||||
overrides:
|
|
||||||
genre: "News"
|
|
||||||
genre_finance:
|
|
||||||
overrides:
|
|
||||||
genre: "Finance"
|
|
||||||
genre_podcast:
|
|
||||||
overrides:
|
|
||||||
genre: "Podcast"
|
|
||||||
genre_cooking:
|
|
||||||
overrides:
|
|
||||||
genre: "Cooking"
|
|
||||||
genre_gaming:
|
|
||||||
overrides:
|
|
||||||
genre: "Gaming"
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Other
|
|
||||||
include_description:
|
|
||||||
nfo_tags:
|
|
||||||
tags:
|
|
||||||
plot: "{webpage_url}\n\n{description}"
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Other
|
|
||||||
yt_collection:
|
|
||||||
preset: "yt_show"
|
|
||||||
generic:
|
|
||||||
download_strategy: "collection"
|
|
||||||
urls:
|
|
||||||
- url: "{channel_url}"
|
|
||||||
variables:
|
|
||||||
url_season_name: "{channel_season_name}"
|
|
||||||
url_season_index: "{channel_season_index}"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "season{channel_season_index}-poster.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
- name: "poster.jpg"
|
|
||||||
uid: "avatar_uncropped"
|
|
||||||
- name: "fanart.jpg"
|
|
||||||
uid: "banner_uncropped"
|
|
||||||
|
|
||||||
output_directory_nfo_tags:
|
|
||||||
nfo_name: "tvshow.nfo"
|
|
||||||
nfo_root: "tvshow"
|
|
||||||
kodi_safe: True
|
|
||||||
tags:
|
|
||||||
namedseason:
|
|
||||||
- tag: "{channel_season_name}"
|
|
||||||
attributes:
|
|
||||||
number: "{channel_season_index}"
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
# Need to fill out channel_url
|
|
||||||
channel_season_name: "Videos"
|
|
||||||
channel_season_index: "1"
|
|
||||||
season: "{url_season_index}"
|
|
||||||
season_name: "{url_season_name}"
|
|
||||||
|
|
||||||
yt_collection_p1:
|
|
||||||
preset: "yt_collection"
|
|
||||||
generic:
|
|
||||||
download_strategy: "collection"
|
|
||||||
urls:
|
|
||||||
- url: "{playlist1_url}"
|
|
||||||
variables:
|
|
||||||
url_season_name: "{playlist1_season_name}"
|
|
||||||
url_season_index: "{playlist1_season_index}"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "season{playlist1_season_index}-poster.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
|
|
||||||
output_directory_nfo_tags:
|
|
||||||
tags:
|
|
||||||
namedseason:
|
|
||||||
- tag: "{playlist1_season_name}"
|
|
||||||
attributes:
|
|
||||||
number: "{playlist1_season_index}"
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
# Need channel_url, playlist1_url, playlist1_season_name
|
|
||||||
playlist1_season_index: "2"
|
|
||||||
|
|
||||||
yt_collection_p2:
|
|
||||||
preset: "yt_collection_p1"
|
|
||||||
generic:
|
|
||||||
download_strategy: "collection"
|
|
||||||
urls:
|
|
||||||
- url: "{playlist2_url}"
|
|
||||||
variables:
|
|
||||||
url_season_name: "{playlist2_season_name}"
|
|
||||||
url_season_index: "{playlist2_season_index}"
|
|
||||||
playlist_thumbnails:
|
|
||||||
- name: "season{playlist2_season_index}-poster.jpg"
|
|
||||||
uid: "latest_entry"
|
|
||||||
|
|
||||||
output_directory_nfo_tags:
|
|
||||||
tags:
|
|
||||||
namedseason:
|
|
||||||
- tag: "{playlist2_season_name}"
|
|
||||||
attributes:
|
|
||||||
number: "{playlist2_season_index}"
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
# Need channel_url, playlist1_url, playlist1_season_name, playlist2_url, playlist2_season_name
|
|
||||||
playlist2_season_index: "3"
|
|
||||||
31
src/ytdl_sub/prebuilt_presets/tv_show/media_players.yaml
Normal file
31
src/ytdl_sub/prebuilt_presets/tv_show/media_players.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
presets:
|
||||||
|
|
||||||
|
plex-tv-show:
|
||||||
|
overrides:
|
||||||
|
season_poster_name: "Season{season_number_padded}.jpg"
|
||||||
|
|
||||||
|
jellyfin-tv-show:
|
||||||
|
overrides:
|
||||||
|
season_poster_name: "season{season_number_padded}-poster.jpg"
|
||||||
|
|
||||||
|
nfo_tags:
|
||||||
|
nfo_name: "{episode_file_path}.nfo"
|
||||||
|
nfo_root: "episodedetails"
|
||||||
|
tags:
|
||||||
|
season: "{season_number}"
|
||||||
|
episode: "{episode_number}"
|
||||||
|
title: "{episode_title}"
|
||||||
|
plot: "{episode_plot}"
|
||||||
|
year: "{episode_year}"
|
||||||
|
aired: "{episode_air_date_standardized}"
|
||||||
|
|
||||||
|
output_directory_nfo_tags:
|
||||||
|
nfo_name: "tvshow.nfo"
|
||||||
|
nfo_root: "tvshow"
|
||||||
|
tags:
|
||||||
|
title: "{tv_show_name}"
|
||||||
|
|
||||||
|
kodi-tv-show:
|
||||||
|
preset:
|
||||||
|
- "jellyfin-tv-show"
|
||||||
|
- "kodi-safe-nfo"
|
||||||
97
src/ytdl_sub/prebuilt_presets/tv_show/out.py
Normal file
97
src/ytdl_sub/prebuilt_presets/tv_show/out.py
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
from typing import Any, Dict
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
Preset = Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
class PrebuiltPresets:
|
||||||
|
BASE_PRESET: str
|
||||||
|
|
||||||
|
def _preset(self, name: str, presets: List[str]) -> Preset:
|
||||||
|
return {
|
||||||
|
"presets": {
|
||||||
|
name: {
|
||||||
|
"preset": [self.BASE_PRESET] + presets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _tv_show_url(self, name: str) -> Preset:
|
||||||
|
"""
|
||||||
|
Kodi TV Show with seasons as years, episodes ordered by upload date
|
||||||
|
"""
|
||||||
|
return self._preset(name=name, presets=["tv-show-url", "episode-by-month-day"])
|
||||||
|
|
||||||
|
def _tv_show_url_reversed(self, name: str) -> Preset:
|
||||||
|
return self._preset(name=name, presets=["tv-show-url", "episode-by-month-day-reversed"])
|
||||||
|
|
||||||
|
def _tv_show_youtube_channel(self, name: str) -> Preset:
|
||||||
|
return self._preset(name=name, presets=["tv-show-youtube-channel", "episode-by-month-day"])
|
||||||
|
|
||||||
|
def _tv_show_youtube_channel_reversed(self, name: str) -> Preset:
|
||||||
|
return self._preset(name=name, presets=["tv-show-youtube-channel", "episode-by-month-day"])
|
||||||
|
|
||||||
|
def _tv_show_collection(self, name: str) -> Preset:
|
||||||
|
return self._preset(name=name, presets=["tv-show-collection", "episode-by-year-month-day"])
|
||||||
|
|
||||||
|
def _tv_show_collection_reversed(self, name: str) -> Preset:
|
||||||
|
return self._preset(
|
||||||
|
name=name, presets=["tv-show-collection", "episode-by-year-month-day-reversed"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_preset_names(cls) -> List[str]:
|
||||||
|
preset_names = [prop for prop in dir(cls) if isinstance(getattr(cls, prop), property)]
|
||||||
|
return preset_names
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_presets(cls) -> List[Preset]:
|
||||||
|
return [getattr(cls(), preset_name) for preset_name in cls.get_preset_names()]
|
||||||
|
|
||||||
|
|
||||||
|
class PrebuiltKodiTVShowPresets(PrebuiltPresets):
|
||||||
|
BASE_PRESET = "kodi-tv-show"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_tv_show_url(self) -> Preset:
|
||||||
|
"""
|
||||||
|
Kodi TV Show with seasons as years, episodes ordered by upload date
|
||||||
|
"""
|
||||||
|
return self._tv_show_url(name="kodi_tv_show_url")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_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="kodi_tv_show_url_reversed")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_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="kodi_tv_show_youtube_channel")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_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="kodi_tv_show_youtube_channel_reversed")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_tv_show_collection(self) -> Preset:
|
||||||
|
"""
|
||||||
|
Kodi TV Show from a collection of multiple URLs. TODO: finish docstring
|
||||||
|
"""
|
||||||
|
return self._tv_show_collection(name="kodi_tv_show_collection")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def kodi_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="kodi_tv_show_collection_reversed")
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
@ -8,7 +9,7 @@ from ytdl_sub.utils.exceptions import FileNotFoundException
|
||||||
from ytdl_sub.utils.exceptions import InvalidYamlException
|
from ytdl_sub.utils.exceptions import InvalidYamlException
|
||||||
|
|
||||||
|
|
||||||
def load_yaml(file_path: str) -> Dict:
|
def load_yaml(file_path: str | Path) -> Dict:
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
|
||||||
0
tests/unit/prebuilt_presets/__init__.py
Normal file
0
tests/unit/prebuilt_presets/__init__.py
Normal file
31
tests/unit/prebuilt_presets/test_prebuilt_presets.py
Normal file
31
tests/unit/prebuilt_presets/test_prebuilt_presets.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ytdl_sub.config.config_file import ConfigFile
|
||||||
|
from ytdl_sub.config.preset import Preset
|
||||||
|
from ytdl_sub.prebuilt_presets import PrebuiltKodiTVShowPresets
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config() -> ConfigFile:
|
||||||
|
return ConfigFile(
|
||||||
|
name="config", value={"configuration": {"working_directory": "test"}, "presets": {}}
|
||||||
|
)
|
||||||
|
|
||||||
|
class TestPrebuiltTVShowPresets:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("preset_name", PrebuiltKodiTVShowPresets.get_preset_names())
|
||||||
|
def test_presets_compile(self, config, preset_name: str):
|
||||||
|
Preset.from_dict(
|
||||||
|
config=config,
|
||||||
|
preset_name=f"{preset_name}-test",
|
||||||
|
preset_dict={
|
||||||
|
'preset': preset_name,
|
||||||
|
'overrides': {
|
||||||
|
'tv_show_name': 'test tv show',
|
||||||
|
'tv_show_directory': 'output_path'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
Loading…
Reference in a new issue