Skip to content

Commit 9ce50b0

Browse files
committed
[plugin.video.sandmann] v2.4.1
address issues found by Kodiai Review Summary
1 parent a211311 commit 9ce50b0

File tree

6 files changed

+99
-26
lines changed

6 files changed

+99
-26
lines changed

plugin.video.sandmann/addon.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<addon id="plugin.video.sandmann" name="Sandmann" version="2.4.0" provider-name="sorax">
2+
<addon id="plugin.video.sandmann" name="Sandmann" version="2.4.1" provider-name="sorax">
33
<requires>
44
<import addon="xbmc.python" version="3.0.0"></import>
55
<import addon="script.module.requests"/>
@@ -24,8 +24,8 @@
2424
<icon>resources/assets/icon.png</icon>
2525
<fanart>resources/assets/fanart.jpg</fanart>
2626
</assets>
27-
<news>v2.4.0:
28-
- use sandmann.de as the official source for videos and metadata
27+
<news>v2.4.1:
28+
- improve error handling
2929
</news>
3030
</extension>
3131
</addon>

plugin.video.sandmann/changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
version 2.4.1:
2+
* improve error handling
13
version 2.4.0:
24
* use sandmann.de as the official source for videos and metadata
35
version 2.3.1:

plugin.video.sandmann/libs/network.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717

1818
import requests
1919

20+
TIMEOUT = 10
21+
2022

2123
def fetchHtml(url):
22-
r = requests.get(url)
24+
r = requests.get(url, timeout=TIMEOUT)
25+
r.raise_for_status()
2326
r.encoding = "utf-8"
2427
return r.text
2528

2629

2730
def fetchJson(url):
28-
r = requests.get(url)
31+
r = requests.get(url, timeout=TIMEOUT)
32+
r.raise_for_status()
2933
return r.json()

plugin.video.sandmann/libs/sandmann.py

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616
#
1717

18+
import xbmc
1819
import xbmcaddon
1920
import xbmcgui
2021
import xbmcplugin
@@ -23,6 +24,7 @@
2324
import sys
2425

2526
from bs4 import BeautifulSoup
27+
from requests.exceptions import RequestException
2628

2729
from libs.network import fetchHtml
2830
from libs.network import fetchJson
@@ -49,7 +51,15 @@ def sandmann():
4951
li_refresh = xbmcgui.ListItem(label=addon.getLocalizedString(30020))
5052
xbmcplugin.addDirectoryItem(addon_handle, base_path, li_refresh, True)
5153

52-
html = fetchWebsite()
54+
try:
55+
html = fetchWebsite()
56+
except RequestException as e:
57+
xbmc.log(f"[{addon_name}] Failed to fetch website: {e}", xbmc.LOGERROR)
58+
xbmcgui.Dialog().notification(
59+
addon_name, addon.getLocalizedString(30200), xbmcgui.NOTIFICATION_ERROR
60+
)
61+
xbmcplugin.endOfDirectory(addon_handle)
62+
return
5363

5464
if dgs == 0:
5565
episodes = getEpisodes(html, 1)
@@ -60,12 +70,20 @@ def sandmann():
6070

6171
item_list = []
6272
for episode, description in episodes:
63-
path = getEpisodePath(episode)
64-
details = fetchEpisodeDetails(path)
73+
try:
74+
path = getEpisodePath(episode)
75+
details = fetchEpisodeDetails(path)
76+
item_list.append((details["stream"], getListItem(details, description), False))
77+
except (RequestException, KeyError, IndexError, TypeError, ValueError,
78+
json.JSONDecodeError) as e:
79+
xbmc.log(f"[{addon_name}] Skipping episode: {e}", xbmc.LOGWARNING)
80+
continue
81+
82+
if not item_list:
83+
xbmcgui.Dialog().notification(
84+
addon_name, addon.getLocalizedString(30201), xbmcgui.NOTIFICATION_WARNING
85+
)
6586

66-
item_list.append((details["stream"], getListItem(details, description), False))
67-
68-
# xbmcgui.Dialog().ok("DEBUG", f'{item_list[0]}')
6987
xbmcplugin.addDirectoryItems(addon_handle, item_list, len(item_list))
7088
xbmcplugin.endOfDirectory(addon_handle)
7189

@@ -83,34 +101,67 @@ def getEpisodes(html, count):
83101
html_descriptions = soup.select(f"#main > .count{count} .manualteasershorttext p")
84102
descriptions = [p.get_text() for p in html_descriptions]
85103

