diff --git a/VERSION b/VERSION index f7beb8ab..27142b20 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.5-develop25 +1.16.5-develop26 diff --git a/docs/metadata/overlay.md b/docs/metadata/overlay.md index de21e8cf..9a2735ac 100644 --- a/docs/metadata/overlay.md +++ b/docs/metadata/overlay.md @@ -56,14 +56,17 @@ You can specify the Overlay Name in 3 ways. 3. Using a dictionary for more overlay location options. -| Attribute | Description | Required | -|:----------|:-------------------------------------------------------------------------------------------------------------|:--------:| -| `name` | Name of the overlay. Each overlay name should be unique. | ✅ | -| `url` | URL of Overlay Image Online | ❌ | -| `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) of the Overlay Image | ❌ | -| `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image | ❌ | +| Attribute | Description | Required | +|:-----------|:--------------------------------------------------------------------------------------------------------------|:--------:| +| `name` | Name of the overlay. Each overlay name should be unique. | ✅ | +| `url` | URL of Overlay Image Onlin. | ❌ | +| `git` | Location in the [Configs Repo](https://github.com/meisnate12/Plex-Meta-Manager-Configs) of the Overlay Image. | ❌ | +| `repo` | Location in the [Custom Repo](../config/settings.md#custom-repo) of the Overlay Image. | ❌ | +| `group` | Name of the Grouping for this overlay. **`priority` is required when using `group`** | ❌ | +| `priority` | Priority of this overlay in its group. **`group` is required when using `priority`** | ❌ | * If `url`, `git`, and `repo` are all not defined then PMM will look in your `config/overlays` folder for a `.png` file named the same as the `name` attribute. +* Only one overlay with the highest priority per group will be applied. ```yaml overlays: diff --git a/modules/builder.py b/modules/builder.py index 2569c661..3a69fe4e 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -269,6 +269,8 @@ class CollectionBuilder: methods[attr.lower()] = attr self.suppress_overlays = [] + self.overlay_group = None + self.overlay_priority = None if self.overlay: if "overlay" in methods: logger.debug("") @@ -278,6 +280,15 @@ class CollectionBuilder: if "name" not in data[methods["overlay"]] or not data[methods["overlay"]]["name"]: raise Failed(f"{self.Type} Error: overlay must have the name attribute") self.overlay = str(data[methods["overlay"]]["name"]) + if "group" in data[methods["overlay"]] and data[methods["overlay"]]["group"]: + self.overlay_group = str(data[methods["overlay"]]["group"]) + if "priority" in data[methods["overlay"]] and data[methods["overlay"]]["priority"]: + pri = util.check_num(data[methods["overlay"]]["group"]) + if pri is None: + raise Failed(f"{self.Type} Error: overlay priority must be a number") + self.overlay_priority = pri + else: + raise Failed(f"{self.Type} Error: overlay group and overlay priority must be used together") if "git" in data[methods["overlay"]] and data[methods["overlay"]]["git"]: url = f"{util.github_base}{data[methods['overlay']]['git']}.png" elif "repo" in data[methods["overlay"]] and data[methods["overlay"]]["repo"]: @@ -1889,8 +1900,14 @@ class CollectionBuilder: for name, key in names: if name not in used and re.compile(reg).search(name): valid_list.append((name, key) if plex_search else key) + if not valid_list: + error = f"Plex Error: {attribute}: No matches found with regex pattern {data}" + if validate: + raise Failed(error) + else: + logger.error(error) return valid_list - elif attribute in plex.tag_attributes and modifier in ["", ".not", ".regex"]: + elif attribute in plex.tag_attributes and modifier in ["", ".not"]: if attribute in plex.tmdb_attributes: final_values = [] for value in util.get_list(data): diff --git a/modules/overlays.py b/modules/overlays.py index d3f45f4c..cd710fc1 100644 --- a/modules/overlays.py +++ b/modules/overlays.py @@ -18,18 +18,15 @@ class Overlays: logger.info("") logger.separator(f"{self.library.name} Library Overlays") logger.info("") - overlay_to_keys = {} key_to_item = {} os.makedirs(self.library.overlay_backup, exist_ok=True) - overlay_updated = {} - overlay_images = {} key_to_overlays = {} + overlay_attrs = {} if self.library.remove_overlays: logger.info("") logger.separator(f"Removing Overlays for the {self.library.name} Library") logger.info("") else: - overlay_suppression = {} for overlay_file in self.library.overlay_files: for k, v in overlay_file.overlays.items(): try: @@ -38,8 +35,17 @@ class Overlays: logger.separator(f"Gathering Items for {k} Overlay", space=False, border=False) - if builder.overlay not in overlay_to_keys: - overlay_to_keys[builder.overlay] = [] + if builder.overlay not in overlay_attrs: + overlay_attrs[builder.overlay] = { + "keys": [], "suppress": [], "group": None, + "priority": None, "updated": False, "image": None + } + + for method, value in builder.builders: + logger.debug("") + logger.debug(f"Builder: {method}: {value}") + logger.info("") + builder.filter_and_save_items(builder.gather_ids(method, value)) if builder.filters or builder.tmdb_filters: logger.info("") @@ -48,48 +54,68 @@ class Overlays: for filter_key, filter_value in builder.tmdb_filters: logger.info(f"Collection Filter {filter_key}: {filter_value}") - for method, value in builder.builders: - logger.debug("") - logger.debug(f"Builder: {method}: {value}") - logger.info("") - builder.filter_and_save_items(builder.gather_ids(method, value)) - if builder.added_items: - for item in builder.added_items: - key_to_item[item.ratingKey] = item - if item.ratingKey not in overlay_to_keys[builder.overlay]: - overlay_to_keys[builder.overlay].append(item.ratingKey) + added_titles = [] + if builder.added_items: + for item in builder.added_items: + key_to_item[item.ratingKey] = item + added_titles.append(item.title) + if item.ratingKey not in overlay_attrs[builder.overlay]["keys"]: + overlay_attrs[builder.overlay]["keys"].append(item.ratingKey) + if added_titles: + logger.debug(f"{len(added_titles)} Titles Found: {added_titles}") + logger.info(f"{len(added_titles) if added_titles else 'No'} Items found for {builder.overlay}") if builder.suppress_overlays: - overlay_suppression[builder.overlay] = builder.suppress_overlays + overlay_attrs[builder.overlay]["suppress"] = builder.suppress_overlays except Failed as e: logger.error(e) - for over_name, suppress_names in overlay_suppression.items(): - for rk in overlay_to_keys[over_name]: - for suppress_name in suppress_names: - if suppress_name in overlay_to_keys and rk in overlay_to_keys[suppress_name]: - overlay_to_keys[suppress_name].remove(rk) - - for overlay_name, over_keys in overlay_to_keys.items(): - if overlay_name == "blur": - overlay_updated[overlay_name] = False - overlay_images[overlay_name] = None - else: + overlay_groups = {} + for overlay_name, over_attrs in overlay_attrs.items(): + if overlay_attrs["group"]: + if overlay_attrs["group"] not in overlay_groups: + overlay_groups[overlay_attrs["group"]] = {} + overlay_groups[overlay_attrs["group"]][overlay_name] = overlay_attrs["priority"] + for rk in over_attrs["keys"]: + for suppress_name in over_attrs["suppress"]: + if suppress_name in overlay_attrs and rk in overlay_attrs[suppress_name]["keys"]: + overlay_attrs[suppress_name]["keys"].remove(rk) + if not overlay_name.startswith("blur"): clean_name, _ = util.validate_filename(overlay_name) image_compare = None if self.config.Cache: _, image_compare, _ = self.config.Cache.query_image_map(overlay_name, f"{self.library.image_table_name}_overlays") overlay_file = os.path.join(self.library.overlay_folder, f"{clean_name}.png") overlay_size = os.stat(overlay_file).st_size - overlay_updated[overlay_name] = not image_compare or str(overlay_size) != str(image_compare) - overlay_images[overlay_name] = Image.open(overlay_file).convert("RGBA") + overlay_attrs[overlay_name]["updated"] = not image_compare or str(overlay_size) != str(image_compare) + overlay_attrs[overlay_name]["image"] = Image.open(overlay_file).convert("RGBA") if self.config.Cache: self.config.Cache.update_image_map(overlay_name, f"{self.library.image_table_name}_overlays", overlay_name, overlay_size) - for over_key in over_keys: + + for overlay_name, over_attrs in overlay_attrs.items(): + for over_key in over_attrs["keys"]: if over_key not in key_to_overlays: key_to_overlays[over_key] = (key_to_item[over_key], []) key_to_overlays[over_key][1].append(overlay_name) + for over_key, (item, over_names) in key_to_overlays.items(): + group_status = {} + for over_name in over_names: + for overlay_group, group_names in overlay_groups.items(): + if over_name in group_names: + if overlay_group not in group_status: + group_status[overlay_group] = [] + group_status[overlay_group].append(over_name) + for gk, gv in group_status.items(): + if len(gv) > 1: + final = None + for v in gv: + if final is None or overlay_groups[gk][v] > overlay_groups[gk][final]: + final = v + for v in gv: + if final != v: + key_to_overlays[over_key][1].remove(v) + def find_poster_url(plex_item): if isinstance(plex_item, Movie): if plex_item.ratingKey in self.library.movie_rating_key_map: @@ -166,7 +192,7 @@ class Overlays: overlay_change = True if not overlay_change: for over_name in over_names: - if over_name not in overlay_compare or overlay_updated[over_name]: + if over_name not in overlay_compare or overlay_attrs[over_name]["updated"]: overlay_change = True clean_name, _ = util.validate_filename(item.title) @@ -232,8 +258,8 @@ class Overlays: new_poster = new_poster.filter(ImageFilter.GaussianBlur(blur_num)) for over_name in over_names: if not over_name.startswith("blur"): - new_poster = new_poster.resize(overlay_images[over_name].size, Image.ANTIALIAS) - new_poster.paste(overlay_images[over_name], (0, 0), overlay_images[over_name]) + new_poster = new_poster.resize(overlay_attrs[over_name]["image"].size, Image.ANTIALIAS) + new_poster.paste(overlay_attrs[over_name]["image"], (0, 0), overlay_attrs[over_name]["image"]) new_poster.save(temp, "PNG") self.library.upload_poster(item, temp) self.library.edit_tags("label", item, add_tags=["Overlay"], do_print=False) diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 2b53e53c..033be426 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -485,6 +485,12 @@ def run_collection(config, library, metadata, requested_collections): logger.info("") logger.info(f"Sync Mode: {'sync' if builder.sync else 'append'}") + for method, value in builder.builders: + logger.debug("") + logger.debug(f"Builder: {method}: {value}") + logger.info("") + builder.filter_and_save_items(builder.gather_ids(method, value)) + if builder.filters or builder.tmdb_filters: logger.info("") for filter_key, filter_value in builder.filters: @@ -492,12 +498,6 @@ def run_collection(config, library, metadata, requested_collections): for filter_key, filter_value in builder.tmdb_filters: logger.info(f"Collection Filter {filter_key}: {filter_value}") - for method, value in builder.builders: - logger.debug("") - logger.debug(f"Builder: {method}: {value}") - logger.info("") - builder.filter_and_save_items(builder.gather_ids(method, value)) - if len(builder.added_items) > 0 and len(builder.added_items) + builder.beginning_count >= builder.minimum and builder.build_collection: items_added, items_unchanged = builder.add_to_collection() library.stats["added"] += items_added @@ -649,13 +649,6 @@ def run_playlists(config): logger.info("") logger.info(f"Sync Mode: {'sync' if builder.sync else 'append'}") - if builder.filters or builder.tmdb_filters: - logger.info("") - for filter_key, filter_value in builder.filters: - logger.info(f"Playlist Filter {filter_key}: {filter_value}") - for filter_key, filter_value in builder.tmdb_filters: - logger.info(f"Playlist Filter {filter_key}: {filter_value}") - method, value = builder.builders[0] logger.debug("") logger.debug(f"Builder: {method}: {value}") @@ -673,6 +666,13 @@ def run_playlists(config): builder.filter_and_save_items(ids) + if builder.filters or builder.tmdb_filters: + logger.info("") + for filter_key, filter_value in builder.filters: + logger.info(f"Playlist Filter {filter_key}: {filter_value}") + for filter_key, filter_value in builder.tmdb_filters: + logger.info(f"Playlist Filter {filter_key}: {filter_value}") + if len(builder.added_items) >= builder.minimum: items_added, items_unchanged = builder.add_to_collection() stats["added"] += items_added