#66 filter by duration and filter validation

pull/76/head
meisnate12 4 years ago
parent a43e2a26d9
commit c25ee45e94

@ -22,6 +22,8 @@ class CollectionBuilder:
self.posters = [] self.posters = []
self.backgrounds = [] self.backgrounds = []
self.schedule = None self.schedule = None
current_time = datetime.now()
current_year = current_time.year
if "template" in data: if "template" in data:
if not self.library.templates: if not self.library.templates:
@ -113,7 +115,6 @@ class CollectionBuilder:
skip_collection = False skip_collection = False
else: else:
schedule_list = util.get_list(data["schedule"]) schedule_list = util.get_list(data["schedule"])
current_time = datetime.now()
next_month = current_time.replace(day=28) + timedelta(days=4) next_month = current_time.replace(day=28) + timedelta(days=4)
last_day = next_month - timedelta(days=next_month.day) last_day = next_month - timedelta(days=next_month.day)
for schedule in schedule_list: for schedule in schedule_list:
@ -316,10 +317,24 @@ class CollectionBuilder:
logger.warning(f"Collection Warning: {f} filter will run as {filter_method}") logger.warning(f"Collection Warning: {f} filter will run as {filter_method}")
else: else:
filter_method = f filter_method = f
if filter_method in util.movie_only_filters and self.library.is_show: raise Failed(f"Collection Error: {filter_method} filter only works for movie libraries") if filter_method in util.movie_only_filters and self.library.is_show:
elif data[m][f] is None: raise Failed(f"Collection Error: {filter_method} filter is blank") raise Failed(f"Collection Error: {filter_method} filter only works for movie libraries")
elif filter_method in util.all_filters: self.filters.append((filter_method, data[m][f])) elif data[m][f] is None:
else: raise Failed(f"Collection Error: {filter_method} filter not supported") raise Failed(f"Collection Error: {filter_method} filter is blank")
elif filter_method == "year":
self.filters.append((filter_method, util.get_year_list(data[m][f], f"{filter_method} filter")))
elif filter_method in ["max_age", "duration.gte", "duration.lte"]:
self.filters.append((filter_method, util.check_number(data[m][f], f"{filter_method} filter", minimum=1)))
elif filter_method in ["year.gte", "year.lte"]:
self.filters.append((filter_method, util.check_number(data[m][f], f"{filter_method} filter", minimum=1800, maximum=current_year)))
elif filter_method in ["rating.gte", "rating.lte"]:
self.filters.append((filter_method, util.check_number(data[m][f], f"{filter_method} filter", number_type="float", minimum=0.1, maximum=10)))
elif filter_method in ["originally_available.gte", "originally_available.lte"]:
self.filters.append((filter_method, util.check_date(data[m][f], f"{filter_method} filter")))
elif filter_method in util.all_filters:
self.filters.append((filter_method, data[m][f]))
else:
raise Failed(f"Collection Error: {filter_method} filter not supported")
elif method_name == "plex_collectionless": elif method_name == "plex_collectionless":
new_dictionary = {} new_dictionary = {}
prefix_list = [] prefix_list = []
@ -396,24 +411,11 @@ class CollectionBuilder:
if attr_data is True: if attr_data is True:
new_dictionary[attr] = attr_data new_dictionary[attr] = attr_data
elif attr in ["primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte"]: elif attr in ["primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte"]:
if re.compile("[0-1]?[0-9][/-][0-3]?[0-9][/-][1-2][890][0-9][0-9]").match(str(attr_data)): new_dictionary[attr] = util.check_date(attr_data, f"{m} attribute {attr}", return_string=True)
the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-")
new_dictionary[attr] = f"{the_date[2]}-{the_date[0]}-{the_date[1]}"
elif re.compile("[1-2][890][0-9][0-9][/-][0-1]?[0-9][/-][0-3]?[0-9]").match(str(attr_data)):
the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-")
new_dictionary[attr] = f"{the_date[0]}-{the_date[1]}-{the_date[2]}"
else:
raise Failed(f"Collection Error: {m} attribute {attr}: {attr_data} must match pattern MM/DD/YYYY e.g. 12/25/2020")
elif attr in ["primary_release_year", "year", "first_air_date_year"]: elif attr in ["primary_release_year", "year", "first_air_date_year"]:
if isinstance(attr_data, int) and 1800 < attr_data < 2200: new_dictionary[attr] = util.check_number(attr_data, f"{m} attribute {attr}", minimum=1800, maximum=current_year + 1)
new_dictionary[attr] = attr_data
else:
raise Failed(f"Collection Error: {m} attribute {attr}: must be a valid year e.g. 1990")
elif attr in ["vote_count.gte", "vote_count.lte", "vote_average.gte", "vote_average.lte", "with_runtime.gte", "with_runtime.lte"]: elif attr in ["vote_count.gte", "vote_count.lte", "vote_average.gte", "vote_average.lte", "with_runtime.gte", "with_runtime.lte"]:
if (isinstance(attr_data, int) or isinstance(attr_data, float)) and 0 < attr_data: new_dictionary[attr] = util.check_number(attr_data, f"{m} attribute {attr}", minimum=1)
new_dictionary[attr] = attr_data
else:
raise Failed(f"Collection Error: {m} attribute {attr}: must be a valid number greater then 0")
elif attr in ["with_cast", "with_crew", "with_people", "with_companies", "with_networks", "with_genres", "without_genres", "with_keywords", "without_keywords", "with_original_language", "timezone"]: elif attr in ["with_cast", "with_crew", "with_people", "with_companies", "with_networks", "with_genres", "without_genres", "with_keywords", "without_keywords", "with_original_language", "timezone"]:
new_dictionary[attr] = attr_data new_dictionary[attr] = attr_data
else: else:
@ -448,7 +450,6 @@ class CollectionBuilder:
elif data[m]["sort_by"] not in util.mal_season_sort: logger.warning(f"Collection Warning: mal_season sort_by attribute {data[m]['sort_by']} invalid must be either 'members' or 'score' using members as default") elif data[m]["sort_by"] not in util.mal_season_sort: logger.warning(f"Collection Warning: mal_season sort_by attribute {data[m]['sort_by']} invalid must be either 'members' or 'score' using members as default")
else: new_dictionary["sort_by"] = util.mal_season_sort[data[m]["sort_by"]] else: new_dictionary["sort_by"] = util.mal_season_sort[data[m]["sort_by"]]
current_time = datetime.now()
if current_time.month in [1, 2, 3]: new_dictionary["season"] = "winter" if current_time.month in [1, 2, 3]: new_dictionary["season"] = "winter"
elif current_time.month in [4, 5, 6]: new_dictionary["season"] = "spring" elif current_time.month in [4, 5, 6]: new_dictionary["season"] = "spring"
elif current_time.month in [7, 8, 9]: new_dictionary["season"] = "summer" elif current_time.month in [7, 8, 9]: new_dictionary["season"] = "summer"

