[79] dimensional_asset_rename works for metadata and added prioritize_assets

pull/865/head
meisnate12 3 years ago
parent c623390bfe
commit 4c9509c5a9

@ -1 +1 @@
1.16.5-develop78 1.16.5-develop79

@ -27,6 +27,7 @@ The available setting attributes which can be set at each level are outlined bel
| [`asset_folders`](#image-asset-folders) | ✅ | ✅ | ❌ | | [`asset_folders`](#image-asset-folders) | ✅ | ✅ | ❌ |
| [`asset_depth`](#asset-depth) | ✅ | ✅ | ❌ | | [`asset_depth`](#asset-depth) | ✅ | ✅ | ❌ |
| [`create_asset_folders`](#create-asset-folders) | ✅ | ✅ | ❌ | | [`create_asset_folders`](#create-asset-folders) | ✅ | ✅ | ❌ |
| [`prioritize_assets`](#prioritize-assets) | ✅ | ✅ | ❌ |
| [`dimensional_asset_rename`](#dimensional-asset-rename) | ✅ | ✅ | ❌ | | [`dimensional_asset_rename`](#dimensional-asset-rename) | ✅ | ✅ | ❌ |
| [`download_url_assets`](#download-url-assets) | ✅ | ✅ | ❌ | | [`download_url_assets`](#download-url-assets) | ✅ | ✅ | ❌ |
| [`show_missing_season_assets`](#show-missing-season-assets) | ✅ | ✅ | ❌ | | [`show_missing_season_assets`](#show-missing-season-assets) | ✅ | ✅ | ❌ |
@ -147,6 +148,20 @@ Whilst searching for assets, if an asset folder cannot be found within the `asse
</tr> </tr>
</table> </table>
## Prioritize Assets
When determining which image to use on an item prioritize the `asset_directory` over all other images types.
<table class="dualTable colwidths-auto align-default table">
<tr>
<th>Default Value</th>
<td><code>false</code></td>
</tr>
<tr>
<th>Allowed Values</th>
<td><code>true</code> or <code>false</code>
</td>
</tr>
</table>
## Dimensional Asset Rename ## Dimensional Asset Rename
Whilst searching for assets, scan the folders within the `asset_directory` and if an asset poster (i.e. `/ASSET_NAME/poster.ext`) was not found, rename the first image found that has a height greater than or equal to its width to `poster.ext`. If an asset background (i.e. `/ASSET_NAME/background.ext`), rename the first image found that has a width greater than its height to `background.ext`. Whilst searching for assets, scan the folders within the `asset_directory` and if an asset poster (i.e. `/ASSET_NAME/poster.ext`) was not found, rename the first image found that has a height greater than or equal to its width to `poster.ext`. If an asset background (i.e. `/ASSET_NAME/background.ext`), rename the first image found that has a width greater than its height to `background.ext`.

@ -55,9 +55,11 @@ Builders use third-party services to source items to be added to the collection.
* [Tautulli Builders](builders/tautulli) * [Tautulli Builders](builders/tautulli)
* [Radarr Builders](builders/radarr) * [Radarr Builders](builders/radarr)
* [Sonarr Builders](builders/sonarr) * [Sonarr Builders](builders/sonarr)
* [MdbList Builders](builders/mdblist)
* [Letterboxd Builders](builders/letterboxd) * [Letterboxd Builders](builders/letterboxd)
* [ICheckMovies Builders](builders/icheckmovies) * [ICheckMovies Builders](builders/icheckmovies)
* [FlixPatrol Builders](builders/flixpatrol) * [FlixPatrol Builders](builders/flixpatrol)
* [Reciperr Builders](builders/reciperr)
* [StevenLu Builders](builders/stevenlu) * [StevenLu Builders](builders/stevenlu)
* [AniDB Builders](builders/anidb) * [AniDB Builders](builders/anidb)
* [AniList Builders](builders/anilist) * [AniList Builders](builders/anilist)

@ -126,15 +126,18 @@ overlays:
Builders use third-party services to source items for overlays. Multiple builders can be used in the same overlay from a variety of sources listed below. Builders use third-party services to source items for overlays. Multiple builders can be used in the same overlay from a variety of sources listed below.
* [Plex Builders](builders/plex) * [Plex Builders](builders/plex)
* [Smart Builders](builders/smart)
* [TMDb Builders](builders/tmdb) * [TMDb Builders](builders/tmdb)
* [TVDb Builders](builders/tvdb) * [TVDb Builders](builders/tvdb)
* [IMDb Builders](builders/imdb) * [IMDb Builders](builders/imdb)
* [Trakt Builders](builders/trakt) * [Trakt Builders](builders/trakt)
* [Tautulli Builders](builders/tautulli) * [Tautulli Builders](builders/tautulli)
* [Radarr Builders](builders/radarr)
* [Sonarr Builders](builders/sonarr)
* [MdbList Builders](builders/mdblist)
* [Letterboxd Builders](builders/letterboxd) * [Letterboxd Builders](builders/letterboxd)
* [ICheckMovies Builders](builders/icheckmovies) * [ICheckMovies Builders](builders/icheckmovies)
* [FlixPatrol Builders](builders/flixpatrol) * [FlixPatrol Builders](builders/flixpatrol)
* [Reciperr Builders](builders/reciperr)
* [StevenLu Builders](builders/stevenlu) * [StevenLu Builders](builders/stevenlu)
* [AniDB Builders](builders/anidb) * [AniDB Builders](builders/anidb)
* [AniList Builders](builders/anilist) * [AniList Builders](builders/anilist)

@ -53,15 +53,18 @@ There are three types of attributes that can be utilized within a playlist:
Builders use third-party services to source items to be added to the playlist. Multiple builders can be used in the same playlist from a variety of sources listed below. Builders use third-party services to source items to be added to the playlist. Multiple builders can be used in the same playlist from a variety of sources listed below.
* [Plex Builders](builders/plex) * [Plex Builders](builders/plex)
* [Smart Builders](builders/smart)
* [TMDb Builders](builders/tmdb) * [TMDb Builders](builders/tmdb)
* [TVDb Builders](builders/tvdb) * [TVDb Builders](builders/tvdb)
* [IMDb Builders](builders/imdb) * [IMDb Builders](builders/imdb)
* [Trakt Builders](builders/trakt) * [Trakt Builders](builders/trakt)
* [Tautulli Builders](builders/tautulli) * [Tautulli Builders](builders/tautulli)
* [Radarr Builders](builders/radarr)
* [Sonarr Builders](builders/sonarr)
* [MdbList Builders](builders/mdblist)
* [Letterboxd Builders](builders/letterboxd) * [Letterboxd Builders](builders/letterboxd)
* [ICheckMovies Builders](builders/icheckmovies) * [ICheckMovies Builders](builders/icheckmovies)
* [FlixPatrol Builders](builders/flixpatrol) * [FlixPatrol Builders](builders/flixpatrol)
* [Reciperr Builders](builders/reciperr)
* [StevenLu Builders](builders/stevenlu) * [StevenLu Builders](builders/stevenlu)
* [AniDB Builders](builders/anidb) * [AniDB Builders](builders/anidb)
* [AniList Builders](builders/anilist) * [AniList Builders](builders/anilist)

@ -2575,8 +2575,6 @@ class CollectionBuilder:
if advance_update and "Metadata" not in updated_details: if advance_update and "Metadata" not in updated_details:
updated_details.append("Metadata") updated_details.append("Metadata")
poster_image = None
background_image = None
asset_location = None asset_location = None
if self.asset_directory: if self.asset_directory:
name_mapping = self.name name_mapping = self.name
@ -2584,87 +2582,17 @@ class CollectionBuilder:
if self.details["name_mapping"]: name_mapping = self.details["name_mapping"] if self.details["name_mapping"]: name_mapping = self.details["name_mapping"]
else: logger.error(f"{self.Type} Error: name_mapping attribute is blank") else: logger.error(f"{self.Type} Error: name_mapping attribute is blank")
try: try:
poster_image, background_image, asset_location, _ = self.library.find_item_assets(name_mapping, asset_directory=self.asset_directory) asset_poster, asset_background, asset_location, _ = self.library.find_item_assets(name_mapping, asset_directory=self.asset_directory)
if poster_image: if asset_poster:
self.posters["asset_directory"] = poster_image self.posters["asset_directory"] = asset_poster
if background_image: if asset_background:
self.backgrounds["asset_directory"] = background_image self.backgrounds["asset_directory"] = asset_background
except Failed as e: except Failed as e:
if self.library.asset_folders and (self.library.show_missing_assets or self.library.create_asset_folders): if self.library.asset_folders and (self.library.show_missing_assets or self.library.create_asset_folders):
logger.warning(e) logger.warning(e)
self.collection_poster = None self.collection_poster = util.pick_image(self.obj.title, self.posters, self.library.prioritize_assets, self.library.download_url_assets, asset_location)
if len(self.posters) > 0: self.collection_background = util.pick_image(self.obj.title, self.backgrounds, self.library.prioritize_assets, self.library.download_url_assets, asset_location, is_poster=False)
logger.debug(f"{len(self.posters)} posters found:")
for p in self.posters:
logger.debug(f"Method: {p} Poster: {self.posters[p]}")
if "url_poster" in self.posters:
if self.library.download_url_assets and asset_location:
if poster_image:
self.collection_poster = self.posters["asset_directory"]
else:
response = self.config.get(self.posters["url_poster"], headers=util.header())
if response.status_code >= 400 or "Content-Type" not in response.headers or response.headers["Content-Type"] not in ["image/png", "image/jpeg"]:
logger.error(f"Image Error: Failed to parse Image at {self.posters['url_poster']}")
else:
new_image = os.path.join(asset_location, f"poster{'.png' if response.headers['Content-Type'] == 'image/png' else '.jpg'}")
with open(new_image, "wb") as handler:
handler.write(response.content)
self.collection_poster = ImageData("asset_directory", new_image, prefix=f"{self.obj.title}'s ", is_url=False)
if not self.collection_poster:
self.collection_poster = ImageData("url_poster", self.posters["url_poster"])
elif "file_poster" in self.posters: self.collection_poster = ImageData("file_poster", self.posters["file_poster"], is_url=False)
elif "tmdb_poster" in self.posters: self.collection_poster = ImageData("tmdb_poster", self.posters["tmdb_poster"])
elif "tmdb_profile" in self.posters: self.collection_poster = ImageData("tmdb_poster", self.posters["tmdb_profile"])
elif "tvdb_poster" in self.posters: self.collection_poster = ImageData("tvdb_poster", self.posters["tvdb_poster"])
elif "asset_directory" in self.posters: self.collection_poster = self.posters["asset_directory"]
elif "tmdb_person" in self.posters: self.collection_poster = ImageData("tmdb_person", self.posters["tmdb_person"])
elif "tmdb_collection_details" in self.posters: self.collection_poster = ImageData("tmdb_collection_details", self.posters["tmdb_collection_details"])
elif "tmdb_actor_details" in self.posters: self.collection_poster = ImageData("tmdb_actor_details", self.posters["tmdb_actor_details"])
elif "tmdb_crew_details" in self.posters: self.collection_poster = ImageData("tmdb_crew_details", self.posters["tmdb_crew_details"])
elif "tmdb_director_details" in self.posters: self.collection_poster = ImageData("tmdb_director_details", self.posters["tmdb_director_details"])
elif "tmdb_producer_details" in self.posters: self.collection_poster = ImageData("tmdb_producer_details", self.posters["tmdb_producer_details"])
elif "tmdb_writer_details" in self.posters: self.collection_poster = ImageData("tmdb_writer_details", self.posters["tmdb_writer_details"])
elif "tmdb_movie_details" in self.posters: self.collection_poster = ImageData("tmdb_movie_details", self.posters["tmdb_movie_details"])
elif "tvdb_movie_details" in self.posters: self.collection_poster = ImageData("tvdb_movie_details", self.posters["tvdb_movie_details"])
elif "tvdb_show_details" in self.posters: self.collection_poster = ImageData("tvdb_show_details", self.posters["tvdb_show_details"])
elif "tmdb_show_details" in self.posters: self.collection_poster = ImageData("tmdb_show_details", self.posters["tmdb_show_details"])
else:
logger.info(f"No poster {self.type} detail or asset folder found")
self.collection_background = None
if len(self.backgrounds) > 0:
logger.debug(f"{len(self.backgrounds)} backgrounds found:")
for b in self.backgrounds:
logger.debug(f"Method: {b} Background: {self.backgrounds[b]}")
if "url_background" in self.backgrounds:
if self.library.download_url_assets and asset_location:
if background_image:
self.collection_background = self.backgrounds["asset_directory"]
else:
response = self.config.get(self.backgrounds["url_background"], headers=util.header())
if response.status_code >= 400 or "Content-Type" not in response.headers or response.headers["Content-Type"] not in ["image/png", "image/jpeg"]:
logger.error(f"Image Error: Failed to parse Image at {self.backgrounds['url_background']}")
else:
new_image = os.path.join(asset_location, f"background{'.png' if response.headers['Content-Type'] == 'image/png' else '.jpg'}")
with open(new_image, "wb") as handler:
handler.write(response.content)
self.collection_background = ImageData("asset_directory", new_image, prefix=f"{self.obj.title}'s ", is_url=False, is_poster=False)
if not self.collection_background:
self.collection_background = ImageData("url_background", self.backgrounds["url_background"], is_poster=False)
elif "file_background" in self.backgrounds: self.collection_background = ImageData("file_background", self.backgrounds["file_background"], is_poster=False, is_url=False)
elif "tmdb_background" in self.backgrounds: self.collection_background = ImageData("tmdb_background", self.backgrounds["tmdb_background"], is_poster=False)
elif "tvdb_background" in self.backgrounds: self.collection_background = ImageData("tvdb_background", self.backgrounds["tvdb_background"], is_poster=False)
elif "asset_directory" in self.backgrounds: self.collection_background = self.backgrounds["asset_directory"]
elif "tmdb_collection_details" in self.backgrounds: self.collection_background = ImageData("tmdb_collection_details", self.backgrounds["tmdb_collection_details"], is_poster=False)
elif "tmdb_movie_details" in self.backgrounds: self.collection_background = ImageData("tmdb_movie_details", self.backgrounds["tmdb_movie_details"], is_poster=False)
elif "tvdb_movie_details" in self.backgrounds: self.collection_background = ImageData("tvdb_movie_details", self.backgrounds["tvdb_movie_details"], is_poster=False)
elif "tvdb_show_details" in self.backgrounds: self.collection_background = ImageData("tvdb_show_details", self.backgrounds["tvdb_show_details"], is_poster=False)
elif "tmdb_show_details" in self.backgrounds: self.collection_background = ImageData("tmdb_show_details", self.backgrounds["tmdb_show_details"], is_poster=False)
else:
logger.info(f"No background {self.type} detail or asset folder found")
if self.collection_poster or self.collection_background: if self.collection_poster or self.collection_background:
pu, bu = self.library.upload_images(self.obj, poster=self.collection_poster, background=self.collection_background) pu, bu = self.library.upload_images(self.obj, poster=self.collection_poster, background=self.collection_background)

@ -294,6 +294,7 @@ class ConfigFile:
"asset_folders": check_for_attribute(self.data, "asset_folders", parent="settings", var_type="bool", default=True), "asset_folders": check_for_attribute(self.data, "asset_folders", parent="settings", var_type="bool", default=True),
"asset_depth": check_for_attribute(self.data, "asset_depth", parent="settings", var_type="int", default=0), "asset_depth": check_for_attribute(self.data, "asset_depth", parent="settings", var_type="int", default=0),
"create_asset_folders": check_for_attribute(self.data, "create_asset_folders", parent="settings", var_type="bool", default=False), "create_asset_folders": check_for_attribute(self.data, "create_asset_folders", parent="settings", var_type="bool", default=False),
"prioritize_assets": check_for_attribute(self.data, "prioritize_assets", parent="settings", var_type="bool", default=False),
"dimensional_asset_rename": check_for_attribute(self.data, "dimensional_asset_rename", parent="settings", var_type="bool", default=False), "dimensional_asset_rename": check_for_attribute(self.data, "dimensional_asset_rename", parent="settings", var_type="bool", default=False),
"download_url_assets": check_for_attribute(self.data, "download_url_assets", parent="settings", var_type="bool", default=False), "download_url_assets": check_for_attribute(self.data, "download_url_assets", parent="settings", var_type="bool", default=False),
"show_missing_season_assets": check_for_attribute(self.data, "show_missing_season_assets", parent="settings", var_type="bool", default=False), "show_missing_season_assets": check_for_attribute(self.data, "show_missing_season_assets", parent="settings", var_type="bool", default=False),
@ -605,6 +606,7 @@ class ConfigFile:
params["only_filter_missing"] = check_for_attribute(lib, "only_filter_missing", parent="settings", var_type="bool", default=self.general["only_filter_missing"], do_print=False, save=False) params["only_filter_missing"] = check_for_attribute(lib, "only_filter_missing", parent="settings", var_type="bool", default=self.general["only_filter_missing"], do_print=False, save=False)
params["create_asset_folders"] = check_for_attribute(lib, "create_asset_folders", parent="settings", var_type="bool", default=self.general["create_asset_folders"], do_print=False, save=False) params["create_asset_folders"] = check_for_attribute(lib, "create_asset_folders", parent="settings", var_type="bool", default=self.general["create_asset_folders"], do_print=False, save=False)
params["dimensional_asset_rename"] = check_for_attribute(lib, "dimensional_asset_rename", parent="settings", var_type="bool", default=self.general["dimensional_asset_rename"], do_print=False, save=False) params["dimensional_asset_rename"] = check_for_attribute(lib, "dimensional_asset_rename", parent="settings", var_type="bool", default=self.general["dimensional_asset_rename"], do_print=False, save=False)
params["prioritize_assets"] = check_for_attribute(lib, "prioritize_assets", parent="settings", var_type="bool", default=self.general["prioritize_assets"], do_print=False, save=False)
params["download_url_assets"] = check_for_attribute(lib, "download_url_assets", parent="settings", var_type="bool", default=self.general["download_url_assets"], do_print=False, save=False) params["download_url_assets"] = check_for_attribute(lib, "download_url_assets", parent="settings", var_type="bool", default=self.general["download_url_assets"], do_print=False, save=False)
params["show_missing_season_assets"] = check_for_attribute(lib, "show_missing_season_assets", parent="settings", var_type="bool", default=self.general["show_missing_season_assets"], do_print=False, save=False) params["show_missing_season_assets"] = check_for_attribute(lib, "show_missing_season_assets", parent="settings", var_type="bool", default=self.general["show_missing_season_assets"], do_print=False, save=False)
params["show_missing_episode_assets"] = check_for_attribute(lib, "show_missing_episode_assets", parent="settings", var_type="bool", default=self.general["show_missing_episode_assets"], do_print=False, save=False) params["show_missing_episode_assets"] = check_for_attribute(lib, "show_missing_episode_assets", parent="settings", var_type="bool", default=self.general["show_missing_episode_assets"], do_print=False, save=False)

@ -51,6 +51,7 @@ class Library(ABC):
self.asset_folders = params["asset_folders"] self.asset_folders = params["asset_folders"]
self.create_asset_folders = params["create_asset_folders"] self.create_asset_folders = params["create_asset_folders"]
self.dimensional_asset_rename = params["dimensional_asset_rename"] self.dimensional_asset_rename = params["dimensional_asset_rename"]
self.prioritize_assets = params["prioritize_assets"]
self.download_url_assets = params["download_url_assets"] self.download_url_assets = params["download_url_assets"]
self.show_missing_season_assets = params["show_missing_season_assets"] self.show_missing_season_assets = params["show_missing_season_assets"]
self.show_missing_episode_assets = params["show_missing_episode_assets"] self.show_missing_episode_assets = params["show_missing_episode_assets"]
@ -210,47 +211,6 @@ class Library(ABC):
def get_all(self, collection_level=None, load=False): def get_all(self, collection_level=None, load=False):
pass pass
def find_assets(self, name="poster", folder_name=None, item_directory=None, prefix=""):
poster = None
background = None
item_dir = None
search_dir = item_directory if item_directory else None
for ad in self.asset_directory:
item_dir = None
if not search_dir:
search_dir = ad
if folder_name:
if os.path.isdir(os.path.join(ad, folder_name)):
item_dir = os.path.join(ad, folder_name)
else:
for n in range(1, self.asset_depth + 1):
new_path = ad
for i in range(1, n + 1):
new_path = os.path.join(new_path, "*")
matches = util.glob_filter(os.path.join(new_path, folder_name))
if len(matches) > 0:
item_dir = os.path.abspath(matches[0])
break
if item_dir is None:
continue
search_dir = item_dir
if item_directory:
item_dir = item_directory
file_name = name if item_dir else f"{folder_name}_{name}"
poster_filter = os.path.join(search_dir, f"{file_name}.*")
background_filter = os.path.join(search_dir, "background.*" if file_name == "poster" else f"{file_name}_background.*")
poster_matches = util.glob_filter(poster_filter)
if len(poster_matches) > 0:
poster = ImageData("asset_directory", os.path.abspath(poster_matches[0]), prefix=prefix, is_url=False)
background_matches = util.glob_filter(background_filter)
if len(background_matches) > 0:
background = ImageData("asset_directory", os.path.abspath(background_matches[0]), prefix=prefix, is_poster=False, is_url=False)
break
return poster, background, item_dir
def add_missing(self, collection, items, is_movie): def add_missing(self, collection, items, is_movie):
if collection not in self.missing: if collection not in self.missing:
self.missing[collection] = {} self.missing[collection] = {}

@ -652,28 +652,6 @@ class MetadataFile(DataFile):
return len(self.library.edit_tags(attr, obj, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags)) > 0 return len(self.library.edit_tags(attr, obj, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags)) > 0
return False return False
def set_images(self, obj, group, alias):
def set_image(attr, is_poster=True, is_url=True):
if group[alias[attr]]:
return ImageData(attr, group[alias[attr]], is_poster=is_poster, is_url=is_url)
else:
logger.error(f"Metadata Error: {attr} attribute is blank")
poster = None
background = None
if "url_poster" in alias:
poster = set_image("url_poster")
elif "file_poster" in alias:
poster = set_image("file_poster", is_url=False)
if "url_background" in alias:
background = set_image("url_background", is_poster=False)
elif "file_background" in alias:
background = set_image("file_background",is_poster=False, is_url=False)
if poster or background:
self.library.upload_images(obj, poster=poster, background=background)
def update_metadata(self): def update_metadata(self):
if not self.metadata: if not self.metadata:
return None return None
@ -894,7 +872,7 @@ class MetadataFile(DataFile):
logger.info(f"{self.library.type}: {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") logger.info(f"{self.library.type}: {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")
self.set_images(item, meta, methods) asset_location = self.library.item_images(item, meta, methods)
if "seasons" in methods and self.library.is_show: if "seasons" in methods and self.library.is_show:
if not meta[methods["seasons"]]: if not meta[methods["seasons"]]:
@ -923,7 +901,9 @@ class MetadataFile(DataFile):
if self.edit_tags("label", season, season_dict, season_methods): if self.edit_tags("label", season, season_dict, season_methods):
updated = True updated = True
finish_edit(season, f"Season: {season_id}") finish_edit(season, f"Season: {season_id}")
self.set_images(season, season_dict, season_methods) self.library.item_images(season, season_dict, season_methods, asset_location=asset_location, top_item=item,
title=f"{item.title} Season {season.seasonNumber}",
image_name=f"Season{'0' if season.seasonNumber < 10 else ''}{season.seasonNumber}")
logger.info(f"Season {season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") logger.info(f"Season {season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")
if "episodes" in season_methods and self.library.is_show: if "episodes" in season_methods and self.library.is_show:
@ -958,7 +938,9 @@ class MetadataFile(DataFile):
if self.edit_tags(tag_edit, episode, episode_dict, episode_methods): if self.edit_tags(tag_edit, episode, episode_dict, episode_methods):
updated = True updated = True
finish_edit(episode, f"Episode: {episode_str} in Season: {season_id}") finish_edit(episode, f"Episode: {episode_str} in Season: {season_id}")
self.set_images(episode, episode_dict, episode_methods) self.library.item_images(episode, episode_dict, episode_methods, asset_location=asset_location, top_item=item,
title=f"{item.title} {episode.seasonEpisode.upper()}",
image_name=episode.seasonEpisode.upper())
logger.info(f"Episode {episode_str} in Season {season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") logger.info(f"Episode {episode_str} in Season {season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")
if "episodes" in methods and self.library.is_show: if "episodes" in methods and self.library.is_show:
@ -996,7 +978,9 @@ class MetadataFile(DataFile):
if self.edit_tags(tag_edit, episode, episode_dict, episode_methods): if self.edit_tags(tag_edit, episode, episode_dict, episode_methods):
updated = True updated = True
finish_edit(episode, f"Episode: {episode_str} in Season: {season_id}") finish_edit(episode, f"Episode: {episode_str} in Season: {season_id}")
self.set_images(episode, episode_dict, episode_methods) self.library.item_images(episode, episode_dict, episode_methods, asset_location=asset_location, top_item=item,
title=f"{item.title} {episode.seasonEpisode.upper()}",
image_name=episode.seasonEpisode.upper())
logger.info(f"Episode S{season_id}E{episode_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") logger.info(f"Episode S{season_id}E{episode_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")
if "albums" in methods and self.library.is_music: if "albums" in methods and self.library.is_music:
@ -1034,7 +1018,8 @@ class MetadataFile(DataFile):
if self.edit_tags(tag_edit, album, album_dict, album_methods): if self.edit_tags(tag_edit, album, album_dict, album_methods):
updated = True updated = True
finish_edit(album, f"Album: {title}") finish_edit(album, f"Album: {title}")
self.set_images(album, album_dict, album_methods) self.library.item_images(album, album_dict, album_methods, asset_location=asset_location, top_item=item,
title=f"{item.title} Album {album.title}", image_name=album.title)
logger.info(f"Album: {title} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") logger.info(f"Album: {title} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}")
if "tracks" in album_methods: if "tracks" in album_methods:

@ -93,7 +93,7 @@ class Operations:
found_season = False found_season = False
found_episode = False found_episode = False
for season in self.library.query(item.seasons): for season in self.library.query(item.seasons):
season_poster, season_background, _, _ = self.library.find_item_assets(season, item_asset_directory=item_dir) season_poster, season_background, _, _ = self.library.find_item_assets(season, item_asset_directory=item_dir, top_item=item)
if season_poster: if season_poster:
found_season = True found_season = True
elif self.library.show_missing_season_assets and season.seasonNumber > 0: elif self.library.show_missing_season_assets and season.seasonNumber > 0:
@ -102,7 +102,7 @@ class Operations:
self.library.upload_images(season, poster=season_poster, background=season_background) self.library.upload_images(season, poster=season_poster, background=season_background)
for episode in self.library.query(season.episodes): for episode in self.library.query(season.episodes):
if episode.seasonEpisode: if episode.seasonEpisode:
episode_poster, episode_background, _, _ = self.library.find_item_assets(episode, item_asset_directory=item_dir) episode_poster, episode_background, _, _ = self.library.find_item_assets(episode, item_asset_directory=item_dir, top_item=item)
if episode_poster or episode_background: if episode_poster or episode_background:
found_episode = True found_episode = True
self.library.upload_images(episode, poster=episode_poster, background=episode_background) self.library.upload_images(episode, poster=episode_poster, background=episode_background)
@ -114,7 +114,7 @@ class Operations:
missing_assets = "" missing_assets = ""
found_album = False found_album = False
for album in self.library.query(item.albums): for album in self.library.query(item.albums):
album_poster, album_background, _, _ = self.library.find_item_assets(album, item_asset_directory=item_dir) album_poster, album_background, _, _ = self.library.find_item_assets(album, item_asset_directory=item_dir, top_item=item)
if album_poster or album_background: if album_poster or album_background:
found_album = True found_album = True
elif self.library.show_missing_season_assets: elif self.library.show_missing_season_assets:

@ -887,7 +887,32 @@ class Plex(Library):
logger.info(final) logger.info(final)
return final return final
def find_item_assets(self, item, item_asset_directory=None, asset_directory=None): def item_images(self, item, group, alias, asset_location=None, top_item=None, title=None, image_name=None):
if title is None:
title = item.title
posters, backgrounds = util.get_image_dicts(group, alias)
try:
asset_poster, asset_background, item_dir, _ = self.find_item_assets(item, item_asset_directory=asset_location, top_item=top_item)
if asset_poster:
posters["asset_directory"] = asset_poster
if asset_background:
backgrounds["asset_directory"] = asset_background
if asset_location is None:
asset_location = item_dir
except Failed as e:
logger.warning(e)
poster = util.pick_image(title, posters, self.prioritize_assets, self.download_url_assets, asset_location, image_name=image_name)
background = util.pick_image(title, backgrounds, self.prioritize_assets, self.download_url_assets, asset_location,
is_poster=False, image_name=f"{image_name}_background" if image_name else image_name)
if poster and poster.attribute == "asset_directory":
poster = None
if background and background.attribute == "asset_directory":
background = None
if poster or background:
self.upload_images(item, poster=poster, background=background)
return asset_location
def find_item_assets(self, item, item_asset_directory=None, asset_directory=None, top_item=None):
poster = None poster = None
background = None background = None
folder_name = None folder_name = None
@ -897,13 +922,13 @@ class Plex(Library):
is_top_level = isinstance(item, (Movie, Artist, Show, Collection, Playlist, str)) is_top_level = isinstance(item, (Movie, Artist, Show, Collection, Playlist, str))
if isinstance(item, Album): if isinstance(item, Album):
prefix = f"{item.title} Album {item.title}'s " prefix = f"{top_item.title} Album {item.title}'s "
file_name = item.title file_name = item.title
elif isinstance(item, Season): elif isinstance(item, Season):
prefix = f"{item.title} Season {item.seasonNumber}'s " prefix = f"{top_item.title} Season {item.seasonNumber}'s "
file_name = f"Season{'0' if item.seasonNumber < 10 else ''}{item.seasonNumber}" file_name = f"Season{'0' if item.seasonNumber < 10 else ''}{item.seasonNumber}"
elif isinstance(item, Episode): elif isinstance(item, Episode):
prefix = f"{item.title} {item.seasonEpisode.upper()}'s " prefix = f"{top_item.title} {item.seasonEpisode.upper()}'s "
file_name = item.seasonEpisode.upper() file_name = item.seasonEpisode.upper()
else: else:
prefix = f"{item if isinstance(item, str) else item.title}'s " prefix = f"{item if isinstance(item, str) else item.title}'s "
@ -972,11 +997,11 @@ class Plex(Library):
if not poster and _h >= _w: if not poster and _h >= _w:
new_path = os.path.join(os.path.dirname(file), f"poster{os.path.splitext(file)[1].lower()}") new_path = os.path.join(os.path.dirname(file), f"poster{os.path.splitext(file)[1].lower()}")
os.rename(file, new_path) os.rename(file, new_path)
poster = ImageData("asset_directory", os.path.abspath(new_path), prefix=f"{item.title}'s ", is_url=False) poster = ImageData("asset_directory", os.path.abspath(new_path), prefix=prefix, is_url=False)
elif not background and _w > _h: elif not background and _w > _h:
new_path = os.path.join(os.path.dirname(file), f"background{os.path.splitext(file)[1].lower()}") new_path = os.path.join(os.path.dirname(file), f"background{os.path.splitext(file)[1].lower()}")
os.rename(file, new_path) os.rename(file, new_path)
background = ImageData("asset_directory", os.path.abspath(new_path), prefix=f"{item.title}'s ", is_poster=False, is_url=False) background = ImageData("asset_directory", os.path.abspath(new_path), prefix=prefix, is_poster=False, is_url=False)
if poster and background: if poster and background:
break break
except OSError: except OSError:

@ -120,6 +120,88 @@ def parse_version(version):
split_version = version.split("-develop") split_version = version.split("-develop")
return version, split_version[0], int(split_version[1]) if len(split_version) > 1 else 0 return version, split_version[0], int(split_version[1]) if len(split_version) > 1 else 0
def download_image(title, image_url, download_directory, filename):
response = requests.get(image_url, headers=header())
if response.status_code >= 400 or "Content-Type" not in response.headers or response.headers["Content-Type"] not in ["image/png", "image/jpeg"]:
raise Failed(f"Image Error: Failed to download Image URL: {image_url}")
new_image = os.path.join(download_directory, f"{filename}{'.png' if response.headers['Content-Type'] == 'image/png' else '.jpg'}")
with open(new_image, "wb") as handler:
handler.write(response.content)
return ImageData("asset_directory", new_image, prefix=f"{title}'s ", is_url=False)
def get_image_dicts(group, alias):
posters = {}
backgrounds = {}
for attr in ["url_poster", "file_poster", "url_background", "file_background"]:
if attr in alias:
if group[alias[attr]]:
if "poster" in attr:
posters[attr] = ImageData(attr, group[alias[attr]], is_url="url" in attr)
else:
backgrounds[attr] = ImageData(attr, group[alias[attr]], is_poster=False, is_url="url" in attr)
else:
logger.error(f"Metadata Error: {attr} attribute is blank")
return posters, backgrounds
def pick_image(title, images, prioritize_assets, download_url_assets, item_dir, is_poster=True, image_name=None):
image_type = "poster" if is_poster else "background"
if image_name is None:
image_name = image_type
if images:
logger.debug(f"{len(images)} {image_type}s found:")
for i in images:
logger.debug(f"Method: {i} {image_type.capitalize()}: {images[i]}")
is_url = True
final_attr = None
if prioritize_assets and "asset_directory" in images:
return images["asset_directory"]
elif f"url_{image_type}" in images:
if download_url_assets and item_dir:
if "asset_directory" in images:
return images["asset_directory"]
else:
try:
return download_image(title, images[f"url_{image_type}"], item_dir, image_name)
except Failed as e:
logger.error(e)
final_attr = f"url_{image_type}"
elif f"file_{image_type}" in images:
final_attr = f"file_{image_type}"
is_url = False
elif f"tmdb_{image_type}" in images:
final_attr = f"tmdb_{image_type}"
elif "tmdb_profile" in images:
final_attr = "tmdb_profile"
elif f"tvdb_{image_type}" in images:
final_attr = f"tvdb_{image_type}"
elif "asset_directory" in images:
return images["asset_directory"]
elif "tmdb_person" in images:
final_attr = "tmdb_person"
elif "tmdb_collection_details" in images:
final_attr = "tmdb_collection_details"
elif "tmdb_actor_details" in images:
final_attr = "tmdb_actor_details"
elif "tmdb_crew_details" in images:
final_attr = "tmdb_crew_details"
elif "tmdb_director_details" in images:
final_attr = "tmdb_director_details"
elif "tmdb_producer_details" in images:
final_attr = "tmdb_producer_details"
elif "tmdb_writer_details" in images:
final_attr = "tmdb_writer_details"
elif "tmdb_movie_details" in images:
final_attr = "tmdb_movie_details"
elif "tvdb_movie_details" in images:
final_attr = "tvdb_movie_details"
elif "tvdb_show_details" in images:
final_attr = "tvdb_show_details"
elif "tmdb_show_details" in images:
final_attr = "tmdb_show_details"
if final_attr:
return ImageData(final_attr, images[final_attr], is_poster=is_poster, is_url=is_url)
def add_dict_list(keys, value, dict_map): def add_dict_list(keys, value, dict_map):
for key in keys: for key in keys:
if key in dict_map: if key in dict_map:

Loading…
Cancel
Save