[10] builder_level

This commit is contained in:
meisnate12 2022-07-26 15:58:53 -04:00
parent ff14c2d80f
commit f44589e853
14 changed files with 84 additions and 76 deletions

View file

@ -1 +1 @@
1.17.2-develop9 1.17.2-develop10

View file

@ -112,7 +112,7 @@ You can also find episodes using `imdb_list` like so.
collections: collections:
The Simpsons Top 100 Episodes: The Simpsons Top 100 Episodes:
collection_order: custom collection_order: custom
collection_level: episode builder_level: episode
sync_mode: sync sync_mode: sync
imdb_list: imdb_list:
url: https://www.imdb.com/search/title/?series=tt0096697&sort=user_rating,desc url: https://www.imdb.com/search/title/?series=tt0096697&sort=user_rating,desc

View file

@ -27,12 +27,12 @@ collections:
## Plex Pilots ## Plex Pilots
Gets the first episode of every show in your library. This Only works with `collection_level: episode` Gets the first episode of every show in your library. This only works with `builder_level: episode`
```yaml ```yaml
collection: collection:
Pilots: Pilots:
collection_level: episode builder_level: episode
plex_pilots: true plex_pilots: true
``` ```
@ -77,7 +77,7 @@ like Plex's [Advanced Filters](https://support.plex.tv/articles/201273953-collec
Inside the base attribute you can use any search below or nest more `any` or `all`. You can have as many nested `any` or `all` next to each other as you want. If using multiple `any` or `all` you will have to do so in the form of a list. Inside the base attribute you can use any search below or nest more `any` or `all`. You can have as many nested `any` or `all` next to each other as you want. If using multiple `any` or `all` you will have to do so in the form of a list.
**Note: To search by `season`, `episode`, `album`, or `track` you must use the `collection_level` [Detail](../details/metadata) to change the type of items the collection holds.** **Note: To search by `season`, `episode`, `album`, or `track` you must use the `builder_level` [Detail](../details/metadata) to change the type of items the collection holds.**
There are a couple other attributes you can have at the top level only along with the base attribute are: There are a couple other attributes you can have at the top level only along with the base attribute are:
@ -383,7 +383,7 @@ Here's an example of an episode collection using `plex_search`.
collections: collections:
Top 100 Simpsons Episodes: Top 100 Simpsons Episodes:
collection_order: custom collection_order: custom
collection_level: episode builder_level: episode
plex_search: plex_search:
type: episodes type: episodes
sort_by: audience_rating.desc sort_by: audience_rating.desc

View file

@ -46,7 +46,7 @@ like Plex's [Advanced Filters](https://support.plex.tv/articles/201273953-collec
Inside the base attribute you can use any filter below or nest more `any` or `all`. You can have as many nested `any` or `all` next to each other as you want. If using multiple `any` or `all` you will have to do so in the form of a list. Inside the base attribute you can use any filter below or nest more `any` or `all`. You can have as many nested `any` or `all` next to each other as you want. If using multiple `any` or `all` you will have to do so in the form of a list.
**Note: To search by `season`, `episode`, `album`, or `track` you must use the `collection_level` [Detail](../details/metadata) to change the type of items the collection holds.** **Note: To search by `season`, `episode`, `album`, or `track` you must use the `builder_level` [Detail](../details/metadata) to change the type of items the collection holds.**
There are a couple other attributes you can have at the top level only along with the base attribute are: There are a couple other attributes you can have at the top level only along with the base attribute are:

View file

