From 90a2288f3f0561b4185431c0f7c0d5a6dec75652 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:42:51 -0700 Subject: [PATCH 1/4] Add logos to artist/album/track, collection, photoalbum/photo, playlist --- plexapi/audio.py | 8 ++++---- plexapi/collection.py | 4 ++-- plexapi/photo.py | 6 +++--- plexapi/playlist.py | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 3bc6f514e..0c13b0026 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -12,7 +12,7 @@ from plexapi.exceptions import BadRequest from plexapi.mixins import ( AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, PlayedUnplayedMixin, RatingMixin, - ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, ThemeMixin, ThemeUrlMixin, + ArtUrlMixin, ArtMixin, LogoMixin, LogoUrlMixin, PosterUrlMixin, PosterMixin, ThemeMixin, ThemeUrlMixin, ArtistEditMixins, AlbumEditMixins, TrackEditMixins ) from plexapi.playlist import Playlist @@ -181,7 +181,7 @@ def sonicallySimilar( class Artist( Audio, AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, RatingMixin, - ArtMixin, PosterMixin, ThemeMixin, + ArtMixin, LogoMixin, PosterMixin, ThemeMixin, ArtistEditMixins ): """ Represents a single Artist. @@ -351,7 +351,7 @@ def metadataDirectory(self): class Album( Audio, SplitMergeMixin, UnmatchMatchMixin, RatingMixin, - ArtMixin, PosterMixin, ThemeUrlMixin, + ArtMixin, LogoMixin, PosterMixin, ThemeUrlMixin, AlbumEditMixins ): """ Represents a single Album. @@ -504,7 +504,7 @@ def metadataDirectory(self): class Track( Audio, Playable, ExtrasMixin, RatingMixin, - ArtUrlMixin, PosterUrlMixin, ThemeUrlMixin, + ArtUrlMixin, LogoUrlMixin, PosterUrlMixin, ThemeUrlMixin, TrackEditMixins ): """ Represents a single Track. diff --git a/plexapi/collection.py b/plexapi/collection.py index 308604a0b..e97a0a9de 100644 --- a/plexapi/collection.py +++ b/plexapi/collection.py @@ -8,7 +8,7 @@ from plexapi.library import LibrarySection, ManagedHub from plexapi.mixins import ( AdvancedSettingsMixin, SmartFilterMixin, HubsMixin, RatingMixin, - ArtMixin, PosterMixin, ThemeMixin, + ArtMixin, LogoMixin, PosterMixin, ThemeMixin, CollectionEditMixins ) from plexapi.utils import deprecated @@ -18,7 +18,7 @@ class Collection( PlexPartialObject, AdvancedSettingsMixin, SmartFilterMixin, HubsMixin, RatingMixin, - ArtMixin, PosterMixin, ThemeMixin, + ArtMixin, LogoMixin, PosterMixin, ThemeMixin, CollectionEditMixins ): """ Represents a single Collection. diff --git a/plexapi/photo.py b/plexapi/photo.py index e7c7239e8..e6953c756 100644 --- a/plexapi/photo.py +++ b/plexapi/photo.py @@ -8,7 +8,7 @@ from plexapi.exceptions import BadRequest from plexapi.mixins import ( RatingMixin, - ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, + ArtUrlMixin, ArtMixin, LogoMixin, LogoUrlMixin, PosterUrlMixin, PosterMixin, PhotoalbumEditMixins, PhotoEditMixins ) @@ -17,7 +17,7 @@ class Photoalbum( PlexPartialObject, RatingMixin, - ArtMixin, PosterMixin, + ArtMixin, LogoMixin, PosterMixin, PhotoalbumEditMixins ): """ Represents a single Photoalbum (collection of photos). @@ -159,7 +159,7 @@ def metadataDirectory(self): class Photo( PlexPartialObject, Playable, RatingMixin, - ArtUrlMixin, PosterUrlMixin, + ArtUrlMixin, LogoUrlMixin, PosterUrlMixin, PhotoEditMixins ): """ Represents a single Photo. diff --git a/plexapi/playlist.py b/plexapi/playlist.py index 0fc79bf50..03428e549 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -8,7 +8,7 @@ from plexapi.base import Playable, PlexPartialObject, cached_data_property from plexapi.exceptions import BadRequest, NotFound, Unsupported from plexapi.library import LibrarySection, MusicSection -from plexapi.mixins import SmartFilterMixin, ArtMixin, PosterMixin, PlaylistEditMixins +from plexapi.mixins import SmartFilterMixin, ArtMixin, LogoMixin, PosterMixin, PlaylistEditMixins from plexapi.utils import deprecated @@ -16,7 +16,7 @@ class Playlist( PlexPartialObject, Playable, SmartFilterMixin, - ArtMixin, PosterMixin, + ArtMixin, LogoMixin, PosterMixin, PlaylistEditMixins ): """ Represents a single Playlist. From 1ca45b6aef27c837688379205ac399f8232d12a8 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Tue, 21 Oct 2025 18:46:16 -0700 Subject: [PATCH 2/4] Add logo tests --- tests/test_audio.py | 3 +++ tests/test_collection.py | 1 + tests/test_mixins.py | 4 ++++ tests/test_photo.py | 1 + tests/test_playlist.py | 3 +++ tests/test_video.py | 6 ++++++ 6 files changed, 18 insertions(+) diff --git a/tests/test_audio.py b/tests/test_audio.py index 43f941d36..059f10261 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -107,6 +107,7 @@ def test_audio_Artist_mixins_images(artist): test_mixins.edit_art(artist) test_mixins.edit_poster(artist) test_mixins.attr_artUrl(artist) + test_mixins.attr_logoUrl(artist) test_mixins.attr_posterUrl(artist) @@ -237,6 +238,7 @@ def test_audio_Album_mixins_images(album): test_mixins.edit_art(album) test_mixins.edit_poster(album) test_mixins.attr_artUrl(album) + test_mixins.attr_logoUrl(album) test_mixins.attr_posterUrl(album) @@ -424,6 +426,7 @@ def test_audio_Track_sonicAdventure(account_plexpass, music): def test_audio_Track_mixins_images(track): test_mixins.attr_artUrl(track) + test_mixins.attr_logoUrl(track) test_mixins.attr_posterUrl(track) diff --git a/tests/test_collection.py b/tests/test_collection.py index 7522e9430..d591fcadd 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -356,6 +356,7 @@ def test_Collection_mixins_images(collection): test_mixins.edit_art(collection) test_mixins.edit_poster(collection) test_mixins.attr_artUrl(collection) + test_mixins.attr_logoUrl(collection) test_mixins.attr_posterUrl(collection) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index be7409cf6..39aadb902 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -303,6 +303,10 @@ def attr_artUrl(obj): _test_mixins_imageUrl(obj, "art") +def attr_logoUrl(obj): + _test_mixins_imageUrl(obj, "logo") + + def attr_posterUrl(obj): _test_mixins_imageUrl(obj, "thumb") diff --git a/tests/test_photo.py b/tests/test_photo.py index b1613bc44..a08e2fff3 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -22,6 +22,7 @@ def test_photo_Photoalbum_mixins_images(photoalbum): test_mixins.edit_art(photoalbum) test_mixins.edit_poster(photoalbum) test_mixins.attr_artUrl(photoalbum) + test_mixins.attr_logoUrl(photoalbum) test_mixins.attr_posterUrl(photoalbum) diff --git a/tests/test_playlist.py b/tests/test_playlist.py index 564176e37..fabf9f1f4 100644 --- a/tests/test_playlist.py +++ b/tests/test_playlist.py @@ -330,6 +330,9 @@ def test_Playlist_mixins_images(playlist): test_mixins.lock_poster(playlist) test_mixins.edit_art(playlist) test_mixins.edit_poster(playlist) + test_mixins.attr_artUrl(playlist) + test_mixins.attr_logoUrl(playlist) + test_mixins.attr_posterUrl(playlist) def test_Playlist_mixins_fields(playlist): diff --git a/tests/test_video.py b/tests/test_video.py index 2b10ff0b6..fb9a637ae 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -697,6 +697,9 @@ def test_video_Movie_mixins_images(movie): test_mixins.lock_poster(movie) test_mixins.edit_art(movie) test_mixins.edit_poster(movie) + test_mixins.attr_artUrl(movie) + test_mixins.attr_logoUrl(movie) + test_mixins.attr_posterUrl(movie) def test_video_Movie_mixins_themes(movie): @@ -965,6 +968,7 @@ def test_video_Show_mixins_images(show): test_mixins.edit_art(show) test_mixins.edit_poster(show) test_mixins.attr_artUrl(show) + test_mixins.attr_logoUrl(show) test_mixins.attr_posterUrl(show) @@ -1122,6 +1126,7 @@ def test_video_Season_mixins_images(show): test_mixins.edit_art(season) test_mixins.edit_poster(season) test_mixins.attr_artUrl(season) + test_mixins.attr_logoUrl(season) test_mixins.attr_posterUrl(season) @@ -1340,6 +1345,7 @@ def test_video_Episode_mixins_images(episode): # test_mixins.edit_art(episode) # Uploading episode artwork is broken in Plex test_mixins.edit_poster(episode) test_mixins.attr_artUrl(episode) + test_mixins.attr_logoUrl(episode) test_mixins.attr_posterUrl(episode) From e330dd8c662b35447775e24965cea057d549a067 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:51:14 +0000 Subject: [PATCH 3/4] Update all image resource tests --- tests/test_audio.py | 8 ++++---- tests/test_collection.py | 4 ++-- tests/test_mixins.py | 4 ++-- tests/test_photo.py | 7 ++++--- tests/test_playlist.py | 5 +++-- tests/test_video.py | 19 ++++++++++--------- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/tests/test_audio.py b/tests/test_audio.py index dba32a427..91be580ec 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -104,10 +104,10 @@ def test_audio_Artist_mixins_edit_advanced_settings(artist): def test_audio_Artist_mixins_images(artist): test_mixins.lock_art(artist) test_mixins.lock_poster(artist) - test_mixins.lock_square_art(artist) + test_mixins.lock_squareArt(artist) test_mixins.edit_art(artist) test_mixins.edit_poster(artist) - test_mixins.edit_square_art(artist) + test_mixins.edit_squareArt(artist) test_mixins.attr_artUrl(artist) test_mixins.attr_logoUrl(artist) test_mixins.attr_posterUrl(artist) @@ -238,10 +238,10 @@ def test_audio_Album_artist(album): def test_audio_Album_mixins_images(album): test_mixins.lock_art(album) test_mixins.lock_poster(album) - test_mixins.lock_square_art(album) + test_mixins.lock_squareArt(album) test_mixins.edit_art(album) test_mixins.edit_poster(album) - test_mixins.edit_square_art(album) + test_mixins.edit_squareArt(album) test_mixins.attr_artUrl(album) test_mixins.attr_logoUrl(album) test_mixins.attr_posterUrl(album) diff --git a/tests/test_collection.py b/tests/test_collection.py index 83e1a00ea..03632530c 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -353,10 +353,10 @@ def test_Collection_art(collection): def test_Collection_mixins_images(collection): test_mixins.lock_art(collection) test_mixins.lock_poster(collection) - test_mixins.lock_square_art(collection) + test_mixins.lock_squareArt(collection) test_mixins.edit_art(collection) test_mixins.edit_poster(collection) - test_mixins.edit_square_art(collection) + test_mixins.edit_squareArt(collection) test_mixins.attr_artUrl(collection) test_mixins.attr_logoUrl(collection) test_mixins.attr_posterUrl(collection) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 2701eb818..f80fa5479 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -223,7 +223,7 @@ def lock_poster(obj): _test_mixins_lock_image(obj, "posters") -def lock_square_art(obj): +def lock_squareArt(obj): _test_mixins_lock_image(obj, "squareArts") @@ -298,7 +298,7 @@ def edit_poster(obj): _test_mixins_edit_image(obj, "posters") -def edit_square_art(obj): +def edit_squareArt(obj): _test_mixins_edit_image(obj, "squareArts") diff --git a/tests/test_photo.py b/tests/test_photo.py index d52835fae..96fa7b389 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -17,11 +17,12 @@ def test_photo_Photoalbum(photoalbum): @pytest.mark.xfail(reason="Changing images fails randomly") def test_photo_Photoalbum_mixins_images(photoalbum): - # test_mixins.lock_art(photoalbum) # Unlocking photoalbum artwork is broken in Plex - # test_mixins.lock_poster(photoalbum) # Unlocking photoalbum poster is broken in Plex test_mixins.edit_art(photoalbum) test_mixins.edit_poster(photoalbum) - test_mixins.lock_square_art(photoalbum) + test_mixins.edit_squareArt(photoalbum) + test_mixins.lock_art(photoalbum) + test_mixins.lock_poster(photoalbum) + test_mixins.lock_squareArt(photoalbum) test_mixins.attr_artUrl(photoalbum) test_mixins.attr_logoUrl(photoalbum) test_mixins.attr_posterUrl(photoalbum) diff --git a/tests/test_playlist.py b/tests/test_playlist.py index 7d489f770..f1c2f5f13 100644 --- a/tests/test_playlist.py +++ b/tests/test_playlist.py @@ -328,13 +328,14 @@ def test_Playlist_PlexWebURL(plex, show): def test_Playlist_mixins_images(playlist): test_mixins.lock_art(playlist) test_mixins.lock_poster(playlist) - test_mixins.lock_square_art(playlist) + test_mixins.lock_squareArt(playlist) test_mixins.edit_art(playlist) test_mixins.edit_poster(playlist) - test_mixins.edit_square_art(playlist) + test_mixins.edit_squareArt(playlist) test_mixins.attr_artUrl(playlist) test_mixins.attr_logoUrl(playlist) test_mixins.attr_posterUrl(playlist) + test_mixins.attr_squareArtUrl(playlist) def test_Playlist_mixins_fields(playlist): diff --git a/tests/test_video.py b/tests/test_video.py index cf9d167dc..0c8ff6e93 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -695,13 +695,14 @@ def test_video_Movie_mixins_edit_advanced_settings(movie): def test_video_Movie_mixins_images(movie): test_mixins.lock_art(movie) test_mixins.lock_poster(movie) - test_mixins.lock_square_art(movie) + test_mixins.lock_squareArt(movie) test_mixins.edit_art(movie) test_mixins.edit_poster(movie) - test_mixins.edit_square_art(movie) + test_mixins.edit_squareArt(movie) test_mixins.attr_artUrl(movie) test_mixins.attr_logoUrl(movie) test_mixins.attr_posterUrl(movie) + test_mixins.attr_squareArtUrl(movie) def test_video_Movie_mixins_themes(movie): @@ -967,10 +968,10 @@ def test_video_Show_mixins_edit_advanced_settings(show): def test_video_Show_mixins_images(show): test_mixins.lock_art(show) test_mixins.lock_poster(show) - test_mixins.lock_square_art(show) + test_mixins.lock_squareArt(show) test_mixins.edit_art(show) test_mixins.edit_poster(show) - test_mixins.edit_square_art(show) + test_mixins.edit_squareArt(show) test_mixins.attr_artUrl(show) test_mixins.attr_logoUrl(show) test_mixins.attr_posterUrl(show) @@ -1175,10 +1176,10 @@ def test_video_Season_mixins_images(show): season = show.season(season=1) test_mixins.lock_art(season) test_mixins.lock_poster(season) - test_mixins.lock_square_art(season) + test_mixins.lock_squareArt(season) test_mixins.edit_art(season) test_mixins.edit_poster(season) - test_mixins.edit_square_art(season) + test_mixins.edit_squareArt(season) test_mixins.attr_artUrl(season) test_mixins.attr_logoUrl(season) test_mixins.attr_posterUrl(season) @@ -1397,10 +1398,10 @@ def test_video_Episode_unwatched(tvshows): def test_video_Episode_mixins_images(episode): test_mixins.lock_art(episode) test_mixins.lock_poster(episode) - test_mixins.lock_square_art(episode) - # test_mixins.edit_art(episode) # Uploading episode artwork is broken in Plex + test_mixins.lock_squareArt(episode) + test_mixins.edit_art(episode) test_mixins.edit_poster(episode) - test_mixins.edit_square_art(episode) + test_mixins.edit_squareArt(episode) test_mixins.attr_artUrl(episode) test_mixins.attr_logoUrl(episode) test_mixins.attr_posterUrl(episode) From 4f6715584ab908c35b01c637041a2044f5836969 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:56:17 +0000 Subject: [PATCH 4/4] Add logo propery --- plexapi/mixins.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plexapi/mixins.py b/plexapi/mixins.py index 37a0449ec..b9710e43c 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -406,11 +406,15 @@ def setArt(self, art): class LogoUrlMixin: """ Mixin for Plex objects that can have a logo url. """ + @property + def logo(self): + """ Return the API path to the logo image. """ + return next((i.url for i in self.images if i.type == 'clearLogo'), None) + @property def logoUrl(self): """ Return the logo url for the Plex object. """ - image = next((i for i in self.images if i.type == 'clearLogo'), None) - return self._server.url(image.url, includeToken=True) if image else None + return self._server.url(self.logo, includeToken=True) if self.logo else None class LogoLockMixin: