1515# along with this program. If not, see <http://www.gnu.org/licenses/>.
1616#
1717
18+ import xbmc
1819import xbmcaddon
1920import xbmcgui
2021import xbmcplugin
2324import sys
2425
2526from bs4 import BeautifulSoup
27+ from requests .exceptions import RequestException
2628
2729from libs .network import fetchHtml
2830from 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
89119def 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
96132def 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
0 commit comments