66import warnings
77from collections import defaultdict
88from datetime import datetime
9- from functools import cached_property
109from urllib .parse import parse_qs , quote_plus , urlencode , urlparse
1110
1211from plexapi import log , media , utils
@@ -44,9 +43,8 @@ def _loadData(self, data):
4443 self .mediaTagVersion = data .attrib .get ('mediaTagVersion' )
4544 self .title1 = data .attrib .get ('title1' )
4645 self .title2 = data .attrib .get ('title2' )
47- self ._sectionsByID = {} # cached sections by key
48- self ._sectionsByTitle = {} # cached sections by title
4946
47+ @cached_data_property
5048 def _loadSections (self ):
5149 """ Loads and caches all the library sections. """
5250 key = '/library/sections'
@@ -64,15 +62,23 @@ def _loadSections(self):
6462 sectionsByID [section .key ] = section
6563 sectionsByTitle [section .title .lower ().strip ()].append (section )
6664
67- self ._sectionsByID = sectionsByID
68- self ._sectionsByTitle = dict (sectionsByTitle )
65+ return sectionsByID , dict (sectionsByTitle )
66+
67+ @property
68+ def _sectionsByID (self ):
69+ """ Returns a dictionary of all library sections by ID. """
70+ return self ._loadSections [0 ]
71+
72+ @property
73+ def _sectionsByTitle (self ):
74+ """ Returns a dictionary of all library sections by title. """
75+ return self ._loadSections [1 ]
6976
7077 def sections (self ):
7178 """ Returns a list of all media sections in this library. Library sections may be any of
7279 :class:`~plexapi.library.MovieSection`, :class:`~plexapi.library.ShowSection`,
7380 :class:`~plexapi.library.MusicSection`, :class:`~plexapi.library.PhotoSection`.
7481 """
75- self ._loadSections ()
7682 return list (self ._sectionsByID .values ())
7783
7884 def section (self , title ):
@@ -87,8 +93,6 @@ def section(self, title):
8793 :exc:`~plexapi.exceptions.NotFound`: The library section title is not found on the server.
8894 """
8995 normalized_title = title .lower ().strip ()
90- if not self ._sectionsByTitle or normalized_title not in self ._sectionsByTitle :
91- self ._loadSections ()
9296 try :
9397 sections = self ._sectionsByTitle [normalized_title ]
9498 except KeyError :
@@ -110,8 +114,6 @@ def sectionByID(self, sectionID):
110114 Raises:
111115 :exc:`~plexapi.exceptions.NotFound`: The library section ID is not found on the server.
112116 """
113- if not self ._sectionsByID or sectionID not in self ._sectionsByID :
114- self ._loadSections ()
115117 try :
116118 return self ._sectionsByID [sectionID ]
117119 except KeyError :
@@ -448,35 +450,25 @@ def _loadData(self, data):
448450 self .type = data .attrib .get ('type' )
449451 self .updatedAt = utils .toDatetime (data .attrib .get ('updatedAt' ))
450452 self .uuid = data .attrib .get ('uuid' )
451- # Private attrs as we don't want a reload.
452- self ._filterTypes = None
453- self ._fieldTypes = None
454- self ._totalViewSize = None
455- self ._totalDuration = None
456- self ._totalStorage = None
457453
458454 @cached_data_property
459455 def locations (self ):
460456 return self .listAttrs (self ._data , 'path' , etag = 'Location' )
461457
462- @cached_property
458+ @cached_data_property
463459 def totalSize (self ):
464460 """ Returns the total number of items in the library for the default library type. """
465461 return self .totalViewSize (includeCollections = False )
466462
467463 @property
468464 def totalDuration (self ):
469465 """ Returns the total duration (in milliseconds) of items in the library. """
470- if self ._totalDuration is None :
471- self ._getTotalDurationStorage ()
472- return self ._totalDuration
466+ return self ._getTotalDurationStorage [0 ]
473467
474468 @property
475469 def totalStorage (self ):
476470 """ Returns the total storage (in bytes) of items in the library. """
477- if self ._totalStorage is None :
478- self ._getTotalDurationStorage ()
479- return self ._totalStorage
471+ return self ._getTotalDurationStorage [1 ]
480472
481473 def __getattribute__ (self , attr ):
482474 # Intercept to call EditFieldMixin and EditTagMixin methods
@@ -492,6 +484,7 @@ def __getattribute__(self, attr):
492484 )
493485 return value
494486
487+ @cached_data_property
495488 def _getTotalDurationStorage (self ):
496489 """ Queries the Plex server for the total library duration and storage and caches the values. """
497490 data = self ._server .query ('/media/providers?includeStorage=1' )
@@ -502,8 +495,10 @@ def _getTotalDurationStorage(self):
502495 )
503496 directory = next (iter (data .findall (xpath )), None )
504497 if directory :
505- self ._totalDuration = utils .cast (int , directory .attrib .get ('durationTotal' ))
506- self ._totalStorage = utils .cast (int , directory .attrib .get ('storageTotal' ))
498+ totalDuration = utils .cast (int , directory .attrib .get ('durationTotal' ))
499+ totalStorage = utils .cast (int , directory .attrib .get ('storageTotal' ))
500+ return totalDuration , totalStorage
501+ return None , None
507502
508503 def totalViewSize (self , libtype = None , includeCollections = True ):
509504 """ Returns the total number of items in the library for a specified libtype.
@@ -875,6 +870,7 @@ def deleteMediaPreviews(self):
875870 self ._server .query (key , method = self ._server ._session .delete )
876871 return self
877872
873+ @cached_data_property
878874 def _loadFilters (self ):
879875 """ Retrieves and caches the list of :class:`~plexapi.library.FilteringType` and
880876 list of :class:`~plexapi.library.FilteringFieldType` for this library section.
@@ -884,23 +880,23 @@ def _loadFilters(self):
884880
885881 key = _key .format (key = self .key , filter = 'all' )
886882 data = self ._server .query (key )
887- self . _filterTypes = self .findItems (data , FilteringType , rtag = 'Meta' )
888- self . _fieldTypes = self .findItems (data , FilteringFieldType , rtag = 'Meta' )
883+ filterTypes = self .findItems (data , FilteringType , rtag = 'Meta' )
884+ fieldTypes = self .findItems (data , FilteringFieldType , rtag = 'Meta' )
889885
890886 if self .TYPE != 'photo' : # No collections for photo library
891887 key = _key .format (key = self .key , filter = 'collections' )
892888 data = self ._server .query (key )
893- self . _filterTypes .extend (self .findItems (data , FilteringType , rtag = 'Meta' ))
889+ filterTypes .extend (self .findItems (data , FilteringType , rtag = 'Meta' ))
894890
895891 # Manually add guid field type, only allowing "is" operator
896892 guidFieldType = '<FieldType type="guid"><Operator key="=" title="is"/></FieldType>'
897- self ._fieldTypes .append (self ._manuallyLoadXML (guidFieldType , FilteringFieldType ))
893+ fieldTypes .append (self ._manuallyLoadXML (guidFieldType , FilteringFieldType ))
894+
895+ return filterTypes , fieldTypes
898896
899897 def filterTypes (self ):
900898 """ Returns a list of available :class:`~plexapi.library.FilteringType` for this library section. """
901- if self ._filterTypes is None :
902- self ._loadFilters ()
903- return self ._filterTypes
899+ return self ._loadFilters [0 ]
904900
905901 def getFilterType (self , libtype = None ):
906902 """ Returns a :class:`~plexapi.library.FilteringType` for a specified libtype.
@@ -922,9 +918,7 @@ def getFilterType(self, libtype=None):
922918
923919 def fieldTypes (self ):
924920 """ Returns a list of available :class:`~plexapi.library.FilteringFieldType` for this library section. """
925- if self ._fieldTypes is None :
926- self ._loadFilters ()
927- return self ._fieldTypes
921+ return self ._loadFilters [1 ]
928922
929923 def getFieldType (self , fieldType ):
930924 """ Returns a :class:`~plexapi.library.FilteringFieldType` for a specified fieldType.
@@ -2233,10 +2227,13 @@ def _loadData(self, data):
22332227 self .style = data .attrib .get ('style' )
22342228 self .title = data .attrib .get ('title' )
22352229 self .type = data .attrib .get ('type' )
2236- self ._section = None # cache for self.section
2230+
2231+ def __len__ (self ):
2232+ return self .size
22372233
22382234 @cached_data_property
2239- def items (self ):
2235+ def _items (self ):
2236+ """ Cache for items. """
22402237 if self .more and self .key : # If there are more items to load, fetch them
22412238 items = self .fetchItems (self .key )
22422239 self .more = False
@@ -2245,8 +2242,9 @@ def items(self):
22452242 # Otherwise, all the data is in the initial _data XML response
22462243 return self .findItems (self ._data )
22472244
2248- def __len__ (self ):
2249- return self .size
2245+ def items (self ):
2246+ """ Returns a list of all items in the hub. """
2247+ return self ._items
22502248
22512249 def reload (self ):
22522250 """ Delete cached data to allow reloading of hub items. """
@@ -2255,11 +2253,14 @@ def reload(self):
22552253 self .more = utils .cast (bool , self ._data .attrib .get ('more' ))
22562254 self .size = utils .cast (int , self ._data .attrib .get ('size' ))
22572255
2256+ @cached_data_property
2257+ def _section (self ):
2258+ """ Cache for section. """
2259+ return self ._server .library .sectionByID (self .librarySectionID )
2260+
22582261 def section (self ):
22592262 """ Returns the :class:`~plexapi.library.LibrarySection` this hub belongs to.
22602263 """
2261- if self ._section is None :
2262- self ._section = self ._server .library .sectionByID (self .librarySectionID )
22632264 return self ._section
22642265
22652266
0 commit comments