@ -17,7 +17,7 @@ Only `tmdb_person` works with Playlists.
| `collection_mode` | **Description:** Changes the Collection Mode<br>**Normal Collections Only**<br>**Values:**<table class="clearTable"><tr><td>`default`</td><td>Library default</td></tr><tr><td>`hide`</td><td>Hide Collection</td></tr><tr><td>`hide_items`</td><td>Hide Items in this Collection</td></tr><tr><td>`show_items`</td><td>Show this Collection and its Items</td></tr></table> | | `collection_mode` | **Description:** Changes the Collection Mode<br>**Normal Collections Only**<br>**Values:**<table class="clearTable"><tr><td>`default`</td><td>Library default</td></tr><tr><td>`hide`</td><td>Hide Collection</td></tr><tr><td>`hide_items`</td><td>Hide Items in this Collection</td></tr><tr><td>`show_items`</td><td>Show this Collection and its Items</td></tr></table> |
| `collection_order` | **Description:** Changes the Collection Order<br>**Normal Collections Only**<br>When using `custom` you can only have a single builder in the collection.<br>**Values:**<table class="clearTable"><tr><td>`release`</td><td>Order Collection by Release Dates</td></tr><tr><td>`alpha`</td><td>Order Collection Alphabetically</td></tr><tr><td>`custom`</td><td>Order Collection Via the Builder Order</td></tr><tr><td>[Any `plex_search` Sort Option](../builders/plex.md#sort-options)</td><td>Order Collection by any `plex_search` Sort Option</td></tr></table> | | `collection_order` | **Description:** Changes the Collection Order<br>**Normal Collections Only**<br>When using `custom` you can only have a single builder in the collection.<br>**Values:**<table class="clearTable"><tr><td>`release`</td><td>Order Collection by Release Dates</td></tr><tr><td>`alpha`</td><td>Order Collection Alphabetically</td></tr><tr><td>`custom`</td><td>Order Collection Via the Builder Order</td></tr><tr><td>[Any `plex_search` Sort Option](../builders/plex.md#sort-options)</td><td>Order Collection by any `plex_search` Sort Option</td></tr></table> |
| `collection_filtering` | **Description:** Changes the Collection Filtering<br>**Smart Collections Only**<br>**Values:**<table class="clearTable"><tr><td>`admin`</td><td>Always the server admin user</td></tr><tr><td>`user`</td><td>User currently viewing the content</td></tr></table> | | `collection_filtering` | **Description:** Changes the Collection Filtering<br>**Smart Collections Only**<br>**Values:**<table class="clearTable"><tr><td>`admin`</td><td>Always the server admin user</td></tr><tr><td>`user`</td><td>User currently viewing the content</td></tr></table> |
| `collection_level` | **Description:** Make season, episode, album or track collections from `plex_all`, `plex_search`, `trakt_list`, or `imdb_list` Builders and Filters<br>**Values:**<table class="clearTable"><tr><td>`season`</td><td>Collection contains seasons</td></tr><tr><td>`episode`</td><td>Collection contains episodes</td></tr><tr><td>`album`</td><td>Collection contains albums</td></tr><tr><td>`track`</td><td>Collection contains tracks</td></tr></table> | | `builder_level` | **Description:** Make season, episode, album or track collections/overlays from `plex_all`, `plex_search`, `trakt_list`, or `imdb_list` Builders and Filters<br>**Values:**<table class="clearTable"><tr><td>`season`</td><td>Collection contains seasons</td></tr><tr><td>`episode`</td><td>Collection contains episodes</td></tr><tr><td>`album`</td><td>Collection contains albums</td></tr><tr><td>`track`</td><td>Collection contains tracks</td></tr></table> |
| `visible_library` | **Description:** Changes collection visible on Library (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> | | `visible_library` | **Description:** Changes collection visible on Library (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> |
| `visible_home` | **Description:** Changes collection visible on Home (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> | | `visible_home` | **Description:** Changes collection visible on Home (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> |
| `visible_shared` | **Description:** Changes collection visible on Shared Users' Home (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> | | `visible_shared` | **Description:** Changes collection visible on Shared Users' Home (Only works with Plex Pass)<br>**Values:**<table class="clearTable"><tr><td>`true`</td><td>Visible</td></tr><tr><td>`false`</td><td>Not Visible</td></tr><tr><td>[Any `schedule` Option](schedule)</td><td>Visible When Scheduled</td></tr></table> |

View file

@ -1614,7 +1614,7 @@ default_template:
```yaml ```yaml
templates: templates:
style collection: style collection:
collection_level: album builder_level: album
smart_filter: smart_filter:
limit: 10 limit: 10
sort_by: plays.desc sort_by: plays.desc

View file

@ -133,7 +133,7 @@ overlays:
blur: blur:
overlay: overlay:
name: blur(50) name: blur(50)
collection_level: episode builder_level: episode
plex_search: plex_search:
all: all:
resolution: 4K resolution: 4K
@ -177,12 +177,12 @@ The final text can be formatted using the `text_format` attribute and the format
The available options are: The available options are:
| Attribute | Requirements | Format Variables | | Attribute | Notes | Format Variables |
|:---------------------------|:-------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:---------------------------|:-------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| text(audience_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | | text(audience_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) |
| text(critic_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | | text(critic_rating) | Doesnt work with Seasons | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) |
| text(user_rating) | | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) | | text(user_rating) | | `<<value>>` -> ratings (`8.7`, `9.0`)<br>`<<value%>>` -> rating out of 100 (`87`, `90`)<br>`<<value#>>` -> rating removing `.0` as needed (`8.7`, `9`) |
| text(title) | &#9989; | `<<value>>` -> Title of the Item | | text(title) | | `<<value>>` -> Title of the Item |
| text(show_title) | Doesnt work with Movies and Shows | `<<value>>` -> Title of the Item's Show | | text(show_title) | Doesnt work with Movies and Shows | `<<value>>` -> Title of the Item's Show |
| text(season_title) | Only works with Episodes | `<<value>>` -> Title of the Item's Season | | text(season_title) | Only works with Episodes | `<<value>>` -> Title of the Item's Season |
| text(original_title) | Only works with Movies and Shows | `<<value>>` -> Original Title of the Item | | text(original_title) | Only works with Movies and Shows | `<<value>>` -> Original Title of the Item |

View file

@ -18,7 +18,7 @@ all_builders = anidb.builders + anilist.builders + flixpatrol.builders + icheckm
tmdb.builders + trakt.builders + tvdb.builders + mdblist.builders + radarr.builders + sonarr.builders tmdb.builders + trakt.builders + tvdb.builders + mdblist.builders + radarr.builders + sonarr.builders
show_only_builders = [ show_only_builders = [
"tmdb_network", "tmdb_show", "tmdb_show_details", "tvdb_show", "tvdb_show_details", "tmdb_airing_today", "tmdb_network", "tmdb_show", "tmdb_show_details", "tvdb_show", "tvdb_show_details", "tmdb_airing_today",
"tmdb_on_the_air", "collection_level", "item_tmdb_season_titles", "sonarr_all", "sonarr_taglist" "tmdb_on_the_air", "builder_level", "item_tmdb_season_titles", "sonarr_all", "sonarr_taglist"
] ]
movie_only_builders = [ movie_only_builders = [
"letterboxd_list", "letterboxd_list_details", "icheckmovies_list", "icheckmovies_list_details", "stevenlu_popular", "letterboxd_list", "letterboxd_list_details", "icheckmovies_list", "icheckmovies_list_details", "stevenlu_popular",
@ -40,7 +40,7 @@ scheduled_boolean = ["visible_library", "visible_home", "visible_shared"]
string_details = ["sort_title", "content_rating", "name_mapping"] string_details = ["sort_title", "content_rating", "name_mapping"]
ignored_details = [ ignored_details = [
"smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "suppress_overlays", "smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "suppress_overlays",
"delete_not_scheduled", "tmdb_person", "build_collection", "collection_order", "collection_level", "overlay", "delete_not_scheduled", "tmdb_person", "build_collection", "collection_order", "builder_level", "overlay",
"validate_builders", "libraries", "sync_to_users", "collection_name", "playlist_name", "name", "blank_collection", "validate_builders", "libraries", "sync_to_users", "collection_name", "playlist_name", "name", "blank_collection",
"allowed_library_types", "delete_playlist", "ignore_blank_results" "allowed_library_types", "delete_playlist", "ignore_blank_results"
] ]
@ -137,7 +137,7 @@ number_attributes = plex.number_attributes + ["channels", "height", "width"]
tag_attributes = plex.tag_attributes + ["audio_codec", "audio_profile", "video_codec", "video_profile"] tag_attributes = plex.tag_attributes + ["audio_codec", "audio_profile", "video_codec", "video_profile"]
float_attributes = plex.float_attributes + ["aspect"] float_attributes = plex.float_attributes + ["aspect"]
boolean_attributes = plex.boolean_attributes + boolean_filters boolean_attributes = plex.boolean_attributes + boolean_filters
smart_invalid = ["collection_order", "collection_level"] smart_invalid = ["collection_order", "builder_level"]
smart_only = ["collection_filtering"] smart_only = ["collection_filtering"]
smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_report", "smart_label"] + radarr_details + sonarr_details smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_report", "smart_label"] + radarr_details + sonarr_details
custom_sort_builders = [ custom_sort_builders = [
@ -245,28 +245,36 @@ class CollectionBuilder:
if not found_type: if not found_type:
raise NotScheduled(f"Skipped because allowed_library_types {self.data[methods['allowed_library_types']]} doesn't match the library type: {self.library.Plex.type}") raise NotScheduled(f"Skipped because allowed_library_types {self.data[methods['allowed_library_types']]} doesn't match the library type: {self.library.Plex.type}")
if self.playlist: self.collection_level = "item" if self.playlist: self.builder_level = "item"
elif self.library.is_show: self.collection_level = "show" elif self.library.is_show: self.builder_level = "show"
elif self.library.is_music: self.collection_level = "artist" elif self.library.is_music: self.builder_level = "artist"
else: self.collection_level = "movie" else: self.builder_level = "movie"
if "collection_level" in methods and not self.library.is_movie and not self.playlist: level = None
for level_attr in ["builder_level", "collection_level", "overlay_level"]:
if level_attr in methods:
level = self.data[methods[level_attr]]
if level_attr != "builder_level":
logger.warning(f"Collection Warning: {level_attr} attribute will run as builder_level")
break
if level and not self.library.is_movie and not self.playlist:
logger.debug("") logger.debug("")
logger.debug("Validating Method: collection_level") logger.debug("Validating Method: builder_level")
level = self.data[methods["collection_level"]] level = self.data[methods["builder_level"]]
if level is None: if level is None:
logger.error(f"{self.Type} Error: collection_level attribute is blank") logger.error(f"{self.Type} Error: builder_level attribute is blank")
else: else:
logger.debug(f"Value: {level}") logger.debug(f"Value: {level}")
level = level.lower() level = level.lower()
if (self.library.is_show and level in plex.collection_level_show_options) or (self.library.is_music and level in plex.collection_level_music_options): if (self.library.is_show and level in plex.builder_level_show_options) or (self.library.is_music and level in plex.builder_level_music_options):
self.collection_level = level self.builder_level = level
elif (self.library.is_show and level != "show") or (self.library.is_music and level != "artist"): elif (self.library.is_show and level != "show") or (self.library.is_music and level != "artist"):
if self.library.is_show: if self.library.is_show:
options = "\n\tseason (Collection at the Season Level)\n\tepisode (Collection at the Episode Level)" options = "\n\tseason (Collection at the Season Level)\n\tepisode (Collection at the Episode Level)"
else: else:
options = "\n\talbum (Collection at the Album Level)\n\ttrack (Collection at the Track Level)" options = "\n\talbum (Collection at the Album Level)\n\ttrack (Collection at the Track Level)"
raise Failed(f"{self.Type} Error: {self.data[methods['collection_level']]} collection_level invalid{options}") raise Failed(f"{self.Type} Error: {self.data[methods['builder_level']]} builder_level invalid{options}")
self.parts_collection = self.collection_level in plex.collection_level_options self.parts_collection = self.builder_level in plex.builder_level_options
if self.overlay: if self.overlay:
if "overlay" in methods: if "overlay" in methods:
@ -283,7 +291,7 @@ class CollectionBuilder:
suppress = util.get_list(data[methods["suppress_overlays"]]) suppress = util.get_list(data[methods["suppress_overlays"]])
else: else:
logger.error(f"Overlay Error: suppress_overlays attribute is blank") logger.error(f"Overlay Error: suppress_overlays attribute is blank")
self.overlay = Overlay(config, library, str(self.mapping_name), overlay_data, suppress, self.collection_level) self.overlay = Overlay(config, library, str(self.mapping_name), overlay_data, suppress, self.builder_level)
self.sync_to_users = None self.sync_to_users = None
self.valid_users = [] self.valid_users = []
@ -581,7 +589,7 @@ class CollectionBuilder:
raise Failed(f"{self.Type} Error: smart_filter is not compatible with smart_label") raise Failed(f"{self.Type} Error: smart_filter is not compatible with smart_label")
if self.parts_collection and "smart_url" in methods: if self.parts_collection and "smart_url" in methods:
raise Failed(f"{self.Type} Error: smart_url is not compatible with collection_level: {self.collection_level}") raise Failed(f"{self.Type} Error: smart_url is not compatible with builder_level: {self.builder_level}")
self.smart = self.smart_url or self.smart_label_collection self.smart = self.smart_url or self.smart_label_collection
@ -650,14 +658,14 @@ class CollectionBuilder:
raise Failed(f"{self.Type} Error: {method_final} plex search only allowed for show libraries") raise Failed(f"{self.Type} Error: {method_final} plex search only allowed for show libraries")
elif self.library.is_music and method_name not in music_attributes: elif self.library.is_music and method_name not in music_attributes:
raise Failed(f"{self.Type} Error: {method_final} attribute not allowed for music libraries") raise Failed(f"{self.Type} Error: {method_final} attribute not allowed for music libraries")
elif self.library.is_music and method_name in album_details and self.collection_level != "album": elif self.library.is_music and method_name in album_details and self.builder_level != "album":
raise Failed(f"{self.Type} Error: {method_final} attribute only allowed for album collections") raise Failed(f"{self.Type} Error: {method_final} attribute only allowed for album collections")
elif not self.library.is_music and method_name in music_only_builders: elif not self.library.is_music and method_name in music_only_builders:
raise Failed(f"{self.Type} Error: {method_final} attribute only allowed for music libraries") raise Failed(f"{self.Type} Error: {method_final} attribute only allowed for music libraries")
elif not self.playlist and self.collection_level != "episode" and method_name in episode_parts_only: elif not self.playlist and self.builder_level != "episode" and method_name in episode_parts_only:
raise Failed(f"{self.Type} Error: {method_final} attribute only allowed with Collection Level: episode") raise Failed(f"{self.Type} Error: {method_final} attribute only allowed with Collection Level: episode")
elif self.parts_collection and method_name not in parts_collection_valid: elif self.parts_collection and method_name not in parts_collection_valid:
raise Failed(f"{self.Type} Error: {method_final} attribute not allowed with Collection Level: {self.collection_level.capitalize()}") raise Failed(f"{self.Type} Error: {method_final} attribute not allowed with Collection Level: {self.builder_level.capitalize()}")
elif self.smart and method_name in smart_invalid: elif self.smart and method_name in smart_invalid:
raise Failed(f"{self.Type} Error: {method_final} attribute only allowed with normal collections") raise Failed(f"{self.Type} Error: {method_final} attribute only allowed with normal collections")
elif not self.smart and method_name in smart_only: elif not self.smart and method_name in smart_only:
@ -1258,7 +1266,7 @@ class CollectionBuilder:
def _plex(self, method_name, method_data): def _plex(self, method_name, method_data):
if method_name in ["plex_all", "plex_pilots"]: if method_name in ["plex_all", "plex_pilots"]:
self.builders.append((method_name, self.collection_level)) self.builders.append((method_name, self.builder_level))
elif method_name in ["plex_search", "plex_collectionless"]: elif method_name in ["plex_search", "plex_collectionless"]:
for dict_data in util.parse(self.Type, method_name, method_data, datatype="listdict"): for dict_data in util.parse(self.Type, method_name, method_data, datatype="listdict"):
dict_methods = {dm.lower(): dm for dm in dict_data} dict_methods = {dm.lower(): dm for dm in dict_data}
@ -1455,16 +1463,16 @@ class CollectionBuilder:
message = None message = None
if filter_final not in all_filters: if filter_final not in all_filters:
message = f"{self.Type} Error: {filter_final} is not a valid filter attribute" message = f"{self.Type} Error: {filter_final} is not a valid filter attribute"
elif self.collection_level in filters and filter_attr not in filters[self.collection_level]: elif self.builder_level in filters and filter_attr not in filters[self.builder_level]:
message = f"{self.Type} Error: {filter_final} is not a valid {self.collection_level} filter attribute" message = f"{self.Type} Error: {filter_final} is not a valid {self.builder_level} filter attribute"
elif filter_final is None: elif filter_final is None:
message = f"{self.Type} Error: {filter_final} filter attribute is blank" message = f"{self.Type} Error: {filter_final} filter attribute is blank"
else: else:
final_data = self.validate_attribute(filter_attr, modifier, f"{filter_final} filter", filter_data, validate) final_data = self.validate_attribute(filter_attr, modifier, f"{filter_final} filter", filter_data, validate)
if filter_attr in tmdb_filters: if filter_attr in tmdb_filters:
self.tmdb_filters.append((filter_final, final_data)) self.tmdb_filters.append((filter_final, final_data))
elif self.collection_level in ["show", "season", "artist", "album"] and filter_attr in sub_filters: elif self.builder_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})) self.filters.append(("episodes" if self.builder_level in ["show", "season"] else "tracks", {filter_final: final_data, "percentage": self.default_percent}))
else: else:
self.filters.append((filter_final, final_data)) self.filters.append((filter_final, final_data))
if message: if message:
@ -1547,10 +1555,10 @@ class CollectionBuilder:
found = True found = True
rating_keys = pl_library.imdb_map[input_id] rating_keys = pl_library.imdb_map[input_id]
break break
if not found and (self.collection_level == "episode" or self.playlist or self.do_missing): if not found and (self.builder_level == "episode" or self.playlist or self.do_missing):
try: try:
_id, tmdb_type = self.config.Convert.imdb_to_tmdb(input_id, fail=True) _id, tmdb_type = self.config.Convert.imdb_to_tmdb(input_id, fail=True)
if tmdb_type == "episode" and (self.collection_level == "episode" or self.playlist): if tmdb_type == "episode" and (self.builder_level == "episode" or self.playlist):
try: try:
tmdb_id, season_num, episode_num = _id.split("_") tmdb_id, season_num, episode_num = _id.split("_")
tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True) tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
@ -1619,7 +1627,7 @@ class CollectionBuilder:
break break
if not found and tvdb_id not in self.missing_shows: if not found and tvdb_id not in self.missing_shows:
self.missing_shows.append(tvdb_id) self.missing_shows.append(tvdb_id)
elif id_type == "tvdb_season" and (self.collection_level == "season" or self.playlist): elif id_type == "tvdb_season" and (self.builder_level == "season" or self.playlist):
tvdb_id, season_num = input_id.split("_") tvdb_id, season_num = input_id.split("_")
tvdb_id = int(tvdb_id) tvdb_id = int(tvdb_id)
found = False found = False
@ -1638,7 +1646,7 @@ class CollectionBuilder:
break break
if not found and tvdb_id not in self.missing_shows: if not found and tvdb_id not in self.missing_shows:
self.missing_shows.append(tvdb_id) self.missing_shows.append(tvdb_id)
elif id_type == "tvdb_episode" and (self.collection_level == "episode" or self.playlist): elif id_type == "tvdb_episode" and (self.builder_level == "episode" or self.playlist):
tvdb_id, season_num, episode_num = input_id.split("_") tvdb_id, season_num, episode_num = input_id.split("_")
tvdb_id = int(tvdb_id) tvdb_id = int(tvdb_id)
found = False found = False
@ -1712,7 +1720,7 @@ class CollectionBuilder:
if "any" in filter_alias and "all" in filter_alias: if "any" in filter_alias and "all" in filter_alias:
raise Failed(f"{self.Type} Error: Cannot have more then one base") raise Failed(f"{self.Type} Error: Cannot have more then one base")
if self.collection_level == "item": if self.builder_level == "item":
if self.library.is_show: if self.library.is_show:
sort_type = "show" sort_type = "show"
elif self.library.is_music: elif self.library.is_music:
@ -1720,7 +1728,7 @@ class CollectionBuilder:
else: else:
sort_type = "movie" sort_type = "movie"
else: else:
sort_type = self.collection_level sort_type = self.builder_level
ms = method.split("_") ms = method.split("_")
filter_details = f"{ms[0].capitalize()} {sort_type.capitalize()} {ms[1].capitalize()}\n" filter_details = f"{ms[0].capitalize()} {sort_type.capitalize()} {ms[1].capitalize()}\n"
@ -2077,7 +2085,7 @@ class CollectionBuilder:
self.library.add_additions(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in items_added], self.library.is_movie) self.library.add_additions(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in items_added], self.library.is_movie)
logger.exorcise() logger.exorcise()
logger.info("") logger.info("")
logger.info(f"{total} {self.collection_level.capitalize()}{'s' if total > 1 else ''} Processed") logger.info(f"{total} {self.builder_level.capitalize()}{'s' if total > 1 else ''} Processed")
return amount_added, amount_unchanged return amount_added, amount_unchanged
def sync_collection(self): def sync_collection(self):
@ -2105,7 +2113,7 @@ class CollectionBuilder:
if self.details["save_report"] is True and items_removed: if self.details["save_report"] is True and items_removed:
self.library.add_removed(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in items_removed], self.library.is_movie) self.library.add_removed(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in items_removed], self.library.is_movie)
logger.info("") logger.info("")
logger.info(f"{amount_removed} {self.collection_level.capitalize()}{'s' if amount_removed == 1 else ''} Removed") logger.info(f"{amount_removed} {self.builder_level.capitalize()}{'s' if amount_removed == 1 else ''} Removed")
return amount_removed return amount_removed
def check_tmdb_filter(self, item_id, is_movie, item=None, check_released=False): def check_tmdb_filter(self, item_id, is_movie, item=None, check_released=False):
@ -2322,7 +2330,7 @@ class CollectionBuilder:
if "non_item_remove_label" in self.item_details: if "non_item_remove_label" in self.item_details:
rk_compare = [item.ratingKey for item in self.items] rk_compare = [item.ratingKey for item in self.items]
for non_item in self.library.search(label=self.item_details["non_item_remove_label"], libtype=self.collection_level): for non_item in self.library.search(label=self.item_details["non_item_remove_label"], libtype=self.builder_level):
if non_item.ratingKey not in rk_compare: if non_item.ratingKey not in rk_compare:
self.library.edit_tags("label", non_item, remove_tags=self.item_details["non_item_remove_label"]) self.library.edit_tags("label", non_item, remove_tags=self.item_details["non_item_remove_label"])
@ -2578,9 +2586,9 @@ class CollectionBuilder:
items = self.added_items items = self.added_items
else: else:
plex_search = {"sort_by": self.custom_sort} plex_search = {"sort_by": self.custom_sort}
if self.collection_level in ["season", "episode"]: if self.builder_level in ["season", "episode"]:
plex_search["type"] = f"{self.collection_level}s" plex_search["type"] = f"{self.builder_level}s"
plex_search["any"] = {f"{self.collection_level}_collection": self.name} plex_search["any"] = {f"{self.builder_level}_collection": self.name}
else: else:
plex_search["any"] = {"collection": self.name} plex_search["any"] = {"collection": self.name}
search_data = self.build_filter("plex_search", plex_search) search_data = self.build_filter("plex_search", plex_search)
@ -2712,7 +2720,7 @@ class CollectionBuilder:
add_id = None add_id = None
self.notification_additions.append(util.item_set(current, add_id)) self.notification_additions.append(util.item_set(current, add_id))
self.send_notifications() self.send_notifications()
logger.info(f"{len(rating_keys)} {self.collection_level.capitalize()}{'s' if len(rating_keys) > 1 else ''} Processed") logger.info(f"{len(rating_keys)} {self.builder_level.capitalize()}{'s' if len(rating_keys) > 1 else ''} Processed")
if len(self.run_again_movies) > 0: if len(self.run_again_movies) > 0:
logger.info("") logger.info("")

