mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-10 06:04:15 +00:00
Improve reload performance (#1451)
* Change default includes to false * Update isFullObject() check * Fix checkFiles in tests
This commit is contained in:
parent
d5604670d5
commit
bbe3e8e49f
5 changed files with 61 additions and 46 deletions
|
@ -3,7 +3,7 @@ import re
|
|||
from typing import TYPE_CHECKING, Generic, Iterable, List, Optional, TypeVar, Union
|
||||
import weakref
|
||||
from functools import cached_property
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import parse_qsl, urlencode, urlparse
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
|
@ -391,10 +391,9 @@ class PlexObject:
|
|||
|
||||
Parameters:
|
||||
key (string, optional): Override the key to reload.
|
||||
**kwargs (dict): A dictionary of XML include parameters to exclude or override.
|
||||
All parameters are included by default with the option to override each parameter
|
||||
or disable each parameter individually by setting it to False or 0.
|
||||
**kwargs (dict): A dictionary of XML include parameters to include/exclude or override.
|
||||
See :class:`~plexapi.base.PlexPartialObject` for all the available include parameters.
|
||||
Set parameter to True to include and False to exclude.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -402,20 +401,28 @@ class PlexObject:
|
|||
|
||||
from plexapi.server import PlexServer
|
||||
plex = PlexServer('http://localhost:32400', token='xxxxxxxxxxxxxxxxxxxx')
|
||||
movie = plex.library.section('Movies').get('Cars')
|
||||
|
||||
# Partial reload of the movie without the `checkFiles` parameter.
|
||||
# Excluding `checkFiles` will prevent the Plex server from reading the
|
||||
# file to check if the file still exists and is accessible.
|
||||
# The movie object will remain as a partial object.
|
||||
movie.reload(checkFiles=False)
|
||||
# Search results are partial objects.
|
||||
movie = plex.library.section('Movies').get('Cars')
|
||||
movie.isPartialObject() # Returns True
|
||||
|
||||
# Full reload of the movie with all include parameters.
|
||||
# Partial reload of the movie without a default include parameter.
|
||||
# The movie object will remain as a partial object.
|
||||
movie.reload(includeMarkers=False)
|
||||
movie.isPartialObject() # Returns True
|
||||
|
||||
# Full reload of the movie with all default include parameters.
|
||||
# The movie object will be a full object.
|
||||
movie.reload()
|
||||
movie.isFullObject() # Returns True
|
||||
|
||||
# Full reload of the movie with all default and extra include parameter.
|
||||
# Including `checkFiles` will tell the Plex server to check if the file
|
||||
# still exists and is accessible.
|
||||
# The movie object will be a full object.
|
||||
movie.reload(checkFiles=True)
|
||||
movie.isFullObject() # Returns True
|
||||
|
||||
"""
|
||||
return self._reload(key=key, **kwargs)
|
||||
|
||||
|
@ -505,25 +512,25 @@ class PlexPartialObject(PlexObject):
|
|||
automatically and update itself.
|
||||
"""
|
||||
_INCLUDES = {
|
||||
'checkFiles': 1,
|
||||
'includeAllConcerts': 1,
|
||||
'checkFiles': 0,
|
||||
'includeAllConcerts': 0,
|
||||
'includeBandwidths': 1,
|
||||
'includeChapters': 1,
|
||||
'includeChildren': 1,
|
||||
'includeConcerts': 1,
|
||||
'includeExternalMedia': 1,
|
||||
'includeExtras': 1,
|
||||
'includeChildren': 0,
|
||||
'includeConcerts': 0,
|
||||
'includeExternalMedia': 0,
|
||||
'includeExtras': 0,
|
||||
'includeFields': 'thumbBlurHash,artBlurHash',
|
||||
'includeGeolocation': 1,
|
||||
'includeLoudnessRamps': 1,
|
||||
'includeMarkers': 1,
|
||||
'includeOnDeck': 1,
|
||||
'includePopularLeaves': 1,
|
||||
'includePreferences': 1,
|
||||
'includeRelated': 1,
|
||||
'includeRelatedCount': 1,
|
||||
'includeReviews': 1,
|
||||
'includeStations': 1,
|
||||
'includeOnDeck': 0,
|
||||
'includePopularLeaves': 0,
|
||||
'includePreferences': 0,
|
||||
'includeRelated': 0,
|
||||
'includeRelatedCount': 0,
|
||||
'includeReviews': 0,
|
||||
'includeStations': 0,
|
||||
}
|
||||
_EXCLUDES = {
|
||||
'excludeElements': (
|
||||
|
@ -592,7 +599,11 @@ class PlexPartialObject(PlexObject):
|
|||
search result for a movie often only contain a portion of the attributes a full
|
||||
object (main url) for that movie would contain.
|
||||
"""
|
||||
return not self.key or (self._details_key or self.key) == self._initpath
|
||||
parsed_key = urlparse(self._details_key or self.key)
|
||||
parsed_initpath = urlparse(self._initpath)
|
||||
query_key = set(parse_qsl(parsed_key.query))
|
||||
query_init = set(parse_qsl(parsed_initpath.query))
|
||||
return not self.key or (parsed_key.path == parsed_initpath.path and query_key <= query_init)
|
||||
|
||||
def isPartialObject(self):
|
||||
""" Returns True if this is not a full object. """
|
||||
|
|
|
@ -106,12 +106,16 @@ class MediaPart(PlexObject):
|
|||
Attributes:
|
||||
TAG (str): 'Part'
|
||||
accessible (bool): True if the file is accessible.
|
||||
Requires reloading the media with ``checkFiles=True``.
|
||||
Refer to :func:`~plexapi.base.PlexObject.reload`.
|
||||
audioProfile (str): The audio profile of the file.
|
||||
container (str): The container type of the file (ex: avi).
|
||||
decision (str): Unknown.
|
||||
deepAnalysisVersion (int): The Plex deep analysis version for the file.
|
||||
duration (int): The duration of the file in milliseconds.
|
||||
exists (bool): True if the file exists.
|
||||
Requires reloading the media with ``checkFiles=True``.
|
||||
Refer to :func:`~plexapi.base.PlexObject.reload`.
|
||||
file (str): The path to this file on disk (ex: /media/Movies/Cars (2006)/Cars (2006).mkv)
|
||||
has64bitOffsets (bool): True if the file has 64 bit offsets.
|
||||
hasThumbnail (bool): True if the file (track) has an embedded thumbnail.
|
||||
|
|
|
@ -14,8 +14,8 @@ class AdvancedSettingsMixin:
|
|||
|
||||
def preferences(self):
|
||||
""" Returns a list of :class:`~plexapi.settings.Preferences` objects. """
|
||||
data = self._server.query(self._details_key)
|
||||
return self.findItems(data, settings.Preferences, rtag='Preferences')
|
||||
key = f'{self.key}?includePreferences=1'
|
||||
return self.fetchItems(key, cls=settings.Preferences, rtag='Preferences')
|
||||
|
||||
def preference(self, pref):
|
||||
""" Returns a :class:`~plexapi.settings.Preferences` object for the specified pref.
|
||||
|
@ -240,8 +240,7 @@ class UnmatchMatchMixin:
|
|||
params['agent'] = utils.getAgentIdentifier(self.section(), agent)
|
||||
|
||||
key = key + '?' + urlencode(params)
|
||||
data = self._server.query(key, method=self._server._session.get)
|
||||
return self.findItems(data, initpath=key)
|
||||
return self.fetchItems(key, cls=media.SearchResult)
|
||||
|
||||
def fixMatch(self, searchResult=None, auto=False, agent=None):
|
||||
""" Use match result to update show metadata.
|
||||
|
@ -278,8 +277,8 @@ class ExtrasMixin:
|
|||
def extras(self):
|
||||
""" Returns a list of :class:`~plexapi.video.Extra` objects. """
|
||||
from plexapi.video import Extra
|
||||
data = self._server.query(self._details_key)
|
||||
return self.findItems(data, Extra, rtag='Extras')
|
||||
key = f'{self.key}/extras'
|
||||
return self.fetchItems(key, cls=Extra)
|
||||
|
||||
|
||||
class HubsMixin:
|
||||
|
@ -289,8 +288,7 @@ class HubsMixin:
|
|||
""" Returns a list of :class:`~plexapi.library.Hub` objects. """
|
||||
from plexapi.library import Hub
|
||||
key = f'{self.key}/related'
|
||||
data = self._server.query(key)
|
||||
return self.findItems(data, Hub)
|
||||
return self.fetchItems(key, cls=Hub)
|
||||
|
||||
|
||||
class PlayedUnplayedMixin:
|
||||
|
|
|
@ -456,8 +456,8 @@ class Movie(
|
|||
|
||||
def reviews(self):
|
||||
""" Returns a list of :class:`~plexapi.media.Review` objects. """
|
||||
data = self._server.query(self._details_key)
|
||||
return self.findItems(data, media.Review, rtag='Video')
|
||||
key = f'{self.key}?includeReviews=1'
|
||||
return self.fetchItems(key, cls=media.Review, rtag='Video')
|
||||
|
||||
def editions(self):
|
||||
""" Returns a list of :class:`~plexapi.video.Movie` objects
|
||||
|
@ -614,8 +614,8 @@ class Show(
|
|||
""" Returns show's On Deck :class:`~plexapi.video.Video` object or `None`.
|
||||
If show is unwatched, return will likely be the first episode.
|
||||
"""
|
||||
data = self._server.query(self._details_key)
|
||||
return next(iter(self.findItems(data, rtag='OnDeck')), None)
|
||||
key = f'{self.key}?includeOnDeck=1'
|
||||
return next(iter(self.fetchItems(key, cls=Episode, rtag='OnDeck')), None)
|
||||
|
||||
def season(self, title=None, season=None):
|
||||
""" Returns the season with the specified title or number.
|
||||
|
@ -796,8 +796,8 @@ class Season(
|
|||
""" 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)
|
||||
key = f'{self.key}?includeOnDeck=1'
|
||||
return next(iter(self.fetchItems(key, cls=Episode, rtag='OnDeck')), None)
|
||||
|
||||
def episode(self, title=None, episode=None):
|
||||
""" Returns the episode with the given title or number.
|
||||
|
|
|
@ -217,13 +217,13 @@ def test_video_Movie_attrs(movies):
|
|||
assert utils.is_int(video.width, gte=400)
|
||||
# Part
|
||||
part = media.parts[0]
|
||||
assert part.accessible
|
||||
assert part.accessible is None
|
||||
assert part.audioProfile == "lc"
|
||||
assert part.container in utils.CONTAINERS
|
||||
assert part.decision is None
|
||||
assert part.deepAnalysisVersion is None or utils.is_int(part.deepAnalysisVersion)
|
||||
assert utils.is_int(part.duration, gte=160000)
|
||||
assert part.exists
|
||||
assert part.exists is None
|
||||
assert len(part.file) >= 10
|
||||
assert part.has64bitOffsets is False
|
||||
assert part.hasPreviewThumbnails is False
|
||||
|
@ -323,10 +323,12 @@ def test_video_Movie_getStreamURL(movie, account):
|
|||
def test_video_Movie_isFullObject_and_reload(plex):
|
||||
movie = plex.library.section("Movies").get("Sita Sings the Blues")
|
||||
assert movie.isFullObject() is False
|
||||
movie.reload(checkFiles=False)
|
||||
movie.reload(includeChapters=False)
|
||||
assert movie.isFullObject() is False
|
||||
movie.reload()
|
||||
assert movie.isFullObject() is True
|
||||
movie.reload(includeExtras=True)
|
||||
assert movie.isFullObject() is True
|
||||
movie_via_search = plex.library.search(movie.title)[0]
|
||||
assert movie_via_search.isFullObject() is False
|
||||
movie_via_search.reload()
|
||||
|
@ -1285,8 +1287,8 @@ def test_video_Episode_attrs(episode):
|
|||
assert len(part.key) >= 10
|
||||
assert part._server._baseurl == utils.SERVER_BASEURL
|
||||
assert utils.is_int(part.size, gte=18184197)
|
||||
assert part.exists
|
||||
assert part.accessible
|
||||
assert part.exists is None
|
||||
assert part.accessible is None
|
||||
|
||||
|
||||
def test_video_Episode_watched(tvshows):
|
||||
|
@ -1434,13 +1436,13 @@ def test_that_reload_return_the_same_object(plex):
|
|||
def test_video_exists_accessible(movie, episode):
|
||||
assert movie.media[0].parts[0].exists is None
|
||||
assert movie.media[0].parts[0].accessible is None
|
||||
movie.reload()
|
||||
movie.reload(checkFiles=True)
|
||||
assert movie.media[0].parts[0].exists is True
|
||||
assert movie.media[0].parts[0].accessible is True
|
||||
|
||||
assert episode.media[0].parts[0].exists is None
|
||||
assert episode.media[0].parts[0].accessible is None
|
||||
episode.reload()
|
||||
episode.reload(checkFiles=True)
|
||||
assert episode.media[0].parts[0].exists is True
|
||||
assert episode.media[0].parts[0].accessible is True
|
||||
|
||||
|
|
Loading…
Reference in a new issue