[6] removed plex_search `type` now use `collection_level`

pull/909/head
meisnate12 3 years ago
parent 062394c6d3
commit b617f4a258

@ -1 +1 @@
1.17.0-develop5 1.17.0-develop6

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

@ -34,25 +34,38 @@ This example outlines what a "standard" config.yml file might look like when in
<br /> <br />
```yaml ```yaml
libraries: libraries: # This is called out once within the config.yml file
Movies - 4K: Movies: # Each library must match the Plex library name
metadata_path: metadata_path:
- file: config/Movies.yml - file: config/Movies.yml # This is a local file on the system
- git: meisnate12/MovieCharts - folder: config/Movies/ # This is a local directory on the system
TV Shows: - git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
- git: PMM/chart/imdb # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
overlay_path:
- remove_overlays: false # Set this to true to remove all overlays
- file: config/Overlays.yml # This is a local file on the system
- git: PMM/overlays/imdb_top_250 # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
TV Shows:
metadata_path: metadata_path:
- file: config/TVShows.yml - file: config/TVShows.yml
- folder: config/TV Shows/ - folder: config/TV Shows/
- git: meisnate12/ShowCharts - git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
Animé: - git: PMM/chart/imdb # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
overlay_path:
- remove_overlays: false # Set this to true to remove all overlays
- file: config/Overlays.yml # This is a local file on the system
- git: PMM/overlays/imdb_top_250 # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
Anime:
metadata_path: metadata_path:
- file: config/Anime.yml - file: config/Anime.yml
- git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
- git: PMM/chart/anilist # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
Music: Music:
metadata_path: metadata_path:
- file: config/Music.yml - file: config/Music.yml
playlist_files: playlist_files:
- file: config/playlists.yml - file: config/playlists.yml
- git: meisnate12/Playlists - git: PMM/playlist # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository
settings: settings:
cache: true cache: true
cache_expiration: 60 cache_expiration: 60
@ -183,7 +196,8 @@ As can be seen in the original config.yml example, there are three metadata_path
metadata_path: metadata_path:
- file: config/TVShows.yml - file: config/TVShows.yml
- folder: config/TV Shows/ - folder: config/TV Shows/
- git: meisnate12/ShowCharts - git: PMM/chart/basic
- git: PMM/chart/imdb
``` ```
These path types are outlined as follows: These path types are outlined as follows:
@ -194,17 +208,22 @@ These path types are outlined as follows:
* `- git:` refers to a YAML file which is hosted on the [GitHub Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) unless the user has specified a custom repository within the settings section of the config.yml file. * `- git:` refers to a YAML file which is hosted on the [GitHub Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) unless the user has specified a custom repository within the settings section of the config.yml file.
Within the above example, PMM will: Within the above example, PMM will:
* First, look within the root of the PMM directory (also known as `config/`) for a metadata file named `Movies.yml`. If this file does not exist, PMM will skip the entry and move to the next one in the list. * First, look within the root of the PMM directory (also known as `config/`) for a metadata file named `TVShows.yml`. If this file does not exist, PMM will skip the entry and move to the next one in the list.
* Then, look within the root of the PMM directory (also known as `config/`) for a directory called `TV Shows`, and then load any metadata/YAML files within that directory. * Then, look within the root of the PMM directory (also known as `config/`) for a directory called `TV Shows`, and then load any metadata/YAML files within that directory.
* Finally, look at the [meisnate12 folder](https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/meisnate12) within the GitHub Configs Repo for a file called `MovieCharts.yml` which it finds [here](https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/meisnate12/MovieCharts.yml). * After that, look at the [PMM/chart folder](https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/PMM/chart) within the GitHub Configs Repo for a file called `basic.yml` which it finds [here](https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/PMM/chart/basic.yml).
* Finally, look at the [PMM/chart folder](https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/PMM/chart) within the GitHub Configs Repo for a file called `imdb.yml` which it finds [here](https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/PMM/chart/imdb.yml).
It should be noted that whilst the user should be able to edit any metadata files which are `- file:` or `- folder:` based, they have little to no control over `- git:` metadata files **unless a copy of the YAML file is downloaded and ran locally**. In the above example, if the user downloaded the [MovieCharts.yml file](https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/meisnate12/MovieCharts.yml) from the [GitHub Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) and placed it in the root directory of PMM (`config/`), then the metadata_path mapping would be updated to reflect this as follows: It should be noted that whilst the user should be able to edit any metadata files which are `- file:` or `- folder:` based, they have little to no control over `- git:` metadata files **unless a copy of the YAML file is downloaded and ran locally**. In the above example, if the user downloaded the [basic.yml file](https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/PMM/chart/basic.yml) from the [GitHub Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) and placed it in the root directory of PMM (`config/`), then the metadata_path mapping would be updated to reflect this as follows:
```yaml ```yaml
Movies - 4K: TV Shows:
metadata_path: metadata_path:
- file: config/Movies.yml - file: config/TVShows.yml
- file: config/MovieCharts.yml <------ HERE - folder: config/TV Shows/
- file: PMM/chart/basic # <------ HERE
- git: PMM/chart/imdb
``` ```
## Playlists (`playlist_files:` mappings) ## Playlists (`playlist_files:` mappings)
@ -213,7 +232,7 @@ Playlists can be seen as an extension of Libraries in that they are both handled
```yaml ```yaml
playlist_files: playlist_files:
- file: config/playlists.yml - file: config/playlists.yml
- git: meisnate12/Playlists - git: PMM/playlists
``` ```
As with `libraries:`, YAML files are defined to create the Playlists. It should be noted that whilst in `libraries:` when working with `playlist_files:` you call out the libraries being connected to within the Metadata/YAML file as Playlists can combine media from multiple libraries. You can view an example playlists.yml file as follows: As with `libraries:`, YAML files are defined to create the Playlists. It should be noted that whilst in `libraries:` when working with `playlist_files:` you call out the libraries being connected to within the Metadata/YAML file as Playlists can combine media from multiple libraries. You can view an example playlists.yml file as follows:

