mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-22 12:33:10 +00:00
[4] add remove_overlay and overlay fixes
This commit is contained in:
parent
7bc9b7f95b
commit
7f5c066229
5 changed files with 134 additions and 64 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.16.5-develop3
|
||||
1.16.5-develop4
|
||||
|
|
|
@ -6,7 +6,7 @@ Overlays and templates are defined within one or more Overlay files, which are l
|
|||
|
||||
**To remove all overlays use the `remove_overlays` library operation.**
|
||||
|
||||
**To change a single overlay original Image either replace the image in the assets folder or remove the `Overlay` shared label and then PMM will overlay the new image**
|
||||
**To change a single overlay original image either replace the image in the assets folder or remove the `Overlay` shared label and then PMM will overlay the new image**
|
||||
|
||||
These are the attributes which can be used within the Overlay File:
|
||||
|
||||
|
@ -35,6 +35,26 @@ overlays:
|
|||
|
||||
Each section must have the only required attribute, `overlay`.
|
||||
|
||||
### Overlay Name
|
||||
|
||||
You can specify the Overlay Name in 3 ways.
|
||||
|
||||
1. If there is no `overlay` attribute PMM will look in your `config/overlays` folder for a `.png` file named the same as the mapping name of the overlay definition.
|
||||
```yaml
|
||||
overlays:
|
||||
IMDb Top 250:
|
||||
imdb_chart: top_movies
|
||||
```
|
||||
|
||||
2. If the `overlay` attribute is given a string PMM will look in your `config/overlays` folder for a `.png` file named the same as the string given.
|
||||
```yaml
|
||||
overlays:
|
||||
overlay: IMDb Top 250
|
||||
IMDb Top 250:
|
||||
imdb_chart: top_movies
|
||||
```
|
||||
|
||||
3. Using a dictionary for more overlay location options.
|
||||
|
||||
| Attribute | Description | Required |
|
||||
|:----------|:-------------------------------------------------------------------------------------------------------------|:--------:|
|
||||
|
@ -53,7 +73,29 @@ overlays:
|
|||
imdb_chart: top_movies
|
||||
```
|
||||
|
||||
There are three types of attributes that can be utilized within an overlay:
|
||||
### Remove Overlay
|
||||
|
||||
You can add `remove_overlay` to an overlay definition and give it a list or comma separated string of overlay names you want removed from this item if this overlay is attached to the item.
|
||||
|
||||
```yaml
|
||||
overlays:
|
||||
4K:
|
||||
plex_search:
|
||||
all:
|
||||
resolution: 4K
|
||||
HDR:
|
||||
plex_search:
|
||||
all:
|
||||
hdr: true
|
||||
4K-HDR:
|
||||
remove_overlay:
|
||||
- 4K
|
||||
- HDR
|
||||
plex_search:
|
||||
all:
|
||||
resolution: 4K
|
||||
hdr: true
|
||||
```
|
||||
|
||||
### Builders
|
||||
|
||||
|
@ -93,7 +135,7 @@ These filter media items added to the collection by any of the Builders.
|
|||
overlays:
|
||||
4K:
|
||||
overlay:
|
||||
name: 4K # This will look for a local overlays/4K.png in your configs folder
|
||||
name: 4K # This will look for a local overlays/4K.png in your config folder
|
||||
plex_search:
|
||||
all:
|
||||
resolution: 4K
|
||||
|
|
|
@ -97,7 +97,7 @@ boolean_details = [
|
|||
scheduled_boolean = ["visible_library", "visible_home", "visible_shared"]
|
||||
string_details = ["sort_title", "content_rating", "name_mapping"]
|
||||
ignored_details = [
|
||||
"smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test",
|
||||
"smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "remove_overlay",
|
||||
"delete_not_scheduled", "tmdb_person", "build_collection", "collection_order", "collection_level", "overlay",
|
||||
"validate_builders", "libraries", "sync_to_users", "collection_name", "playlist_name", "name", "blank_collection"
|
||||
]
|
||||
|
@ -190,7 +190,7 @@ custom_sort_builders = [
|
|||
"mal_popular", "mal_favorite", "mal_suggested", "mal_userlist", "mal_season", "mal_genre", "mal_studio"
|
||||
]
|
||||
episode_parts_only = ["plex_pilots"]
|
||||
overlay_only = ["overlay"]
|
||||
overlay_only = ["overlay", "remove_overlay"]
|
||||
overlay_attributes = [
|
||||
"filters", "limit", "show_missing", "save_missing", "missing_only_released", "minimum_items", "cache_builders", "tmdb_region"
|
||||
] + all_builders + overlay_only
|
||||
|
@ -211,7 +211,7 @@ music_attributes = [
|
|||
] + details + summary_details + poster_details + background_details
|
||||
|
||||
class CollectionBuilder:
|
||||
def __init__(self, config, metadata, name, data, library=None, overlay=None):
|
||||
def __init__(self, config, metadata, name, data, library=None, overlay=None, extra=None):
|
||||
self.config = config
|
||||
self.metadata = metadata
|
||||
self.mapping_name = name
|
||||
|
@ -229,6 +229,14 @@ class CollectionBuilder:
|
|||
self.type = "collection"
|
||||
self.Type = self.type.capitalize()
|
||||
|
||||
logger.separator(f"{self.mapping_name} {self.Type}{f' in {self.library.name}' if self.library else ''}")
|
||||
logger.info("")
|
||||
if extra:
|
||||
logger.info(extra)
|
||||
logger.info("")
|
||||
|
||||
logger.separator(f"Validating {self.mapping_name} Attributes", space=False, border=False)
|
||||
|
||||
if "name" in methods:
|
||||
name = self.data[methods["name"]]
|
||||
elif f"{self.type}_name" in methods:
|
||||
|
@ -256,6 +264,7 @@ class CollectionBuilder:
|
|||
self.data[attr] = new_attributes[attr]
|
||||
methods[attr.lower()] = attr
|
||||
|
||||
self.remove_overlays = []
|
||||
if self.overlay:
|
||||
if "overlay" in methods:
|
||||
logger.debug("")
|
||||
|
@ -294,10 +303,20 @@ class CollectionBuilder:
|
|||
self.overlay = data[methods["overlay"]]
|
||||
else:
|
||||
self.overlay = self.mapping_name
|
||||
logger.warning(f"{self.Type} Warning: No overlay attribute using mapping name {self.mapping_name} as the overlay name")
|
||||
overlay_path = os.path.join(library.overlay_folder, f"{self.overlay}.png")
|
||||
if not os.path.exists(overlay_path):
|
||||
raise Failed(f"{self.Type} Error: Overlay Image not found at: {overlay_path}")
|
||||
|
||||
if "remove_overlay" in methods:
|
||||
logger.debug("")
|
||||
logger.debug("Validating Method: remove_overlay")
|
||||
logger.debug(f"Value: {data[methods['remove_overlay']]}")
|
||||
if data[methods["remove_overlay"]]:
|
||||
self.remove_overlays = util.get_list(data[methods["remove_overlay"]])
|
||||
else:
|
||||
logger.error(f"{self.Type} Error: remove_overlay attribute is blank")
|
||||
|
||||
if self.playlist:
|
||||
if "libraries" in methods:
|
||||
logger.debug("")
|
||||
|
@ -388,8 +407,8 @@ class CollectionBuilder:
|
|||
s_attr = f"sync_to_user{'s' if 'sync_to_users' in methods else ''}"
|
||||
logger.debug("")
|
||||
logger.debug(f"Validating Method: {s_attr}")
|
||||
logger.debug(f"Value: {self.data[methods[s_attr]]}")
|
||||
if self.data[methods[s_attr]]:
|
||||
logger.debug(f"Value: {self.data[methods[s_attr]]}")
|
||||
self.sync_to_users = self.data[methods[s_attr]]
|
||||
else:
|
||||
logger.warning(f"Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_users: {self.sync_to_users}")
|
||||
|
|
|
@ -30,7 +30,10 @@ class Overlays:
|
|||
builder = CollectionBuilder(self.config, overlay_file, k, v, library=self.library, overlay=True)
|
||||
logger.info("")
|
||||
|
||||
logger.separator(f"Running {k} Overlay", space=False, border=False)
|
||||
logger.separator(f"Gathering Items for {k} Overlay", space=False, border=False)
|
||||
|
||||
if builder.overlay not in overlay_rating_keys:
|
||||
overlay_rating_keys[builder.overlay] = []
|
||||
|
||||
if builder.filters or builder.tmdb_filters:
|
||||
logger.info("")
|
||||
|
@ -45,13 +48,17 @@ class Overlays:
|
|||
logger.info("")
|
||||
builder.filter_and_save_items(builder.gather_ids(method, value))
|
||||
if builder.added_items:
|
||||
if builder.overlay not in overlay_rating_keys:
|
||||
overlay_rating_keys[builder.overlay] = []
|
||||
for item in builder.added_items:
|
||||
item_keys[item.ratingKey] = item
|
||||
if item.ratingKey not in overlay_rating_keys[builder.overlay]:
|
||||
overlay_rating_keys[builder.overlay].append(item.ratingKey)
|
||||
|
||||
if builder.remove_overlays:
|
||||
for rk in overlay_rating_keys[builder.overlay]:
|
||||
for remove_overlay in builder.remove_overlays:
|
||||
if remove_overlay in overlay_rating_keys and rk in overlay_rating_keys[remove_overlay]:
|
||||
overlay_rating_keys[remove_overlay].remove(rk)
|
||||
|
||||
for overlay_name, over_keys in overlay_rating_keys.items():
|
||||
clean_name, _ = util.validate_filename(overlay_name)
|
||||
image_compare = None
|
||||
|
@ -68,6 +75,20 @@ class Overlays:
|
|||
if self.config.Cache:
|
||||
self.config.Cache.update_image_map(overlay_name, f"{self.library.image_table_name}_overlays", overlay_name, overlay_size)
|
||||
|
||||
def find_poster_url(plex_item):
|
||||
if self.library.is_movie:
|
||||
if plex_item.ratingKey in self.library.movie_rating_key_map:
|
||||
return self.config.TMDb.get_movie(self.library.movie_rating_key_map[plex_item.ratingKey]).poster_url
|
||||
elif self.library.is_show:
|
||||
check_key = plex_item.ratingKey if isinstance(plex_item, Show) else plex_item.show().ratingKey
|
||||
tmdb_id = self.config.Convert.tvdb_to_tmdb(self.library.show_rating_key_map[check_key])
|
||||
if isinstance(plex_item, Show) and plex_item.ratingKey in self.library.show_rating_key_map:
|
||||
return self.config.TMDb.get_show(tmdb_id).poster_url
|
||||
elif isinstance(plex_item, Season):
|
||||
return self.config.TMDb.get_season(tmdb_id, plex_item.seasonNumber).poster_url
|
||||
elif isinstance(plex_item, Episode):
|
||||
return self.config.TMDb.get_episode(tmdb_id, plex_item.seasonNumber, plex_item.episodeNumber).still_url
|
||||
|
||||
def get_overlay_items(libtype=None):
|
||||
return [o for o in self.library.search(label="Overlay", libtype=libtype) if o.ratingKey not in item_overlays]
|
||||
|
||||
|
@ -78,6 +99,11 @@ class Overlays:
|
|||
elif self.library.is_music:
|
||||
remove_overlays.extend(get_overlay_items(libtype="album"))
|
||||
|
||||
if remove_overlays:
|
||||
logger.info("")
|
||||
logger.separator(f"Removing Overlays for the {self.library.name} Library")
|
||||
logger.info("")
|
||||
|
||||
for i, item in enumerate(remove_overlays, 1):
|
||||
logger.ghost(f"Restoring: {i}/{len(remove_overlays)} {item.title}")
|
||||
clean_name, _ = util.validate_filename(item.title)
|
||||
|
@ -86,29 +112,33 @@ class Overlays:
|
|||
folder_name=clean_name if self.library.asset_folders else None,
|
||||
prefix=f"{item.title}'s "
|
||||
)
|
||||
poster_location = None
|
||||
is_url = False
|
||||
original = None
|
||||
if poster:
|
||||
poster_location = poster.location
|
||||
elif os.path.exists(os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png")):
|
||||
poster_location = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png")
|
||||
original = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png")
|
||||
poster_location = original
|
||||
elif os.path.exists(os.path.join(self.library.overlay_backup, f"{item.ratingKey}.jpg")):
|
||||
poster_location = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.jpg")
|
||||
original = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.jpg")
|
||||
poster_location = original
|
||||
else:
|
||||
is_url = True
|
||||
if self.library.is_movie:
|
||||
if item.ratingKey in self.library.movie_rating_key_map:
|
||||
poster_location = self.config.TMDb.get_movie(self.library.movie_rating_key_map[item.ratingKey]).poster_url
|
||||
elif self.library.is_show:
|
||||
if item.ratingKey in self.library.show_rating_key_map:
|
||||
poster_location = self.config.TMDb.get_show(self.config.Convert.tvdb_to_tmdb(self.library.show_rating_key_map[item.ratingKey])).poster_url
|
||||
poster_location = find_poster_url(item)
|
||||
if poster_location:
|
||||
self.library.upload_poster(item, poster_location, url=is_url)
|
||||
self.library.edit_tags("label", item, remove_tags=["Overlay"])
|
||||
if original:
|
||||
os.remove(original)
|
||||
else:
|
||||
logger.error(f"No Poster found to restore for {item.title}")
|
||||
logger.exorcise()
|
||||
|
||||
if item_overlays:
|
||||
logger.info("")
|
||||
logger.separator(f"Applying Overlays for the {self.library.name} Library")
|
||||
logger.info("")
|
||||
|
||||
for i, (over_key, over_names) in enumerate(item_overlays.items(), 1):
|
||||
try:
|
||||
item = item_keys[over_key]
|
||||
|
@ -136,43 +166,38 @@ class Overlays:
|
|||
folder_name=clean_name if self.library.asset_folders else None,
|
||||
prefix=f"{item.title}'s "
|
||||
)
|
||||
|
||||
has_original = False
|
||||
changed_image = False
|
||||
new_backup = None
|
||||
if poster:
|
||||
if image_compare and str(poster.compare) != str(image_compare):
|
||||
changed_image = True
|
||||
else:
|
||||
elif has_overlay:
|
||||
if os.path.exists(os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png")):
|
||||
has_original = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png")
|
||||
elif os.path.exists(os.path.join(self.library.overlay_backup, f"{item.ratingKey}.jpg")):
|
||||
has_original = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.jpg")
|
||||
else:
|
||||
changed_image = True
|
||||
self.library.reload(item)
|
||||
poster_url = item.posterUrl
|
||||
if has_overlay:
|
||||
if self.library.is_movie:
|
||||
if item.ratingKey in self.library.movie_rating_key_map:
|
||||
poster_url = self.config.TMDb.get_movie(self.library.movie_rating_key_map[item.ratingKey]).poster_url
|
||||
elif self.library.is_show:
|
||||
check_key = item.ratingKey if isinstance(item, Show) else item.show().ratingKey
|
||||
tmdb_id = self.config.Convert.tvdb_to_tmdb(self.library.show_rating_key_map[check_key])
|
||||
if isinstance(item, Show) and item.ratingKey in self.library.show_rating_key_map:
|
||||
poster_url = self.config.TMDb.get_show(tmdb_id).poster_url
|
||||
elif isinstance(item, Season):
|
||||
poster_url = self.config.TMDb.get_season(tmdb_id, item.seasonNumber).poster_url
|
||||
elif isinstance(item, Episode):
|
||||
poster_url = self.config.TMDb.get_episode(tmdb_id, item.seasonNumber, item.episodeNumber).still_url
|
||||
response = self.config.get(poster_url)
|
||||
if response.status_code >= 400:
|
||||
raise Failed(f"Overlay Error: Poster Download Failed for {item.title}")
|
||||
ext = "jpg" if response.headers["Content-Type"] == "image/jpeg" else "png"
|
||||
backup_image = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.{ext}")
|
||||
with open(backup_image, "wb") as handler:
|
||||
handler.write(response.content)
|
||||
while util.is_locked(backup_image):
|
||||
time.sleep(1)
|
||||
has_original = backup_image
|
||||
new_backup = find_poster_url(item)
|
||||
if new_backup is None:
|
||||
new_backup = item.posterUrl
|
||||
else:
|
||||
self.library.reload(item)
|
||||
new_backup = item.posterUrl
|
||||
if new_backup:
|
||||
changed_image = True
|
||||
image_response = self.config.get(new_backup)
|
||||
if image_response.status_code >= 400:
|
||||
raise Failed(f"Overlay Error: Poster Download Failed for {item.title}")
|
||||
i_ext = "jpg" if image_response.headers["Content-Type"] == "image/jpeg" else "png"
|
||||
backup_image_path = os.path.join(self.library.overlay_backup, f"{item.ratingKey}.{i_ext}")
|
||||
with open(backup_image_path, "wb") as handler:
|
||||
handler.write(image_response.content)
|
||||
while util.is_locked(backup_image_path):
|
||||
time.sleep(1)
|
||||
has_original = backup_image_path
|
||||
|
||||
poster_uploaded = False
|
||||
if changed_image or overlay_change:
|
||||
|
|
|
@ -449,15 +449,7 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
library.status[mapping_name] = {"status": "", "errors": [], "created": False, "modified": False, "deleted": False, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
|
||||
|
||||
try:
|
||||
logger.separator(f"{mapping_name} Collection in {library.name}")
|
||||
logger.info("")
|
||||
if output_str:
|
||||
logger.info(output_str)
|
||||
logger.info("")
|
||||
|
||||
logger.separator(f"Validating {mapping_name} Attributes", space=False, border=False)
|
||||
|
||||
builder = CollectionBuilder(config, metadata, mapping_name, collection_attrs, library=library)
|
||||
builder = CollectionBuilder(config, metadata, mapping_name, collection_attrs, library=library, extra=output_str)
|
||||
library.stats["names"].append(builder.name)
|
||||
logger.info("")
|
||||
|
||||
|
@ -625,15 +617,7 @@ def run_playlists(config):
|
|||
server_name = None
|
||||
library_names = None
|
||||
try:
|
||||
logger.separator(f"{mapping_name} Playlist")
|
||||
logger.info("")
|
||||
if output_str:
|
||||
logger.info(output_str)
|
||||
logger.info("")
|
||||
|
||||
logger.separator(f"Validating {mapping_name} Attributes", space=False, border=False)
|
||||
|
||||
builder = CollectionBuilder(config, playlist_file, mapping_name, playlist_attrs)
|
||||
builder = CollectionBuilder(config, playlist_file, mapping_name, playlist_attrs, extra=output_str)
|
||||
stats["names"].append(builder.name)
|
||||
logger.info("")
|
||||
|
||||
|
|
Loading…
Reference in a new issue