View file

@ -229,7 +229,7 @@ class Library(ABC):
pass pass
@abstractmethod @abstractmethod
def get_all(self, collection_level=None, load=False): def get_all(self, builder_level=None, load=False):
pass pass
def add_additions(self, collection, items, is_movie): def add_additions(self, collection, items, is_movie):

View file

@ -495,7 +495,7 @@ class MetadataFile(DataFile):
final_var = auto_type if auto_type.startswith("album") else f"artist_{auto_type}" final_var = auto_type if auto_type.startswith("album") else f"artist_{auto_type}"
default_template = {"smart_filter": {"limit": 50, "sort_by": "plays.desc", "any": {final_var: f"<<value>>"}}} default_template = {"smart_filter": {"limit": 50, "sort_by": "plays.desc", "any": {final_var: f"<<value>>"}}}
if auto_type.startswith("album"): if auto_type.startswith("album"):
default_template["collection_level"] = "album" default_template["builder_level"] = "album"
default_title_format = f"Most Played <<key_name>> {'Albums' if auto_type.startswith('album') else '<<library_type>>'}s" default_title_format = f"Most Played <<key_name>> {'Albums' if auto_type.startswith('album') else '<<library_type>>'}s"
elif auto_type == "resolution": elif auto_type == "resolution":
default_template = {"smart_filter": {"sort_by": "title.asc", "any": {auto_type: f"<<value>>"}}} default_template = {"smart_filter": {"sort_by": "title.asc", "any": {auto_type: f"<<value>>"}}}

