66import xbmcgui
77import xbmcvfs
88
9- from resources .lib import utils
109from resources .lib .native_plugin import Plugin
1110from . import settings
1211
@@ -64,7 +63,7 @@ def to_jsonable(payload):
6463 if isinstance (payload , dict ):
6564 return {str (key ): to_jsonable (value ) for key , value in payload .items ()}
6665 if isinstance (payload , xbmcgui .ListItem ):
67- return utils . getDictFromListItem (payload )
66+ return get_dict_from_list_item (payload )
6867 if isinstance (payload , bytes ):
6968 return payload .decode ('utf-8' , 'replace' )
7069
@@ -87,3 +86,74 @@ def to_jsonable(payload):
8786def format_headers (headers ):
8887 """Map headers into a readable string to be logged."""
8988 return '\n ' .join (f'{ k } : { v } ' for k , v in headers .items ())
89+
90+
91+ def get_dict_from_info_tag_video (li ):
92+ """Extract common video InfoTag fields from a ListItem's VideoInfoTag into a dict.
93+
94+ This probes the tag for known getters and only calls them when present,
95+ avoiding broad exception handling.
96+ """
97+ if not hasattr (li , 'getVideoInfoTag' ):
98+ return None
99+
100+ tag = li .getVideoInfoTag ()
101+ if not tag :
102+ return None
103+
104+ info = {}
105+ info ['title' ] = tag .getTitle ()
106+ info ['plot' ] = tag .getPlot ()
107+ info ['plotoutline' ] = tag .getPlotOutline ()
108+ # No mpaa exposed, but Nexus and earlier does
109+ if hasattr (tag , 'getMpaa' ):
110+ info ['mpaa' ] = tag .getMpaa ()
111+ info ['duration' ] = tag .getDuration ()
112+ info ['firstairedasw3c' ] = tag .getFirstAiredAsW3C ()
113+ genres = tag .getGenres ()
114+ info ['genres' ] = list (genres ) if genres is not None else None
115+ directors = tag .getDirectors ()
116+ info ['directors' ] = list (directors ) if directors is not None else None
117+ writers = tag .getWriters ()
118+ info ['writers' ] = list (writers ) if writers is not None else None
119+ # No countries exposed, but Nexus and earlier does
120+ if hasattr (tag , 'getCountries' ):
121+ countries = tag .getCountries ()
122+ info ['countries' ] = list (countries ) if countries is not None else None
123+ info ['year' ] = tag .getYear ()
124+
125+ return info
126+
127+
128+ def get_dict_from_list_item (li ):
129+ """
130+ Serialize an xbmcgui.ListItem into a plain dict so it can be
131+ JSON-serialized / persisted. Be defensive: not all ListItem
132+ implementations expose the same getter methods, so wrap calls.
133+ """
134+ result = {}
135+ result ['label' ] = li .getLabel ()
136+ result ['path' ] = li .getPath ()
137+ result ['art' ] = {}
138+ for art_key in ['thumb' , 'fanart' ]:
139+ result ['art' ][art_key ] = li .getArt (art_key )
140+ props = {}
141+ if hasattr (li , 'getProperties' ):
142+ props = li .getProperties ()
143+ else :
144+ # fall back to probing a set of commonly used property keys
145+ if hasattr (li , 'getProperty' ):
146+ for key in ('is_playable' , 'StartOffset' , 'StartPercent' ):
147+ val = li .getProperty (key )
148+ if val is not None and val != '' :
149+ props [key ] = val
150+ result ['properties' ] = props
151+
152+ # info labels (metadata)
153+ if hasattr (li , 'getInfoLabels' ) and li .getInfoLabels ():
154+ result ['info' ] = li .getInfoLabels ()
155+
156+ # video InfoTag (detailed metadata) when available
157+ result ['video_info_tag' ] = get_dict_from_info_tag_video (li )
158+
159+ return result
0 commit comments