From 777d050e08a298fea7ed5be940e9be9d62173af2 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Wed, 22 Jun 2022 17:46:48 +0200 Subject: [PATCH 1/5] Remove audio support in `play_video` --- lib/srgssr.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index 16cf5a1..067f302 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -1010,20 +1010,18 @@ def get_auth_url(self, url, segment_data=None): url += ('?' if '?' not in url else '&') + auth_params return url - def play_video(self, media_id_or_urn, audio=False): + def play_video(self, media_id_or_urn): """ Gets the stream information starts to play it. Keyword arguments: media_id_or_urn -- the urn or id of the media to play - audio -- boolean value to indicate if the content is - audio (default: False) """ if media_id_or_urn.startswith('urn:'): urn = media_id_or_urn media_id = media_id_or_urn.split(':')[-1] else: - media_type = 'audio' if audio else 'video' + media_type = 'video' urn = 'urn:' + self.bu + ':' + media_type + ':' + media_id_or_urn media_id = media_id_or_urn self.log('play_video, urn = ' + urn + ', media_id = ' + media_id) @@ -1053,20 +1051,6 @@ def play_video(self, media_id_or_urn, audio=False): 'HD': '', } - if audio: - candidates = [res for res in resource_list if utils.try_get( - res, 'protocol') in ('HTTP', 'HTTPS', 'HTTP-MP3-STREAM')] - for candi in candidates: - if utils.try_get(candi, 'quality') in ('HD', 'HQ'): - stream_url = candi['url'] - break - else: - stream_url = candidates[0]['url'] - - play_item = xbmcgui.ListItem(media_id, path=stream_url) - xbmcplugin.setResolvedUrl(self.handle, True, play_item) - return - mf_type = 'hls' for resource in resource_list: if utils.try_get(resource, 'protocol') == 'HLS': From 9fffbe40b12497ed5ebb0013d9994d9f0b1b9bfd Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Thu, 23 Jun 2022 16:21:50 +0200 Subject: [PATCH 2/5] Add support for WIDEVINE encrypted DASH streams --- addon.xml | 1 + lib/srgssr.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/addon.xml b/addon.xml index 68e4624..774a36b 100644 --- a/addon.xml +++ b/addon.xml @@ -2,6 +2,7 @@ + diff --git a/lib/srgssr.py b/lib/srgssr.py index 067f302..c7eb023 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -37,6 +37,7 @@ import xbmcaddon import xbmcvfs +import inputstreamhelper import simplecache import youtube_channels @@ -1021,6 +1022,7 @@ def play_video(self, media_id_or_urn): urn = media_id_or_urn media_id = media_id_or_urn.split(':')[-1] else: + # TODO: Could fail for livestreams media_type = 'video' urn = 'urn:' + self.bu + ':' + media_type + ':' + media_id_or_urn media_id = media_id_or_urn @@ -1029,6 +1031,8 @@ def play_video(self, media_id_or_urn): detail_url = ('https://il.srgssr.ch/integrationlayer/2.0/' 'mediaComposition/byUrn/' + urn) json_response = json.loads(self.open_url(detail_url)) + title = utils.try_get(json_response, ['episode', 'title'], str, urn) + chapter_list = utils.try_get( json_response, 'chapterList', data_type=list, default=[]) if not chapter_list: @@ -1052,12 +1056,21 @@ def play_video(self, media_id_or_urn): } mf_type = 'hls' + drm = False for resource in resource_list: + if utils.try_get(resource, 'drmList', data_type=list, default=[]): + drm = True + break + if utils.try_get(resource, 'protocol') == 'HLS': for key in ('SD', 'HD'): if utils.try_get(resource, 'quality') == key: stream_urls[key] = utils.try_get(resource, 'url') + if drm: + self.play_drm(urn, title, resource_list) + return + if not stream_urls['SD'] and not stream_urls['HD']: self.log('play_video: no stream URL found.') return @@ -1105,7 +1118,6 @@ def play_video(self, media_id_or_urn): new_query, parsed_url.fragment) auth_url = surl_result.geturl() self.log(f'play_video, auth_url = {auth_url}') - title = utils.try_get(json_response, ['episode', 'title'], str, urn) play_item = xbmcgui.ListItem(title, path=auth_url) if self.subtitles: subs = self.get_subtitles(stream_url, urn) @@ -1118,6 +1130,40 @@ def play_video(self, media_id_or_urn): xbmcplugin.setResolvedUrl(self.handle, True, play_item) + def play_drm(self, urn, title, resource_list): + self.log(f'play_drm: urn = {urn}') + preferred_quality = 'HD' if self.prefer_hd else 'SD' + for resource in resource_list: + url = utils.try_get(resource, 'url') + quality = utils.try_get(resource, 'quality') + lic_url = '' + if utils.try_get(resource, 'protocol') == 'DASH': + drmlist = utils.try_get( + resource, 'drmList', data_type=list, default=[]) + for item in drmlist: + if utils.try_get(item, 'type') == 'WIDEVINE': + lic_url = utils.try_get(item, 'licenseUrl') + if lic_url and quality == preferred_quality: + break + + if not url or not lic_url: + self.log('play_drm: No stream found') + return + + helper = inputstreamhelper.Helper('mpd', drm='com.widevine.alpha') + if not helper.check_inputstream(): + self.log('play_drm: Unable to setup drm') + return + + play_item = xbmcgui.ListItem(title, path=self.get_auth_url(url)) + ia = 'inputstream.adaptive' + play_item.setProperty('inputstream', ia) + lic_key = f'{lic_url}|Content-Type=application/octet-stream|R{{SSM}}|' + play_item.setProperty(f'{ia}.manifest_type', 'mpd') + play_item.setProperty(f'{ia}.license_type', 'com.widevine.alpha') + play_item.setProperty(f'{ia}.license_key', lic_key) + xbmcplugin.setResolvedUrl(self.handle, True, play_item) + def get_subtitles(self, url, name): """ Returns subtitles from an url From b5f1f14d91b5e17641a2d7844fa632cf73bacccf Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Thu, 23 Jun 2022 16:22:42 +0200 Subject: [PATCH 3/5] Python version in checks --- .github/workflows/addoncheck-matrix.yml | 2 +- .github/workflows/addoncheck-nexus.yml | 2 +- .github/workflows/flake8.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/addoncheck-matrix.yml b/.github/workflows/addoncheck-matrix.yml index 7a8d89c..18145fb 100644 --- a/.github/workflows/addoncheck-matrix.yml +++ b/.github/workflows/addoncheck-matrix.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v1 with: - python-version: 3.10.4 + python-version: 3.10.5 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/addoncheck-nexus.yml b/.github/workflows/addoncheck-nexus.yml index 77bcd8f..44df1bd 100644 --- a/.github/workflows/addoncheck-nexus.yml +++ b/.github/workflows/addoncheck-nexus.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v1 with: - python-version: 3.10.4 + python-version: 3.10.5 - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index bf9949f..3a8a4a7 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Python 3.10.4 uses: actions/setup-python@v1 with: - python-version: 3.10.4 + python-version: 3.10.5 - name: Install dependencies run: | python -m pip install --upgrade pip From c4bd60a3a87d777d0a295d4e2ca8bac8dbe17434 Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Fri, 24 Jun 2022 05:59:55 +0200 Subject: [PATCH 4/5] Simplify --- lib/srgssr.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index c7eb023..fe49354 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -1150,7 +1150,9 @@ def play_drm(self, urn, title, resource_list): self.log('play_drm: No stream found') return - helper = inputstreamhelper.Helper('mpd', drm='com.widevine.alpha') + manifest_type = 'mpd' + drm = 'com.widevine.alpha' + helper = inputstreamhelper.Helper(manifest_type, drm=drm) if not helper.check_inputstream(): self.log('play_drm: Unable to setup drm') return @@ -1159,8 +1161,8 @@ def play_drm(self, urn, title, resource_list): ia = 'inputstream.adaptive' play_item.setProperty('inputstream', ia) lic_key = f'{lic_url}|Content-Type=application/octet-stream|R{{SSM}}|' - play_item.setProperty(f'{ia}.manifest_type', 'mpd') - play_item.setProperty(f'{ia}.license_type', 'com.widevine.alpha') + play_item.setProperty(f'{ia}.manifest_type', manifest_type) + play_item.setProperty(f'{ia}.license_type', drm) play_item.setProperty(f'{ia}.license_key', lic_key) xbmcplugin.setResolvedUrl(self.handle, True, play_item) From f85bcbc546f9fea229acd3af64927c7215412a7e Mon Sep 17 00:00:00 2001 From: Alexander Seiler Date: Fri, 24 Jun 2022 06:51:55 +0200 Subject: [PATCH 5/5] Fix extraction when preferred quality does not match --- lib/srgssr.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/srgssr.py b/lib/srgssr.py index fe49354..7d0ca4f 100644 --- a/lib/srgssr.py +++ b/lib/srgssr.py @@ -1133,8 +1133,14 @@ def play_video(self, media_id_or_urn): def play_drm(self, urn, title, resource_list): self.log(f'play_drm: urn = {urn}') preferred_quality = 'HD' if self.prefer_hd else 'SD' + resource_data = { + 'url': '', + 'lic_url': '', + } for resource in resource_list: url = utils.try_get(resource, 'url') + if not url: + continue quality = utils.try_get(resource, 'quality') lic_url = '' if utils.try_get(resource, 'protocol') == 'DASH': @@ -1143,10 +1149,12 @@ def play_drm(self, urn, title, resource_list): for item in drmlist: if utils.try_get(item, 'type') == 'WIDEVINE': lic_url = utils.try_get(item, 'licenseUrl') - if lic_url and quality == preferred_quality: + resource_data['url'] = url + resource_data['lic_url'] = lic_url + if resource_data['lic_url'] and quality == preferred_quality: break - if not url or not lic_url: + if not resource_data['url'] or not resource_data['lic_url']: self.log('play_drm: No stream found') return @@ -1157,10 +1165,12 @@ def play_drm(self, urn, title, resource_list): self.log('play_drm: Unable to setup drm') return - play_item = xbmcgui.ListItem(title, path=self.get_auth_url(url)) + play_item = xbmcgui.ListItem( + title, path=self.get_auth_url(resource_data['url'])) ia = 'inputstream.adaptive' play_item.setProperty('inputstream', ia) - lic_key = f'{lic_url}|Content-Type=application/octet-stream|R{{SSM}}|' + lic_key = f'{resource_data["lic_url"]}|' \ + 'Content-Type=application/octet-stream|R{SSM}|' play_item.setProperty(f'{ia}.manifest_type', manifest_type) play_item.setProperty(f'{ia}.license_type', drm) play_item.setProperty(f'{ia}.license_key', lic_key)