Skip to content

Commit

Permalink
Merge pull request #91 from CollinHeist/develop
Browse files Browse the repository at this point in the history
Implement YAML templates, ignore aired status of downloaded episodes
  • Loading branch information
CollinHeist authored Apr 25, 2022
2 parents bbdfe3d + b05ea5b commit 253ef8d
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 40 deletions.
7 changes: 5 additions & 2 deletions modules/Debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from tqdm import tqdm

"""TQDM bar format string"""
TQDM_BAR = ('{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} '
'[{elapsed}, {rate_fmt}{postfix}]')
TQDM_KWARGS = {
'bar_format': ('{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} '
'[{elapsed}, {rate_fmt}{postfix}]'),
'leave': False,
}

class LogHandler(Handler):
"""Handler subclass to integrate logging messages with TQDM"""
Expand Down
21 changes: 11 additions & 10 deletions modules/Manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from yaml import dump
from tqdm import tqdm

from modules.Debug import log, TQDM_BAR
from modules.Debug import log, TQDM_KWARGS
from modules.PlexInterface import PlexInterface
import modules.preferences as global_preferences
from modules.Show import Show
Expand Down Expand Up @@ -93,7 +93,7 @@ def check_tmdb_for_translations(self) -> None:
return None

# For each show in the Manager, add translation
for show in (pbar := tqdm(self.shows, bar_format=TQDM_BAR)):
for show in (pbar := tqdm(self.shows, **TQDM_KWARGS)):
pbar.set_description(f'Adding translations for '
f'"{show.series_info.short_name}"')
show.add_translations(self.tmdb_interface)
Expand All @@ -105,13 +105,14 @@ def read_show_source(self) -> None:
for all Show and ShowArchives, and also looks for multipart episodes.
"""

for show in tqdm(self.shows, desc='Reading source files',
bar_format=TQDM_BAR):
# Read source files for Show objects
for show in tqdm(self.shows, desc='Reading source files',**TQDM_KWARGS):
show.read_source()
show.find_multipart_episodes()

# Read source files for ShowSummary objects
for archive in tqdm(self.archives, desc='Reading archive source files',
bar_format=TQDM_BAR):
**TQDM_KWARGS):
archive.read_source()
archive.find_multipart_episodes()

Expand All @@ -128,7 +129,7 @@ def check_sonarr_for_new_episodes(self) -> None:

# Go through each show in the Manager and query Sonarr
for show in tqdm(self.shows + self.archives, desc='Querying Sonarr',
bar_format=TQDM_BAR):
**TQDM_KWARGS):
show.query_sonarr(self.sonarr_interface)


Expand All @@ -139,7 +140,7 @@ def create_missing_title_cards(self) -> None:
"""

