Bump plexapi from 4.9.2 to 4.11.0 (#1690)

* Bump plexapi from 4.9.2 to 4.10.1

Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.9.2 to 4.10.1.
- [Release notes](https://github.com/pkkid/python-plexapi/releases)
- [Commits](https://github.com/pkkid/python-plexapi/compare/4.9.2...4.10.1)

---
updated-dependencies:
- dependency-name: plexapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update plexapi==4.11.0

* Update requirements.txt

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>

[skip ci]
This commit is contained in:
dependabot[bot] 2022-05-18 11:24:15 -07:00 committed by GitHub
parent f1b95f5837
commit 399fd6ff91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1421 additions and 589 deletions

View file

@ -43,7 +43,7 @@ class Library(PlexObject):
if elem.attrib.get('type') == cls.TYPE:
section = cls(self._server, elem, key)
self._sectionsByID[section.key] = section
self._sectionsByTitle[section.title.lower()] = section
self._sectionsByTitle[section.title.lower().strip()] = section
def sections(self):
""" Returns a list of all media sections in this library. Library sections may be any of
@ -59,10 +59,11 @@ class Library(PlexObject):
Parameters:
title (str): Title of the section to return.
"""
if not self._sectionsByTitle or title not in self._sectionsByTitle:
normalized_title = title.lower().strip()
if not self._sectionsByTitle or normalized_title not in self._sectionsByTitle:
self._loadSections()
try:
return self._sectionsByTitle[title.lower()]
return self._sectionsByTitle[normalized_title]
except KeyError:
raise NotFound('Invalid library section: %s' % title) from None
@ -125,7 +126,7 @@ class Library(PlexObject):
def search(self, title=None, libtype=None, **kwargs):
""" Searching within a library section is much more powerful. It seems certain
attributes on the media objects can be targeted to filter this search down
a bit, but I havent found the documentation for it.
a bit, but I haven't found the documentation for it.
Example: "studio=Comedy%20Central" or "year=1999" "title=Kung Fu" all work. Other items
such as actor=<id> seem to work, but require you already know the id of the actor.
@ -396,7 +397,7 @@ class LibrarySection(PlexObject):
self.type = data.attrib.get('type')
self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt'))
self.uuid = data.attrib.get('uuid')
# Private attrs as we dont want a reload.
# Private attrs as we don't want a reload.
self._filterTypes = None
self._fieldTypes = None
self._totalViewSize = None
@ -599,12 +600,13 @@ class LibrarySection(PlexObject):
return self.fetchItem(key, title__iexact=title)
def getGuid(self, guid):
""" Returns the media item with the specified external IMDB, TMDB, or TVDB ID.
""" Returns the media item with the specified external Plex, IMDB, TMDB, or TVDB ID.
Note: Only available for the Plex Movie and Plex TV Series agents.
Parameters:
guid (str): The external guid of the item to return.
Examples: IMDB ``imdb://tt0944947``, TMDB ``tmdb://1399``, TVDB ``tvdb://121361``.
Examples: Plex ``plex://show/5d9c086c46115600200aa2fe``
IMDB ``imdb://tt0944947``, TMDB ``tmdb://1399``, TVDB ``tvdb://121361``.
Raises:
:exc:`~plexapi.exceptions.NotFound`: The guid is not found in the library.
@ -613,21 +615,32 @@ class LibrarySection(PlexObject):
.. code-block:: python
result1 = library.getGuid('imdb://tt0944947')
result2 = library.getGuid('tmdb://1399')
result3 = library.getGuid('tvdb://121361')
result1 = library.getGuid('plex://show/5d9c086c46115600200aa2fe')
result2 = library.getGuid('imdb://tt0944947')
result3 = library.getGuid('tmdb://1399')
result4 = library.getGuid('tvdb://121361')
# Alternatively, create your own guid lookup dictionary for faster performance
guidLookup = {guid.id: item for item in library.all() for guid in item.guids}
result1 = guidLookup['imdb://tt0944947']
result2 = guidLookup['tmdb://1399']
result3 = guidLookup['tvdb://121361']
guidLookup = {}
for item in library.all():
guidLookup[item.guid] = item
guidLookup.update({guid.id for guid in item.guids}}
result1 = guidLookup['plex://show/5d9c086c46115600200aa2fe']
result2 = guidLookup['imdb://tt0944947']
result4 = guidLookup['tmdb://1399']
result5 = guidLookup['tvdb://121361']
"""
try:
dummy = self.search(maxresults=1)[0]
match = dummy.matches(agent=self.agent, title=guid.replace('://', '-'))
return self.search(guid=match[0].guid)[0]
if guid.startswith('plex://'):
result = self.search(guid=guid)[0]
return result
else:
dummy = self.search(maxresults=1)[0]
match = dummy.matches(agent=self.agent, title=guid.replace('://', '-'))
return self.search(guid=match[0].guid)[0]
except IndexError:
raise NotFound("Guid '%s' is not found in the library" % guid) from None
@ -1271,7 +1284,7 @@ class LibrarySection(PlexObject):
* See :func:`~plexapi.library.LibrarySection.listOperators` to get a list of all available operators.
* See :func:`~plexapi.library.LibrarySection.listFilterChoices` to get a list of all available filter values.
The following filter fields are just some examples of the possible filters. The list is not exaustive,
The following filter fields are just some examples of the possible filters. The list is not exhaustive,
and not all filters apply to all library types.
* **actor** (:class:`~plexapi.media.MediaTag`): Search for the name of an actor.
@ -1334,7 +1347,7 @@ class LibrarySection(PlexObject):
Some filters may be prefixed by the ``libtype`` separated by a ``.`` (e.g. ``show.collection``,
``episode.title``, ``artist.style``, ``album.genre``, ``track.userRating``, etc.). This should not be
confused with the ``libtype`` parameter. If no ``libtype`` prefix is provided, then the default library
type is assumed. For example, in a TV show library ``viewCout`` is assumed to be ``show.viewCount``.
type is assumed. For example, in a TV show library ``viewCount`` is assumed to be ``show.viewCount``.
If you want to filter using episode view count then you must specify ``episode.viewCount`` explicitly.
In addition, if the filter does not exist for the default library type it will fallback to the most
specific ``libtype`` available. For example, ``show.unwatched`` does not exists so it will fallback to
@ -2236,16 +2249,61 @@ class FilteringType(PlexObject):
self.title = data.attrib.get('title')
self.type = data.attrib.get('type')
# Add additional manual sorts and fields which are available
self._librarySectionID = self._parent().key
# Add additional manual filters, sorts, and fields which are available
# but not exposed on the Plex server
self.filters += self._manualFilters()
self.sorts += self._manualSorts()
self.fields += self._manualFields()
def _manualFilters(self):
""" Manually add additional filters which are available
but not exposed on the Plex server.
"""
# Filters: (filter, type, title)
additionalFilters = [
]
if self.type == 'season':
additionalFilters.extend([
('label', 'string', 'Labels')
])
elif self.type == 'episode':
additionalFilters.extend([
('label', 'string', 'Labels')
])
elif self.type == 'artist':
additionalFilters.extend([
('label', 'string', 'Labels')
])
elif self.type == 'track':
additionalFilters.extend([
('label', 'string', 'Labels')
])
elif self.type == 'collection':
additionalFilters.extend([
('label', 'string', 'Labels')
])
manualFilters = []
for filterTag, filterType, filterTitle in additionalFilters:
filterKey = '/library/sections/%s/%s?type=%s' % (
self._librarySectionID, filterTag, utils.searchType(self.type)
)
filterXML = (
'<Filter filter="%s" filterType="%s" key="%s" title="%s" type="filter" />'
% (filterTag, filterType, filterKey, filterTitle)
)
manualFilters.append(self._manuallyLoadXML(filterXML, FilteringFilter))
return manualFilters
def _manualSorts(self):
""" Manually add additional sorts which are available
but not exposed on the Plex server.
"""
# Sorts: key, dir, title
# Sorts: (key, dir, title)
additionalSorts = [
('guid', 'asc', 'Guid'),
('id', 'asc', 'Rating Key'),
@ -2275,8 +2333,10 @@ class FilteringType(PlexObject):
manualSorts = []
for sortField, sortDir, sortTitle in additionalSorts:
sortXML = ('<Sort defaultDirection="%s" descKey="%s:desc" key="%s" title="%s" />'
% (sortDir, sortField, sortField, sortTitle))
sortXML = (
'<Sort defaultDirection="%s" descKey="%s:desc" key="%s" title="%s" />'
% (sortDir, sortField, sortField, sortTitle)
)
manualSorts.append(self._manuallyLoadXML(sortXML, FilteringSort))
return manualSorts
@ -2285,7 +2345,7 @@ class FilteringType(PlexObject):
""" Manually add additional fields which are available
but not exposed on the Plex server.
"""
# Fields: key, type, title
# Fields: (key, type, title)
additionalFields = [
('guid', 'string', 'Guid'),
('id', 'integer', 'Rating Key'),
@ -2311,31 +2371,41 @@ class FilteringType(PlexObject):
additionalFields.extend([
('addedAt', 'date', 'Date Season Added'),
('unviewedLeafCount', 'integer', 'Episode Unplayed Count'),
('year', 'integer', 'Season Year')
('year', 'integer', 'Season Year'),
('label', 'tag', 'Label')
])
elif self.type == 'episode':
additionalFields.extend([
('audienceRating', 'integer', 'Audience Rating'),
('duration', 'integer', 'Duration'),
('rating', 'integer', 'Critic Rating'),
('viewOffset', 'integer', 'View Offset')
('viewOffset', 'integer', 'View Offset'),
('label', 'tag', 'Label')
])
elif self.type == 'artist':
additionalFields.extend([
('label', 'tag', 'Label')
])
elif self.type == 'track':
additionalFields.extend([
('duration', 'integer', 'Duration'),
('viewOffset', 'integer', 'View Offset')
('viewOffset', 'integer', 'View Offset'),
('label', 'tag', 'Label')
])
elif self.type == 'collection':
additionalFields.extend([
('addedAt', 'date', 'Date Added')
('addedAt', 'date', 'Date Added'),
('label', 'tag', 'Label')
])
prefix = '' if self.type == 'movie' else self.type + '.'
manualFields = []
for field, fieldType, fieldTitle in additionalFields:
fieldXML = ('<Field key="%s%s" title="%s" type="%s"/>'
% (prefix, field, fieldTitle, fieldType))
fieldXML = (
'<Field key="%s%s" title="%s" type="%s"/>'
% (prefix, field, fieldTitle, fieldType)
)
manualFields.append(self._manuallyLoadXML(fieldXML, FilteringField))
return manualFields