Skip to content

Commit c9b2c36

Browse files
authored
Merge pull request #648 from JonnyWong16/feature/posters
Update poster and art and move to a mixin
2 parents 3418de2 + 1f2c0e8 commit c9b2c36

File tree

10 files changed

+160
-146
lines changed

10 files changed

+160
-146
lines changed

plexapi/audio.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from plexapi import library, media, utils
55
from plexapi.base import Playable, PlexPartialObject
66
from plexapi.exceptions import BadRequest
7-
from plexapi.mixins import SplitMergeMixin, UnmatchMatchMixin
7+
from plexapi.mixins import ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin
88
from plexapi.mixins import CollectionMixin, CountryMixin, GenreMixin, LabelMixin, MoodMixin, SimilarArtistMixin, StyleMixin
99

1010

@@ -125,7 +125,7 @@ def sync(self, bitrate, client=None, clientId=None, limit=None, title=None):
125125

126126

127127
@utils.registerPlexObject
128-
class Artist(Audio, SplitMergeMixin, UnmatchMatchMixin,
128+
class Artist(Audio, ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin,
129129
CollectionMixin, CountryMixin, GenreMixin, MoodMixin, SimilarArtistMixin, StyleMixin):
130130
""" Represents a single Artist.
131131
@@ -229,7 +229,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs):
229229

230230

231231
@utils.registerPlexObject
232-
class Album(Audio, UnmatchMatchMixin,
232+
class Album(Audio, ArtMixin, PosterMixin, UnmatchMatchMixin,
233233
CollectionMixin, GenreMixin, LabelMixin, MoodMixin, StyleMixin):
234234
""" Represents a single Album.
235235

plexapi/base.py

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -513,53 +513,6 @@ def history(self, maxresults=9999999, mindate=None):
513513
"""
514514
return self._server.history(maxresults=maxresults, mindate=mindate, ratingKey=self.ratingKey)
515515

516-
def posters(self):
517-
""" Returns list of available poster objects. :class:`~plexapi.media.Poster`. """
518-
519-
return self.fetchItems('%s/posters' % self.key)
520-
521-
def uploadPoster(self, url=None, filepath=None):
522-
""" Upload poster from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
523-
if url:
524-
key = '%s/posters?url=%s' % (self.key, quote_plus(url))
525-
self._server.query(key, method=self._server._session.post)
526-
elif filepath:
527-
key = '%s/posters?' % self.key
528-
data = open(filepath, 'rb').read()
529-
self._server.query(key, method=self._server._session.post, data=data)
530-
531-
def setPoster(self, poster):
532-
""" Set . :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
533-
poster.select()
534-
535-
def arts(self):
536-
""" Returns list of available art objects. :class:`~plexapi.media.Poster`. """
537-
538-
return self.fetchItems('%s/arts' % self.key)
539-
540-
def uploadArt(self, url=None, filepath=None):
541-
""" Upload art from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
542-
if url:
543-
key = '/library/metadata/%s/arts?url=%s' % (self.ratingKey, quote_plus(url))
544-
self._server.query(key, method=self._server._session.post)
545-
elif filepath:
546-
key = '/library/metadata/%s/arts?' % self.ratingKey
547-
data = open(filepath, 'rb').read()
548-
self._server.query(key, method=self._server._session.post, data=data)
549-
550-
def setArt(self, art):
551-
""" Set :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
552-
art.select()
553-
554-
# The photo tag cant be built atm. TODO
555-
# def arts(self):
556-
# part = '%s/arts' % self.key
557-
# return self.fetchItem(part)
558-
559-
# def poster(self):
560-
# part = '%s/posters' % self.key
561-
# return self.fetchItem(part, etag='Photo')
562-
563516

564517
class Playable(object):
565518
""" This is a general place to store functions specific to media that is Playable.

plexapi/library.py

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from plexapi import X_PLEX_CONTAINER_SIZE, log, media, utils
55
from plexapi.base import OPERATORS, PlexObject, PlexPartialObject
66
from plexapi.exceptions import BadRequest, NotFound
7+
from plexapi.mixins import ArtMixin, PosterMixin
78
from plexapi.mixins import LabelMixin
89
from plexapi.settings import Setting
910
from plexapi.utils import deprecated
@@ -1527,7 +1528,7 @@ def _loadData(self, data):
15271528

15281529

15291530
@utils.registerPlexObject
1530-
class Collections(PlexPartialObject, LabelMixin):
1531+
class Collections(PlexPartialObject, ArtMixin, PosterMixin, LabelMixin):
15311532
""" Represents a single Collection.
15321533
15331534
Attributes:
@@ -1684,44 +1685,6 @@ def sortUpdate(self, sort=None):
16841685
part = '/library/metadata/%s/prefs?collectionSort=%s' % (self.ratingKey, key)
16851686
return self._server.query(part, method=self._server._session.put)
16861687

1687-
def posters(self):
1688-
""" Returns list of available poster objects. :class:`~plexapi.media.Poster`. """
1689-
1690-
return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey)
1691-
1692-
def uploadPoster(self, url=None, filepath=None):
1693-
""" Upload poster from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
1694-
if url:
1695-
key = '/library/metadata/%s/posters?url=%s' % (self.ratingKey, quote_plus(url))
1696-
self._server.query(key, method=self._server._session.post)
1697-
elif filepath:
1698-
key = '/library/metadata/%s/posters?' % self.ratingKey
1699-
data = open(filepath, 'rb').read()
1700-
self._server.query(key, method=self._server._session.post, data=data)
1701-
1702-
def setPoster(self, poster):
1703-
""" Set . :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
1704-
poster.select()
1705-
1706-
def arts(self):
1707-
""" Returns list of available art objects. :class:`~plexapi.media.Poster`. """
1708-
1709-
return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey)
1710-
1711-
def uploadArt(self, url=None, filepath=None):
1712-
""" Upload art from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
1713-
if url:
1714-
key = '/library/metadata/%s/arts?url=%s' % (self.ratingKey, quote_plus(url))
1715-
self._server.query(key, method=self._server._session.post)
1716-
elif filepath:
1717-
key = '/library/metadata/%s/arts?' % self.ratingKey
1718-
data = open(filepath, 'rb').read()
1719-
self._server.query(key, method=self._server._session.post, data=data)
1720-
1721-
def setArt(self, art):
1722-
""" Set :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
1723-
art.select()
1724-
17251688
# def edit(self, **kwargs):
17261689
# TODO
17271690

plexapi/media.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -807,20 +807,25 @@ class Style(MediaTag):
807807
FILTER = 'style'
808808

809809

810-
@utils.registerPlexObject
811-
class Poster(PlexObject):
812-
""" Represents a Poster.
810+
class BasePosterArt(PlexObject):
811+
""" Base class for all Poster and Art objects.
813812
814813
Attributes:
815814
TAG (str): 'Photo'
815+
key (str): API URL (/library/metadata/<ratingkey>).
816+
provider (str): The source of the poster or art.
817+
ratingKey (str): Unique key identifying the poster or art.
818+
selected (bool): True if the poster or art is currently selected.
819+
thumb (str): The URL to retrieve the poster or art thumbnail.
816820
"""
817821
TAG = 'Photo'
818822

819823
def _loadData(self, data):
820824
self._data = data
821825
self.key = data.attrib.get('key')
826+
self.provider = data.attrib.get('provider')
822827
self.ratingKey = data.attrib.get('ratingKey')
823-
self.selected = data.attrib.get('selected')
828+
self.selected = cast(bool, data.attrib.get('selected'))
824829
self.thumb = data.attrib.get('thumb')
825830

826831
def select(self):
@@ -832,6 +837,14 @@ def select(self):
832837
pass
833838

834839

840+
class Poster(BasePosterArt):
841+
""" Represents a single Poster object. """
842+
843+
844+
class Art(BasePosterArt):
845+
""" Represents a single Art object. """
846+
847+
835848
@utils.registerPlexObject
836849
class Producer(MediaTag):
837850
""" Represents a single Producer media tag.

plexapi/mixins.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,72 @@
11
# -*- coding: utf-8 -*-
2-
from urllib.parse import urlencode
2+
from urllib.parse import quote_plus, urlencode
33

4-
from plexapi import utils
4+
from plexapi import media, utils
55
from plexapi.exceptions import NotFound
66

77

8+
class ArtMixin(object):
9+
""" Mixin for Plex objects that can have artwork."""
10+
11+
def arts(self):
12+
""" Returns list of available :class:`~plexapi.media.Art` objects. """
13+
return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey, cls=media.Art)
14+
15+
def uploadArt(self, url=None, filepath=None):
16+
""" Upload art from url or filepath and set it as the selected art.
17+
18+
Parameters:
19+
url (str): The full URL to the image to upload.
20+
filepath (str): The full file path the the image to upload.
21+
"""
22+
if url:
23+
key = '/library/metadata/%s/arts?url=%s' % (self.ratingKey, quote_plus(url))
24+
self._server.query(key, method=self._server._session.post)
25+
elif filepath:
26+
key = '/library/metadata/%s/arts?' % self.ratingKey
27+
data = open(filepath, 'rb').read()
28+
self._server.query(key, method=self._server._session.post, data=data)
29+
30+
def setArt(self, art):
31+
""" Set the artwork for a Plex object.
32+
33+
Parameters:
34+
art (:class:`~plexapi.media.Art`): The art object to select.
35+
"""
36+
art.select()
37+
38+
39+
class PosterMixin(object):
40+
""" Mixin for Plex objects that can have posters."""
41+
42+
def posters(self):
43+
""" Returns list of available :class:`~plexapi.media.Poster` objects. """
44+
return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey, cls=media.Poster)
45+
46+
def uploadPoster(self, url=None, filepath=None):
47+
""" Upload poster from url or filepath and set it as the selected poster.
48+
49+
Parameters:
50+
url (str): The full URL to the image to upload.
51+
filepath (str): The full file path the the image to upload.
52+
"""
53+
if url:
54+
key = '/library/metadata/%s/posters?url=%s' % (self.ratingKey, quote_plus(url))
55+
self._server.query(key, method=self._server._session.post)
56+
elif filepath:
57+
key = '/library/metadata/%s/posters?' % self.ratingKey
58+
data = open(filepath, 'rb').read()
59+
self._server.query(key, method=self._server._session.post, data=data)
60+
61+
def setPoster(self, poster):
62+
""" Set the poster for a Plex object.
63+
64+
Parameters:
65+
poster (:class:`~plexapi.media.Poster`): The poster object to select.
66+
"""
67+
poster.select()
68+
69+
870
class SplitMergeMixin(object):
971
""" Mixin for Plex objects that can be split and merged."""
1072

@@ -319,7 +381,7 @@ def removeTag(self, tags):
319381
self._edit_tags('tag', tags, remove=True)
320382

321383

322-
class EditWriter(object):
384+
class WriterMixin(object):
323385
""" Mixin for Plex objects that can have writers. """
324386

325387
def addWriter(self, writers):

plexapi/playlist.py

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
from plexapi.base import Playable, PlexPartialObject
66
from plexapi.exceptions import BadRequest, NotFound, Unsupported
77
from plexapi.library import LibrarySection
8+
from plexapi.mixins import ArtMixin, PosterMixin
89
from plexapi.playqueue import PlayQueue
910
from plexapi.utils import cast, toDatetime
1011

1112

1213
@utils.registerPlexObject
13-
class Playlist(PlexPartialObject, Playable):
14+
class Playlist(PlexPartialObject, Playable, ArtMixin, PosterMixin):
1415
""" Represents a single Playlist.
1516
1617
Attributes:
@@ -311,41 +312,3 @@ def sync(self, videoQuality=None, photoResolution=None, audioBitrate=None, clien
311312
raise Unsupported('Unsupported playlist content')
312313

313314
return myplex.sync(sync_item, client=client, clientId=clientId)
314-
315-
def posters(self):
316-
""" Returns list of available poster objects. :class:`~plexapi.media.Poster`. """
317-
318-
return self.fetchItems('/library/metadata/%s/posters' % self.ratingKey)
319-
320-
def uploadPoster(self, url=None, filepath=None):
321-
""" Upload poster from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
322-
if url:
323-
key = '/library/metadata/%s/posters?url=%s' % (self.ratingKey, quote_plus(url))
324-
self._server.query(key, method=self._server._session.post)
325-
elif filepath:
326-
key = '/library/metadata/%s/posters?' % self.ratingKey
327-
data = open(filepath, 'rb').read()
328-
self._server.query(key, method=self._server._session.post, data=data)
329-
330-
def setPoster(self, poster):
331-
""" Set . :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
332-
poster.select()
333-
334-
def arts(self):
335-
""" Returns list of available art objects. :class:`~plexapi.media.Poster`. """
336-
337-
return self.fetchItems('/library/metadata/%s/arts' % self.ratingKey)
338-
339-
def uploadArt(self, url=None, filepath=None):
340-
""" Upload art from url or filepath. :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video`. """
341-
if url:
342-
key = '/library/metadata/%s/arts?url=%s' % (self.ratingKey, quote_plus(url))
343-
self._server.query(key, method=self._server._session.post)
344-
elif filepath:
345-
key = '/library/metadata/%s/arts?' % self.ratingKey
346-
data = open(filepath, 'rb').read()
347-
self._server.query(key, method=self._server._session.post, data=data)
348-
349-
def setArt(self, art):
350-
""" Set :class:`~plexapi.media.Poster` to :class:`~plexapi.video.Video` """
351-
art.select()

plexapi/video.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from plexapi import library, media, settings, utils
66
from plexapi.base import Playable, PlexPartialObject
77
from plexapi.exceptions import BadRequest, NotFound
8-
from plexapi.mixins import SplitMergeMixin, UnmatchMatchMixin
9-
from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, EditWriter
8+
from plexapi.mixins import ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin
9+
from plexapi.mixins import CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin
1010

1111

1212
class Video(PlexPartialObject):
@@ -261,8 +261,8 @@ def sync(self, videoQuality, client=None, clientId=None, limit=None, unwatched=F
261261

262262

263263
@utils.registerPlexObject
264-
class Movie(Playable, Video, SplitMergeMixin, UnmatchMatchMixin,
265-
CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, EditWriter):
264+
class Movie(Video, Playable, ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin,
265+
CollectionMixin, CountryMixin, DirectorMixin, GenreMixin, LabelMixin, ProducerMixin, WriterMixin):
266266
""" Represents a single Movie.
267267
268268
Attributes:
@@ -388,7 +388,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs):
388388

389389

390390
@utils.registerPlexObject
391-
class Show(Video, SplitMergeMixin, UnmatchMatchMixin,
391+
class Show(Video, ArtMixin, PosterMixin, SplitMergeMixin, UnmatchMatchMixin,
392392
CollectionMixin, GenreMixin, LabelMixin):
393393
""" Represents a single Show (including all seasons and episodes).
394394
@@ -587,7 +587,7 @@ def download(self, savepath=None, keep_original_name=False, **kwargs):
587587

588588

589589
@utils.registerPlexObject
590-
class Season(Video):
590+
class Season(Video, ArtMixin, PosterMixin):
591591
""" Represents a single Show Season (including all episodes).
592592
593593
Attributes:
@@ -713,7 +713,8 @@ def _defaultSyncTitle(self):
713713

714714

715715
@utils.registerPlexObject
716-
class Episode(Playable, Video, DirectorMixin, EditWriter):
716+
class Episode(Video, Playable, ArtMixin, PosterMixin,
717+
DirectorMixin, WriterMixin):
717718
""" Represents a single Shows Episode.
718719
719720
Attributes:
@@ -850,7 +851,7 @@ def _defaultSyncTitle(self):
850851

851852

852853
@utils.registerPlexObject
853-
class Clip(Playable, Video):
854+
class Clip(Video, Playable):
854855
"""Represents a single Clip.
855856
856857
Attributes:

0 commit comments

Comments
 (0)