# Go through every show in the Manager, create cards
for show in (pbar := tqdm(self.shows, bar_format=TQDM_BAR)):
for show in (pbar := tqdm(self.shows, **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Creating Title Cards for '
f'"{show.series_info.short_name}"')
Expand All @@ -163,7 +164,7 @@ def update_plex(self) -> None:
return None

# Go through each show in the Manager, update Plex
for show in (pbar := tqdm(self.shows, bar_format=TQDM_BAR)):
for show in (pbar := tqdm(self.shows, **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Updating Plex for '
f'"{show.series_info.short_name}"')
Expand All @@ -181,7 +182,7 @@ def update_archive(self) -> None:
if not self.preferences.create_archive:
return None

for show_archive in (pbar := tqdm(self.archives, bar_format=TQDM_BAR)):
for show_archive in (pbar := tqdm(self.archives, **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Updating archive for '
f'"{show_archive.series_info.short_name}"')
Expand All @@ -202,7 +203,7 @@ def create_summaries(self) -> None:
if not self.preferences.create_summaries:
return None

for show_archive in (pbar := tqdm(self.archives, bar_format=TQDM_BAR)):
for show_archive in (pbar := tqdm(self.archives, **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Creating ShowSummary for "'
f'{show_archive.series_info.short_name}"')
Expand Down
2 changes: 1 addition & 1 deletion modules/PlexInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def __get_library(self, library_name: str) -> 'Library':
try:
return self.__server.library.section(library_name)
except NotFound:
log.error(f'Library "{library}" was not found in Plex')
log.error(f'Library "{library_name}" was not found in Plex')
return None


Expand Down
86 changes: 74 additions & 12 deletions modules/PreferenceParser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from pathlib import Path
from re import findall

from tqdm import tqdm
from yaml import safe_load

from modules.Debug import log, TQDM_BAR
from modules.Debug import log, TQDM_KWARGS
from modules.ImageMagickInterface import ImageMagickInterface
from modules.ImageMaker import ImageMaker
from modules.Show import Show
from modules.ShowSummary import ShowSummary
from modules.Template import Template
from modules.TitleCard import TitleCard
from modules.TMDbInterface import TMDbInterface
from modules.YamlReader import YamlReader
Expand Down Expand Up @@ -212,6 +214,46 @@ def __parse_yaml(self) -> None:
self.imagemagick_container = value


def __apply_template(self, templates: dict, series_yaml: dict,
series_name: str) -> bool:
"""
Apply the correct Template object (if indicated) to the given series
YAML. This effectively "fill out" the indicated template, and updates
the series YAML directly.
:param templates: Dictionary of Template objects to
potentially apply.
:param series_yaml: The YAML of the series to modify.
:param series_name: The name of the series being modified.
:returns: True if the given series contained all the required template
variables for application, False if it did not.
"""

# No templates defined, skip
if templates == {} or 'template' not in series_yaml:
return True

# Get the specified template for this series
if isinstance((series_template := series_yaml['template']), str):
# Assume if only a string, then its the template name
series_template = {'name': series_template}
series_yaml['template'] = series_template

# Warn and return if no template name given
if not (template_name := series_template.get('name', None)):
log.error(f'Missing template name for "{series_name}"')
return False

# Warn and return if template name not mapped
if not (template := templates.get(template_name, None)):
log.error(f'Template "{template_name}" not defined')
return False

# Apply using Template object
return template.apply_to_series(series_name, series_yaml)


def read_file(self) -> None:
"""
Reads this associated preference file and store in `__yaml` attribute.
Expand Down Expand Up @@ -243,41 +285,60 @@ def iterate_series_files(self) -> [Show]:
"""

# For each file in the cards list
for file in (pbar := tqdm(self.series_files, bar_format=TQDM_BAR)):
for file_ in (pbar := tqdm(self.series_files, **TQDM_KWARGS)):
# Create Path object for this file
file_object = Path(file)
file = Path(file_)

# Update progress bar for this file
pbar.set_description(f'Reading {file_object.name}')
pbar.set_description(f'Reading {file.name}')

# If the file doesn't exist, error and skip
if not file_object.exists():
log.error(f'Series file "{file_object.resolve()}" does not '
if not file.exists():
log.error(f'Series file "{file.resolve()}" does not '
f'exist')
continue

# Read file, parse yaml
if (file_yaml := self._read_file(file_object)) == {}:
if (file_yaml := self._read_file(file)) == {}:
continue

# Skip if there are no series to yield
if file_yaml is None or 'series' not in file_yaml:
log.warning(f'Series file has no entries')
log.info(f'Series file has no entries')
continue

# Get library map for this file; error+skip missing library paths
if (library_map := file_yaml.get('libraries', {})):
if not all('path' in library_map[lib] for lib in library_map):
log.error(f'Libraries in series file "{file_object.resolve()}'
f'"are missing their "path" attributes.')
log.error(f'Libraries are missing required "path" in series'
f' file "{file.resolve()}"')
continue

# Get font map for this file
font_map = file_yaml.get('fonts', {})

# Get templates for this file
templates = {}
for name, template in file_yaml.get('templates', {}).items():
# If not specified as dictionary, error and skip
if not isinstance(template, dict):
log.error(f'Invalid template specification for "{name}" in '
f'series file "{file.resolve()}"')
continue
templates[name] = Template(name, template)

# Go through each series in this file
for show_name in tqdm(file_yaml['series'], leave=False,
desc='Creating Shows', bar_format=TQDM_BAR):
for show_name in tqdm(file_yaml['series'], desc='Creating Shows',
**TQDM_KWARGS):
# Apply template to series
valid = self.__apply_template(
templates, file_yaml['series'][show_name], show_name,
)

# Skip if series is not valid
if not valid:
continue

# Yield the Show object created from this entry
yield Show(
show_name,
Expand Down Expand Up @@ -326,3 +387,4 @@ def get_season_folder(self, season_number: int) -> str:

# Return non-zero-padded season name
return f'Season {season_number}'

24 changes: 10 additions & 14 deletions modules/Show.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from pathlib import Path
from re import match

from tqdm import tqdm

from modules.DataFileInterface import DataFileInterface
from modules.Debug import log, TQDM_BAR
from modules.Debug import log, TQDM_KWARGS
from modules.Episode import Episode
from modules.Font import Font
from modules.MultiEpisode import MultiEpisode
Expand Down Expand Up @@ -63,11 +62,11 @@ def __init__(self, name: str, yaml_dict: dict, library_map: dict,
return None

# Year is given, parse and update year/full name of this show
if not match(r'^\d{4}$', str(year)):
if not isinstance(year, int) or year < 0:
log.error(f'Year "{year}" of series "{name}" is invalid')
self.valid = False
return None

# Setup default values that can be overwritten by YAML
self.series_info = SeriesInfo(name, year)
self.media_directory = None
Expand All @@ -81,7 +80,7 @@ def __init__(self, name: str, yaml_dict: dict, library_map: dict,
self.tmdb_sync = True
self.hide_seasons = False
self.__episode_range = {}
self.__season_map = {n: f'Season {n}' for n in range(1, 1000)}
self.__season_map = {n: f'Season {n}' for n in range(1, 100)}
self.__season_map[0] = 'Specials'
self.title_language = {}

Expand Down Expand Up @@ -141,19 +140,18 @@ def __parse_yaml(self):

if (library := self['library']):
# If the given library isn't in libary map, invalid
if library not in self.__library_map:
if not (this_library := self.__library_map.get(library, None)):
log.error(f'Library "{library}" of series {self} is not found '
f'in libraries list')
self.valid = False
else:
# Valid library, update library and media directory
self.library_name = self.__library_map.get('plex_name', library)
self.library = Path(self.__library_map[library]['path'])
self.library_name = this_library.get('plex_name', library)
self.library = Path(this_library['path'])
self.media_directory = self.library / self.series_info.full_name

# If card type was specified for this library, set that
if 'card_type' in self.__library_map[library]:
card_type = self.__library_map[library]['card_type']
if (card_type := this_library.get('card_type', None)):
if card_type not in TitleCard.CARD_TYPES:
log.error(f'Unknown card type "{card_type}" of series '
f'{self}')
Expand Down Expand Up @@ -400,8 +398,7 @@ def add_translations(self, tmdb_interface: 'TMDbInterface') -> None:

# Go through every episode and look for translations
modified = False
for _, episode in (pbar := tqdm(self.episodes.items(), leave=False,
bar_format=TQDM_BAR)):
for _, episode in (pbar := tqdm(self.episodes.items(), **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Checking {episode}')

Expand Down Expand Up @@ -451,8 +448,7 @@ def create_missing_title_cards(self,
return False

# Go through each episode for this show
for _, episode in (pbar := tqdm(self.episodes.items(), leave=False,
bar_format=TQDM_BAR)):
for _, episode in (pbar := tqdm(self.episodes.items(), **TQDM_KWARGS)):
# Update progress bar
pbar.set_description(f'Creating {episode}')

Expand Down
2 changes: 1 addition & 1 deletion modules/SonarrInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def __get_all_episode_info(self, series_id: int) -> [EpisodeInfo]:
# Go through each episode and get its season/episode number, and title
for episode in all_episodes:
# Unaired episodes (such as specials) won't have airDateUtc key
if 'airDateUtc' in episode:
if 'airDateUtc' in episode and not episode['hasFile']:
# Verify this episode has already aired, skip if not
air_datetime = datetime.strptime(
episode['airDateUtc'],
Expand Down
Loading

0 comments on commit 253ef8d

Please sign in to comment.