@ -139,8 +139,7 @@ class PlexAPI:
method = util.filter_alias[f[0][:-4]] if modifier in [".not", ".lte", ".gte"] else util.filter_alias[f[0]] method = util.filter_alias[f[0][:-4]] if modifier in [".not", ".lte", ".gte"] else util.filter_alias[f[0]]
if method == "max_age": if method == "max_age":
threshold_date = datetime.now() - timedelta(days=f[1]) threshold_date = datetime.now() - timedelta(days=f[1])
attr = getattr(current, "originallyAvailableAt") if current.originallyAvailableAt is None or current.originallyAvailableAt < threshold_date:
if attr is None or attr < threshold_date:
match = False match = False
break break
elif method == "original_language": elif method == "original_language":
@ -160,17 +159,12 @@ class PlexAPI:
match = False match = False
break break
elif modifier in [".gte", ".lte"]: elif modifier in [".gte", ".lte"]:
if method == "originallyAvailableAt": attr = getattr(current, method)
threshold_date = datetime.strptime(f[1], "%m/%d/%y") if method == "duration":
attr = getattr(current, "originallyAvailableAt") attr = attr / 60000
if (modifier == ".lte" and attr > threshold_date) or (modifier == ".gte" and attr < threshold_date): if (modifier == ".lte" and attr > f[1]) or (modifier == ".gte" and attr < f[1]):
match = False match = False
break break
elif method in ["year", "rating"]:
attr = getattr(current, method)
if (modifier == ".lte" and attr > f[1]) or (modifier == ".gte" and attr < f[1]):
match = False
break
else: else:
terms = util.get_list(f[1]) terms = util.get_list(f[1])
attrs = [] attrs = []
@ -214,11 +208,7 @@ class PlexAPI:
logger.info("") logger.info("")
year = None year = None
if "year" in self.metadata[m]: if "year" in self.metadata[m]:
now = datetime.now() year = util.check_number(self.metadata[m]["year"], "year", minimum=1800, maximum=datetime.now().year + 1)
if self.metadata[m]["year"] is None: logger.error("Metadata Error: year attribute is blank")
elif not isinstance(self.metadata[m]["year"], int): logger.error("Metadata Error: year attribute must be an integer")
elif self.metadata[m]["year"] not in range(1800, now.year + 2): logger.error(f"Metadata Error: year attribute must be between 1800-{now.year + 1}")
else: year = self.metadata[m]["year"]
title = m title = m
if "title" in self.metadata[m]: if "title" in self.metadata[m]:

