Skip to content

Commit 4fda902

Browse files
committed
fix missing context menu for cached zone - Remove cached categories
- remove cached categories. enable removing of conversion from listitem to dict. - fix play single video by removing native_plugin.play_video which rely on dict instead of listitem. - remove unused native_plugin.set_content - fix pylint: indentation, snake case, etc...
1 parent 1f6a22a commit 4fda902

8 files changed

Lines changed: 114 additions & 229 deletions

File tree

plugin.video.arteplussept/resources/lib/logger.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import xbmcgui
77
import xbmcvfs
88

9-
from resources.lib import utils
109
from resources.lib.native_plugin import Plugin
1110
from . 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):
8786
def 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

plugin.video.arteplussept/resources/lib/mapper/arteitem.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ def _parse_date_artetv(self, datestr):
255255
date = None
256256
return date
257257

258-
259258
def _get_image_url(self, wished_res, wished_text):
260259
item = self.json_dict
261260
image_url = None

plugin.video.arteplussept/resources/lib/mapper/artezone.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
Module for Arte Zone
33
"""
44

5+
import xbmc
56
import xbmcgui
67
from resources.lib import api
78
from resources.lib.mapper.artecollection import ArteCollection
8-
from resources.lib import utils
99

1010

1111
class ArteZone(ArteCollection):
@@ -14,33 +14,38 @@ class ArteZone(ArteCollection):
1414
ArteSearch is a special type of zone.
1515
"""
1616

17-
def __init__(self, plugin, settings, cached_categories=None):
18-
super().__init__(plugin, settings)
19-
self.cached_categories = cached_categories
20-
2117
def build_item(self, zone):
2218
"""
2319
Return a menu entry to access content of cached category item i.e.
2420
a zone in the HOME page or SEARH page result.
2521
"""
26-
zone_id = zone.get('id')
27-
cached_category = self._build_menu(
28-
zone.get('content'), 'category_page', zone_id=zone_id, page_id='HOME')
29-
if self._is_valid_menu(cached_category):
30-
self.cached_categories[zone_id] = utils.getDictFromListItemInList(cached_category)
22+
23+
if self._is_valid_zone(zone):
24+
zone_id = zone.get('id')
3125
li = xbmcgui.ListItem(label=zone.get('title'))
32-
li.setPath(self.plugin.url_for('cached_category', zone_id=zone_id))
26+
li.setPath(self.plugin.url_for(
27+
'category_page', zone_id=zone_id, page_id='HOME', page='1'))
3328
li.setProperty('is_playable', 'False')
3429
return li
30+
xbmc.log(f"Ignore zone {zone.get('label')}, no valid content", xbmc.LOGINFO)
3531
return None
3632

37-
def _is_valid_menu(self, cached_category):
33+
def _is_valid_zone(self, zone):
3834
"""
39-
Menu is valid, if it is a list with at least one element is not None.
40-
It is not valid if the list contains only None elements
35+
Zone is valid, if it contains content data which empty
36+
or which contains not only EXTERNAL items
4137
"""
42-
return isinstance(cached_category, list) and \
43-
any(elem is not None for elem in cached_category)
38+
data = (zone or {}).get("content", {}).get("data")
39+
if isinstance(data, list) and len(data) >= 1:
40+
valid_count = 0
41+
for item in data:
42+
item_kind = (item or {}).get("kind", {}).get("code")
43+
if item_kind != 'EXTERNAL':
44+
valid_count = valid_count + 1
45+
# if there is not at least one valid item, them zone is not valid
46+
return valid_count >= 1
47+
# we cannot be sure it is valid or not, we don't know what it contains
48+
return True
4449

4550
def build_menu(self, zone_id, page, page_id):
4651
"""

plugin.video.arteplussept/resources/lib/mapper/mapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,17 +185,17 @@ def map_stream(video_item, stream):
185185
return [map_stream(video_item, stream) for stream in sorted_filtered_streams]
186186

187187

188-
def map_zone_to_item(plugin, settings, zone, cached_categories):
188+
def map_zone_to_item(plugin, settings, zone):
189189
"""Arte TV API page is split into zones. Map a 'zone' to menu item(s).
190-
Populate cached_categories for zones with videos available in child 'content'"""
190+
Never use cache, because we cannot store ListItem in it"""
191191
menu_item = None
192192
title = zone.get('title')
193193
if get_authenticated_content_type(zone) == 'sso-favorites':
194194
menu_item = ArteFavorites(plugin, settings).build_item(title)
195195
elif get_authenticated_content_type(zone) == 'sso-personalzone':
196196
menu_item = ArteHistory(plugin, settings).build_item(title)
197197
elif zone.get('content') and zone.get('content').get('data'):
198-
menu_item = ArteZone(plugin, settings, cached_categories).build_item(zone)
198+
menu_item = ArteZone(plugin, settings).build_item(zone)
199199
elif zone.get('link'):
200200
menu_item = map_api_categories_item(plugin, zone)
201201
else:

