From f2f28a59d5d873ea4dfc0b45daf611123d748a2d Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Thu, 27 May 2021 16:09:39 -0400 Subject: [PATCH] added more options to plex_search and smart_filter --- modules/builder.py | 67 ++++++++++++------------------------ modules/plex.py | 85 +++++++++++++--------------------------------- 2 files changed, 45 insertions(+), 107 deletions(-) diff --git a/modules/builder.py b/modules/builder.py index bd975dad..38af19a1 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -551,7 +551,7 @@ class CollectionBuilder: for smart_key, smart_data in filter_dict.items(): smart, smart_mod, smart_final = self._split(smart_key) - def build_url_arg(arg, mod=None, arg_s=None, mod_s=None, param_s=None): + def build_url_arg(arg, mod=None, arg_s=None, mod_s=None): arg_key = plex.search_translation[smart] if smart in plex.search_translation else smart if mod is None: mod = plex.modifier_translation[smart_mod] if smart_mod in plex.modifier_translation else smart_mod @@ -561,17 +561,16 @@ class CollectionBuilder: mod_s = "does not contain" if smart_mod == ".not" else "contains" elif mod_s is None: mod_s = plex.mod_displays[smart_mod] - if param_s is None: - param_s = smart.title().replace('_', ' ') + param_s = plex.search_display[smart] if smart in plex.search_display else smart.title().replace('_', ' ') display_line = f"{indent}{param_s} {mod_s} {arg_s}" return f"{arg_key}{mod}={arg}&", display_line - if smart_final in plex.movie_only_smart_searches and self.library.is_show: - raise Failed(f"Collection Error: {smart_final} smart filter attribute only works for movie libraries") - elif smart_final in plex.show_only_smart_searches and self.library.is_movie: - raise Failed(f"Collection Error: {smart_final} smart filter attribute only works for show libraries") - elif smart_final not in plex.smart_searches: + if smart_final not in plex.searches and smart_final not in ["any", "all"] and smart_mod != ".and": raise Failed(f"Collection Error: {smart_final} is not a valid smart filter attribute") + elif smart_final in plex.movie_only_searches and self.library.is_show: + raise Failed(f"Collection Error: {smart_final} smart filter attribute only works for movie libraries") + elif smart_final in plex.show_only_searches and self.library.is_movie: + raise Failed(f"Collection Error: {smart_final} smart filter attribute only works for show libraries") elif smart_data is None: raise Failed(f"Collection Error: {smart_final} smart filter attribute is blank") elif smart in ["all", "any"]: @@ -589,16 +588,16 @@ class CollectionBuilder: validation = self.validate_attribute(smart, smart_mod, smart_final, smart_data, validate, smart=True) if validation is None: continue - elif smart in ["added", "episode_added", "release", "episode_air_date"] and smart_mod in ["", ".not"]: + elif smart in plex.date_searches and smart_mod in ["", ".not"]: last_text = "is not in the last" if smart_mod == ".not" else "is in the last" last_mod = "%3E%3E" if smart_mod == "" else "%3C%3C" results, display_add = build_url_arg(f"-{validation}d", mod=last_mod, arg_s=f"{validation} Days", mod_s=last_text) - elif smart in ["duration"] and smart_mod in [".gt", ".gte", ".lt", ".lte"]: + elif smart == "duration" and smart_mod in [".gt", ".gte", ".lt", ".lte"]: results, display_add = build_url_arg(validation * 60000) - elif smart == "hdr": - hdr_mod = "" if validation else "!" - hdr_arg = "true" if validation else "false" - results, display_add = build_url_arg(1, mod=hdr_mod, arg_s=hdr_arg, mod_s="is", param_s="HDR") + elif smart in plex.boolean_searches: + bool_mod = "" if validation else "!" + bool_arg = "true" if validation else "false" + results, display_add = build_url_arg(1, mod=bool_mod, arg_s=bool_arg, mod_s="is") elif (smart in ["title", "episode_title", "studio", "decade", "year", "episode_year"] or smart in plex.tags) and smart_mod in ["", ".not", ".begins", ".ends"]: results = "" display_add = "" @@ -730,21 +729,21 @@ class CollectionBuilder: elif method_name == "sync_mode": if str(method_data).lower() in ["append", "sync"]: self.details[method_name] = method_data.lower() else: raise Failed("Collection Error: sync_mode attribute must be either 'append' or 'sync'") - elif method_name in ["label", "label.remove", "label.sync"]: + elif method_name == "label": if "label" in self.data and "label.sync" in self.data: raise Failed(f"Collection Error: Cannot use label and label.sync together") if "label.remove" in self.data and "label.sync" in self.data: raise Failed(f"Collection Error: Cannot use label.remove and label.sync together") - if method_name == "label" and "label_sync_mode" in self.data and self.data["label_sync_mode"] == "sync": + if method_final == "label" and "label_sync_mode" in self.data and self.data["label_sync_mode"] == "sync": self.details["label.sync"] = util.get_list(method_data) else: - self.details[method_name] = util.get_list(method_data) - elif method_name in ["item_label", "item_label.remove", "item_label.sync"]: + self.details[method_final] = util.get_list(method_data) + elif method_name == "item_label": if "item_label" in self.data and "item_label.sync" in self.data: raise Failed(f"Collection Error: Cannot use item_label and item_label.sync together") if "item_label.remove" in self.data and "item_label.sync" in self.data: raise Failed(f"Collection Error: Cannot use item_label.remove and item_label.sync together") - self.item_details[method_name] = util.get_list(method_data) + self.item_details[method_final] = util.get_list(method_data) elif method_name in plex.item_advance_keys: key, options = plex.item_advance_keys[method_name] if method_name in advance_new_agent and self.library.agent not in plex.new_plex_agents: @@ -799,31 +798,8 @@ class CollectionBuilder: self.sonarr_options[method_name[7:]] = util.get_bool(method_name, method_data) elif method_name == "sonarr_tag": self.sonarr_options["tag"] = util.get_list(method_data) - elif method_name in ["title", "title.and", "title.not", "title.begins", "studio.ends", "studio", "studio.and", "studio.not", "studio.begins", "studio.ends"]: - self.methods.append(("plex_search", [{method_name: util.get_list(method_data, split=False)}])) - elif method_name in ["year.gt", "year.gte", "year.lt", "year.lte"]: - self.methods.append(("plex_search", [{method_name: util.check_year(method_data, self.current_year, method_name)}])) - elif method_name in ["added.before", "added.after", "release.before", "release.after"]: - self.methods.append(("plex_search", [{method_name: util.check_date(method_data, method_name, return_string=True, plex_date=True)}])) - elif method_name in ["added", "added.not", "release", "release.not", "duration.gt", "duration.gte", "duration.lt", "duration.lte"]: - self.methods.append(("plex_search", [{method_name: util.check_number(method_data, method_name, minimum=1)}])) - elif method_name in ["user_rating.gt", "user_rating.gte", "user_rating.lt", "user_rating.lte", "critic_rating.gt", "critic_rating.gte", "critic_rating.lt", "critic_rating.lte", "audience_rating.gt", "audience_rating.gte", "audience_rating.lt", "audience_rating.lte"]: - self.methods.append(("plex_search", [{method_name: util.check_number(method_data, method_name, number_type="float", minimum=0, maximum=10)}])) - elif method_name in ["decade", "year", "year.not"]: - self.methods.append(("plex_search", [{method_name: util.get_year_list(method_data, self.current_year, method_name)}])) - elif method_name in plex.searches: - if method_name in plex.tmdb_searches: - final_values = [] - for value in util.get_list(method_data): - if value.lower() == "tmdb" and "tmdb_person" in self.details: - for name in self.details["tmdb_person"]: - final_values.append(name) - else: - final_values.append(value) - else: - final_values = method_data - search = self.library.validate_search_list(final_values, os.path.splitext(method_name)[0]) - self.methods.append(("plex_search", [{method_name: search}])) + elif method_final in plex.searches: + self.methods.append(("plex_search", [{method_name: self.validate_attribute(method_name, method_mod, method_final, method_data, True)}])) elif method_name == "plex_all": self.methods.append((method_name, [""])) elif method_name == "anidb_popular": @@ -1334,7 +1310,7 @@ class CollectionBuilder: return util.check_number(data, final, number_type="float", minimum=0, maximum=10) elif attribute in ["decade", "year", "episode_year"] and modifier in ["", ".not"]: return smart_pair(util.get_year_list(data, self.current_year, final)) - elif attribute == "hdr": + elif attribute in plex.boolean_searches: return util.get_bool(attribute, data) else: raise Failed(f"Collection Error: modifier: {modifier} not supported with the {attribute} attribute") @@ -1358,6 +1334,7 @@ class CollectionBuilder: current = self.library.fetchItem(item.ratingKey if isinstance(item, (Movie, Show)) else int(item)) if not isinstance(current, (Movie, Show)): raise NotFound + return current except (BadRequest, NotFound): raise Failed(f"Plex Error: Item {item} not found") diff --git a/modules/plex.py b/modules/plex.py index 1b28ccdf..1edf0152 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -91,20 +91,21 @@ searches = [ "producer", "producer.and", "producer.not", "subtitle_language", "subtitle_language.and", "subtitle_language.not", "writer", "writer.and", "writer.not", - "decade", "resolution", "hdr", + "decade", "resolution", "hdr", "unmatched", "duplicate", "unplayed", "progress", "trash", + "last_played", "last_played.not", "last_played.before", "last_played.after", "added", "added.not", "added.before", "added.after", - "release", "release.not", - "release.before", "release.after", + "release", "release.not", "release.before", "release.after", "duration.gt", "duration.gte", "duration.lt", "duration.lte", "plays.gt", "plays.gte", "plays.lt", "plays.lte", "user_rating.gt", "user_rating.gte", "user_rating.lt", "user_rating.lte", "critic_rating.gt", "critic_rating.gte", "critic_rating.lt", "critic_rating.lte", "audience_rating.gt", "audience_rating.gte", "audience_rating.lt", "audience_rating.lte", "year", "year.not", "year.gt", "year.gte", "year.lt", "year.lte", + "unplayed_episodes", "episode_unplayed", "episode_duplicate", "episode_progress", "episode_unmatched", "episode_title", "episode_title.and", "episode_title.not", "episode_title.begins", "episode_title.ends", "episode_added", "episode_added.not", "episode_added.before", "episode_added.after", - "episode_air_date", "episode_air_date.not", - "episode_air_date.before", "episode_air_date.after", + "episode_air_date", "episode_air_date.not", "episode_air_date.before", "episode_air_date.after", + "episode_last_played", "episode_last_played.not", "episode_last_played.before", "episode_last_played.after", "episode_plays.gt", "episode_plays.gte", "episode_plays.lt", "episode_plays.lte", "episode_user_rating.gt", "episode_user_rating.gte", "episode_user_rating.lt", "episode_user_rating.lte", "episode_year", "episode_year.not", "episode_year.gt", "episode_year.gte", "episode_year.lt", "episode_year.lte" @@ -114,7 +115,8 @@ movie_only_searches = [ "director", "director.and", "director.not", "producer", "producer.and", "producer.not", "writer", "writer.and", "writer.not", - "decade", "resolution", + "decade", "duplicate", "unplayed", "progress", "trash", + "plays.gt", "plays.gte", "plays.lt", "plays.lte", "duration.gt", "duration.gte", "duration.lt", "duration.lte" ] show_only_searches = [ @@ -133,6 +135,18 @@ tmdb_searches = [ "producer", "producer.and", "producer.not", "writer", "writer.and", "writer.not" ] +boolean_searches = [ + "hdr", "unmatched", "duplicate", "unplayed", "progress", "trash", + "unplayed_episodes", "episode_unplayed", "episode_duplicate", "episode_progress", "episode_unmatched", +] +date_searches = ["added", "episode_added", "release", "episode_air_date", "last_played", "episode_last_played"] +search_display = { + "added": "Date Added", + "release": "Release Date", + "hdr": "HDR", + "progress": "In Progress", + "episode_progress": "Episode In Progress" +} sorts = { None: None, "title.asc": "titleSort:asc", "title.desc": "titleSort:desc", @@ -183,59 +197,6 @@ tags = [ "subtitle_language", "writer" ] -smart_searches = [ - "all", "any", - "title", "title.not", "title.begins", "title.ends", - "studio", "studio.not", "studio.begins", "studio.ends", - "actor", "actor.not", - "audio_language", "audio_language.not", - "collection", "collection.not", - "content_rating", "content_rating.not", - "country", "country.not", - "director", "director.not", - "genre", "genre.not", - "label", "label.not", - "network", "network.not", - "producer", "producer.not", - "subtitle_language", "subtitle_language.not", - "writer", "writer.not", - "decade", "resolution", "hdr", - "added", "added.not", "added.before", "added.after", - "release", "release.not", - "release.before", "release.after", - "plays.gt", "plays.gte", "plays.lt", "plays.lte", - "duration.gt", "duration.gte", "duration.lt", "duration.lte", - "user_rating.gt", "user_rating.gte", "user_rating.lt", "user_rating.lte", - "audience_rating.gt", "audience_rating.gte", "audience_rating.lt","audience_rating.lte", - "critic_rating.gt", "critic_rating.gte", "critic_rating.lt","critic_rating.lte", - "year", "year.not", "year.gt", "year.gte", "year.lt","year.lte", - "episode_title", "episode_title.not", "episode_title.begins", "episode_title.ends", - "episode_added", "episode_added.not", "episode_added.before", "episode_added.after", - "episode_air_date", "episode_air_date.not", - "episode_air_date.before", "episode_air_date.after", - "episode_year", "episode_year.not", "episode_year.gt", "episode_year.gte", "episode_year.lt","episode_year.lte", - "episode_user_rating.gt", "episode_user_rating.gte", "episode_user_rating.lt","episode_user_rating.lte", - "episode_plays.gt", "episode_plays.gte", "episode_plays.lt", "episode_plays.lte" -] -movie_only_smart_searches = [ - "country", "country.not", - "director", "director.not", - "producer", "producer.not", - "writer", "writer.not", - "decade", - "plays.gt", "plays.gte", "plays.lt", "plays.lte", - "duration.gt", "duration.gte", "duration.lt", "duration.lte" -] -show_only_smart_searches = [ - "network", "network.not", - "episode_title", "episode_title.not", "episode_title.begins", "episode_title.ends", - "episode_added", "episode_added.not", "episode_added.before", "episode_added.after", - "episode_air_date", "episode_air_date.not", - "episode_air_date.before", "episode_air_date.after", - "episode_year", "episode_year.not", "episode_year.gt", "episode_year.gte", "episode_year.lt","episode_year.lte", - "episode_user_rating.gt", "episode_user_rating.gte", "episode_user_rating.lt","episode_user_rating.lte", - "episode_plays.gt", "episode_plays.gte", "episode_plays.lt", "episode_plays.lte" -] movie_smart_sorts = { "title.asc": "titleSort", "title.desc": "titleSort%3Adesc", "year.asc": "year", "year.desc": "year%3Adesc", @@ -569,9 +530,9 @@ class PlexAPI: else: search, modifier = os.path.splitext(str(search_method).lower()) final_search = search_translation[search] if search in search_translation else search - if search in ["added", "release", "episode_air_date"] and modifier == "": + if search in date_searches and modifier == "": final_mod = ">>" - elif search in ["added", "release", "episode_air_date"] and modifier == ".not": + elif search in date_searches and modifier == ".not": final_mod = "<<" elif search in ["critic_rating", "audience_rating"] and modifier == ".gt": final_mod = "__gt" @@ -583,7 +544,7 @@ class PlexAPI: if search == "duration": search_terms[final_method] = search_data * 60000 - elif search in ["added", "release", "episode_air_date"] and modifier in ["", ".not"]: + elif search in date_searches and modifier in ["", ".not"]: search_terms[final_method] = f"{search_data}d" else: search_terms[final_method] = search_data