@ -77,13 +77,14 @@ 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.**
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:
## Special Attributes ## Special Attributes
| Attribute | Description & Values | | Attribute | Description & Values |
|:-----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:-----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | **Description:** The Type of items inside this collection/playlist.<br>**Default:**<table class="clearTable"><tr><td>`movies` for Movies Libraries</td></tr><tr><td>`shows` for Show Libraries</td></tr><tr><td>`artists` for Music Libraries</td></tr></table>**Values:** `movies`, `shows`, `seasons`, `episodes`, `artists`, `albums`, or `tracks` |
| `limit` | **Description:** The max number of item for the search.<br>**Default:** `all`<br>**Values:** `all` or a number greater than 0 | | `limit` | **Description:** The max number of item for the search.<br>**Default:** `all`<br>**Values:** `all` or a number greater than 0 |
| `sort_by` | **Description:** This will control how the filter is sorted in your library.<br>**Default:** `random`<br>**Values:** Any sort options for your search type in the [Sorts Options Table](#sort-options) | | `sort_by` | **Description:** This will control how the filter is sorted in your library.<br>**Default:** `random`<br>**Values:** Any sort options for your search type in the [Sorts Options Table](#sort-options) |
| `validate` | **Description:** Determines if a collection/playlist will fail on a validation error<br>**Default:** `true`<br>**Values**: `true` or `false` | | `validate` | **Description:** Determines if a collection/playlist will fail on a validation error<br>**Default:** `true`<br>**Values**: `true` or `false` |

@ -46,13 +46,14 @@ 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.**
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:
## Special Attributes ## Special Attributes
| Attribute | Description & Values | | Attribute | Description & Values |
|:-----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:-----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | **Description:** The Type of items inside this collection.<br>**Default:**<table class="clearTable"><tr><td>`movies` for Movies Libraries</td></tr><tr><td>`shows` for Show Libraries</td></tr><tr><td>`artists` for Music Libraries</td></tr></table>**Values:** `movies`, `shows`, `seasons`, `episodes`, `artists`, `albums`, or `tracks` |
| `limit` | **Description:** The max number of item for the filter.<br>**Default:** `all`<br>**Values:** `all` or a number greater than 0 | | `limit` | **Description:** The max number of item for the filter.<br>**Default:** `all`<br>**Values:** `all` or a number greater than 0 |
| `sort_by` | **Description:** This will control how the filter is sorted in your library.<br>**Default:** `random`<br>**Values:** Any sort options for your filter type in the [Sorts Options Table](#sort-options) | | `sort_by` | **Description:** This will control how the filter is sorted in your library.<br>**Default:** `random`<br>**Values:** Any sort options for your filter type in the [Sorts Options Table](#sort-options) |
| `validate` | **Description:** Determines if a collection will fail on a validation error<br>**Default:** `true`<br>**Values**: `true` or `false` | | `validate` | **Description:** Determines if a collection will fail on a validation error<br>**Default:** `true`<br>**Values**: `true` or `false` |

@ -454,17 +454,18 @@ class CollectionBuilder:
logger.debug("Validating Method: collection_level") logger.debug("Validating Method: collection_level")
level = self.data[methods["collection_level"]] level = self.data[methods["collection_level"]]
if level is None: if level is None:
raise Failed(f"{self.Type} Error: collection_level attribute is blank") logger.error(f"{self.Type} Error: collection_level attribute is blank")
logger.debug(f"Value: {level}") else:
level = level.lower() logger.debug(f"Value: {level}")
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): level = level.lower()
self.collection_level = level 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):
elif (self.library.is_show and level != "show") or (self.library.is_music and level != "artist"): self.collection_level = level
if self.library.is_show: elif (self.library.is_show and level != "show") or (self.library.is_music and level != "artist"):
options = "\n\tseason (Collection at the Season Level)\n\tepisode (Collection at the Episode Level)" if self.library.is_show:
else: options = "\n\tseason (Collection at the Season Level)\n\tepisode (Collection at the Episode Level)"
options = "\n\talbum (Collection at the Album Level)\n\ttrack (Collection at the Track Level)" else:
raise Failed(f"{self.Type} Error: {self.data[methods['collection_level']]} collection_level invalid{options}") 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}")
self.parts_collection = self.collection_level in plex.collection_level_options self.parts_collection = self.collection_level in plex.collection_level_options
if "tmdb_person" in methods: if "tmdb_person" in methods:
@ -1232,19 +1233,15 @@ class CollectionBuilder:
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}
new_dictionary = {}
if method_name == "plex_search": if method_name == "plex_search":
type_override = f"{self.collection_level}s" if self.collection_level in plex.collection_level_options else None self.builders.append((method_name, self.build_filter("plex_search", dict_data)))
new_dictionary = self.build_filter("plex_search", dict_data, type_override=type_override)
elif method_name == "plex_collectionless": elif method_name == "plex_collectionless":
prefix_list = util.parse(self.Type, "exclude_prefix", dict_data, datatype="list", methods=dict_methods) if "exclude_prefix" in dict_methods else [] prefix_list = util.parse(self.Type, "exclude_prefix", dict_data, datatype="list", methods=dict_methods) if "exclude_prefix" in dict_methods else []
exact_list = util.parse(self.Type, "exclude", dict_data, datatype="list", methods=dict_methods) if "exclude" in dict_methods else [] exact_list = util.parse(self.Type, "exclude", dict_data, datatype="list", methods=dict_methods) if "exclude" in dict_methods else []
if len(prefix_list) == 0 and len(exact_list) == 0: if len(prefix_list) == 0 and len(exact_list) == 0:
raise Failed(f"{self.Type} Error: you must have at least one exclusion") raise Failed(f"{self.Type} Error: you must have at least one exclusion")
exact_list.append(self.name) exact_list.append(self.name)
new_dictionary["exclude_prefix"] = prefix_list self.builders.append((method_name, {"exclude_prefix": prefix_list, "exclude": exact_list}))
new_dictionary["exclude"] = exact_list
self.builders.append((method_name, new_dictionary))
else: else:
self.builders.append(("plex_search", self.build_filter("plex_search", {"any": {method_name: method_data}}))) self.builders.append(("plex_search", self.build_filter("plex_search", {"any": {method_name: method_data}})))
@ -1670,7 +1667,7 @@ class CollectionBuilder:
if self.details["save_report"] is True and filtered_items: if self.details["save_report"] is True and filtered_items:
self.library.add_filtered(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in filtered_items], self.library.is_movie) self.library.add_filtered(self.name, [(i.title, self.library.get_id_from_maps(i.ratingKey)) for i in filtered_items], self.library.is_movie)
def build_filter(self, method, plex_filter, display=False, default_sort=None, type_override=None): def build_filter(self, method, plex_filter, display=False, default_sort=None):
if display: if display:
logger.info("") logger.info("")
logger.info(f"Validating Method: {method}") logger.info(f"Validating Method: {method}")
@ -1686,24 +1683,18 @@ 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 type_override: if self.collection_level == "item":
sort_type = type_override if self.library.is_show:
elif "type" in filter_alias and self.library.is_show: sort_type = "show"
if plex_filter[filter_alias["type"]] not in ["shows", "seasons", "episodes"]: elif self.library.is_music:
raise Failed(f"{self.Type} Error: type: {plex_filter[filter_alias['type']]} is invalid, must be either shows, season, or episodes") sort_type = "artist"
sort_type = plex_filter[filter_alias["type"]] else:
elif "type" in filter_alias and self.library.is_music: sort_type = "movie"
if plex_filter[filter_alias["type"]] not in ["artists", "albums", "tracks"]:
raise Failed(f"{self.Type} Error: type: {plex_filter[filter_alias['type']]} is invalid, must be either artists, albums, or tracks")
sort_type = plex_filter[filter_alias["type"]]
elif self.library.is_show:
sort_type = "shows"
elif self.library.is_music:
sort_type = "artists"
else: else:
sort_type = "movies" sort_type = self.collection_level
ms = method.split("_") ms = method.split("_")
filter_details = f"{ms[0].capitalize()} {sort_type.capitalize()[:-1]} {ms[1].capitalize()}\n" filter_details = f"{ms[0].capitalize()} {sort_type.capitalize()} {ms[1].capitalize()}\n"
type_default_sort, type_key, sorts = plex.sort_types[sort_type] type_default_sort, type_key, sorts = plex.sort_types[sort_type]
sort = default_sort if default_sort else type_default_sort sort = default_sort if default_sort else type_default_sort

