mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-22 20:43:07 +00:00
[10] builder_level
This commit is contained in:
parent
ff14c2d80f
commit
f44589e853
14 changed files with 84 additions and 76 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.17.2-develop9
|
1.17.2-develop10
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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> |
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) | ✅ | `<<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 |
|
||||||
|
|
|
@ -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("")
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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>>"}}}
|
||||||
|
|
|
@ -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}")
|
||||||
|
|
|
@ -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]})"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
Loading…
Reference in a new issue