python-plexapi/plexapi/photo.py

170 lines
7.6 KiB
Python
Raw Normal View History

2016-04-10 03:59:47 +00:00
# -*- coding: utf-8 -*-
from plexapi import media, utils
from plexapi.base import PlexPartialObject
from plexapi.exceptions import NotFound, BadRequest
2018-09-08 15:25:16 +00:00
from plexapi.compat import quote_plus
2016-04-10 03:59:47 +00:00
@utils.registerPlexObject
2016-04-10 03:59:47 +00:00
class Photoalbum(PlexPartialObject):
""" Represents a photoalbum (collection of photos).
Attributes:
TAG (str): 'Directory'
TYPE (str): 'photo'
addedAt (datetime): Datetime this item was added to the library.
art (str): Photo art (/library/metadata/<ratingkey>/art/<artid>)
composite (str): Unknown
guid (str): Unknown (unique ID)
index (sting): Index number of this album.
key (str): API URL (/library/metadata/<ratingkey>).
librarySectionID (int): :class:`~plexapi.library.LibrarySection` ID.
listType (str): Hardcoded as 'photo' (useful for search filters).
ratingKey (int): Unique key identifying this item.
summary (str): Summary of the photoalbum.
thumb (str): URL to thumbnail image.
title (str): Photoalbum title. (Trip to Disney World)
type (str): Unknown
updatedAt (datatime): Datetime this item was updated.
2017-01-02 21:06:40 +00:00
"""
TAG = 'Directory'
TYPE = 'photo'
2016-04-10 03:59:47 +00:00
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self.listType = 'photo'
2017-02-04 17:43:50 +00:00
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
self.art = data.attrib.get('art')
self.composite = data.attrib.get('composite')
self.guid = data.attrib.get('guid')
self.index = utils.cast(int, data.attrib.get('index'))
self.key = data.attrib.get('key')
self.librarySectionID = data.attrib.get('librarySectionID')
self.ratingKey = data.attrib.get('ratingKey')
self.summary = data.attrib.get('summary')
self.thumb = data.attrib.get('thumb')
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt'))
2017-01-02 21:06:40 +00:00
def albums(self, **kwargs):
""" Returns a list of :class:`~plexapi.photo.Photoalbum` objects in this album. """
key = '/library/metadata/%s/children' % self.ratingKey
return self.fetchItems(key, etag='Directory', **kwargs)
def album(self, title):
""" Returns the :class:`~plexapi.photo.Photoalbum` that matches the specified title. """
for album in self.albums():
if album.title.lower() == title.lower():
return album
raise NotFound('Unable to find album: %s' % title)
2017-02-09 04:08:25 +00:00
def photos(self, **kwargs):
""" Returns a list of :class:`~plexapi.photo.Photo` objects in this album. """
key = '/library/metadata/%s/children' % self.ratingKey
return self.fetchItems(key, etag='Photo', **kwargs)
2016-04-10 03:59:47 +00:00
def photo(self, title):
""" Returns the :class:`~plexapi.photo.Photo` that matches the specified title. """
for photo in self.photos():
if photo.title.lower() == title.lower():
return photo
raise NotFound('Unable to find photo: %s' % title)
2017-01-02 21:06:40 +00:00
2016-04-10 03:59:47 +00:00
@utils.registerPlexObject
2016-04-10 03:59:47 +00:00
class Photo(PlexPartialObject):
""" Represents a single photo.
Attributes:
TAG (str): 'Photo'
TYPE (str): 'photo'
addedAt (datetime): Datetime this item was added to the library.
index (sting): Index number of this photo.
key (str): API URL (/library/metadata/<ratingkey>).
listType (str): Hardcoded as 'photo' (useful for search filters).
media (TYPE): Unknown
originallyAvailableAt (datetime): Datetime this photo was added to Plex.
parentKey (str): Photoalbum API URL.
parentRatingKey (int): Unique key identifying the photoalbum.
ratingKey (int): Unique key identifying this item.
summary (str): Summary of the photo.
thumb (str): URL to thumbnail image.
title (str): Photo title.
type (str): Unknown
updatedAt (datatime): Datetime this item was updated.
year (int): Year this photo was taken.
2017-01-02 21:06:40 +00:00
"""
TAG = 'Photo'
2016-04-10 03:59:47 +00:00
TYPE = 'photo'
2018-09-08 15:25:16 +00:00
METADATA_TYPE = 'photo'
2016-04-10 03:59:47 +00:00
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self.listType = 'photo'
2017-02-04 17:43:50 +00:00
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
self.index = utils.cast(int, data.attrib.get('index'))
self.key = data.attrib.get('key')
2017-01-02 21:06:40 +00:00
self.originallyAvailableAt = utils.toDatetime(
2017-02-04 17:43:50 +00:00
data.attrib.get('originallyAvailableAt'), '%Y-%m-%d')
self.parentKey = data.attrib.get('parentKey')
self.parentRatingKey = data.attrib.get('parentRatingKey')
self.ratingKey = data.attrib.get('ratingKey')
self.summary = data.attrib.get('summary')
self.thumb = data.attrib.get('thumb')
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt'))
self.year = utils.cast(int, data.attrib.get('year'))
self.media = self.findItems(data, media.Media)
2019-11-11 14:33:56 +00:00
self.tag = self.findItems(data, media.Tag)
2017-01-02 21:06:40 +00:00
2016-04-10 03:59:47 +00:00
def photoalbum(self):
""" Return this photo's :class:`~plexapi.photo.Photoalbum`. """
return self.fetchItem(self.parentKey)
def section(self):
""" Returns the :class:`~plexapi.library.LibrarySection` this item belongs to. """
if hasattr(self, 'librarySectionID'):
return self._server.library.sectionByID(self.librarySectionID)
elif self.parentKey:
return self._server.library.sectionByID(self.photoalbum().librarySectionID)
else:
raise BadRequest('Unable to get section for photo, can`t find librarySectionID')
2018-09-08 15:25:16 +00:00
def sync(self, resolution, client=None, clientId=None, limit=None, title=None):
""" Add current photo as sync item for specified device.
See :func:`plexapi.myplex.MyPlexAccount.sync()` for possible exceptions.
Parameters:
resolution (str): maximum allowed resolution for synchronized photos, see PHOTO_QUALITY_* values in the
module :mod:`plexapi.sync`.
client (:class:`plexapi.myplex.MyPlexDevice`): sync destination, see
:func:`plexapi.myplex.MyPlexAccount.sync`.
clientId (str): sync destination, see :func:`plexapi.myplex.MyPlexAccount.sync`.
limit (int): maximum count of items to sync, unlimited if `None`.
title (str): descriptive title for the new :class:`plexapi.sync.SyncItem`, if empty the value would be
generated from metadata of current photo.
Returns:
:class:`plexapi.sync.SyncItem`: an instance of created syncItem.
"""
from plexapi.sync import SyncItem, Policy, MediaSettings
myplex = self._server.myPlexAccount()
sync_item = SyncItem(self._server, None)
sync_item.title = title if title else self.title
sync_item.rootTitle = self.title
sync_item.contentType = self.listType
sync_item.metadataType = self.METADATA_TYPE
sync_item.machineIdentifier = self._server.machineIdentifier
Improvements in tests process (#297) * lets begin * skip plexpass tests if there is not plexpass on account * test new myplex attrubutes * bootstrap: proper photos organisation * fix rest of photos tests * fix myplex new attributes test * fix music bootstrap by setting agent to lastfm * fix sync tests * increase bootstrap timeout * remove timeout from .travis.yml * do not create playlist-style photoalbums in plex-bootstraptest.py * allow negative filtering in LibrarySection.search() * fix sync tests once again * use sendCrashReports in test_settings * fix test_settings * fix test_video * do not accept eula in bootstrap * fix PlexServer.isLatest() * add test against old version of PlexServer * fix MyPlexAccount.OutOut * add flag for one-time testing in Travis * fix test_library onDeck tests * fix more tests * use tqdm in plex-bootstraptest for media scanning progress * create sections one-by-one * update docs on AlertListener for timeline entries * fix plex-bootstraptest for server version 1.3.2 * display skip/xpass/xfail reasons * fix tests on 1.3 * wait for music to be fully processed in plex-bootstraptest * fix misplaced TEST_ACCOUNT_ONCE * fix test_myplex_users, not sure if in proper-way * add pytest-rerunfailures; mark test_myplex_optout as flaky * fix comment * Revert "add pytest-rerunfailures; mark test_myplex_optout as flaky" This reverts commit 580e4c95a758c92329d757eb2f3fc3bf44b26f09. * restart plex container on failure * add conftest.wait_until() and used where some retries are required * add more wait_until() usage in test_sync * fix managed user search * fix updating managed users in myplex * allow to add new servers to existent users * add new server to a shared user while bootstrapping * add some docs on testing process * perform few attemps when unable to get the claim token * unlock websocket-client in requirements_dev * fix docblock in tools/plex-teardowntest * do not hardcode mediapart size in test_video * remove cache:pip from travis * Revert "unlock websocket-client in requirements_dev" This reverts commit 0d536bd06dbdc4a4b869a1686f8cd008898859fe. * remove debug from server.py * improve webhook tests * fix type() check to isinstance() * remove excessive `else` branch due to Hellowlol advice * add `unknown` as allowed `myPlexMappingState` in test_server
2018-09-14 18:03:23 +00:00
section = self.section()
2018-09-08 15:25:16 +00:00
sync_item.location = 'library://%s/item/%s' % (section.uuid, quote_plus(self.key))
sync_item.policy = Policy.create(limit)
sync_item.mediaSettings = MediaSettings.createPhoto(resolution)
return myplex.sync(sync_item, client=client, clientId=clientId)