104+
if not episodes:
105+
xbmc.log(f"[{addon_name}] No episodes found for count{count}", xbmc.LOGWARNING)
106+
return []
107+
108+
if len(episodes) != len(descriptions):
109+
xbmc.log(
110+
f"[{addon_name}] Episode/description count mismatch: "
111+
f"{len(episodes)} episodes vs {len(descriptions)} descriptions",
112+
xbmc.LOGWARNING,
113+
)
114+
descriptions.extend([""] * (len(episodes) - len(descriptions)))
115+
86116
return list(zip(episodes, descriptions))
87117

88118

89119
def getEpisodePath(episode):
90120
jsb_string = episode.get("data-jsb")
121+
if not jsb_string:
122+
raise ValueError("Missing 'data-jsb' attribute on episode element")
123+
91124
jsb_object = json.loads(jsb_string)
92125

126+
if "media" not in jsb_object:
127+
raise KeyError("Missing 'media' key in episode JSON data")
128+
93129
return jsb_object["media"]
94130

95131

96132
def fetchEpisodeDetails(path):
97-
json = fetchJson(f"https://www.sandmann.de{path}")
133+
data = fetchJson(f"https://www.sandmann.de{path}")
134+
135+
media_array = data.get("_mediaArray")
136+
if not media_array or not media_array[0].get("_mediaStreamArray"):
137+
raise KeyError("Missing media stream data in API response")
98138

99139
streams = {}
100-
for stream in json["_mediaArray"][0]["_mediaStreamArray"]:
101-
streams[stream["_quality"]] = stream["_stream"]
140+
for stream in media_array[0]["_mediaStreamArray"]:
141+
quality = stream.get("_quality")
142+
url = stream.get("_stream")
143+
if quality is not None and url:
144+
streams[quality] = url
145+
146+
if "auto" not in streams:
147+
raise KeyError("No 'auto' quality stream available")
148+
149+
title_parts = data.get("rbbtitle", "").split(" | ")
150+
date = title_parts[2] if len(title_parts) > 2 else ""
151+
name = title_parts[0] if title_parts else ""
152+
title = f"{date} | {name}" if date and name else data.get("rbbtitle", "Unbekannt")
102153

103-
title = json["rbbtitle"].split(" | ")
104-
previewImage = "https://www.sandmann.de" + json["_previewImage"].rsplit("/", 1)[0]
154+
preview_image = data.get("_previewImage", "")
155+
if preview_image:
156+
preview_image = "https://www.sandmann.de" + preview_image.rsplit("/", 1)[0]
105157

106158
return {
107-
"date": title[2],
108-
# "dgs": json["dgs"],
109-
"duration": json["_duration"],
110-
"fanart": previewImage + "/size=1920x1080.jpg",
159+
"date": date,
160+
"duration": data.get("_duration", 0),
161+
"fanart": f"{preview_image}/size=1920x1080.jpg" if preview_image else "",
111162
"stream": streams["auto"],
112-
"thumb": previewImage + "/size=640x360.jpg",
113-
"title": f"{title[2]} | {title[0]}"
163+
"thumb": f"{preview_image}/size=640x360.jpg" if preview_image else "",
164+
"title": title,
114165
}
115166

116167

plugin.video.sandmann/resources/language/resource.language.de_de/strings.po

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Kodi Media Center language file
2-
# Addon Name: detektor.fm
3-
# Addon id: plugin.audio.detektorfm
2+
# Addon Name: Sandmann
3+
# Addon id: plugin.video.sandmann
44
# Addon Provider: sorax
55
msgid ""
66
msgstr ""
@@ -67,3 +67,11 @@ msgstr "Manuell"
6767
msgctxt "#30104"
6868
msgid "On Start"
6969
msgstr "Nur beim Start"
70+
71+
msgctxt "#30200"
72+
msgid "Connection error"
73+
msgstr "Verbindungsfehler"
74+
75+
msgctxt "#30201"
76+
msgid "No episodes found"
77+
msgstr "Keine Folgen gefunden"

plugin.video.sandmann/resources/language/resource.language.en_gb/strings.po

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Kodi Media Center language file
2-
# Addon Name: detektor.fm
3-
# Addon id: plugin.audio.detektorfm
2+
# Addon Name: Sandmann
3+
# Addon id: plugin.video.sandmann
44
# Addon Provider: sorax
55
msgid ""
66
msgstr ""
@@ -67,3 +67,11 @@ msgstr ""
6767
msgctxt "#30104"
6868
msgid "On Start"
6969
msgstr ""
70+
71+
msgctxt "#30200"
72+
msgid "Connection error"
73+
msgstr ""
74+
75+
msgctxt "#30201"
76+
msgid "No episodes found"
77+
msgstr ""

0 commit comments

Comments
 (0)