From b960e2f7a5ec5ac1096ff139db02f31b718c911f Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 15:08:49 -0800 Subject: [PATCH 01/23] Remove unmatch from base PlexObject --- plexapi/base.py | 5 ----- tests/test_video.py | 4 ---- 2 files changed, 9 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 92c00bf92..e0022e2ee 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -580,11 +580,6 @@ def iterParts(self): for part in item.parts: yield part - def unmatch(self): - """Unmatch a media file.""" - key = '%s/unmatch' % self.key - return self._server.query(key, method=self._server._session.put) - def play(self, client): """ Start playback on the specified client. diff --git a/tests/test_video.py b/tests/test_video.py index 5e14cae83..e3f421752 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -567,10 +567,6 @@ def test_video_Show(show): assert show.title == "Game of Thrones" -def test_video_Episode_unmatch(episode, patched_http_call): - episode.unmatch() - - def test_video_Episode_updateProgress(episode, patched_http_call): episode.updateProgress(10 * 60 * 1000) # 10 minutes. From 427d90bd3bdba7cf1638a93686c75df7d0bfa70b Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 15:59:57 -0800 Subject: [PATCH 02/23] Add mixins tests for all objects with art and posters --- tests/test_audio.py | 10 +++++++ tests/test_library.py | 5 ++++ tests/test_mixins.py | 42 ++++++++++++++++++++++++++++ tests/test_video.py | 65 ++++++++++++++----------------------------- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/tests/test_audio.py b/tests/test_audio.py index 13ab8066a..22f98a4e9 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -64,6 +64,11 @@ def test_audio_Artist_albums(artist): assert len(albums) == 1 and albums[0].title == "Layers" +def test_audio_Artist_mixins_images(artist): + test_mixins.edit_art(artist) + test_mixins.edit_poster(artist) + + def test_audio_Artist_mixins_tags(artist): test_mixins.edit_collection(artist) test_mixins.edit_country(artist) @@ -221,6 +226,11 @@ def test_audio_Album_artist(album): artist.title == "Broke For Free" +def test_audio_Album_mixins_images(album): + test_mixins.edit_art(album) + test_mixins.edit_poster(album) + + def test_audio_Album_mixins_tags(album): test_mixins.edit_collection(album) test_mixins.edit_genre(album) diff --git a/tests/test_library.py b/tests/test_library.py index b8602defb..83dc14690 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -334,6 +334,11 @@ def test_library_Collection_art(collection): assert not arts # Collection has no default art +def test_library_Collection_mixins_images(collection): + test_mixins.edit_art(collection) + test_mixins.edit_poster(collection) + + def test_library_Collection_mixins_tags(collection): test_mixins.edit_label(collection) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index c0ff9e445..a69eef4a4 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- from plexapi.utils import tag_singular +from . import conftest as utils + TEST_MIXIN_TAG = "Test Tag" +CUTE_CAT_SHA1 = "9f7003fc401761d8e0b0364d428b2dab2f789dbb" def _test_mixins_tag(obj, attr, tag_method): @@ -71,3 +74,42 @@ def edit_tag(obj): def edit_writer(obj): _test_mixins_tag(obj, "writers", "Writer") + + +def _test_mixins_art_poster(obj, attr): + cap_attr = attr[:-1].capitalize() + get_img_method = getattr(obj, attr) + set_img_method = getattr(obj, "set" + cap_attr) + upload_img_method = getattr(obj, "upload" + cap_attr) + images = get_img_method() + if images: + image = images[0] + assert len(image.key) >= 10 + if not image.ratingKey.startswith(("default://", "media://", "upload://")): + assert image.provider + assert len(image.ratingKey) >= 10 + assert utils.is_bool(image.selected) + assert len(image.thumb) >= 10 + if len(images) >= 2: + # Select a different image + set_img_method(images[1]) + images = get_img_method() + assert images[0].selected is False + assert images[1].selected is True + # Test upload image from file + upload_img_method(filepath=utils.STUB_IMAGE_PATH) + images = get_img_method() + file_image = [ + i for i in images + if i.ratingKey.startswith('upload://') and i.ratingKey.endswith(CUTE_CAT_SHA1) + ] + assert file_image + set_img_method(images[0]) # Reset to default image + + +def edit_art(obj): + _test_mixins_art_poster(obj, 'arts') + + +def edit_poster(obj): + _test_mixins_art_poster(obj, 'posters') diff --git a/tests/test_video.py b/tests/test_video.py index e3f421752..1f4e857b7 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -40,6 +40,11 @@ def test_video_Movie_merge(movie, patched_http_call): movie.merge(1337) +def test_video_Movie_mixins_images(movie): + test_mixins.edit_art(movie) + test_mixins.edit_poster(movie) + + def test_video_Movie_mixins_tags(movie): test_mixins.edit_collection(movie) test_mixins.edit_country(movie) @@ -495,50 +500,6 @@ def parse_params(key): assert len(results) == 0 -def test_video_Movie_poster(movie): - posters = movie.posters() - poster = posters[0] - assert len(poster.key) >= 10 - if not poster.ratingKey.startswith("media://"): - assert poster.provider - assert len(poster.ratingKey) >= 10 - assert utils.is_bool(poster.selected) - assert len(poster.thumb) >= 10 - # Select a different poster - movie.setPoster(posters[1]) - posters = movie.posters() - assert posters[0].selected is False - assert posters[1].selected is True - # Test upload poster from file - movie.uploadPoster(filepath=utils.STUB_IMAGE_PATH) - posters = movie.posters() - file_poster = next(p for p in posters if p.ratingKey.startswith('upload://')) - assert file_poster.selected is True - movie.setPoster(posters[0]) # Reset to default poster - - -def test_video_Movie_art(movie): - arts = movie.arts() - art = arts[0] - assert len(art.key) >= 10 - if not art.ratingKey.startswith("media://"): - assert art.provider - assert len(art.ratingKey) >= 10 - assert utils.is_bool(art.selected) - assert len(art.thumb) >= 10 - # Select a different art - movie.setArt(arts[1]) - arts = movie.arts() - assert arts[0].selected is False - assert arts[1].selected is True - # Test upload poster from file - movie.uploadArt(filepath=utils.STUB_IMAGE_PATH) - arts = movie.arts() - file_art = next(a for a in arts if a.ratingKey.startswith('upload://')) - assert file_art.selected is True - movie.setArt(arts[0]) # Reset to default art - - def test_video_Movie_hubs(movies): movie = movies.get('Big Buck Bunny') hubs = movie.hubs() @@ -759,6 +720,11 @@ def test_video_Show_section(show): assert section.title == "TV Shows" +def test_video_Show_mixins_images(show): + test_mixins.edit_art(show) + test_mixins.edit_poster(show) + + def test_video_Show_mixins_tags(show): test_mixins.edit_collection(show) test_mixins.edit_genre(show) @@ -881,6 +847,11 @@ def test_video_Episode_attrs(episode): assert part.accessible +def test_video_Episode_mixins_images(episode): + #test_mixins.edit_art(episode) # Uploading episode artwork is broken in Plex + test_mixins.edit_poster(episode) + + def test_video_Episode_mixins_tags(episode): test_mixins.edit_director(episode) test_mixins.edit_writer(episode) @@ -965,6 +936,12 @@ def test_video_Season_episodes(show): assert len(episodes) >= 1 +def test_video_Season_mixins_images(show): + season = show.season(season=1) + test_mixins.edit_art(season) + test_mixins.edit_poster(season) + + def test_that_reload_return_the_same_object(plex): # we want to check this that all the urls are correct movie_library_search = plex.library.section("Movies").search("Elephants Dream")[0] From 1cc60b435874ac3a4e6e320e96b7471928762874 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:11:50 -0800 Subject: [PATCH 03/23] Add banner mixin for shows --- plexapi/media.py | 16 ++++++++++------ plexapi/mixins.py | 39 +++++++++++++++++++++++++++++++++++---- plexapi/video.py | 4 ++-- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/plexapi/media.py b/plexapi/media.py index 9c247d688..735bbe1bf 100644 --- a/plexapi/media.py +++ b/plexapi/media.py @@ -807,8 +807,8 @@ class Style(MediaTag): FILTER = 'style' -class BasePosterArt(PlexObject): - """ Base class for all Poster and Art objects. +class BaseImage(PlexObject): + """ Base class for all Art, Banner, and Poster objects. Attributes: TAG (str): 'Photo' @@ -837,12 +837,16 @@ def select(self): pass -class Poster(BasePosterArt): - """ Represents a single Poster object. """ +class Art(BaseImage): + """ Represents a single Art object. """ -class Art(BasePosterArt): - """ Represents a single Art object. """ +class Banner(BaseImage): + """ Represents a single Banner object. """ + + +class Poster(BaseImage): + """ Represents a single Poster object. """ @utils.registerPlexObject diff --git a/plexapi/mixins.py b/plexapi/mixins.py index 6faabeffe..88e8aa46f 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -6,14 +6,14 @@ class ArtMixin(object): - """ Mixin for Plex objects that can have artwork.""" + """ Mixin for Plex objects that can have background artwork.""" def arts(self): """ Returns list of available :class:`~plexapi.media.Art` objects. """ return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey, cls=media.Art) def uploadArt(self, url=None, filepath=None): - """ Upload art from url or filepath and set it as the selected art. + """ Upload a background artwork from a url or filepath. Parameters: url (str): The full URL to the image to upload. @@ -28,7 +28,7 @@ def uploadArt(self, url=None, filepath=None): self._server.query(key, method=self._server._session.post, data=data) def setArt(self, art): - """ Set the artwork for a Plex object. + """ Set the background artwork for a Plex object. Parameters: art (:class:`~plexapi.media.Art`): The art object to select. @@ -36,6 +36,37 @@ def setArt(self, art): art.select() +class BannerMixin(object): + """ Mixin for Plex objects that can have banners.""" + + def banners(self): + """ Returns list of available :class:`~plexapi.media.Banner` objects. """ + return self.fetchItems('/library/metadata/%s/banners' % self.ratingKey, cls=media.Banner) + + def uploadBanner(self, url=None, filepath=None): + """ Upload a banner from a url or filepath. + + Parameters: + url (str): The full URL to the image to upload. + filepath (str): The full file path the the image to upload. + """ + if url: + key = '/library/metadata/%s/banners?url=%s' % (self.ratingKey, quote_plus(url)) + self._server.query(key, method=self._server._session.post) + elif filepath: + key = '/library/metadata/%s/banners?' % self.ratingKey + data = open(filepath, 'rb').read() + self._server.query(key, method=self._server._session.post, data=data) + + def setBanner(self, banner): + """ Set the banner for a Plex object. + + Parameters: + banner (:class:`~plexapi.media.Banner`): The banner object to select. + """ + banner.select() + + class PosterMixin(object): """ Mixin for Plex objects that can have posters.""" @@ -44,7 +75,7 @@ def posters(self): return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey, cls=media.Poster) def uploadPoster(self, url=None, filepath=None): - """ Upload poster from url or filepath and set it as the selected poster. + """ Upload a poster from a url or filepath. Parameters: url (str): The full URL to the image to upload. diff --git a/plexapi/video.py b/plexapi/video.py index 98a24c7d7..3ca411260 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -5,7 +5,7 @@ from plexapi import library, media, settings, utils from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest, NotFound -from plexapi.mixins import ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin +from plexapi.mixins import ArtMixin, BannerMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin @@ -388,7 +388,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs): @utils.registerPlexObject -class Show(Video, ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin, +class Show(Video, ArtMixin, BannerMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin, CollectionMixin, GenreMixin, LabelMixin): """ Represents a single Show (including all seasons and episodes). From 00a20b2c9271183815cc7b58397a2a6da18a567b Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:12:06 -0800 Subject: [PATCH 04/23] Add banner mixin to show tests --- tests/test_mixins.py | 4 ++++ tests/test_video.py | 1 + 2 files changed, 5 insertions(+) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index a69eef4a4..2b5068be4 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -111,5 +111,9 @@ def edit_art(obj): _test_mixins_art_poster(obj, 'arts') +def edit_banner(obj): + _test_mixins_art_poster(obj, 'banners') + + def edit_poster(obj): _test_mixins_art_poster(obj, 'posters') diff --git a/tests/test_video.py b/tests/test_video.py index 1f4e857b7..a9e1737e9 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -722,6 +722,7 @@ def test_video_Show_section(show): def test_video_Show_mixins_images(show): test_mixins.edit_art(show) + test_mixins.edit_banner(show) test_mixins.edit_poster(show) From 29e7374de3cad05f21fe3a2b791215fb79da70e4 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:21:12 -0800 Subject: [PATCH 05/23] Rename mixins image test --- tests/test_mixins.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 2b5068be4..e7de281a4 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -76,7 +76,7 @@ def edit_writer(obj): _test_mixins_tag(obj, "writers", "Writer") -def _test_mixins_art_poster(obj, attr): +def _test_mixins_image(obj, attr): cap_attr = attr[:-1].capitalize() get_img_method = getattr(obj, attr) set_img_method = getattr(obj, "set" + cap_attr) @@ -108,12 +108,12 @@ def _test_mixins_art_poster(obj, attr): def edit_art(obj): - _test_mixins_art_poster(obj, 'arts') + _test_mixins_image(obj, 'arts') def edit_banner(obj): - _test_mixins_art_poster(obj, 'banners') + _test_mixins_image(obj, 'banners') def edit_poster(obj): - _test_mixins_art_poster(obj, 'posters') + _test_mixins_image(obj, 'posters') From f5cd5277d8c44494a30292467c1ab0a72a712949 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:34:38 -0800 Subject: [PATCH 06/23] Fix mixins image test reset default image --- tests/test_mixins.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index e7de281a4..27bd552c4 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -83,6 +83,7 @@ def _test_mixins_image(obj, attr): upload_img_method = getattr(obj, "upload" + cap_attr) images = get_img_method() if images: + default_image = images[0] image = images[0] assert len(image.key) >= 10 if not image.ratingKey.startswith(("default://", "media://", "upload://")): @@ -96,6 +97,8 @@ def _test_mixins_image(obj, attr): images = get_img_method() assert images[0].selected is False assert images[1].selected is True + else: + default_image = None # Test upload image from file upload_img_method(filepath=utils.STUB_IMAGE_PATH) images = get_img_method() @@ -104,7 +107,9 @@ def _test_mixins_image(obj, attr): if i.ratingKey.startswith('upload://') and i.ratingKey.endswith(CUTE_CAT_SHA1) ] assert file_image - set_img_method(images[0]) # Reset to default image + # Reset to default image + if default_image: + set_img_method(default_image) def edit_art(obj): From bbafaee1cf6ec6dadee0ddb814fe6bda2ba6c071 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:10:11 -0800 Subject: [PATCH 07/23] Update art and thumb tests --- tests/conftest.py | 12 ++++++++++ tests/test_audio.py | 55 +++++++++++++++++++++++++++++++++++---------- tests/test_video.py | 53 ++++++++++++++++++++++++++++++++----------- 3 files changed, 95 insertions(+), 25 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 5f676fe70..84e88eea5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -377,10 +377,22 @@ def is_string(value, gte=1): return isinstance(value, str) and len(value) >= gte +def is_art(key): + return is_metadata(key, contains="/art/") + + def is_thumb(key): return is_metadata(key, contains="/thumb/") +def is_artUrl(url): + return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/art/" in url + + +def is_thumbUrl(url): + return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/thumb/" in url + + def wait_until(condition_function, delay=0.25, timeout=1, *args, **kwargs): start = time.time() ready = condition_function(*args, **kwargs) diff --git a/tests/test_audio.py b/tests/test_audio.py index 22f98a4e9..95b6844e3 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -8,6 +8,11 @@ def test_audio_Artist_attr(artist): artist.reload() assert utils.is_datetime(artist.addedAt) + if artist.art: + assert utils.is_art(artist.art) + assert utils.is_artUrl(artist.artUrl) + else: + assert artist.artUrl is None if artist.countries: assert "United States of America" in [i.tag for i in artist.countries] #assert "Electronic" in [i.tag for i in artist.genres] @@ -24,6 +29,11 @@ def test_audio_Artist_attr(artist): assert isinstance(artist.similar, list) if artist.summary: assert "Alias" in artist.summary + if artist.thumb: + assert utils.is_thumb(artist.thumb) + assert utils.is_thumbUrl(artist.thumbUrl) + else: + assert artist.thumbUrl is None assert artist.title == "Broke For Free" assert artist.titleSort == "Broke For Free" assert artist.type == "artist" @@ -80,6 +90,11 @@ def test_audio_Artist_mixins_tags(artist): def test_audio_Album_attrs(album): assert utils.is_datetime(album.addedAt) + if album.art: + assert utils.is_art(album.art) + assert utils.is_artUrl(album.artUrl) + else: + assert album.artUrl is None assert isinstance(album.genres, list) assert album.index == 1 assert utils.is_metadata(album._initpath) @@ -90,21 +105,23 @@ def test_audio_Album_attrs(album): assert utils.is_metadata(album.parentKey) assert utils.is_int(album.parentRatingKey) if album.parentThumb: - assert utils.is_metadata(album.parentThumb, contains="/thumb/") + assert utils.is_thumb(album.parentThumb) assert album.parentTitle == "Broke For Free" assert album.ratingKey >= 1 assert album._server._baseurl == utils.SERVER_BASEURL assert album.studio == "[no label]" assert album.summary == "" if album.thumb: - assert utils.is_metadata(album.thumb, contains="/thumb/") + assert utils.is_thumb(album.thumb) + assert utils.is_thumbUrl(album.thumbUrl) + else: + assert album.thumbUrl is None assert album.title == "Layers" assert album.titleSort == "Layers" assert album.type == "album" assert utils.is_datetime(album.updatedAt) assert utils.is_int(album.viewCount, gte=0) assert album.year in (2012,) - assert album.artUrl is None def test_audio_Album_history(album): @@ -133,14 +150,14 @@ def test_audio_Album_tracks(album): assert utils.is_metadata(track.parentKey) assert utils.is_int(track.parentRatingKey) if track.parentThumb: - assert utils.is_metadata(track.parentThumb, contains="/thumb/") + assert utils.is_thumb(track.parentThumb) assert track.parentTitle == "Layers" # assert track.ratingCount == 9 # Flaky assert utils.is_int(track.ratingKey) assert track._server._baseurl == utils.SERVER_BASEURL assert track.summary == "" if track.thumb: - assert utils.is_metadata(track.thumb, contains="/thumb/") + assert utils.is_thumb(track.thumb) assert track.title == "As Colourful as Ever" assert track.titleSort == "As Colourful as Ever" assert not track.transcodeSessions @@ -156,6 +173,11 @@ def test_audio_Album_track(album, track=None): track2 = album.track(track=1) assert track == track2 assert utils.is_datetime(track.addedAt) + if track.art: + assert utils.is_art(track.art) + assert utils.is_artUrl(track.artUrl) + else: + assert track.artUrl is None assert utils.is_int(track.duration) assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) @@ -172,14 +194,17 @@ def test_audio_Album_track(album, track=None): assert utils.is_metadata(track.parentKey) assert utils.is_int(track.parentRatingKey) if track.parentThumb: - assert utils.is_metadata(track.parentThumb, contains="/thumb/") + assert utils.is_thumb(track.parentThumb) assert track.parentTitle == "Layers" # assert track.ratingCount == 9 assert utils.is_int(track.ratingKey) assert track._server._baseurl == utils.SERVER_BASEURL assert track.summary == "" if track.thumb: - assert utils.is_metadata(track.thumb, contains="/thumb/") + assert utils.is_thumb(track.thumb) + assert utils.is_thumbUrl(track.thumbUrl) + else: + assert track.thumbUrl is None assert track.title == "As Colourful as Ever" assert track.titleSort == "As Colourful as Ever" assert not track.transcodeSessions @@ -212,7 +237,6 @@ def test_audio_Album_track(album, track=None): assert utils.is_part(part.key) assert part._server._baseurl == utils.SERVER_BASEURL assert part.size == 3761053 - assert track.artUrl is None def test_audio_Album_get(album): @@ -242,14 +266,18 @@ def test_audio_Album_mixins_tags(album): def test_audio_Track_attrs(album): track = album.get("As Colourful As Ever").reload() assert utils.is_datetime(track.addedAt) - assert track.art is None + if track.art: + assert utils.is_art(track.art) + assert utils.is_artUrl(track.artUrl) + else: + assert track.artUrl is None assert track.chapterSource is None assert utils.is_int(track.duration) assert track.grandparentArt is None assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) if track.grandparentThumb: - assert utils.is_metadata(track.grandparentThumb, contains="/thumb/") + assert utils.is_thumb(track.grandparentThumb) assert track.grandparentTitle == "Broke For Free" assert track.guid.startswith("mbid://") or track.guid.startswith("plex://track/") assert int(track.index) == 1 @@ -268,7 +296,7 @@ def test_audio_Track_attrs(album): assert utils.is_metadata(track.parentKey) assert utils.is_int(track.parentRatingKey) if track.parentThumb: - assert utils.is_metadata(track.parentThumb, contains="/thumb/") + assert utils.is_thumb(track.parentThumb) assert track.parentTitle == "Layers" assert track.playlistItemID is None assert track.primaryExtraKey is None @@ -278,7 +306,10 @@ def test_audio_Track_attrs(album): assert track.sessionKey is None assert track.summary == "" if track.thumb: - assert utils.is_metadata(track.thumb, contains="/thumb/") + assert utils.is_thumb(track.thumb) + assert utils.is_thumbUrl(track.thumbUrl) + else: + assert track.thumbUrl is None assert track.title == "As Colourful as Ever" assert track.titleSort == "As Colourful as Ever" assert not track.transcodeSessions diff --git a/tests/test_video.py b/tests/test_video.py index a9e1737e9..e99039528 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -157,8 +157,11 @@ def test_video_Movie_attrs(movies): assert len(movie.locations) == 1 assert len(movie.locations[0]) >= 10 assert utils.is_datetime(movie.addedAt) - assert utils.is_metadata(movie.art) - assert movie.artUrl + if movie.art: + assert utils.is_art(movie.art) + assert utils.is_artUrl(movie.artUrl) + else: + assert movie.artUrl is None assert float(movie.rating) >= 6.4 assert movie.ratingImage == 'rottentomatoes://image.rating.ripe' assert movie.audienceRating >= 8.5 @@ -199,7 +202,11 @@ def test_video_Movie_attrs(movies): assert movie.studio == "Nina Paley" assert utils.is_string(movie.summary, gte=100) assert movie.tagline == "The Greatest Break-Up Story Ever Told" - assert utils.is_thumb(movie.thumb) + if movie.thumb: + assert utils.is_thumb(movie.thumb) + assert utils.is_thumbUrl(movie.thumbUrl) + else: + assert movie.thumbUrl is None assert movie.title == "Sita Sings the Blues" assert movie.titleSort == "Sita Sings the Blues" assert not movie.transcodeSessions @@ -547,7 +554,11 @@ def test_video_Episode_stop(episode, mocker, patched_http_call): def test_video_Show_attrs(show): assert utils.is_datetime(show.addedAt) - assert utils.is_metadata(show.art, contains="/art/") + if show.art: + assert utils.is_art(show.art) + assert utils.is_artUrl(show.artUrl) + else: + assert show.artUrl is None assert utils.is_metadata(show.banner, contains="/banner/") assert utils.is_int(show.childCount) assert show.contentRating in utils.CONTENTRATINGS @@ -586,7 +597,11 @@ def test_video_Show_attrs(show): assert show.studio == "HBO" assert utils.is_string(show.summary, gte=100) assert utils.is_metadata(show.theme, contains="/theme/") - assert utils.is_metadata(show.thumb, contains="/thumb/") + if show.thumb: + assert utils.is_thumb(show.thumb) + assert utils.is_thumbUrl(show.thumbUrl) + else: + assert show.thumbUrl is None assert show.title == "Game of Thrones" assert show.titleSort == "Game of Thrones" assert show.type == "show" @@ -681,12 +696,6 @@ def test_video_Episode_download(monkeydownload, tmpdir, episode): assert len(with_sceen_size) == 1 -def test_video_Show_thumbUrl(show): - assert utils.SERVER_BASEURL in show.thumbUrl - assert "/library/metadata/" in show.thumbUrl - assert "/thumb/" in show.thumbUrl - - # Analyze seems to fail intermittently @pytest.mark.xfail def test_video_Show_analyze(show): @@ -782,6 +791,11 @@ def test_video_Episode_analyze(tvshows): def test_video_Episode_attrs(episode): assert utils.is_datetime(episode.addedAt) + if episode.art: + assert utils.is_art(episode.art) + assert utils.is_artUrl(episode.artUrl) + else: + assert episode.artUrl is None assert episode.contentRating in utils.CONTENTRATINGS if len(episode.directors): assert [i.tag for i in episode.directors] == ["Tim Van Patten"] @@ -801,7 +815,11 @@ def test_video_Episode_attrs(episode): assert episode._server._baseurl == utils.SERVER_BASEURL assert episode.skipParent is False assert utils.is_string(episode.summary, gte=100) - assert utils.is_metadata(episode.thumb, contains="/thumb/") + if episode.thumb: + assert utils.is_thumb(episode.thumb) + assert utils.is_thumbUrl(episode.thumbUrl) + else: + assert episode.thumbUrl is None assert episode.title == "Winter Is Coming" assert episode.titleSort == "Winter Is Coming" assert not episode.transcodeSessions @@ -876,6 +894,11 @@ def test_video_Season_history(show): def test_video_Season_attrs(show): season = show.season("Season 1") assert utils.is_datetime(season.addedAt) + if season.art: + assert utils.is_art(season.art) + assert utils.is_artUrl(season.artUrl) + else: + assert season.artUrl is None assert season.index == 1 assert utils.is_metadata(season._initpath) assert utils.is_metadata(season.key) @@ -888,7 +911,11 @@ def test_video_Season_attrs(show): assert utils.is_int(season.ratingKey) assert season._server._baseurl == utils.SERVER_BASEURL assert season.summary == "" - assert utils.is_metadata(season.thumb, contains="/thumb/") + if season.thumb: + assert utils.is_thumb(season.thumb) + assert utils.is_thumbUrl(season.thumbUrl) + else: + assert season.thumbUrl is None assert season.title == "Season 1" assert season.titleSort == "Season 1" assert season.type == "season" From 8478ae6e6254e4fc94ea7493f23d5499534d78aa Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:03:19 -0800 Subject: [PATCH 08/23] Add banner url to show tests --- plexapi/audio.py | 8 ++++++-- plexapi/video.py | 9 ++++++++- tests/conftest.py | 8 ++++++++ tests/test_audio.py | 7 ++++++- tests/test_video.py | 15 +++++++++++++-- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 2cbd82d61..0bb04941e 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -69,13 +69,17 @@ def _loadData(self, data): @property def thumbUrl(self): - """ Return url to for the thumbnail image. """ + """ Return the first first thumbnail url starting on + the most specific thumbnail for that item. + """ key = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') return self._server.url(key, includeToken=True) if key else None @property def artUrl(self): - """ Return the first art url starting on the most specific for that item.""" + """ Return the first first art url starting on + the most specific art for that item. + """ art = self.firstAttr('art', 'grandparentArt') return self._server.url(art, includeToken=True) if art else None diff --git a/plexapi/video.py b/plexapi/video.py index 3ca411260..9d3b6a441 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -76,7 +76,9 @@ def thumbUrl(self): @property def artUrl(self): - """ Return the first first art url starting on the most specific for that item.""" + """ Return the first first art url starting on + the most specific art for that item. + """ art = self.firstAttr('art', 'grandparentArt') return self._server.url(art, includeToken=True) if art else None @@ -458,6 +460,11 @@ def isWatched(self): """ Returns True if the show is fully watched. """ return bool(self.viewedLeafCount == self.leafCount) + @property + def bannerUrl(self): + """ Return the banner url for the show.""" + return self._server.url(self.banner, includeToken=True) if self.banner else None + def preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] diff --git a/tests/conftest.py b/tests/conftest.py index 84e88eea5..c0d8bf449 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -381,6 +381,10 @@ def is_art(key): return is_metadata(key, contains="/art/") +def is_banner(key): + return is_metadata(key, contains="/banner/") + + def is_thumb(key): return is_metadata(key, contains="/thumb/") @@ -389,6 +393,10 @@ def is_artUrl(url): return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/art/" in url +def is_bannerUrl(url): + return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/banner/" in url + + def is_thumbUrl(url): return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/thumb/" in url diff --git a/tests/test_audio.py b/tests/test_audio.py index 95b6844e3..f854165f5 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -179,8 +179,12 @@ def test_audio_Album_track(album, track=None): else: assert track.artUrl is None assert utils.is_int(track.duration) + if track.grandparentArt: + assert utils.is_art(track.grandparentArt) assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) + if track.grandparentThumb: + assert utils.is_thumb(track.grandparentThumb) assert track.grandparentTitle == "Broke For Free" assert int(track.index) == 1 assert utils.is_metadata(track._initpath) @@ -273,7 +277,8 @@ def test_audio_Track_attrs(album): assert track.artUrl is None assert track.chapterSource is None assert utils.is_int(track.duration) - assert track.grandparentArt is None + if track.grandparentArt: + assert utils.is_art(track.grandparengrandparentArt) assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) if track.grandparentThumb: diff --git a/tests/test_video.py b/tests/test_video.py index e99039528..8b25f87a4 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -559,7 +559,11 @@ def test_video_Show_attrs(show): assert utils.is_artUrl(show.artUrl) else: assert show.artUrl is None - assert utils.is_metadata(show.banner, contains="/banner/") + if show.banner: + assert utils.is_banner(show.banner) + assert utils.is_bannerUrl(show.bannerUrl) + else: + assert show.bannerUrl is None assert utils.is_int(show.childCount) assert show.contentRating in utils.CONTENTRATINGS assert utils.is_int(show.duration, gte=1600000) @@ -800,6 +804,10 @@ def test_video_Episode_attrs(episode): if len(episode.directors): assert [i.tag for i in episode.directors] == ["Tim Van Patten"] assert utils.is_int(episode.duration, gte=120000) + if episode.grandparentArt: + assert utils.is_art(episode.grandparentArt) + if episode.grandparentThumb: + assert utils.is_thumb(episode.grandparentThumb) assert episode.grandparentTitle == "Game of Thrones" assert episode.index == 1 assert utils.is_metadata(episode._initpath) @@ -809,7 +817,8 @@ def test_video_Episode_attrs(episode): assert utils.is_int(episode.parentIndex) assert utils.is_metadata(episode.parentKey) assert utils.is_int(episode.parentRatingKey) - assert utils.is_metadata(episode.parentThumb, contains="/thumb/") + if episode.parentThumb: + assert utils.is_thumb(episode.parentThumb) assert episode.rating >= 7.7 assert utils.is_int(episode.ratingKey) assert episode._server._baseurl == utils.SERVER_BASEURL @@ -907,6 +916,8 @@ def test_video_Season_attrs(show): assert season.listType == "video" assert utils.is_metadata(season.parentKey) assert utils.is_int(season.parentRatingKey) + if season.parentThumb: + assert utils.is_thumb(season.parentThumb) assert season.parentTitle == "Game of Thrones" assert utils.is_int(season.ratingKey) assert season._server._baseurl == utils.SERVER_BASEURL From 8915134b6bd582c5ede0895a7e9a31602017036a Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:11:53 -0800 Subject: [PATCH 09/23] Fix typo in track test --- tests/test_audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_audio.py b/tests/test_audio.py index f854165f5..6dcbeba7a 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -278,7 +278,7 @@ def test_audio_Track_attrs(album): assert track.chapterSource is None assert utils.is_int(track.duration) if track.grandparentArt: - assert utils.is_art(track.grandparengrandparentArt) + assert utils.is_art(track.grandparentArt) assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) if track.grandparentThumb: From c41f89bf9b1fa307fd7bb859fe360de456c99689 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:12:18 -0800 Subject: [PATCH 10/23] Factor out artUrl, thumbUrl, bannerUrl to mixins --- plexapi/audio.py | 16 ---------------- plexapi/library.py | 10 ---------- plexapi/mixins.py | 18 ++++++++++++++++++ plexapi/photo.py | 2 +- plexapi/video.py | 21 --------------------- 5 files changed, 19 insertions(+), 48 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 0bb04941e..c9c6923e3 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -67,22 +67,6 @@ def _loadData(self, data): self.userRating = utils.cast(float, data.attrib.get('userRating', 0)) self.viewCount = utils.cast(int, data.attrib.get('viewCount', 0)) - @property - def thumbUrl(self): - """ Return the first first thumbnail url starting on - the most specific thumbnail for that item. - """ - key = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') - return self._server.url(key, includeToken=True) if key else None - - @property - def artUrl(self): - """ Return the first first art url starting on - the most specific art for that item. - """ - art = self.firstAttr('art', 'grandparentArt') - return self._server.url(art, includeToken=True) if art else None - def url(self, part): """ Returns the full URL for the audio item. Typically used for getting a specific track. """ return self._server.url(part, includeToken=True) if part else None diff --git a/plexapi/library.py b/plexapi/library.py index 8a6413f6a..e9f70b9b4 100644 --- a/plexapi/library.py +++ b/plexapi/library.py @@ -1598,16 +1598,6 @@ def _loadData(self, data): def children(self): return self.fetchItems(self.key) - @property - def thumbUrl(self): - """ Return the thumbnail url for the collection.""" - return self._server.url(self.thumb, includeToken=True) if self.thumb else None - - @property - def artUrl(self): - """ Return the art url for the collection.""" - return self._server.url(self.art, includeToken=True) if self.art else None - def item(self, title): """ Returns the item in the collection that matches the specified title. diff --git a/plexapi/mixins.py b/plexapi/mixins.py index 88e8aa46f..d0deecb42 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -8,6 +8,12 @@ class ArtMixin(object): """ Mixin for Plex objects that can have background artwork.""" + @property + def artUrl(self): + """ Return the art url for the Plex object.""" + art = self.firstAttr('art', 'grandparentArt') + return self._server.url(art, includeToken=True) if art else None + def arts(self): """ Returns list of available :class:`~plexapi.media.Art` objects. """ return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey, cls=media.Art) @@ -39,6 +45,12 @@ def setArt(self, art): class BannerMixin(object): """ Mixin for Plex objects that can have banners.""" + @property + def bannerUrl(self): + """ Return the banner url for the Plex object.""" + banner = self.firstAttr('banner') + return self._server.url(banner, includeToken=True) if banner else None + def banners(self): """ Returns list of available :class:`~plexapi.media.Banner` objects. """ return self.fetchItems('/library/metadata/%s/banners' % self.ratingKey, cls=media.Banner) @@ -70,6 +82,12 @@ def setBanner(self, banner): class PosterMixin(object): """ Mixin for Plex objects that can have posters.""" + @property + def thumbUrl(self): + """ Return the thumb url for the Plex object.""" + thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') + return self._server.url(thumb, includeToken=True) if thumb else None + def posters(self): """ Returns list of available :class:`~plexapi.media.Poster` objects. """ return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey, cls=media.Poster) diff --git a/plexapi/photo.py b/plexapi/photo.py index eb2867517..6fb16071c 100644 --- a/plexapi/photo.py +++ b/plexapi/photo.py @@ -210,7 +210,7 @@ def _loadData(self, data): @property def thumbUrl(self): - """Return URL for the thumbnail image.""" + """ Return the thumb url for the photo.""" key = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') return self._server.url(key, includeToken=True) if key else None diff --git a/plexapi/video.py b/plexapi/video.py index 9d3b6a441..c9e83af79 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -66,22 +66,6 @@ def isWatched(self): """ Returns True if this video is watched. """ return bool(self.viewCount > 0) if self.viewCount else False - @property - def thumbUrl(self): - """ Return the first first thumbnail url starting on - the most specific thumbnail for that item. - """ - thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') - return self._server.url(thumb, includeToken=True) if thumb else None - - @property - def artUrl(self): - """ Return the first first art url starting on - the most specific art for that item. - """ - art = self.firstAttr('art', 'grandparentArt') - return self._server.url(art, includeToken=True) if art else None - def url(self, part): """ Returns the full url for something. Typically used for getting a specific image. """ return self._server.url(part, includeToken=True) if part else None @@ -460,11 +444,6 @@ def isWatched(self): """ Returns True if the show is fully watched. """ return bool(self.viewedLeafCount == self.leafCount) - @property - def bannerUrl(self): - """ Return the banner url for the show.""" - return self._server.url(self.banner, includeToken=True) if self.banner else None - def preferences(self): """ Returns a list of :class:`~plexapi.settings.Preferences` objects. """ items = [] From 9fa51cee2ac3a46b32d14db2d584f3e0c977ca08 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:38:09 -0800 Subject: [PATCH 11/23] Subclass image url mixins --- plexapi/audio.py | 4 ++-- plexapi/mixins.py | 26 +++++++++++++++++++------- plexapi/photo.py | 12 +++--------- plexapi/video.py | 4 ++-- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index c9c6923e3..6f052e31f 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -4,7 +4,7 @@ from plexapi import library, media, utils from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest -from plexapi.mixins import ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin +from plexapi.mixins import ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin @@ -324,7 +324,7 @@ def _defaultSyncTitle(self): @utils.registerPlexObject -class Track(Audio, Playable, MoodMixin): +class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, MoodMixin): """ Represents a single Track. Attributes: diff --git a/plexapi/mixins.py b/plexapi/mixins.py index d0deecb42..eed30c8ed 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -5,15 +5,19 @@ from plexapi.exceptions import NotFound -class ArtMixin(object): - """ Mixin for Plex objects that can have background artwork.""" - +class ArtUrlMixin(object): + """ Mixin for Plex objects that can have a background artwork url.""" + @property def artUrl(self): """ Return the art url for the Plex object.""" art = self.firstAttr('art', 'grandparentArt') return self._server.url(art, includeToken=True) if art else None + +class ArtMixin(ArtUrlMixin): + """ Mixin for Plex objects that can have background artwork.""" + def arts(self): """ Returns list of available :class:`~plexapi.media.Art` objects. """ return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey, cls=media.Art) @@ -42,8 +46,8 @@ def setArt(self, art): art.select() -class BannerMixin(object): - """ Mixin for Plex objects that can have banners.""" +class BannerUrlMixin(object): + """ Mixin for Plex objects that can have a banner url.""" @property def bannerUrl(self): @@ -51,6 +55,10 @@ def bannerUrl(self): banner = self.firstAttr('banner') return self._server.url(banner, includeToken=True) if banner else None + +class BannerMixin(BannerUrlMixin): + """ Mixin for Plex objects that can have banners.""" + def banners(self): """ Returns list of available :class:`~plexapi.media.Banner` objects. """ return self.fetchItems('/library/metadata/%s/banners' % self.ratingKey, cls=media.Banner) @@ -79,8 +87,8 @@ def setBanner(self, banner): banner.select() -class PosterMixin(object): - """ Mixin for Plex objects that can have posters.""" +class PosterUrlMixin(object): + """ Mixin for Plex objects that can have a poster url.""" @property def thumbUrl(self): @@ -88,6 +96,10 @@ def thumbUrl(self): thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') return self._server.url(thumb, includeToken=True) if thumb else None + +class PosterMixin(PosterUrlMixin): + """ Mixin for Plex objects that can have posters.""" + def posters(self): """ Returns list of available :class:`~plexapi.media.Poster` objects. """ return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey, cls=media.Poster) diff --git a/plexapi/photo.py b/plexapi/photo.py index 6fb16071c..398cd7daf 100644 --- a/plexapi/photo.py +++ b/plexapi/photo.py @@ -4,11 +4,11 @@ from plexapi import media, utils, video from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest -from plexapi.mixins import TagMixin +from plexapi.mixins import ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, TagMixin @utils.registerPlexObject -class Photoalbum(PlexPartialObject): +class Photoalbum(PlexPartialObject, ArtMixin, PosterMixin): """ Represents a single Photoalbum (collection of photos). Attributes: @@ -137,7 +137,7 @@ def download(self, savepath=None, keep_original_name=False, showstatus=False): @utils.registerPlexObject -class Photo(PlexPartialObject, Playable, TagMixin): +class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, TagMixin): """ Represents a single Photo. Attributes: @@ -208,12 +208,6 @@ def _loadData(self, data): self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt')) self.year = utils.cast(int, data.attrib.get('year')) - @property - def thumbUrl(self): - """ Return the thumb url for the photo.""" - key = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') - return self._server.url(key, includeToken=True) if key else None - def photoalbum(self): """ Return the photo's :class:`~plexapi.photo.Photoalbum`. """ return self.fetchItem(self.parentKey) diff --git a/plexapi/video.py b/plexapi/video.py index c9e83af79..fd85e1bc1 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -5,7 +5,7 @@ from plexapi import library, media, settings, utils from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest, NotFound -from plexapi.mixins import ArtMixin, BannerMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin +from plexapi.mixins import ArtUrlMixin, ArtMixin, BannerMixin, PosterUrlMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin @@ -839,7 +839,7 @@ def _defaultSyncTitle(self): @utils.registerPlexObject -class Clip(Video, Playable): +class Clip(Video, Playable, ArtUrlMixin, PosterUrlMixin): """Represents a single Clip. Attributes: From b0780aaec8b1bb2878020ff87caa1c250cd4ccea Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:58:03 -0800 Subject: [PATCH 12/23] Add Playlist thumb alias to composite --- plexapi/base.py | 2 +- plexapi/playlist.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plexapi/base.py b/plexapi/base.py index e0022e2ee..627e33dfe 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -232,7 +232,7 @@ def findItems(self, data, cls=None, initpath=None, **kwargs): def firstAttr(self, *attrs): """ Return the first attribute in attrs that is not None. """ for attr in attrs: - value = self.__dict__.get(attr) + value = getattr(self, attr, None) if value is not None: return value diff --git a/plexapi/playlist.py b/plexapi/playlist.py index 399d55aa9..36179dc58 100644 --- a/plexapi/playlist.py +++ b/plexapi/playlist.py @@ -63,6 +63,11 @@ def __iter__(self): # pragma: no cover for item in self.items(): yield item + @property + def thumb(self): + """ Alias to self.composite. """ + return self.composite + @property def metadataType(self): if self.isVideo: From 32f00e653aef4c161cc413353015d190990a4dfb Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 19:58:18 -0800 Subject: [PATCH 13/23] Add alias posterUrl to thumbUrl --- plexapi/mixins.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plexapi/mixins.py b/plexapi/mixins.py index eed30c8ed..18820e719 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -96,6 +96,11 @@ def thumbUrl(self): thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') return self._server.url(thumb, includeToken=True) if thumb else None + @property + def posterUrl(self): + """ Alias to self.thumbUrl.""" + return self.thumbUrl + class PosterMixin(PosterUrlMixin): """ Mixin for Plex objects that can have posters.""" From bc8e42bbff849852bce619e5645dbdcbae330ae7 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 20:02:14 -0800 Subject: [PATCH 14/23] Add simple test for posterUrl alias --- tests/test_library.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_library.py b/tests/test_library.py index 83dc14690..e871a6018 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -318,6 +318,7 @@ def test_library_Collection_thumbUrl(collection): assert utils.SERVER_BASEURL in collection.thumbUrl assert "/library/collections/" in collection.thumbUrl assert "/composite/" in collection.thumbUrl + assert collection.thumbUrl == collection.posterUrl def test_library_Collection_artUrl(collection): From aa3c37e5b5e233f6705c3b0661dd3c4867d0ab83 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 20:06:49 -0800 Subject: [PATCH 15/23] Clean doc strings --- plexapi/mixins.py | 24 ++++++++++++------------ plexapi/video.py | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plexapi/mixins.py b/plexapi/mixins.py index 18820e719..4aa6fbcb4 100644 --- a/plexapi/mixins.py +++ b/plexapi/mixins.py @@ -6,17 +6,17 @@ class ArtUrlMixin(object): - """ Mixin for Plex objects that can have a background artwork url.""" + """ Mixin for Plex objects that can have a background artwork url. """ @property def artUrl(self): - """ Return the art url for the Plex object.""" + """ Return the art url for the Plex object. """ art = self.firstAttr('art', 'grandparentArt') return self._server.url(art, includeToken=True) if art else None class ArtMixin(ArtUrlMixin): - """ Mixin for Plex objects that can have background artwork.""" + """ Mixin for Plex objects that can have background artwork. """ def arts(self): """ Returns list of available :class:`~plexapi.media.Art` objects. """ @@ -47,17 +47,17 @@ def setArt(self, art): class BannerUrlMixin(object): - """ Mixin for Plex objects that can have a banner url.""" + """ Mixin for Plex objects that can have a banner url. """ @property def bannerUrl(self): - """ Return the banner url for the Plex object.""" + """ Return the banner url for the Plex object. """ banner = self.firstAttr('banner') return self._server.url(banner, includeToken=True) if banner else None class BannerMixin(BannerUrlMixin): - """ Mixin for Plex objects that can have banners.""" + """ Mixin for Plex objects that can have banners. """ def banners(self): """ Returns list of available :class:`~plexapi.media.Banner` objects. """ @@ -88,22 +88,22 @@ def setBanner(self, banner): class PosterUrlMixin(object): - """ Mixin for Plex objects that can have a poster url.""" + """ Mixin for Plex objects that can have a poster url. """ @property def thumbUrl(self): - """ Return the thumb url for the Plex object.""" + """ Return the thumb url for the Plex object. """ thumb = self.firstAttr('thumb', 'parentThumb', 'granparentThumb') return self._server.url(thumb, includeToken=True) if thumb else None @property def posterUrl(self): - """ Alias to self.thumbUrl.""" + """ Alias to self.thumbUrl. """ return self.thumbUrl class PosterMixin(PosterUrlMixin): - """ Mixin for Plex objects that can have posters.""" + """ Mixin for Plex objects that can have posters. """ def posters(self): """ Returns list of available :class:`~plexapi.media.Poster` objects. """ @@ -134,7 +134,7 @@ def setPoster(self, poster): class SplitMergeMixin(object): - """ Mixin for Plex objects that can be split and merged.""" + """ Mixin for Plex objects that can be split and merged. """ def split(self): """ Split duplicated Plex object into separate objects. """ @@ -155,7 +155,7 @@ def merge(self, ratingKeys): class UnmatchMatchMixin(object): - """ Mixin for Plex objects that can be unmatched and matched.""" + """ Mixin for Plex objects that can be unmatched and matched. """ def unmatch(self): """ Unmatches metadata match from object. """ diff --git a/plexapi/video.py b/plexapi/video.py index fd85e1bc1..cc03e6738 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -840,7 +840,7 @@ def _defaultSyncTitle(self): @utils.registerPlexObject class Clip(Video, Playable, ArtUrlMixin, PosterUrlMixin): - """Represents a single Clip. + """ Represents a single Clip. Attributes: TAG (str): 'Video' @@ -862,7 +862,7 @@ class Clip(Video, Playable, ArtUrlMixin, PosterUrlMixin): METADATA_TYPE = 'clip' def _loadData(self, data): - """Load attribute values from Plex XML response.""" + """ Load attribute values from Plex XML response. """ Video._loadData(self, data) Playable._loadData(self, data) self._data = data From c537db61cfc54b3b51d9e72efdfc85e5dc9b0ba0 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 21:19:42 -0800 Subject: [PATCH 16/23] Factor out mixins image url tests --- tests/conftest.py | 12 ------------ tests/test_audio.py | 37 +++++++++++++------------------------ tests/test_library.py | 13 ++----------- tests/test_mixins.py | 22 ++++++++++++++++++++++ tests/test_video.py | 34 +++++++--------------------------- 5 files changed, 44 insertions(+), 74 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index c0d8bf449..ae16a5a18 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -389,18 +389,6 @@ def is_thumb(key): return is_metadata(key, contains="/thumb/") -def is_artUrl(url): - return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/art/" in url - - -def is_bannerUrl(url): - return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/banner/" in url - - -def is_thumbUrl(url): - return url.startswith(SERVER_BASEURL) and "/library/metadata/" in url and "/thumb/" in url - - def wait_until(condition_function, delay=0.25, timeout=1, *args, **kwargs): start = time.time() ready = condition_function(*args, **kwargs) diff --git a/tests/test_audio.py b/tests/test_audio.py index 6dcbeba7a..b54249a48 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -10,9 +10,6 @@ def test_audio_Artist_attr(artist): assert utils.is_datetime(artist.addedAt) if artist.art: assert utils.is_art(artist.art) - assert utils.is_artUrl(artist.artUrl) - else: - assert artist.artUrl is None if artist.countries: assert "United States of America" in [i.tag for i in artist.countries] #assert "Electronic" in [i.tag for i in artist.genres] @@ -31,9 +28,6 @@ def test_audio_Artist_attr(artist): assert "Alias" in artist.summary if artist.thumb: assert utils.is_thumb(artist.thumb) - assert utils.is_thumbUrl(artist.thumbUrl) - else: - assert artist.thumbUrl is None assert artist.title == "Broke For Free" assert artist.titleSort == "Broke For Free" assert artist.type == "artist" @@ -77,6 +71,8 @@ def test_audio_Artist_albums(artist): 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_posterUrl(artist) def test_audio_Artist_mixins_tags(artist): @@ -92,9 +88,6 @@ def test_audio_Album_attrs(album): assert utils.is_datetime(album.addedAt) if album.art: assert utils.is_art(album.art) - assert utils.is_artUrl(album.artUrl) - else: - assert album.artUrl is None assert isinstance(album.genres, list) assert album.index == 1 assert utils.is_metadata(album._initpath) @@ -113,9 +106,6 @@ def test_audio_Album_attrs(album): assert album.summary == "" if album.thumb: assert utils.is_thumb(album.thumb) - assert utils.is_thumbUrl(album.thumbUrl) - else: - assert album.thumbUrl is None assert album.title == "Layers" assert album.titleSort == "Layers" assert album.type == "album" @@ -138,8 +128,12 @@ def test_audio_Album_tracks(album): tracks = album.tracks() track = tracks[0] assert len(tracks) == 1 + if track.grandparentArt: + assert utils.is_art(track.grandparentArt) assert utils.is_metadata(track.grandparentKey) assert utils.is_int(track.grandparentRatingKey) + if track.grandparentThumb: + assert utils.is_thumb(track.grandparentThumb) assert track.grandparentTitle == "Broke For Free" assert track.index == 1 assert utils.is_metadata(track._initpath) @@ -175,9 +169,6 @@ def test_audio_Album_track(album, track=None): assert utils.is_datetime(track.addedAt) if track.art: assert utils.is_art(track.art) - assert utils.is_artUrl(track.artUrl) - else: - assert track.artUrl is None assert utils.is_int(track.duration) if track.grandparentArt: assert utils.is_art(track.grandparentArt) @@ -206,9 +197,6 @@ def test_audio_Album_track(album, track=None): assert track.summary == "" if track.thumb: assert utils.is_thumb(track.thumb) - assert utils.is_thumbUrl(track.thumbUrl) - else: - assert track.thumbUrl is None assert track.title == "As Colourful as Ever" assert track.titleSort == "As Colourful as Ever" assert not track.transcodeSessions @@ -257,6 +245,8 @@ def test_audio_Album_artist(album): 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_posterUrl(album) def test_audio_Album_mixins_tags(album): @@ -272,9 +262,6 @@ def test_audio_Track_attrs(album): assert utils.is_datetime(track.addedAt) if track.art: assert utils.is_art(track.art) - assert utils.is_artUrl(track.artUrl) - else: - assert track.artUrl is None assert track.chapterSource is None assert utils.is_int(track.duration) if track.grandparentArt: @@ -312,9 +299,6 @@ def test_audio_Track_attrs(album): assert track.summary == "" if track.thumb: assert utils.is_thumb(track.thumb) - assert utils.is_thumbUrl(track.thumbUrl) - else: - assert track.thumbUrl is None assert track.title == "As Colourful as Ever" assert track.titleSort == "As Colourful as Ever" assert not track.transcodeSessions @@ -392,6 +376,11 @@ def test_audio_Track_artist(album, artist): assert tracks[0].artist() == artist +def test_audio_Track_mixins_images(track): + test_mixins.attr_artUrl(track) + test_mixins.attr_posterUrl(track) + + def test_audio_Track_mixins_tags(track): test_mixins.edit_mood(track) diff --git a/tests/test_library.py b/tests/test_library.py index e871a6018..d31368a8f 100644 --- a/tests/test_library.py +++ b/tests/test_library.py @@ -314,17 +314,6 @@ def test_library_Collection_items(collection): assert len(items) == 1 -def test_library_Collection_thumbUrl(collection): - assert utils.SERVER_BASEURL in collection.thumbUrl - assert "/library/collections/" in collection.thumbUrl - assert "/composite/" in collection.thumbUrl - assert collection.thumbUrl == collection.posterUrl - - -def test_library_Collection_artUrl(collection): - assert collection.artUrl is None # Collections don't have default art - - def test_library_Collection_posters(collection): posters = collection.posters() assert posters @@ -338,6 +327,8 @@ def test_library_Collection_art(collection): def test_library_Collection_mixins_images(collection): test_mixins.edit_art(collection) test_mixins.edit_poster(collection) + test_mixins.attr_artUrl(collection) + test_mixins.attr_posterUrl(collection) def test_library_Collection_mixins_tags(collection): diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 27bd552c4..b1af5307f 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -122,3 +122,25 @@ def edit_banner(obj): def edit_poster(obj): _test_mixins_image(obj, 'posters') + + +def _test_mixins_imageUrl(obj, attr): + url = getattr(obj, attr + 'Url') + if getattr(obj, attr): + assert url.startswith(utils.SERVER_BASEURL) + assert "/library/metadata/" in url + assert attr in url + else: + assert url is None + + +def attr_artUrl(obj): + _test_mixins_imageUrl(obj, 'art') + + +def attr_bannerUrl(obj): + _test_mixins_imageUrl(obj, 'banner') + + +def attr_posterUrl(obj): + _test_mixins_imageUrl(obj, 'poster') diff --git a/tests/test_video.py b/tests/test_video.py index 8b25f87a4..4eba01b0d 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -159,9 +159,6 @@ def test_video_Movie_attrs(movies): assert utils.is_datetime(movie.addedAt) if movie.art: assert utils.is_art(movie.art) - assert utils.is_artUrl(movie.artUrl) - else: - assert movie.artUrl is None assert float(movie.rating) >= 6.4 assert movie.ratingImage == 'rottentomatoes://image.rating.ripe' assert movie.audienceRating >= 8.5 @@ -204,9 +201,6 @@ def test_video_Movie_attrs(movies): assert movie.tagline == "The Greatest Break-Up Story Ever Told" if movie.thumb: assert utils.is_thumb(movie.thumb) - assert utils.is_thumbUrl(movie.thumbUrl) - else: - assert movie.thumbUrl is None assert movie.title == "Sita Sings the Blues" assert movie.titleSort == "Sita Sings the Blues" assert not movie.transcodeSessions @@ -556,14 +550,8 @@ def test_video_Show_attrs(show): assert utils.is_datetime(show.addedAt) if show.art: assert utils.is_art(show.art) - assert utils.is_artUrl(show.artUrl) - else: - assert show.artUrl is None if show.banner: assert utils.is_banner(show.banner) - assert utils.is_bannerUrl(show.bannerUrl) - else: - assert show.bannerUrl is None assert utils.is_int(show.childCount) assert show.contentRating in utils.CONTENTRATINGS assert utils.is_int(show.duration, gte=1600000) @@ -603,9 +591,6 @@ def test_video_Show_attrs(show): assert utils.is_metadata(show.theme, contains="/theme/") if show.thumb: assert utils.is_thumb(show.thumb) - assert utils.is_thumbUrl(show.thumbUrl) - else: - assert show.thumbUrl is None assert show.title == "Game of Thrones" assert show.titleSort == "Game of Thrones" assert show.type == "show" @@ -737,6 +722,9 @@ def test_video_Show_mixins_images(show): test_mixins.edit_art(show) test_mixins.edit_banner(show) test_mixins.edit_poster(show) + test_mixins.attr_artUrl(show) + test_mixins.attr_bannerUrl(show) + test_mixins.attr_posterUrl(show) def test_video_Show_mixins_tags(show): @@ -797,9 +785,6 @@ def test_video_Episode_attrs(episode): assert utils.is_datetime(episode.addedAt) if episode.art: assert utils.is_art(episode.art) - assert utils.is_artUrl(episode.artUrl) - else: - assert episode.artUrl is None assert episode.contentRating in utils.CONTENTRATINGS if len(episode.directors): assert [i.tag for i in episode.directors] == ["Tim Van Patten"] @@ -826,9 +811,6 @@ def test_video_Episode_attrs(episode): assert utils.is_string(episode.summary, gte=100) if episode.thumb: assert utils.is_thumb(episode.thumb) - assert utils.is_thumbUrl(episode.thumbUrl) - else: - assert episode.thumbUrl is None assert episode.title == "Winter Is Coming" assert episode.titleSort == "Winter Is Coming" assert not episode.transcodeSessions @@ -878,6 +860,8 @@ def test_video_Episode_attrs(episode): 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_posterUrl(episode) def test_video_Episode_mixins_tags(episode): @@ -905,9 +889,6 @@ def test_video_Season_attrs(show): assert utils.is_datetime(season.addedAt) if season.art: assert utils.is_art(season.art) - assert utils.is_artUrl(season.artUrl) - else: - assert season.artUrl is None assert season.index == 1 assert utils.is_metadata(season._initpath) assert utils.is_metadata(season.key) @@ -924,9 +905,6 @@ def test_video_Season_attrs(show): assert season.summary == "" if season.thumb: assert utils.is_thumb(season.thumb) - assert utils.is_thumbUrl(season.thumbUrl) - else: - assert season.thumbUrl is None assert season.title == "Season 1" assert season.titleSort == "Season 1" assert season.type == "season" @@ -979,6 +957,8 @@ def test_video_Season_mixins_images(show): season = show.season(season=1) test_mixins.edit_art(season) test_mixins.edit_poster(season) + test_mixins.attr_artUrl(season) + test_mixins.attr_posterUrl(season) def test_that_reload_return_the_same_object(plex): From 9fde559147a5450eb242e890d57f7616f362351b Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 21:20:27 -0800 Subject: [PATCH 17/23] Remove redundant audio tests --- tests/test_audio.py | 95 --------------------------------------------- 1 file changed, 95 deletions(-) diff --git a/tests/test_audio.py b/tests/test_audio.py index b54249a48..6377c9359 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -126,39 +126,7 @@ def test_audio_Track_history(track): def test_audio_Album_tracks(album): tracks = album.tracks() - track = tracks[0] assert len(tracks) == 1 - if track.grandparentArt: - assert utils.is_art(track.grandparentArt) - assert utils.is_metadata(track.grandparentKey) - assert utils.is_int(track.grandparentRatingKey) - if track.grandparentThumb: - assert utils.is_thumb(track.grandparentThumb) - assert track.grandparentTitle == "Broke For Free" - assert track.index == 1 - assert utils.is_metadata(track._initpath) - assert utils.is_metadata(track.key) - assert track.listType == "audio" - assert track.originalTitle in (None, "Broke For Free") - # assert utils.is_int(track.parentIndex) - assert utils.is_metadata(track.parentKey) - assert utils.is_int(track.parentRatingKey) - if track.parentThumb: - assert utils.is_thumb(track.parentThumb) - assert track.parentTitle == "Layers" - # assert track.ratingCount == 9 # Flaky - assert utils.is_int(track.ratingKey) - assert track._server._baseurl == utils.SERVER_BASEURL - assert track.summary == "" - if track.thumb: - assert utils.is_thumb(track.thumb) - assert track.title == "As Colourful as Ever" - assert track.titleSort == "As Colourful as Ever" - assert not track.transcodeSessions - assert track.type == "track" - assert utils.is_datetime(track.updatedAt) - assert utils.is_int(track.viewCount, gte=0) - assert track.viewOffset == 0 def test_audio_Album_track(album, track=None): @@ -166,69 +134,6 @@ def test_audio_Album_track(album, track=None): track = track or album.track("As Colourful As Ever") track2 = album.track(track=1) assert track == track2 - assert utils.is_datetime(track.addedAt) - if track.art: - assert utils.is_art(track.art) - assert utils.is_int(track.duration) - if track.grandparentArt: - assert utils.is_art(track.grandparentArt) - assert utils.is_metadata(track.grandparentKey) - assert utils.is_int(track.grandparentRatingKey) - if track.grandparentThumb: - assert utils.is_thumb(track.grandparentThumb) - assert track.grandparentTitle == "Broke For Free" - assert int(track.index) == 1 - assert utils.is_metadata(track._initpath) - assert utils.is_metadata(track.key) - assert track.listType == "audio" - # Assign 0 track.media - media = track.media[0] - assert track.originalTitle in (None, "As Colourful As Ever") - # Fix me - assert utils.is_int(track.parentIndex) - assert utils.is_metadata(track.parentKey) - assert utils.is_int(track.parentRatingKey) - if track.parentThumb: - assert utils.is_thumb(track.parentThumb) - assert track.parentTitle == "Layers" - # assert track.ratingCount == 9 - assert utils.is_int(track.ratingKey) - assert track._server._baseurl == utils.SERVER_BASEURL - assert track.summary == "" - if track.thumb: - assert utils.is_thumb(track.thumb) - assert track.title == "As Colourful as Ever" - assert track.titleSort == "As Colourful as Ever" - assert not track.transcodeSessions - assert track.type == "track" - assert utils.is_datetime(track.updatedAt) - assert utils.is_int(track.viewCount, gte=0) - assert track.viewOffset == 0 - assert media.aspectRatio is None - assert media.audioChannels == 2 - assert media.audioCodec == "mp3" - assert media.bitrate == 128 - assert media.container == "mp3" - assert utils.is_int(media.duration) - assert media.height in (None, 1080) - assert utils.is_int(media.id, gte=1) - assert utils.is_metadata(media._initpath) - assert media.optimizedForStreaming in (None, True) - # Assign 0 media.parts - part = media.parts[0] - assert media._server._baseurl == utils.SERVER_BASEURL - assert media.videoCodec is None - assert media.videoFrameRate is None - assert media.videoResolution is None - assert media.width is None - assert part.container == "mp3" - assert utils.is_int(part.duration) - assert part.file.endswith(".mp3") - assert utils.is_int(part.id) - assert utils.is_metadata(part._initpath) - assert utils.is_part(part.key) - assert part._server._baseurl == utils.SERVER_BASEURL - assert part.size == 3761053 def test_audio_Album_get(album): From 0fa6f33c470a4b6782974bc7af7857e0fe5fac00 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 21:32:28 -0800 Subject: [PATCH 18/23] Fix mixins poster url attr typo --- tests/test_mixins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index b1af5307f..29fb776f2 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -130,6 +130,8 @@ def _test_mixins_imageUrl(obj, attr): assert url.startswith(utils.SERVER_BASEURL) assert "/library/metadata/" in url assert attr in url + if attr == 'thumb': + assert getattr(obj, 'posterUrl') == url else: assert url is None @@ -143,4 +145,4 @@ def attr_bannerUrl(obj): def attr_posterUrl(obj): - _test_mixins_imageUrl(obj, 'poster') + _test_mixins_imageUrl(obj, 'thumb') From 43e9685fba652070d6ec003575814cd6aecce91f Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 21:40:08 -0800 Subject: [PATCH 19/23] Fix collections image url mixins test --- tests/test_mixins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index 29fb776f2..f601a2f36 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -128,8 +128,8 @@ def _test_mixins_imageUrl(obj, attr): url = getattr(obj, attr + 'Url') if getattr(obj, attr): assert url.startswith(utils.SERVER_BASEURL) - assert "/library/metadata/" in url - assert attr in url + assert "/library/metadata/" in url or "/library/collections/" in url + assert attr in url or "composite" in url if attr == 'thumb': assert getattr(obj, 'posterUrl') == url else: From 88dbee7508fce27c02f2b9e06517affaaf383712 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 22:41:17 -0800 Subject: [PATCH 20/23] Fix flake8 --- plexapi/audio.py | 3 ++- plexapi/video.py | 3 ++- tests/test_audio.py | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plexapi/audio.py b/plexapi/audio.py index 6f052e31f..caecfbe7c 100644 --- a/plexapi/audio.py +++ b/plexapi/audio.py @@ -4,7 +4,8 @@ from plexapi import library, media, utils from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest -from plexapi.mixins import ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin +from plexapi.mixins import ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin +from plexapi.mixins import SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin diff --git a/plexapi/video.py b/plexapi/video.py index cc03e6738..e32deca14 100644 --- a/plexapi/video.py +++ b/plexapi/video.py @@ -5,7 +5,8 @@ from plexapi import library, media, settings, utils from plexapi.base import Playable, PlexPartialObject from plexapi.exceptions import BadRequest, NotFound -from plexapi.mixins import ArtUrlMixin, ArtMixin, BannerMixin, PosterUrlMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin +from plexapi.mixins import ArtUrlMixin, ArtMixin, BannerMixin, PosterUrlMixin, PosterMixin +from plexapi.mixins import SplitMergeMixin, UnmatchMatchMixin from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin diff --git a/tests/test_audio.py b/tests/test_audio.py index 6377c9359..d6a0d24bf 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from datetime import datetime - from . import conftest as utils from . import test_mixins From 92d10bac17bb0336acb12fc31d4318d7146ab507 Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 22:58:48 -0800 Subject: [PATCH 21/23] Add photos mixins tests --- tests/conftest.py | 5 +++++ tests/test_photo.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index ae16a5a18..059d9ffd5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -276,6 +276,11 @@ def photoalbum(photos): return photos.get("photo_album1") +@pytest.fixture() +def photo(photoalbum): + return photoalbum.photo("photo1") + + @pytest.fixture() def subtitle(): mopen = mock_open() diff --git a/tests/test_photo.py b/tests/test_photo.py index b4eadb98f..ebe85ff7a 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +from . import test_mixins + + def test_photo_Photoalbum(photoalbum): assert len(photoalbum.albums()) == 3 assert len(photoalbum.photos()) == 3 @@ -5,3 +9,14 @@ def test_photo_Photoalbum(photoalbum): assert len(cats_in_bed.photos()) == 7 a_pic = cats_in_bed.photo("photo7") assert a_pic + + +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_posterUrl(photoalbum) + + +def test_photo_Photo_mixins_tags(photo): + test_mixins.edit_tag(photo) From 031eb787112d9ba44d03cf6865092df6d2391c3b Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Sun, 14 Feb 2021 23:05:32 -0800 Subject: [PATCH 22/23] Fix photoalbum mixins image test --- tests/test_mixins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_mixins.py b/tests/test_mixins.py index f601a2f36..cb7549b6a 100644 --- a/tests/test_mixins.py +++ b/tests/test_mixins.py @@ -86,7 +86,7 @@ def _test_mixins_image(obj, attr): default_image = images[0] image = images[0] assert len(image.key) >= 10 - if not image.ratingKey.startswith(("default://", "media://", "upload://")): + if not image.ratingKey.startswith(("default://", "id://", "media://", "upload://")): assert image.provider assert len(image.ratingKey) >= 10 assert utils.is_bool(image.selected) From ee81ebcf93e25516c9b6fdbd40f9fe0a23b1075e Mon Sep 17 00:00:00 2001 From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> Date: Wed, 24 Feb 2021 09:07:40 -0800 Subject: [PATCH 23/23] Rename collection tests --- tests/test_collection.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_collection.py b/tests/test_collection.py index 38d81053e..dbb0d64fc 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -93,22 +93,22 @@ def test_Collection_items(collection): assert len(items) == 1 -def test_library_Collection_posters(collection): +def test_Collection_posters(collection): posters = collection.posters() assert posters -def test_library_Collection_art(collection): +def test_Collection_art(collection): arts = collection.arts() assert not arts # Collection has no default art -def test_library_Collection_mixins_images(collection): +def test_Collection_mixins_images(collection): test_mixins.edit_art(collection) test_mixins.edit_poster(collection) test_mixins.attr_artUrl(collection) test_mixins.attr_posterUrl(collection) -def test_library_Collection_mixins_tags(collection): +def test_Collection_mixins_tags(collection): test_mixins.edit_label(collection)