mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-22 20:43:07 +00:00
[41] add video_codec, video_profile, audio_codec, audio_profile, channels, height, width, and aspect filters
This commit is contained in:
parent
0e98db0644
commit
c7ccae1253
5 changed files with 72 additions and 28 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.17.0-develop40
|
||||
1.17.0-develop41
|
||||
|
|
|
@ -78,6 +78,10 @@ Tag filters can take multiple values as a **list or a comma-separated string**.
|
|||
| `resolution` | Uses the resolution tag to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `audio_language` | Uses the audio language tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `subtitle_language` | Uses the subtitle language tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `video_codec` | Uses the video codec tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `video_profile` | Uses the video profile tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `audio_codec` | Uses the audio codec tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `audio_profile` | Uses the audio profile tags to match | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `tmdb_genre`<sup>2</sup> | Uses the genre from TMDb to match | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `tmdb_keyword`<sup>2</sup> | Uses the keyword from TMDb to match | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `origin_country`<sup>2</sup> | Uses TMDb origin country [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) to match<br>Example: `origin_country: us` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
@ -138,6 +142,8 @@ Number filters can **NOT** take multiple values.
|
|||
|
||||
| Number Modifier | Description | Format |
|
||||
|:----------------|:-------------------------------------------------------------------------------------------|:-------------------------------------------------:|
|
||||
| No Modifier | Matches every item where the number attribute is equal to the given number | **Format:** number<br>e.g. `30`, `1995`, or `7.5` |
|
||||
| `.not` | Matches every item where the number attribute is not equal to the given number | **Format:** number<br>e.g. `30`, `1995`, or `7.5` |
|
||||
| `.gt` | Matches every item where the number attribute is greater than the given number | **Format:** number<br>e.g. `30`, `1995`, or `7.5` |
|
||||
| `.gte` | Matches every item where the number attribute is greater than or equal to the given number | **Format:** number<br>e.g. `30`, `1995`, or `7.5` |
|
||||
| `.lt` | Matches every item where the number attribute is less than the given number | **Format:** number<br>e.g. `30`, `1995`, or `7.5` |
|
||||
|
@ -145,18 +151,24 @@ Number filters can **NOT** take multiple values.
|
|||
|
||||
### Attribute
|
||||
|
||||
| Number Filters | Description | Movies | Shows | Seasons | Episodes | Artists | Albums | Track |
|
||||
|:------------------------------|:---------------------------------------------------------------------|:-------:|:-------:|:--------:|:--------:|:--------:|:--------:|:--------:|
|
||||
| `year` | Uses the year attribute to match<br>minimum: `1` | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
||||
| `tmdb_year`<sup>1</sup> | Uses the year on TMDb to match<br>minimum: `1` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `critic_rating` | Uses the critic rating attribute to match<br>`0.0` - `10.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
|
||||
| `audience_rating` | Uses the audience rating attribute to match<br> `0.0` - `10.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
| `user_rating` | Uses the user rating attribute to match<br>`0.0` - `10.0` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `tmdb_vote_count`<sup>1</sup> | Uses the tmdb vote count to match<br>minimum: `1` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `plays` | Uses the plays attribute to match<br>minimum: `1` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `duration` | Uses the duration attribute to match using minutes<br>minimum: `0.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
|
||||
| Number Filters | Description | Movies | Shows | Seasons | Episodes | Artists | Albums | Track |
|
||||
|:------------------------------|:---------------------------------------------------------------------|:-------:|:-------------------:|:-------------------:|:--------:|:--------:|:--------:|:--------:|
|
||||
| `year` | Uses the year attribute to match<br>minimum: `1` | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
||||
| `tmdb_year`<sup>2</sup> | Uses the year on TMDb to match<br>minimum: `1` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `critic_rating` | Uses the critic rating attribute to match<br>`0.0` - `10.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ |
|
||||
| `audience_rating` | Uses the audience rating attribute to match<br> `0.0` - `10.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
| `user_rating` | Uses the user rating attribute to match<br>`0.0` - `10.0` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `tmdb_vote_count`<sup>2</sup> | Uses the tmdb vote count to match<br>minimum: `1` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `plays` | Uses the plays attribute to match<br>minimum: `1` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `duration` | Uses the duration attribute to match using minutes<br>minimum: `0.0` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ |
|
||||
| `channels` | Uses the audio channels attribute to match<br>minimum: `0` | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `height` | Uses the height attribute to match<br>minimum: `0` | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `width` | Uses the width attribute to match<br>minimum: `0` | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
| `aspect` | Uses the aspect attribute to match<br>minimum: `0.0` | ✅ | ✅<sup>1</sup> | ✅<sup>1</sup> | ✅ | ❌ | ❌ | ❌ |
|
||||
|
||||
<sup>1</sup> Also filters out missing movies/shows from being added to Radarr/Sonarr.
|
||||
<sup>1</sup> Filters using the special `episodes` [filter](#special-filters) with the [default percent](details/setting).
|
||||
|
||||
<sup>2</sup> Also filters out missing movies/shows from being added to Radarr/Sonarr.
|
||||
|
||||
## Special Filters
|
||||
|
||||
|
|
|
@ -70,11 +70,15 @@ discover_status = {
|
|||
"Returning Series": "returning", "Planned": "planned", "In Production": "production",
|
||||
"Ended": "ended", "Canceled": "canceled", "Pilot": "pilot"
|
||||
}
|
||||
sub_filters = [
|
||||
"filepath", "audio_track_title", "resolution", "audio_language", "subtitle_language", "has_dolby_vision",
|
||||
"channels", "height", "width", "aspect", "audio_codec", "audio_profile", "video_codec", "video_profile"
|
||||
]
|
||||
filters_by_type = {
|
||||
"movie_show_season_episode_artist_album_track": ["title", "summary", "collection", "has_collection", "added", "last_played", "user_rating", "plays", "filepath", "label", "audio_track_title"],
|
||||
"movie_show_season_episode_album_track": ["year"],
|
||||
"movie_show_season_episode_artist_album": ["has_overlay"],
|
||||
"movie_show_season_episode": ["resolution", "audio_language", "subtitle_language", "has_dolby_vision"],
|
||||
"movie_show_season_episode": ["resolution", "audio_language", "subtitle_language", "has_dolby_vision", "channels", "height", "width", "aspect", "audio_codec", "audio_profile", "video_codec", "video_profile"],
|
||||
"movie_show_episode_album": ["release", "critic_rating", "history"],
|
||||
"movie_show_episode_track": ["duration"],
|
||||
"movie_show_artist_album": ["genre"],
|
||||
|
@ -105,15 +109,18 @@ tmdb_filters = [
|
|||
string_filters = ["title", "summary", "studio", "record_label", "folder", "filepath", "audio_track_title", "tmdb_title"]
|
||||
string_modifiers = ["", ".not", ".is", ".isnot", ".begins", ".ends", ".regex"]
|
||||
tag_filters = [
|
||||
"actor", "collection", "content_rating", "country", "director", "network", "genre", "label", "producer", "year", "origin_country",
|
||||
"writer", "resolution", "audio_language", "subtitle_language", "tmdb_keyword", "tmdb_genre"
|
||||
"actor", "collection", "content_rating", "country", "director", "network", "genre", "label", "producer", "year",
|
||||
"origin_country", "writer", "resolution", "audio_language", "subtitle_language", "tmdb_keyword", "tmdb_genre",
|
||||
"audio_codec", "audio_profile", "video_codec", "video_profile"
|
||||
]
|
||||
tag_modifiers = ["", ".not", ".regex", ".count_gt", ".count_gte", ".count_lt", ".count_lte"]
|
||||
boolean_filters = ["has_collection", "has_overlay", "has_dolby_vision"]
|
||||
date_filters = ["release", "added", "last_played", "first_episode_aired", "last_episode_aired"]
|
||||
date_modifiers = ["", ".not", ".before", ".after", ".regex"]
|
||||
number_filters = ["year", "tmdb_year", "critic_rating", "audience_rating", "user_rating", "tmdb_vote_count", "plays", "duration"]
|
||||
number_modifiers = [".gt", ".gte", ".lt", ".lte"]
|
||||
number_filters = [
|
||||
"year", "tmdb_year", "critic_rating", "audience_rating", "user_rating", "tmdb_vote_count", "plays", "duration",
|
||||
"channels", "height", "width", "aspect"]
|
||||
number_modifiers = ["", ".not", ".gt", ".gte", ".lt", ".lte"]
|
||||
special_filters = [
|
||||
"history", "episodes", "seasons", "albums", "tracks", "original_language", "original_language.not",
|
||||
"tmdb_status", "tmdb_status.not", "tmdb_type", "tmdb_type.not"
|
||||
|
@ -125,7 +132,10 @@ all_filters = boolean_filters + special_filters + \
|
|||
[f"{f}{m}" for f in number_filters for m in number_modifiers]
|
||||
date_attributes = plex.date_attributes + ["first_episode_aired", "last_episode_aired"]
|
||||
year_attributes = plex.year_attributes + ["tmdb_year"]
|
||||
number_attributes = plex.number_attributes + ["tmdb_vote_count"]
|
||||
number_attributes = plex.number_attributes + ["channels", "height", "width"]
|
||||
tag_attributes = plex.tag_attributes + ["audio_codec", "audio_profile", "video_codec", "video_profile"]
|
||||
float_attributes = plex.float_attributes + ["aspect"]
|
||||
boolean_attributes = plex.boolean_attributes + boolean_filters
|
||||
smart_invalid = ["collection_order", "collection_level"]
|
||||
smart_only = ["collection_filtering"]
|
||||
smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_report", "smart_label"] + radarr_details + sonarr_details
|
||||
|
@ -1437,7 +1447,7 @@ class CollectionBuilder:
|
|||
final_data = self.validate_attribute(filter_attr, modifier, f"{filter_final} filter", filter_data, validate)
|
||||
if filter_attr in tmdb_filters:
|
||||
self.tmdb_filters.append((filter_final, final_data))
|
||||
elif self.collection_level in ["show", "season", "artist", "album"] and filter_attr in ["filepath", "audio_track_title", "resolution", "audio_language", "subtitle_language", "has_dolby_vision"]:
|
||||
elif self.collection_level in ["show", "season", "artist", "album"] and filter_attr in sub_filters:
|
||||
self.filters.append(("episodes" if self.collection_level in ["show", "season"] else "tracks", {filter_final: final_data, "percentage": self.default_percent}))
|
||||
else:
|
||||
self.filters.append((filter_final, final_data))
|
||||
|
@ -1852,7 +1862,7 @@ class CollectionBuilder:
|
|||
def validate_attribute(self, attribute, modifier, final, data, validate, plex_search=False):
|
||||
def smart_pair(list_to_pair):
|
||||
return [(t, t) for t in list_to_pair] if plex_search else list_to_pair
|
||||
if attribute in plex.tag_attributes and modifier in [".regex"]:
|
||||
if attribute in tag_attributes and modifier in [".regex"]:
|
||||
_, names = self.library.get_search_choices(attribute, title=not plex_search, name_pairs=True)
|
||||
valid_list = []
|
||||
used = []
|
||||
|
@ -1892,7 +1902,7 @@ class CollectionBuilder:
|
|||
return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_types.items()])
|
||||
elif attribute == "tmdb_status":
|
||||
return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_status.items()])
|
||||
elif attribute in plex.tag_attributes and modifier in ["", ".not"]:
|
||||
elif attribute in tag_attributes and modifier in ["", ".not"]:
|
||||
if attribute in plex.tmdb_attributes:
|
||||
final_values = []
|
||||
for value in util.get_list(data):
|
||||
|
@ -1948,11 +1958,11 @@ class CollectionBuilder:
|
|||
search_data = util.parse(self.Type, final, data, datatype="int", minimum=0)
|
||||
return f"{search_data}{search_mod}" if plex_search else search_data
|
||||
elif (attribute in number_attributes + year_attributes and modifier in ["", ".not", ".gt", ".gte", ".lt", ".lte"]) \
|
||||
or (attribute in plex.tag_attributes and modifier in [".count_gt", ".count_gte", ".count_lt", ".count_lte"]):
|
||||
or (attribute in tag_attributes and modifier in [".count_gt", ".count_gte", ".count_lt", ".count_lte"]):
|
||||
return util.parse(self.Type, final, data, datatype="int", minimum=0)
|
||||
elif attribute in plex.float_attributes and modifier in [".gt", ".gte", ".lt", ".lte"]:
|
||||
elif attribute in float_attributes and modifier in ["", ".not", ".gt", ".gte", ".lt", ".lte"]:
|
||||
return util.parse(self.Type, final, data, datatype="float", minimum=0, maximum=None if attribute == "duration" else 10)
|
||||
elif attribute in plex.boolean_attributes + boolean_filters:
|
||||
elif attribute in boolean_attributes:
|
||||
return util.parse(self.Type, attribute, data, datatype="bool")
|
||||
elif attribute in ["seasons", "episodes", "albums", "tracks"]:
|
||||
if isinstance(data, dict) and data:
|
||||
|
|
|
@ -120,6 +120,13 @@ modifier_translation = {
|
|||
".before": "%3C%3C", ".after": "%3E%3E", ".begins": "%3C", ".ends": "%3E", ".regex": ""
|
||||
}
|
||||
attribute_translation = {
|
||||
"aspect": "aspectRatio",
|
||||
"channels": "audioChannels",
|
||||
"audio_codec": "audioCodec",
|
||||
"audio_profile ": "audioProfile",
|
||||
"video_codec": "videoCodec",
|
||||
"video_profile": "videoProfile",
|
||||
"resolution": "videoResolution",
|
||||
"record_label": "studio",
|
||||
"actor": "actors",
|
||||
"audience_rating": "audienceRating",
|
||||
|
@ -1257,6 +1264,11 @@ class Plex(Library):
|
|||
for media in item.media:
|
||||
for part in media.parts:
|
||||
values.extend([a.extendedDisplayTitle for a in part.audioStreams() if a.extendedDisplayTitle])
|
||||
elif filter_attr in ["audio_codec", "audio_profile", "video_codec", "video_profile"]:
|
||||
for media in item.media:
|
||||
attr = getattr(media, filter_actual)
|
||||
if attr and attr not in values:
|
||||
values.append(attr)
|
||||
elif filter_attr in ["filepath", "folder"]:
|
||||
values = [loc for loc in item.locations]
|
||||
else:
|
||||
|
@ -1322,12 +1334,20 @@ class Plex(Library):
|
|||
failures += 1
|
||||
if failures > failure_threshold:
|
||||
return False
|
||||
elif modifier in [".gt", ".gte", ".lt", ".lte", ".count_gt", ".count_gte", ".count_lt", ".count_lte"]:
|
||||
elif filter_attr in builder.number_filters or modifier in [".gt", ".gte", ".lt", ".lte", ".count_gt", ".count_gte", ".count_lt", ".count_lte"]:
|
||||
divider = 60000 if filter_attr == "duration" else 1
|
||||
test_number = []
|
||||
if filter_attr == "resolution":
|
||||
if filter_attr in ["resolution", "audio_codec", "audio_profile", "video_codec", "video_profile"]:
|
||||
for media in item.media:
|
||||
test_number.append(media.videoResolution)
|
||||
attr = getattr(media, filter_actual)
|
||||
if attr and attr not in test_number:
|
||||
test_number.append(attr)
|
||||
elif filter_attr in ["channels", "height", "width", "aspect"]:
|
||||
test_number = 0
|
||||
for media in item.media:
|
||||
attr = getattr(media, filter_actual)
|
||||
if attr and attr > test_number:
|
||||
test_number = attr
|
||||
elif filter_attr == "audio_language":
|
||||
for media in item.media:
|
||||
for part in media.parts:
|
||||
|
|
|
@ -488,7 +488,9 @@ def is_date_filter(value, modifier, data, final, current_time):
|
|||
return False
|
||||
|
||||
def is_number_filter(value, modifier, data):
|
||||
return value is None or (modifier == ".gt" and value <= data) \
|
||||
return value is None or (modifier == "" and value == data) \
|
||||
or (modifier == ".not" and value != data) \
|
||||
or (modifier == ".gt" and value <= data) \
|
||||
or (modifier == ".gte" and value < data) \
|
||||
or (modifier == ".lt" and value >= data) \
|
||||
or (modifier == ".lte" and value > data)
|
||||
|
|
Loading…
Reference in a new issue