1
1
# -*- coding: utf-8 -*-
2
2
import re
3
+ from typing import TYPE_CHECKING , Generic , Iterable , List , Optional , Type , TypeVar , Union
3
4
import weakref
4
5
from functools import cached_property
5
6
from urllib .parse import urlencode
6
7
from xml .etree import ElementTree
8
+ from xml .etree .ElementTree import Element
7
9
8
10
from plexapi import CONFIG , X_PLEX_CONTAINER_SIZE , log , utils
9
11
from plexapi .exceptions import BadRequest , NotFound , UnknownType , Unsupported
10
12
13
+ if TYPE_CHECKING :
14
+ from plexapi .server import PlexServer
15
+
16
+ PlexObjectT = TypeVar ("PlexObjectT" , bound = 'PlexObject' )
17
+ MediaContainerT = TypeVar ("MediaContainerT" , bound = "MediaContainer" )
18
+
11
19
USER_DONT_RELOAD_FOR_KEYS = set ()
12
20
_DONT_RELOAD_FOR_KEYS = {'key' }
13
21
OPERATORS = {
@@ -245,8 +253,7 @@ def fetchItems(self, ekey, cls=None, container_start=None, container_size=None,
245
253
if maxresults is not None :
246
254
container_size = min (container_size , maxresults )
247
255
248
- results = []
249
- subresults = []
256
+ results = MediaContainer [cls ](self ._server , Element ('MediaContainer' ), initpath = ekey )
250
257
headers = {}
251
258
252
259
while True :
@@ -324,7 +331,7 @@ def findItems(self, data, cls=None, initpath=None, rtag=None, **kwargs):
324
331
if rtag :
325
332
data = next (utils .iterXMLBFS (data , rtag ), [])
326
333
# loop through all data elements to find matches
327
- items = []
334
+ items = MediaContainer [ cls ]( self . _server , data , initpath = initpath ) if data . tag == 'MediaContainer' else []
328
335
for elem in data :
329
336
if self ._checkAttrs (elem , ** kwargs ):
330
337
item = self ._buildItemOrNone (elem , cls , initpath )
@@ -996,7 +1003,11 @@ def delete(self):
996
1003
return self ._server .query (self .historyKey , method = self ._server ._session .delete )
997
1004
998
1005
999
- class MediaContainer (PlexObject ):
1006
+ class MediaContainer (
1007
+ Generic [PlexObjectT ],
1008
+ List [PlexObjectT ],
1009
+ PlexObject ,
1010
+ ):
1000
1011
""" Represents a single MediaContainer.
1001
1012
1002
1013
Attributes:
@@ -1009,11 +1020,55 @@ class MediaContainer(PlexObject):
1009
1020
librarySectionUUID (str): :class:`~plexapi.library.LibrarySection` UUID.
1010
1021
mediaTagPrefix (str): "/system/bundle/media/flags/"
1011
1022
mediaTagVersion (int): Unknown
1023
+ offset (int): The offset of current results.
1012
1024
size (int): The number of items in the hub.
1025
+ totalSize (int): The total number of items for the query.
1013
1026
1014
1027
"""
1015
1028
TAG = 'MediaContainer'
1016
1029
1030
+ def __init__ (
1031
+ self ,
1032
+ server : "PlexServer" ,
1033
+ data : Element ,
1034
+ * args : PlexObjectT ,
1035
+ initpath : Optional [str ] = None ,
1036
+ parent : Optional [PlexObject ] = None ,
1037
+ ) -> None :
1038
+ # super calls Generic.__init__ which calls list.__init__ eventually
1039
+ super ().__init__ (* args )
1040
+ PlexObject .__init__ (self , server , data , initpath , parent )
1041
+
1042
+ def extend (
1043
+ self : MediaContainerT ,
1044
+ __iterable : Union [Iterable [PlexObjectT ], MediaContainerT ],
1045
+ ) -> None :
1046
+ curr_size = self .size if self .size is not None else len (self )
1047
+ super ().extend (__iterable )
1048
+ # update size, totalSize, and offset
1049
+ if not isinstance (__iterable , MediaContainer ):
1050
+ return
1051
+
1052
+ # prefer the totalSize of the new iterable even if it is smaller
1053
+ self .totalSize = (
1054
+ __iterable .totalSize
1055
+ if __iterable .totalSize is not None
1056
+ else self .totalSize
1057
+ ) # ideally both should be equal
1058
+
1059
+ # the size of the new iterable is added to the current size
1060
+ self .size = curr_size + (
1061
+ __iterable .size if __iterable .size is not None else len (__iterable )
1062
+ )
1063
+
1064
+ # the offset is the minimum of the two, prefering older values
1065
+ if self .offset is not None and __iterable .offset is not None :
1066
+ self .offset = min (self .offset , __iterable .offset )
1067
+ else :
1068
+ self .offset = (
1069
+ self .offset if self .offset is not None else __iterable .offset
1070
+ )
1071
+
1017
1072
def _loadData (self , data ):
1018
1073
self ._data = data
1019
1074
self .allowSync = utils .cast (int , data .attrib .get ('allowSync' ))
@@ -1024,4 +1079,6 @@ def _loadData(self, data):
1024
1079
self .librarySectionUUID = data .attrib .get ('librarySectionUUID' )
1025
1080
self .mediaTagPrefix = data .attrib .get ('mediaTagPrefix' )
1026
1081
self .mediaTagVersion = data .attrib .get ('mediaTagVersion' )
1082
+ self .offset = utils .cast (int , data .attrib .get ("offset" ))
1027
1083
self .size = utils .cast (int , data .attrib .get ('size' ))
1084
+ self .totalSize = utils .cast (int , data .attrib .get ("totalSize" ))
0 commit comments