Skip to content

Commit 89910f2

Browse files
authored
Add livestream support (#14)
* Add livestream support * Do not include segments for livestreams * Revert playback not supported * Remove old code for livestreams
1 parent c6b907c commit 89910f2

File tree

5 files changed

+29
-142
lines changed

5 files changed

+29
-142
lines changed

lib/srgssr.py

Lines changed: 26 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ def build_menu_from_page(self, url, path):
527527
except Exception:
528528
pass
529529

530-
def build_episode_menu(self, video_id, include_segments=True,
530+
def build_episode_menu(self, video_id_or_urn, include_segments=True,
531531
segment_option=False, audio=False):
532532
"""
533533
Builds a list entry for a episode by a given video id.
@@ -536,7 +536,7 @@ def build_episode_menu(self, video_id, include_segments=True,
536536
entry for the segment will be created.
537537
538538
Keyword arguments:
539-
video_id -- the id of the video
539+
video_id_or_urn -- the video id or the urn
540540
include_segments -- indicates if the segments (if available) of the
541541
video should be included in the list
542542
(default: True)
@@ -545,25 +545,30 @@ def build_episode_menu(self, video_id, include_segments=True,
545545
audio -- boolean value to indicate if the episode is a
546546
radio show (default: False)
547547
"""
548-
self.log(f'build_episode_menu, video_id = {video_id}')
548+
self.log(f'build_episode_menu, video_id_or_urn = {video_id_or_urn}')
549549
content_type = 'audio' if audio else 'video'
550-
json_url = f'https://il.srgssr.ch/integrationlayer/2.0/{self.bu}/' \
551-
f'mediaComposition/{content_type}/{video_id}.json'
550+
if ':' in video_id_or_urn:
551+
json_url = 'https://il.srgssr.ch/integrationlayer/2.0/' \
552+
f'mediaComposition/byUrn/{video_id_or_urn}.json'
553+
video_id = video_id_or_urn.split(':')[-1]
554+
else:
555+
json_url = f'https://il.srgssr.ch/integrationlayer/2.0/{self.bu}' \
556+
f'/mediaComposition/{content_type}/{video_id_or_urn}' \
557+
'.json'
558+
video_id = video_id_or_urn
552559
self.log(f'build_episode_menu. Open URL {json_url}')
553560
try:
554561
json_response = json.loads(self.open_url(json_url))
555562
except Exception:
556-
self.log(f'build_episode_menu: Cannot open json for {video_id}.')
563+
self.log(
564+
f'build_episode_menu: Cannot open json for {video_id_or_urn}.')
557565
return
558566

559567
chapter_urn = utils.try_get(json_response, 'chapterUrn')
560568
segment_urn = utils.try_get(json_response, 'segmentUrn')
561569

562-
id_regex = r'[a-z]+:[a-z]+:[a-z]+:(?P<id>.+)'
563-
match_chapter_id = re.match(id_regex, chapter_urn)
564-
match_segment_id = re.match(id_regex, segment_urn)
565-
chapter_id = match_chapter_id.group('id') if match_chapter_id else None
566-
segment_id = match_segment_id.group('id') if match_segment_id else None
570+
chapter_id = chapter_urn.split(':')[-1] if chapter_urn else None
571+
segment_id = segment_urn.split(':')[-1] if segment_urn else None
567572

568573
if not chapter_id:
569574
self.log(f'build_episode_menu: No valid chapter URN \
@@ -588,6 +593,7 @@ def build_episode_menu(self, video_id, include_segments=True,
588593
for video_id {video_id}')
589594
return
590595

596+
# TODO: Simplify
591597
json_segment_list = utils.try_get(
592598
json_chapter, 'segmentList', data_type=list, default=[])
593599
if video_id == chapter_id:
@@ -690,20 +696,9 @@ def build_entry_apiv3(self, data, is_show=False, whitelist_ids=None):
690696
url = self.build_url(mode=100, name=urn)
691697
is_folder = True
692698

693-
# Prevent upcoming live events from being played:
694-
if 'swisstxt' in urn:
695-
url = self.build_url(mode=500, name=urn)
696-
is_folder = False
697-
698699
xbmcplugin.addDirectoryItem(
699700
self.handle, url, list_item, isFolder=is_folder)
700701

701-
def playback_not_supported_dialog(self, urn):
702-
heading = self.language(30500)
703-
message = self.language(30501) + f' {urn} ' + self.language(30502)
704-
dialog = xbmcgui.Dialog()
705-
dialog.notification(heading, message)
706-
707702
def build_menu_by_urn(self, urn):
708703
"""
709704
Builds a menu from an urn.
@@ -714,6 +709,10 @@ def build_menu_by_urn(self, urn):
714709
id = urn.split(':')[-1]
715710
if 'show' in urn:
716711
self.build_menu_apiv3(f'videos-by-show-id?showId={id}')
712+
elif 'swisstxt' in urn:
713+
# Do not include segments for livestreams,
714+
# they fail to play.
715+
self.build_episode_menu(urn, include_segments=False)
717716
elif 'video' in urn:
718717
self.build_episode_menu(id)
719718
elif 'topic' in urn:
@@ -811,7 +810,11 @@ def build_entry(self, json_entry, is_folder=False, audio=False,
811810
url = self.build_url(mode=21, name=name)
812811
else:
813812
list_item.setProperty('IsPlayable', 'true')
814-
url = self.build_url(mode=50, name=name)
813+
# TODO: Simplify this, use URN instead of video id everywhere
814+
if 'swisstxt' in urn:
815+
url = self.build_url(mode=50, name=urn)
816+
else:
817+
url = self.build_url(mode=50, name=name)
815818
xbmcplugin.addDirectoryItem(
816819
self.handle, url, list_item, isFolder=is_folder)
817820

@@ -1329,74 +1332,6 @@ def write_search(self, filename, name, max_entries=10):
13291332
# continue
13301333
# self.build_entry(json_entry)
13311334

1332-
def build_live_menu(self, extract_srf3=False):
1333-
"""
1334-
Builds the menu listing the currently available livestreams.
1335-
"""
1336-
def get_live_ids():
1337-
"""
1338-
Downloads the main webpage and scrapes it for
1339-
possible livestreams. If some live events were found, a list
1340-
of live ids will be returned, otherwise an empty list.
1341-
"""
1342-
live_ids = []
1343-
webpage = self.open_url(self.host_url, use_cache=False)
1344-
event_id_regex = r'(?:data-sport-id=\"|eventId=)(?P<live_id>\d+)'
1345-
try:
1346-
for match in re.finditer(event_id_regex, webpage):
1347-
live_ids.append(match.group('live_id'))
1348-
except StopIteration:
1349-
pass
1350-
return live_ids
1351-
1352-
def get_srf3_live_ids():
1353-
"""
1354-
Returns a list of Radio SRF 3 video streams.
1355-
"""
1356-
url = 'https://www.srf.ch/radio-srf-3'
1357-
webpage = self.open_url(url, use_cache=False)
1358-
video_id_regex = r'''(?x)
1359-
popupvideoplayer\?id=
1360-
(?P<video_id>
1361-
[a-f0-9]{8}-
1362-
[a-f0-9]{4}-
1363-
[a-f0-9]{4}-
1364-
[a-f0-9]{4}-
1365-
[a-f0-9]{12}
1366-
)
1367-
'''
1368-
live_ids = []
1369-
try:
1370-
for match in re.finditer(video_id_regex, webpage):
1371-
live_ids.append(match.group('video_id'))
1372-
except StopIteration:
1373-
pass
1374-
return live_ids
1375-
live_ids = get_live_ids()
1376-
for lid in live_ids:
1377-
api_url = ('https://event.api.swisstxt.ch/v1/events/'
1378-
f'{self.bu}/byEventItemId/?eids={lid}')
1379-
live_json = json.loads(self.open_url(api_url))
1380-
entry = utils.try_get(live_json, 0, data_type=dict, default={})
1381-
if not entry:
1382-
self.log('build_live_menu: No entry found for live id {lid}.')
1383-
continue
1384-
if utils.try_get(entry, 'streamType') == 'noStream':
1385-
continue
1386-
title = utils.try_get(entry, 'title')
1387-
stream_url = utils.try_get(entry, 'hls')
1388-
image = utils.try_get(entry, 'imageUrl')
1389-
item = xbmcgui.ListItem(label=title)
1390-
item.setProperty('IsPlayable', 'true')
1391-
item.setArt({'thumb': image})
1392-
purl = self.build_url(mode=51, name=stream_url)
1393-
xbmcplugin.addDirectoryItem(
1394-
self.handle, purl, item, isFolder=False)
1395-
if extract_srf3:
1396-
srf3_ids = get_srf3_live_ids()
1397-
for vid in srf3_ids:
1398-
self.build_episode_menu(vid, include_segments=False)
1399-
14001335
def _read_youtube_channels(self, fname):
14011336
"""
14021337
Reads YouTube channel IDs from a specified file and returns a list

resources/language/resource.language.de_de/strings.po

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,4 @@ msgstr "Kürzlich gesuchte Audios"
105105

106106
msgctxt "#30118"
107107
msgid "Recently searched shows"
108-
msgstr "Kürzlich gesuchte Sendungen"
109-
110-
msgctxt "#30500"
111-
msgid "Playback not supported"
112-
msgstr "Wiedergabe wird nicht unterstützt"
113-
114-
msgctxt "#30501"
115-
msgid "Playback for item"
116-
msgstr "Wiedergabe für das Medium"
117-
118-
msgctxt "#30502"
119-
msgid "not supported"
120-
msgstr "wird nicht unterstützt"
108+
msgstr "Kürzlich gesuchte Sendungen"

resources/language/resource.language.en_gb/strings.po

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,4 @@ msgstr ""
105105

106106
msgctxt "#30118"
107107
msgid "Recently searched shows"
108-
msgstr ""
109-
110-
msgctxt "#30500"
111-
msgid "Playback not supported"
112-
msgstr ""
113-
114-
msgctxt "#30501"
115-
msgid "Playback for item"
116-
msgstr ""
117-
118-
msgctxt "#30502"
119-
msgid "not supported"
120108
msgstr ""

resources/language/resource.language.fr_fr/strings.po

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,4 @@ msgstr "Audios récemment recherchées"
105105

106106
msgctxt "#30118"
107107
msgid "Recently searched shows"
108-
msgstr "Émissions récemment recherchées"
109-
110-
msgctxt "#30500"
111-
msgid "Playback not supported"
112-
msgstr "La lecture n'est pas prise en charge"
113-
114-
msgctxt "#30501"
115-
msgid "Playback for item"
116-
msgstr "La lecture de l'élément"
117-
118-
msgctxt "#30502"
119-
msgid "not supported"
120-
msgstr "n'est pas prise en charge"
108+
msgstr "Émissions récemment recherchées"

resources/language/resource.language.it_it/strings.po

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,4 @@ msgstr "Audios cercato di recente"
105105

106106
msgctxt "#30118"
107107
msgid "Recently searched shows"
108-
msgstr "Show cercato di recente"
109-
110-
msgctxt "#30500"
111-
msgid "Playback not supported"
112-
msgstr "Riproduzione non supportata"
113-
114-
msgctxt "#30501"
115-
msgid "Playback for item"
116-
msgstr "Riproduzione per l'elemento"
117-
118-
msgctxt "#30502"
119-
msgid "not supported"
120-
msgstr "non supportata"
108+
msgstr "Show cercato di recente"

0 commit comments

Comments
 (0)