mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-25 05:00:22 +00:00
Add search kwargs to LibrarySection.get()
(#1191)
* Optimize methods to use library get/search * Revert methods * Fix reloading of episodes for missing parentRatingKey * Add tests for LibrarySection.get with kwargs
This commit is contained in:
parent
58e279b837
commit
8298a61b64
5 changed files with 52 additions and 36 deletions
|
@ -4,7 +4,7 @@ from urllib.parse import quote_plus
|
|||
|
||||
from plexapi import media, utils
|
||||
from plexapi.base import Playable, PlexPartialObject, PlexHistory, PlexSession
|
||||
from plexapi.exceptions import BadRequest, NotFound
|
||||
from plexapi.exceptions import BadRequest
|
||||
from plexapi.mixins import (
|
||||
AdvancedSettingsMixin, SplitMergeMixin, UnmatchMatchMixin, ExtrasMixin, HubsMixin, PlayedUnplayedMixin, RatingMixin,
|
||||
ArtUrlMixin, ArtMixin, PosterUrlMixin, PosterMixin, ThemeMixin, ThemeUrlMixin,
|
||||
|
@ -178,14 +178,19 @@ class Artist(
|
|||
Parameters:
|
||||
title (str): Title of the album to return.
|
||||
"""
|
||||
try:
|
||||
return self.section().search(title, libtype='album', filters={'artist.id': self.ratingKey})[0]
|
||||
except IndexError:
|
||||
raise NotFound(f"Unable to find album '{title}'") from None
|
||||
return self.section().get(
|
||||
title=title,
|
||||
libtype='album',
|
||||
filters={'artist.id': self.ratingKey}
|
||||
)
|
||||
|
||||
def albums(self, **kwargs):
|
||||
""" Returns a list of :class:`~plexapi.audio.Album` objects by the artist. """
|
||||
return self.section().search(libtype='album', filters={'artist.id': self.ratingKey}, **kwargs)
|
||||
return self.section().search(
|
||||
libtype='album',
|
||||
filters={'artist.id': self.ratingKey},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def track(self, title=None, album=None, track=None):
|
||||
""" Returns the :class:`~plexapi.audio.Track` that matches the specified title.
|
||||
|
@ -430,18 +435,6 @@ class Track(
|
|||
self.viewOffset = utils.cast(int, data.attrib.get('viewOffset', 0))
|
||||
self.year = utils.cast(int, data.attrib.get('year'))
|
||||
|
||||
def _prettyfilename(self):
|
||||
""" Returns a filename for use in download. """
|
||||
return f'{self.grandparentTitle} - {self.parentTitle} - {str(self.trackNumber).zfill(2)} - {self.title}'
|
||||
|
||||
def album(self):
|
||||
""" Return the track's :class:`~plexapi.audio.Album`. """
|
||||
return self.fetchItem(self.parentKey)
|
||||
|
||||
def artist(self):
|
||||
""" Return the track's :class:`~plexapi.audio.Artist`. """
|
||||
return self.fetchItem(self.grandparentKey)
|
||||
|
||||
@property
|
||||
def locations(self):
|
||||
""" This does not exist in plex xml response but is added to have a common
|
||||
|
@ -457,6 +450,18 @@ class Track(
|
|||
""" Returns the track number. """
|
||||
return self.index
|
||||
|
||||
def _prettyfilename(self):
|
||||
""" Returns a filename for use in download. """
|
||||
return f'{self.grandparentTitle} - {self.parentTitle} - {str(self.trackNumber).zfill(2)} - {self.title}'
|
||||
|
||||
def album(self):
|
||||
""" Return the track's :class:`~plexapi.audio.Album`. """
|
||||
return self.fetchItem(self.parentKey)
|
||||
|
||||
def artist(self):
|
||||
""" Return the track's :class:`~plexapi.audio.Artist`. """
|
||||
return self.fetchItem(self.grandparentKey)
|
||||
|
||||
def _defaultSyncTitle(self):
|
||||
""" Returns str, default title for a new syncItem. """
|
||||
return f'{self.grandparentTitle} - {self.parentTitle} - {self.title}'
|
||||
|
|
|
@ -588,19 +588,24 @@ class LibrarySection(PlexObject):
|
|||
raise BadRequest('You are unable to remove all locations from a library.')
|
||||
return self.edit(location=locations)
|
||||
|
||||
def get(self, title):
|
||||
""" Returns the media item with the specified title.
|
||||
def get(self, title, **kwargs):
|
||||
""" Returns the media item with the specified title and kwargs.
|
||||
|
||||
Parameters:
|
||||
title (str): Title of the item to return.
|
||||
kwargs (dict): Additional search parameters.
|
||||
See :func:`~plexapi.library.LibrarySection.search` for more info.
|
||||
|
||||
Raises:
|
||||
:exc:`~plexapi.exceptions.NotFound`: The title is not found in the library.
|
||||
"""
|
||||
try:
|
||||
return self.search(title)[0]
|
||||
return self.search(title, limit=1, **kwargs)[0]
|
||||
except IndexError:
|
||||
raise NotFound(f"Unable to find item '{title}'") from None
|
||||
msg = f"Unable to find item with title '{title}'"
|
||||
if kwargs:
|
||||
msg += f" and kwargs {kwargs}"
|
||||
raise NotFound(msg) from None
|
||||
|
||||
def getGuid(self, guid):
|
||||
""" Returns the media item with the specified external Plex, IMDB, TMDB, or TVDB ID.
|
||||
|
|
|
@ -326,6 +326,7 @@ class Movie(
|
|||
duration (int): Duration of the movie in milliseconds.
|
||||
editionTitle (str): The edition title of the movie (e.g. Director's Cut, Extended Edition, etc.).
|
||||
enableCreditsMarkerGeneration (int): Setting that indicates if credits markers detection is enabled.
|
||||
(-1 = Library default, 0 = Disabled)
|
||||
genres (List<:class:`~plexapi.media.Genre`>): List of genre objects.
|
||||
guids (List<:class:`~plexapi.media.Guid`>): List of guid objects.
|
||||
labels (List<:class:`~plexapi.media.Label`>): List of label objects.
|
||||
|
@ -473,6 +474,7 @@ class Show(
|
|||
contentRating (str) Content rating (PG-13; NR; TV-G).
|
||||
duration (int): Typical duration of the show episodes in milliseconds.
|
||||
enableCreditsMarkerGeneration (int): Setting that indicates if credits markers detection is enabled.
|
||||
(-1 = Library default, 0 = Disabled).
|
||||
episodeSort (int): Setting that indicates how episodes are sorted for the show
|
||||
(-1 = Library default, 0 = Oldest first, 1 = Newest first).
|
||||
flattenSeasons (int): Setting that indicates if seasons are set to hidden for the show
|
||||
|
@ -494,7 +496,8 @@ class Show(
|
|||
roles (List<:class:`~plexapi.media.Role`>): List of role objects.
|
||||
seasonCount (int): Number of seasons (excluding Specials) in the show.
|
||||
showOrdering (str): Setting that indicates the episode ordering for the show
|
||||
(None = Library default).
|
||||
(None = Library default, tmdbAiring = The Movie Database (Aired),
|
||||
aired = TheTVDB (Aired), dvd = TheTVDB (DVD), absolute = TheTVDB (Absolute)).
|
||||
similar (List<:class:`~plexapi.media.Similar`>): List of Similar objects.
|
||||
studio (str): Studio that created show (Di Bonaventura Pictures; 21 Laps Entertainment).
|
||||
subtitleLanguage (str): Setting that indicates the preferred subtitle language.
|
||||
|
@ -738,10 +741,12 @@ class Season(
|
|||
""" Returns the season number. """
|
||||
return self.index
|
||||
|
||||
def episodes(self, **kwargs):
|
||||
""" Returns a list of :class:`~plexapi.video.Episode` objects in the season. """
|
||||
key = f'{self.key}/children'
|
||||
return self.fetchItems(key, Episode, **kwargs)
|
||||
def onDeck(self):
|
||||
""" Returns season's On Deck :class:`~plexapi.video.Video` object or `None`.
|
||||
Will only return a match if the show's On Deck episode is in this season.
|
||||
"""
|
||||
data = self._server.query(self._details_key)
|
||||
return next(iter(self.findItems(data, rtag='OnDeck')), None)
|
||||
|
||||
def episode(self, title=None, episode=None):
|
||||
""" Returns the episode with the given title or number.
|
||||
|
@ -764,17 +769,15 @@ class Season(
|
|||
return self.fetchItem(key, Episode, parentIndex=self.index, index=index)
|
||||
raise BadRequest('Missing argument: title or episode is required')
|
||||
|
||||
def episodes(self, **kwargs):
|
||||
""" Returns a list of :class:`~plexapi.video.Episode` objects in the season. """
|
||||
key = f'{self.key}/children'
|
||||
return self.fetchItems(key, Episode, **kwargs)
|
||||
|
||||
def get(self, title=None, episode=None):
|
||||
""" Alias to :func:`~plexapi.video.Season.episode`. """
|
||||
return self.episode(title, episode)
|
||||
|
||||
def onDeck(self):
|
||||
""" Returns season's On Deck :class:`~plexapi.video.Video` object or `None`.
|
||||
Will only return a match if the show's On Deck episode is in this season.
|
||||
"""
|
||||
data = self._server.query(self._details_key)
|
||||
return next(iter(self.findItems(data, rtag='OnDeck')), None)
|
||||
|
||||
def show(self):
|
||||
""" Return the season's :class:`~plexapi.video.Show`. """
|
||||
return self.fetchItem(self.parentKey)
|
||||
|
@ -903,7 +906,7 @@ class Episode(
|
|||
|
||||
# If seasons are hidden, parentKey and parentRatingKey are missing from the XML response.
|
||||
# https://forums.plex.tv/t/parentratingkey-not-in-episode-xml-when-seasons-are-hidden/300553
|
||||
if self.skipParent and not self.parentRatingKey:
|
||||
if self.skipParent and data.attrib.get('parentRatingKey') is None:
|
||||
# Parse the parentRatingKey from the parentThumb
|
||||
if self.parentThumb and self.parentThumb.startswith('/library/metadata/'):
|
||||
self.parentRatingKey = utils.cast(int, self.parentThumb.split('/')[3])
|
||||
|
|
|
@ -55,6 +55,9 @@ def test_library_sectionByID_with_attrs(plex, movies):
|
|||
|
||||
def test_library_section_get_movie(movies):
|
||||
assert movies.get("Sita Sings the Blues")
|
||||
assert movies.get(None, filters={"title": "Big Buck Bunny", "year": 2008})
|
||||
with pytest.raises(NotFound):
|
||||
movies.get("invalid title")
|
||||
|
||||
|
||||
def test_library_MovieSection_getGuid(movies, movie):
|
||||
|
|
|
@ -670,7 +670,7 @@ def test_video_Movie_mixins_fields(movie):
|
|||
test_mixins.edit_user_rating(movie)
|
||||
|
||||
|
||||
@pytest.mark.anonymous
|
||||
@pytest.mark.anonymously
|
||||
def test_video_Movie_mixins_fields_edition(movie):
|
||||
with pytest.raises(BadRequest):
|
||||
test_mixins.edit_edition_title(movie)
|
||||
|
|
Loading…
Reference in a new issue