@ -375,13 +375,13 @@ track_sorts = {
"random": "random" "random": "random"
} }
sort_types = { sort_types = {
"movies": ("title.asc", 1, movie_sorts), "movie": ("title.asc", 1, movie_sorts),
"shows": ("title.asc", 2, show_sorts), "show": ("title.asc", 2, show_sorts),
"seasons": ("season.asc", 3, season_sorts), "season": ("season.asc", 3, season_sorts),
"episodes": ("title.asc", 4, episode_sorts), "episode": ("title.asc", 4, episode_sorts),
"artists": ("title.asc", 8, artist_sorts), "artist": ("title.asc", 8, artist_sorts),
"albums": ("title.asc", 9, album_sorts), "album": ("title.asc", 9, album_sorts),
"tracks": ("title.asc", 10, track_sorts) "track": ("title.asc", 10, track_sorts)
} }
class Plex(Library): class Plex(Library):
@ -996,6 +996,8 @@ class Plex(Library):
if not item_asset_directory: if not item_asset_directory:
if isinstance(item, (Movie, Artist, Album, Show, Episode, Season)): if isinstance(item, (Movie, Artist, Album, Show, Episode, Season)):
starting = item.show() if isinstance(item, (Episode, Season)) else item starting = item.show() if isinstance(item, (Episode, Season)) else item
if not starting.locations:
raise Failed(f"Asset Warning: No video filepath found fo {item.title}")
path_test = str(starting.locations[0]) path_test = str(starting.locations[0])
if not os.path.dirname(path_test): if not os.path.dirname(path_test):
path_test = path_test.replace("\\", "/") path_test = path_test.replace("\\", "/")