plugin.video.arteplussept/resources/lib/native_plugin.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,6 @@ def map_collection_to_playlist(self, collection):
6161
pl.add(item.getPath(), item)
6262
return pl
6363

64-
def play_video(self, item):
65-
"""Play a dictionary-based playable item with metadata."""
66-
if not isinstance(item, dict):
67-
return False
68-
path = item.getPath()
69-
if not path:
70-
return False
71-
try:
72-
xbmc.Player().play(str(path), item)
73-
return True
74-
# pylint: disable=broad-exception-caught
75-
except Exception:
76-
return False
77-
78-
def set_content(self, content):
79-
"""Set the content type for the current directory (e.g., 'movies', 'tvshows')."""
80-
if self.handle is not None and content:
81-
xbmcplugin.setContent(self.handle, content)
82-
8364
def run(self):
8465
"""Dispatch to a registered route based on the 'route' query parameter."""
8566
self.handle = int(sys.argv[1]) if len(sys.argv) > 1 else None

plugin.video.arteplussept/resources/lib/plugin.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ def display_index():
4040
)
4141
addon.setSetting("last_info_version", current_version)
4242

43-
lst_itms = view.build_home_page(
44-
plugin, settings, plugin.get_storage('cached_categories', ttl=60))
43+
lst_itms = view.build_home_page(plugin, settings)
4544
logger.log_xbmc(lst_itms, 'index')
4645
return lst_itms
4746

@@ -54,20 +53,10 @@ def display_api_category(category_code):
5453
return lst_itms
5554

5655

57-
@plugin.route('/category/cached/<zone_id>', name='cached_category')
58-
def display_cached_category(zone_id):
59-
"""Display the menu for a category that is stored
60-
in cache from previous api call like home page"""
61-
lst_itms = view.get_cached_category(
62-
zone_id, plugin.get_storage('cached_categories', ttl=60))
63-
logger.log_xbmc(lst_itms, 'cached_category')
64-
return lst_itms
65-
66-
6756
@plugin.route('/category/page/<zone_id>/<page>/<page_id>', name='category_page')
6857
def display_category_page(zone_id, page, page_id):
6958
"""Display the menu for a category that needs an api call"""
70-
lst_itms = ArteZone(plugin, settings, plugin.get_storage('cached_categories', ttl=60)) \
59+
lst_itms = ArteZone(plugin, settings) \
7160
.build_menu(zone_id, page, page_id)
7261
logger.log_xbmc(lst_itms, 'category_page')
7362
return lst_itms
@@ -199,12 +188,15 @@ def play(kind, program_id, mpaa, play_from=PlayFrom.ITM, audio_slot='1'):
199188
else:
200189
played_item = None
201190
try:
202-
played_item = view.build_stream_url(plugin, settings, kind, program_id, int(audio_slot))
191+
played_item = view.build_stream_url(
192+
plugin, settings, kind, program_id, int(audio_slot))
193+
# pylint: disable=broad-exception-caught
203194
except Exception as exp:
204-
xbmc.log(f"Exception during stream resolution {traceback.format_tb(exp.__traceback__)}", xbmc.LOGERROR)
195+
stack_trace = traceback.format_tb(exp.__traceback__)
196+
xbmc.log(f"Exception during stream resolution {stack_trace}", xbmc.LOGERROR)
205197
if played_item is not None:
206198
logger.log_xbmc(played_item, 'play')
207-
plugin.play_video(played_item)
199+
xbmc.Player().play(played_item.getPath(), played_item)
208200
else:
209201
xbmc.log("Could not resolve stream...", xbmc.LOGERROR)
210202
addon = xbmcaddon.Addon()

0 commit comments

Comments
 (0)