Refactor use of manual cached attributes by JonnyWong16 · Pull Request #1516 · pushingkarmaorg/python-plexapi

Expand Up @@ -6,7 +6,6 @@ import warnings from collections import defaultdict from datetime import datetime from functools import cached_property from urllib.parse import parse_qs, quote_plus, urlencode, urlparse
from plexapi import log, media, utils Expand Down Expand Up @@ -44,9 +43,8 @@ def _loadData(self, data): self.mediaTagVersion = data.attrib.get('mediaTagVersion') self.title1 = data.attrib.get('title1') self.title2 = data.attrib.get('title2') self._sectionsByID = {} # cached sections by key self._sectionsByTitle = {} # cached sections by title
@cached_data_property def _loadSections(self): """ Loads and caches all the library sections. """ key = '/library/sections' Expand All @@ -64,15 +62,23 @@ def _loadSections(self): sectionsByID[section.key] = section sectionsByTitle[section.title.lower().strip()].append(section)
self._sectionsByID = sectionsByID self._sectionsByTitle = dict(sectionsByTitle) return sectionsByID, dict(sectionsByTitle)
@property def _sectionsByID(self): """ Returns a dictionary of all library sections by ID. """ return self._loadSections[0]
@property def _sectionsByTitle(self): """ Returns a dictionary of all library sections by title. """ return self._loadSections[1]
def sections(self): """ Returns a list of all media sections in this library. Library sections may be any of :class:`~plexapi.library.MovieSection`, :class:`~plexapi.library.ShowSection`, :class:`~plexapi.library.MusicSection`, :class:`~plexapi.library.PhotoSection`. """ self._loadSections() return list(self._sectionsByID.values())
def section(self, title): Expand All @@ -87,8 +93,6 @@ def section(self, title): :exc:`~plexapi.exceptions.NotFound`: The library section title is not found on the server. """ normalized_title = title.lower().strip() if not self._sectionsByTitle or normalized_title not in self._sectionsByTitle: self._loadSections() try: sections = self._sectionsByTitle[normalized_title] except KeyError: Expand All @@ -110,8 +114,6 @@ def sectionByID(self, sectionID): Raises: :exc:`~plexapi.exceptions.NotFound`: The library section ID is not found on the server. """ if not self._sectionsByID or sectionID not in self._sectionsByID: self._loadSections() try: return self._sectionsByID[sectionID] except KeyError: Expand Down Expand Up @@ -385,7 +387,9 @@ def add(self, name='', type='', agent='', scanner='', location='', language='en- if kwargs: prefs_params = {f'prefs[{k}]': v for k, v in kwargs.items()} part += f'&{urlencode(prefs_params)}' return self._server.query(part, method=self._server._session.post) data = self._server.query(part, method=self._server._session.post) self._invalidateCachedProperties() return data
def history(self, maxresults=None, mindate=None): """ Get Play History for all library Sections for the owner. Expand Down Expand Up @@ -448,35 +452,25 @@ def _loadData(self, data): self.type = data.attrib.get('type') self.updatedAt = utils.toDatetime(data.attrib.get('updatedAt')) self.uuid = data.attrib.get('uuid') # Private attrs as we don't want a reload. self._filterTypes = None self._fieldTypes = None self._totalViewSize = None self._totalDuration = None self._totalStorage = None
@cached_data_property def locations(self): return self.listAttrs(self._data, 'path', etag='Location')
@cached_property @cached_data_property def totalSize(self): """ Returns the total number of items in the library for the default library type. """ return self.totalViewSize(includeCollections=False)
@property def totalDuration(self): """ Returns the total duration (in milliseconds) of items in the library. """ if self._totalDuration is None: self._getTotalDurationStorage() return self._totalDuration return self._getTotalDurationStorage[0]
@property def totalStorage(self): """ Returns the total storage (in bytes) of items in the library. """ if self._totalStorage is None: self._getTotalDurationStorage() return self._totalStorage return self._getTotalDurationStorage[1]
def __getattribute__(self, attr): # Intercept to call EditFieldMixin and EditTagMixin methods Expand All @@ -492,6 +486,7 @@ def __getattribute__(self, attr): ) return value
@cached_data_property def _getTotalDurationStorage(self): """ Queries the Plex server for the total library duration and storage and caches the values. """ data = self._server.query('/media/providers?includeStorage=1') Expand All @@ -502,8 +497,10 @@ def _getTotalDurationStorage(self): ) directory = next(iter(data.findall(xpath)), None) if directory: self._totalDuration = utils.cast(int, directory.attrib.get('durationTotal')) self._totalStorage = utils.cast(int, directory.attrib.get('storageTotal')) totalDuration = utils.cast(int, directory.attrib.get('durationTotal')) totalStorage = utils.cast(int, directory.attrib.get('storageTotal')) return totalDuration, totalStorage return None, None
def totalViewSize(self, libtype=None, includeCollections=True): """ Returns the total number of items in the library for a specified libtype. Expand Down Expand Up @@ -534,7 +531,9 @@ def totalViewSize(self, libtype=None, includeCollections=True): def delete(self): """ Delete a library section. """ try: return self._server.query(f'/library/sections/{self.key}', method=self._server._session.delete) data = self._server.query(f'/library/sections/{self.key}', method=self._server._session.delete) self._server.library._invalidateCachedProperties() return data except BadRequest: # pragma: no cover msg = f'Failed to delete library {self.key}' msg += 'You may need to allow this permission in your Plex settings.' Expand Down Expand Up @@ -874,6 +873,7 @@ def deleteMediaPreviews(self): self._server.query(key, method=self._server._session.delete) return self
@cached_data_property def _loadFilters(self): """ Retrieves and caches the list of :class:`~plexapi.library.FilteringType` and list of :class:`~plexapi.library.FilteringFieldType` for this library section. Expand All @@ -883,23 +883,23 @@ def _loadFilters(self):
key = _key.format(key=self.key, filter='all') data = self._server.query(key) self._filterTypes = self.findItems(data, FilteringType, rtag='Meta') self._fieldTypes = self.findItems(data, FilteringFieldType, rtag='Meta') filterTypes = self.findItems(data, FilteringType, rtag='Meta') fieldTypes = self.findItems(data, FilteringFieldType, rtag='Meta')
if self.TYPE != 'photo': # No collections for photo library key = _key.format(key=self.key, filter='collections') data = self._server.query(key) self._filterTypes.extend(self.findItems(data, FilteringType, rtag='Meta')) filterTypes.extend(self.findItems(data, FilteringType, rtag='Meta'))
# Manually add guid field type, only allowing "is" operator guidFieldType = '<FieldType type="guid"><Operator key="=" title="is"/></FieldType>' self._fieldTypes.append(self._manuallyLoadXML(guidFieldType, FilteringFieldType)) fieldTypes.append(self._manuallyLoadXML(guidFieldType, FilteringFieldType))
return filterTypes, fieldTypes
def filterTypes(self): """ Returns a list of available :class:`~plexapi.library.FilteringType` for this library section. """ if self._filterTypes is None: self._loadFilters() return self._filterTypes return self._loadFilters[0]
def getFilterType(self, libtype=None): """ Returns a :class:`~plexapi.library.FilteringType` for a specified libtype. Expand All @@ -921,9 +921,7 @@ def getFilterType(self, libtype=None):
def fieldTypes(self): """ Returns a list of available :class:`~plexapi.library.FilteringFieldType` for this library section. """ if self._fieldTypes is None: self._loadFilters() return self._fieldTypes return self._loadFilters[1]
def getFieldType(self, fieldType): """ Returns a :class:`~plexapi.library.FilteringFieldType` for a specified fieldType. Expand Down Expand Up @@ -1972,7 +1970,7 @@ def albums(self):
def stations(self): """ Returns a list of :class:`~plexapi.playlist.Playlist` stations in this section. """ return next((hub.items for hub in self.hubs() if hub.context == 'hub.music.stations'), None) return next((hub._partialItems for hub in self.hubs() if hub.context == 'hub.music.stations'), None)
def searchArtists(self, **kwargs): """ Search for an artist. See :func:`~plexapi.library.LibrarySection.search` for usage. """ Expand Down Expand Up @@ -2232,26 +2230,38 @@ def _loadData(self, data): self.style = data.attrib.get('style') self.title = data.attrib.get('title') self.type = data.attrib.get('type') self._section = None # cache for self.section
def __len__(self): return self.size
@cached_data_property def items(self): def _partialItems(self): """ Cache for partial items. """ return self.findItems(self._data)
@cached_data_property def _items(self): """ Cache for items. """ if self.more and self.key: # If there are more items to load, fetch them items = self.fetchItems(self.key) self.more = False self.size = len(items) return items # Otherwise, all the data is in the initial _data XML response return self.findItems(self._data) return self._partialItems
def __len__(self): return self.size def items(self): """ Returns a list of all items in the hub. """ return self._items
@cached_data_property def _section(self): """ Cache for section. """ return self._server.library.sectionByID(self.librarySectionID)
def section(self): """ Returns the :class:`~plexapi.library.LibrarySection` this hub belongs to. """ if self._section is None: self._section = self._server.library.sectionByID(self.librarySectionID) return self._section
def _reload(self, **kwargs): Expand Down