@ -780,22 +780,26 @@ def check_time(message, end=False):
logger.debug(f"{message}: {current_time - previous_time}") logger.debug(f"{message}: {current_time - previous_time}")
previous_time = None if end else current_time previous_time = None if end else current_time
system_fonts = []
def get_system_fonts(): def get_system_fonts():
dirs = [] global system_fonts
if sys.platform == "win32": if not system_fonts:
windir = os.environ.get("WINDIR") dirs = []
if windir: if sys.platform == "win32":
dirs.append(os.path.join(windir, "fonts")) windir = os.environ.get("WINDIR")
elif sys.platform in ("linux", "linux2"): if windir:
lindirs = os.environ.get("XDG_DATA_DIRS", "") dirs.append(os.path.join(windir, "fonts"))
if not lindirs: elif sys.platform in ("linux", "linux2"):
lindirs = "/usr/share" lindirs = os.environ.get("XDG_DATA_DIRS", "")
dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")] if not lindirs:
elif sys.platform == "darwin": lindirs = "/usr/share"
dirs += ["/Library/Fonts", "/System/Library/Fonts", os.path.expanduser("~/Library/Fonts")] dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")]
else: elif sys.platform == "darwin":
return dirs dirs += ["/Library/Fonts", "/System/Library/Fonts", os.path.expanduser("~/Library/Fonts")]
return [n for d in dirs for _, _, ns in os.walk(d) for n in ns] else:
return dirs
system_fonts = [n for d in dirs for _, _, ns in os.walk(d) for n in ns]
return system_fonts
class YAML: class YAML:
def __init__(self, path=None, input_data=None, check_empty=False, create=False): def __init__(self, path=None, input_data=None, check_empty=False, create=False):
@ -975,8 +979,7 @@ class Overlay:
if not match: if not match:
raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}") raise Failed(f"Overlay Error: failed to parse overlay text name: {self.name}")
self.name = f"text({match.group(1)})" self.name = f"text({match.group(1)})"
if os.path.exists("fonts/Roboto-Medium.ttf"): self.font_name = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "fonts", "Roboto-Medium.ttf")
self.font_name = "fonts/Roboto-Medium.ttf"
if "font_size" in self.data: if "font_size" in self.data:
self.font_size = parse("Overlay", "font_size", self.data["font_size"], datatype="int", parent="overlay", default=self.font_size) self.font_size = parse("Overlay", "font_size", self.data["font_size"], datatype="int", parent="overlay", default=self.font_size)
if "font" in self.data and self.data["font"]: if "font" in self.data and self.data["font"]:

Loading…
Cancel
Save