@ -1,4 +1,5 @@
import datetime, logging, re, signal, sys, time, traceback import logging, re, signal, sys, time, traceback
from datetime import datetime
try: try:
import msvcrt import msvcrt
@ -369,6 +370,7 @@ all_filters = [
"genre", "genre.not", "genre", "genre.not",
"max_age", "max_age",
"originally_available.gte", "originally_available.lte", "originally_available.gte", "originally_available.lte",
"duration.gte", "duration.lte",
"original_language", "original_language.not", "original_language", "original_language.not",
"rating.gte", "rating.lte", "rating.gte", "rating.lte",
"studio", "studio.not", "studio", "studio.not",
@ -381,6 +383,7 @@ movie_only_filters = [
"audio_language", "audio_language.not", "audio_language", "audio_language.not",
"country", "country.not", "country", "country.not",
"director", "director.not", "director", "director.not",
"duration.gte", "duration.lte",
"original_language", "original_language.not", "original_language", "original_language.not",
"subtitle_language", "subtitle_language.not", "subtitle_language", "subtitle_language.not",
"video_resolution", "video_resolution.not", "video_resolution", "video_resolution.not",
@ -511,33 +514,49 @@ def get_int_list(data, id_type):
def get_year_list(data, method): def get_year_list(data, method):
values = get_list(data) values = get_list(data)
final_years = [] final_years = []
current_year = datetime.datetime.now().year current_year = datetime.now().year
for value in values: for value in values:
try: try:
if "-" in value: if "-" in value:
year_range = re.search("(\\d{4})-(\\d{4}|NOW)", str(value)) year_range = re.search("(\\d{4})-(\\d{4}|NOW)", str(value))
start = year_range.group(1) start = check_year(year_range.group(1), current_year, method)
end = year_range.group(2) end = current_year if year_range.group(2) == "NOW" else check_year(year_range.group(2), current_year, method)
if end == "NOW": if int(start) > int(end):
end = current_year raise Failed(f"Collection Error: {method} starting year: {start} cannot be greater then ending year {end}")
if int(start) < 1800 or int(start) > current_year: logger.error(f"Collection Error: Skipping {method} starting year {start} must be between 1800 and {current_year}")
elif int(end) < 1800 or int(end) > current_year: logger.error(f"Collection Error: Skipping {method} ending year {end} must be between 1800 and {current_year}")
elif int(start) > int(end): logger.error(f"Collection Error: Skipping {method} starting year {start} cannot be greater then ending year {end}")
else: else:
for i in range(int(start), int(end) + 1): for i in range(int(start), int(end) + 1):
final_years.append(i) final_years.append(int(i))
else: else:
year = re.search("(\\d+)", str(value)).group(1) final_years.append(check_year(value, current_year, method))
if int(year) < 1800 or int(year) > current_year:
logger.error(f"Collection Error: Skipping {method} year {year} must be between 1800 and {current_year}")
else:
if len(str(year)) != len(str(value)):
logger.warning(f"Collection Warning: {value} can be replaced with {year}")
final_years.append(year)
except AttributeError: except AttributeError:
logger.error(f"Collection Error: Skipping {method} failed to parse year from {value}") raise Failed(f"Collection Error: {method} failed to parse year from {value}")
return final_years return final_years
def check_year(year, current_year, method):
return check_number(year, method, minimum=1800, maximum=current_year)
def check_number(value, method, number_type="int", minimum=None, maximum=None):
if number_type == "int":
try: num_value = int(str(value))
except ValueError: raise Failed(f"Collection Error: {method}: {value} must be an integer")
elif number_type == "float":
try: num_value = float(str(value))
except ValueError: raise Failed(f"Collection Error: {method}: {value} must be a number")
else: raise Failed(f"Number Type: {number_type} invalid")
if minimum is not None and maximum is not None and (num_value < minimum or num_value > maximum):
raise Failed(f"Collection Error: {method}: {num_value} must be between {minimum} and {maximum}")
elif minimum is not None and num_value < minimum:
raise Failed(f"Collection Error: {method}: {num_value} is less then {minimum}")
elif maximum is not None and num_value > maximum:
raise Failed(f"Collection Error: {method}: {num_value} is greater then {maximum}")
else:
return num_value
def check_date(date_text, method, return_string=False):
try: date_obg = datetime.strptime(str(date_text), "%m/%d/%Y")
except ValueError: raise Failed(f"Collection Error: {method}: {date_text} must match pattern MM/DD/YYYY e.g. 12/25/2020")
return str(date_text) if return_string else date_obg
def logger_input(prompt, timeout=60): def logger_input(prompt, timeout=60):
if windows: return windows_input(prompt, timeout) if windows: return windows_input(prompt, timeout)
elif hasattr(signal, "SIGALRM"): return unix_input(prompt, timeout) elif hasattr(signal, "SIGALRM"): return unix_input(prompt, timeout)

Loading…
Cancel
Save