View file

@ -55,7 +55,7 @@ class Operations:
logger.info(f"{item.title[:25]:<25} | Splitting") logger.info(f"{item.title[:25]:<25} | Splitting")
if self.library.update_blank_track_titles: if self.library.update_blank_track_titles:
tracks = self.library.get_all(collection_level="track") tracks = self.library.get_all(builder_level="track")
num_edited = 0 num_edited = 0
for i, track in enumerate(tracks, 1): for i, track in enumerate(tracks, 1):
logger.ghost(f"Processing Track: {i}/{len(tracks)} {track.title}") logger.ghost(f"Processing Track: {i}/{len(tracks)} {track.title}")

View file

@ -252,21 +252,21 @@ class Overlay:
if self.name in all_special_text: if self.name in all_special_text:
if self.name.startswith("text(critic") and self.level == "season": if self.name.startswith("text(critic") and self.level == "season":
raise Failed("Overlay Error: collection_level season doesn't have critic_ratings") raise Failed("Overlay Error: builder_level season doesn't have critic_ratings")
elif self.name.startswith("text(audience") and self.level == "season": elif self.name.startswith("text(audience") and self.level == "season":
raise Failed("Overlay Error: collection_level season doesn't have audience_ratings") raise Failed("Overlay Error: builder_level season doesn't have audience_ratings")
elif self.name in ["text(season_episode)", "text(show_title)"] and self.level not in ["season", "episode"]: elif self.name in ["text(season_episode)", "text(show_title)"] and self.level not in ["season", "episode"]:
raise Failed(f"Overlay Error: {self.name[5:-1]} only works with collection_level season and episode") raise Failed(f"Overlay Error: {self.name[5:-1]} only works with builder_level season and episode")
elif self.name == "text(runtime)" and self.level not in ["movie", "episode"]: elif self.name == "text(runtime)" and self.level not in ["movie", "episode"]:
raise Failed("Overlay Error: runtime only works with movies and collection_level: episode") raise Failed("Overlay Error: runtime only works with movies and builder_level: episode")
elif self.name == "text(season_title)" and self.level != "episode": elif self.name == "text(season_title)" and self.level != "episode":
raise Failed("Overlay Error: season_title only works with collection_level: episode") raise Failed("Overlay Error: season_title only works with builder_level: episode")
elif self.name == "text(original_title)" and self.level not in ["movie", "show"]: elif self.name == "text(original_title)" and self.level not in ["movie", "show"]:
raise Failed("Overlay Error: original_title only works with movies and shows") raise Failed("Overlay Error: original_title only works with movies and shows")
elif self.name == "text(episode_count)" and self.level not in ["show", "season"]: elif self.name == "text(episode_count)" and self.level not in ["show", "season"]:
raise Failed("Overlay Error: episode_count only works with shows and collection_level: season") raise Failed("Overlay Error: episode_count only works with shows and builder_level: season")
elif self.name == ["text(content_rating)", "text(originally_available)"] and self.level == "season": elif self.name == ["text(content_rating)", "text(originally_available)"] and self.level == "season":
raise Failed(f"Overlay Error: {self.name[5:-1]} only works with movies, shows, and collection_level: episode") raise Failed(f"Overlay Error: {self.name[5:-1]} only works with movies, shows, and builder_level: episode")
elif self.name in old_special_text: elif self.name in old_special_text:
self.text_overlay_format = "<<value#>>" if self.name[-2] == "#" else f"<<value%>>{'' if self.name[-2] == '0' else '%'}" self.text_overlay_format = "<<value#>>" if self.name[-2] == "#" else f"<<value%>>{'' if self.name[-2] == '0' else '%'}"
self.name = f"{self.name[:-2]})" self.name = f"{self.name[:-2]})"

View file

@ -204,7 +204,7 @@ class Overlays:
actual_attr = "seasonNumber" actual_attr = "seasonNumber"
elif full_text == "show_title": elif full_text == "show_title":
actual_attr = "parentTitle" if text_overlay.level == "season" else "grandparentTitle" actual_attr = "parentTitle" if text_overlay.level == "season" else "grandparentTitle"
elif full_text in plex.attribute_translation[full_text]: elif full_text in plex.attribute_translation:
actual_attr = plex.attribute_translation[full_text] actual_attr = plex.attribute_translation[full_text]
else: else:
actual_attr = full_text actual_attr = full_text
@ -241,7 +241,7 @@ class Overlays:
season = actual_value season = actual_value
episode = None episode = None
else: else:
season, episode = actual_value[1:].split("E") season, episode = actual_value.upper()[1:].split("E")
for attr, attr_val in [("season", season), ("episode", episode)]: for attr, attr_val in [("season", season), ("episode", episode)]:
if attr_val and f"<<{attr}>>" in full_text: if attr_val and f"<<{attr}>>" in full_text:
full_text = full_text.replace(f"<<{attr}>>", attr_val) full_text = full_text.replace(f"<<{attr}>>", attr_val)

View file

@ -216,9 +216,9 @@ collection_mode_options = {
"hide_items": "hideItems", "hideitems": "hideItems", "hide_items": "hideItems", "hideitems": "hideItems",
"show_items": "showItems", "showitems": "showItems" "show_items": "showItems", "showitems": "showItems"
} }
collection_level_show_options = ["episode", "season"] builder_level_show_options = ["episode", "season"]
collection_level_music_options = ["album", "track"] builder_level_music_options = ["album", "track"]
collection_level_options = collection_level_show_options + collection_level_music_options builder_level_options = builder_level_show_options + builder_level_music_options
collection_mode_keys = {-1: "default", 0: "hide", 1: "hideItems", 2: "showItems"} collection_mode_keys = {-1: "default", 0: "hide", 1: "hideItems", 2: "showItems"}
collection_order_keys = {0: "release", 1: "alpha", 2: "custom"} collection_order_keys = {0: "release", 1: "alpha", 2: "custom"}
item_advance_keys = { item_advance_keys = {
@ -490,16 +490,16 @@ class Plex(Library):
def fetchItem(self, data): def fetchItem(self, data):
return self.PlexServer.fetchItem(data) return self.PlexServer.fetchItem(data)
def get_all(self, collection_level=None, load=False): def get_all(self, builder_level=None, load=False):
if load and collection_level in [None, "show", "artist", "movie"]: if load and builder_level in [None, "show", "artist", "movie"]:
self._all_items = [] self._all_items = []
if self._all_items and collection_level in [None, "show", "artist", "movie"]: if self._all_items and builder_level in [None, "show", "artist", "movie"]:
return self._all_items return self._all_items
collection_type = collection_level if collection_level else self.Plex.TYPE builder_type = builder_level if builder_level else self.Plex.TYPE
if not collection_level: if not builder_level:
collection_level = self.type builder_level = self.type
logger.info(f"Loading All {collection_level.capitalize()}s from Library: {self.name}") logger.info(f"Loading All {builder_level.capitalize()}s from Library: {self.name}")
key = f"/library/sections/{self.Plex.key}/all?includeGuids=1&type={utils.searchType(collection_type)}" key = f"/library/sections/{self.Plex.key}/all?includeGuids=1&type={utils.searchType(builder_type)}"
container_start = 0 container_start = 0
container_size = plexapi.X_PLEX_CONTAINER_SIZE container_size = plexapi.X_PLEX_CONTAINER_SIZE
results = [] results = []
@ -507,8 +507,8 @@ class Plex(Library):
results.extend(self.fetchItems(key, container_start, container_size)) results.extend(self.fetchItems(key, container_start, container_size))
logger.ghost(f"Loaded: {container_start}/{self.Plex._totalViewSize}") logger.ghost(f"Loaded: {container_start}/{self.Plex._totalViewSize}")
container_start += container_size container_start += container_size
logger.info(f"Loaded {self.Plex._totalViewSize} {collection_level.capitalize()}s") logger.info(f"Loaded {self.Plex._totalViewSize} {builder_level.capitalize()}s")
if collection_level in [None, "show", "artist", "movie"]: if builder_level in [None, "show", "artist", "movie"]:
self._all_items = results self._all_items = results
return results return results
@ -802,7 +802,7 @@ class Plex(Library):
items = [] items = []
if method == "plex_all": if method == "plex_all":
logger.info(f"Processing Plex All {data.capitalize()}s") logger.info(f"Processing Plex All {data.capitalize()}s")
items = self.get_all(collection_level=data) items = self.get_all(builder_level=data)
elif method == "plex_pilots": elif method == "plex_pilots":
logger.info(f"Processing Plex Pilot {data.capitalize()}s") logger.info(f"Processing Plex Pilot {data.capitalize()}s")
items = [] items = []