From 7cc6237e8a78eb1ef38cadb6b0ed6441b2dcb9a7 Mon Sep 17 00:00:00 2001 From: fayer3 Date: Wed, 18 Mar 2020 13:19:42 +0100 Subject: [PATCH] v1.3.2: - add livestreams for ORF III and ORF Sport+ - alternative DRM format for widevine is now disabled by default, because it seems like it is not available any more. If it is still activated, a request is attempted, if failed the default format is used instead. - try to fix Playready support - choose between DASH and SmoothStreaming. SmoothStreaming should give better quality, but doesn't work currently. --- addon.xml | 11 ++- changelog.txt | 8 +- .../resource.language.de_de/strings.po | 14 ++-- .../resource.language.en_gb/strings.po | 9 ++- resources/lib/ids.py | 26 +++--- resources/lib/kodilogging.py | 11 ++- resources/lib/plugin.py | 80 ++++++++++++------- resources/settings.xml | 4 +- 8 files changed, 107 insertions(+), 56 deletions(-) diff --git a/addon.xml b/addon.xml index 78f2c5d..1afe5a2 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -30,7 +30,14 @@ - Bei Favoriten von älteren Versionen wird ein "Legacy" hinweis angezeigt. Solange die alte Api noch funktionert, und die Sendung dort vorhanden war, wird ein Link auf die neue Api erstellt. 1.3.1: - fehler mit single Threading behoben -- Abhängigkeiten minimum Version hinzugefügt, behebt vielleicht automatische Installation +- Abhängigkeiten minimum Version hinzugefügt, behebt vielleicht automatische Installation +1.3.2: +- Livestreams von ORF III und ORF Sport+ hinzugefügt +- alternatives DRM Format für widevine ist nun standardmäßig deaktiviert, weil es scheinbar nicht mehr verfügbar ist. + Ist es dennoch aktiviert, wird versucht es abzurufen, bei Fehlschlag wird auf das standard Format zurrückgegriffen. +- Versuch Playready support zu repariert. + - auswahl zwishen DASH und SmoothStreaming. SmoothStreaming hat zwar die bessere Qualität, funktioniert momentan aber nicht. + resources/icon.png resources/fanart.png diff --git a/changelog.txt b/changelog.txt index c34fb55..31b9450 100644 --- a/changelog.txt +++ b/changelog.txt @@ -49,4 +49,10 @@ v1.3.0: update to new API - favourites of older versions are now marked with "Legacy". As long as the old Api works and that show is available there, a link to the new API is created. v1.3.1: - fix error with single threading -- add minimum version for dependencies, maybe fixes automatic installation \ No newline at end of file +- add minimum version for dependencies, maybe fixes automatic installation +v1.3.2: +- add livestreams for ORF III and ORF Sport+ +- alternative DRM format for widevine is now disabled by default, because it seems like it is not available any more. + If it is still activated, a request is attempted, if failed the default format is used instead. +- try to fix Playready support + - choose between DASH and SmoothStreaming. SmoothStreaming should give better quality, but doesn't work currently. \ No newline at end of file diff --git a/resources/language/resource.language.de_de/strings.po b/resources/language/resource.language.de_de/strings.po index 936e686..cb2eadf 100644 --- a/resources/language/resource.language.de_de/strings.po +++ b/resources/language/resource.language.de_de/strings.po @@ -99,7 +99,7 @@ msgid "'{0}' is not installed" msgstr "'{0}' ist nicht installiert" msgctxt "#32021" -msgid "'inputstream.adaptive' is failed to install. Would you like to switch to a format that doesn't require that?" +msgid "'inputstream.adaptive' failed to install. Would you like to switch to a format that doesn't require that?" msgstr "Die Installation von 'inputstream.adaptive' schlug fehl. Willst du auf ein Format wechseln, welches dies nicht benötigt?" msgctxt "#32022" @@ -116,8 +116,7 @@ msgstr "Version '2.2.2' oder höher wird benötigt, um DRM geschützte Inhalt da msgctxt "#32025" msgid "DRM content requires Kodi 18.0" -msgstr " Für DRM Inhalte wird Kodi 18.0 benötigt" - +msgstr "Für DRM Inhalte wird Kodi 18.0 benötigt" msgctxt "#32026" msgid "Austria" @@ -179,6 +178,7 @@ msgctxt "#32040" msgid "new location" msgstr "neue Position" + msgctxt "#32101" msgid "Debug" msgstr "" @@ -192,8 +192,8 @@ msgid "WIDEVINE" msgstr "" msgctxt "#32104" -msgid "PLAYREADY (Android only)" -msgstr "PLAYREADY (nur Android)" +msgid "PLAYREADY with dash (Android only)" +msgstr "PLAYREADY mit dash (nur Android)" msgctxt "#32105" msgid "alternative format for DRM content (720p with widevine)" @@ -238,3 +238,7 @@ msgstr "video (niedrigere Qualität)" msgctxt "#32115" msgid "keep season and episode number in video title" msgstr "behalte Staffel und Episoden Nummer im Videotitel" + +msgctxt "#32116" +msgid "PLAYREADY with SmoothStreaming (Android only)" +msgstr "PLAYREADY mit SmoothStreaming (nur Android)" diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index d3a25ac..930931b 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -99,7 +99,7 @@ msgid "inputstream.adaptive' is not installed" msgstr "" msgctxt "#32021" -msgid "'inputstream.adaptive' is failed to install. Would you like to switch to a format that doesn't require that?" +msgid "'inputstream.adaptive' failed to install. Would you like to switch to a format that doesn't require that?" msgstr "" msgctxt "#32022" @@ -178,6 +178,7 @@ msgctxt "#32040" msgid "new location" msgstr "" + msgctxt "#32101" msgid "Debug" msgstr "" @@ -191,7 +192,7 @@ msgid "WIDEVINE" msgstr "" msgctxt "#32104" -msgid "PLAYREADY (Android only)" +msgid "PLAYREADY with dash (Android only)" msgstr "" msgctxt "#32105" @@ -237,3 +238,7 @@ msgstr "" msgctxt "#32115" msgid "keep season and episode number in video title" msgstr "" + +msgctxt "#32116" +msgid "PLAYREADY with SmoothStreaming (Android only)" +msgstr "" \ No newline at end of file diff --git a/resources/lib/ids.py b/resources/lib/ids.py index ad6478e..1e11a8d 100644 --- a/resources/lib/ids.py +++ b/resources/lib/ids.py @@ -51,6 +51,8 @@ u'popuptv2': u'popuptv2', u'orf1': u'orf1-at', u'orf2': u'orf2-at', + u'orfiii': u'orf3-at', + u'orfsportplus': u'orf-sport-plus-at', u'atv': u'atv-24x7', u'atv2': u'atv2-24x7' } @@ -93,6 +95,8 @@ default_streams = [('orf1','ORF 1'), ('orf2','ORF 2'), + ('orfiii','ORF III'), + ('orfsportplus','ORF Sport +'), ('puls4_at','PULS 4'), ('popuptv','Puls 24'), ('atv','ATV'), @@ -113,7 +117,10 @@ legacy_categories_request_url = u'https://admin.applicaster.com/v12/accounts/357/broadcasters/410/categories/{id}.json?&api[ver]=1.2&api[bundle]=at.zappn&api[bver]=2.0.3&api[os_type]=android&api[os_version]=25&api[store]=android' -overview_url = u'https://assets-secure.applicaster.com/zapp/accounts/5818cf82279a4a000ff1b6aa/apps/at.zappn/google_play/2.0.3/rivers/rivers.json' +overview_url = u'https://assets-secure.applicaster.com/zapp/accounts/5818cf82279a4a000ff1b6aa/apps/at.zappn/google_play/2.0.5/rivers/rivers.json' + +user_agent_video = u'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36' +user_agent_live = u'vvs-native-android/2.0.5 (Linux;Android 7.1.1) ExoPlayerLib/2.8.1' api_url = u'https://middleware.p7s1.io/zappn/v1' api_limit = 500 @@ -153,8 +160,14 @@ def get_livestream_config_url(livestream_id): livestream_id=convert_channel(livestream_id) - if livestream_id == u'orf1' or livestream_id == u'orf2' or livestream_id == u'kronehittv': + if u'orf' in livestream_id or livestream_id == u'kronehittv': return live_config_url+live_config_ids[u'orf'] + elif u'kabeleins' in livestream_id: + return live_config_url+live_config_ids[livestream_id.replace(u'kabeleins', u'kabel1')] + elif u'prosiebenmaxx' in livestream_id: + return live_config_url+live_config_ids[livestream_id.replace(u'prosiebenmaxx', u'prosieben_maxx')] + elif u'sat1gold' in livestream_id: + return live_config_url+live_config_ids[livestream_id.replace(u'sat1gold', u'sat1_gold')] elif (livestream_id == u'atv' or livestream_id == u'atv2' or livestream_id == u'puls4_at' @@ -162,14 +175,9 @@ def get_livestream_config_url(livestream_id): or livestream_id == u'popuptv2' or livestream_id == u'servustv' or livestream_id == u'schautv' - or livestream_id == u'ric'): + or livestream_id == u'ric' + or livestream_id not in live_config_ids): return live_config_url+live_config_ids[u'puls4_and_atv_at'] - elif u'kabeleins' in livestream_id: - return live_config_url+live_config_ids[livestream_id.replace(u'kabeleins', u'kabel1')] - elif u'prosiebenmaxx' in livestream_id: - return live_config_url+live_config_ids[livestream_id.replace(u'prosiebenmaxx', u'prosieben_maxx')] - elif u'sat1gold' in livestream_id: - return live_config_url+live_config_ids[livestream_id.replace(u'sat1gold', u'sat1_gold')] else: return live_config_url+live_config_ids[livestream_id] diff --git a/resources/lib/kodilogging.py b/resources/lib/kodilogging.py index 06f00d3..8c459d4 100644 --- a/resources/lib/kodilogging.py +++ b/resources/lib/kodilogging.py @@ -26,12 +26,11 @@ def emit(self, record): logging.DEBUG: xbmc.LOGDEBUG, logging.NOTSET: xbmc.LOGNONE, } - if get_setting_as_bool('debug'): - try: - xbmc.log(self.format(record), levels[record.levelno]) - except UnicodeEncodeError: - xbmc.log(self.format(record).encode( - 'utf-8', 'ignore'), levels[record.levelno]) + try: + xbmc.log(self.format(record), levels[record.levelno]) + except UnicodeEncodeError: + xbmc.log(self.format(record).encode( + 'utf-8', 'ignore'), levels[record.levelno]) def flush(self): pass diff --git a/resources/lib/plugin.py b/resources/lib/plugin.py index 28c910d..7c9eea2 100644 --- a/resources/lib/plugin.py +++ b/resources/lib/plugin.py @@ -725,12 +725,15 @@ def play_livestream(livestream_id): xbmcgui.Dialog().ok(heading=kodiutils.get_string(32023), line1=kodiutils.get_string(32024)) setResolvedUrl(plugin.handle, False, ListItem(label='none')) else: - playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=urls['urls']['dash'][drm_name]['url']+'|User-Agent=vvs-native-android/1.0.10 (Linux;Android 7.1.1) ExoPlayerLib/2.8.1') + playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=urls['urls']['dash'][drm_name]['url']+'|User-Agent=' + ids.user_agent_live) playitem.setProperty('inputstreamaddon', is_helper.inputstream_addon) playitem.setProperty('inputstream.adaptive.manifest_type', 'mpd') playitem.setProperty('inputstream.adaptive.license_type', drm) playitem.setProperty('inputstream.adaptive.manifest_update_parameter', 'full') - playitem.setProperty('inputstream.adaptive.license_key', urls['urls']['dash'][drm_name]['drm']['licenseAcquisitionUrl'] + '?token=' + urls['urls']['dash'][drm_name]['drm']['token'] +'|User-Agent=vvs-native-android/1.0.10 (Linux;Android 7.1.1) ExoPlayerLib/2.8.1' +'|R{SSM}|') + if (drm == 'com.microsoft.playready'): + playitem.setProperty('inputstream.adaptive.license_key', urls['urls']['dash'][drm_name]['drm']['licenseAcquisitionUrl'] + '?token=' + urls['urls']['dash'][drm_name]['drm']['token'] +'|User-Agent=' + ids.user_agent_live + '&Content-Type=text/xml' +'|R{SSM}|') + else: + playitem.setProperty('inputstream.adaptive.license_key', urls['urls']['dash'][drm_name]['drm']['licenseAcquisitionUrl'] + '?token=' + urls['urls']['dash'][drm_name]['drm']['token'] +'|User-Agent=' + ids.user_agent_live +'|R{SSM}|') setResolvedUrl(plugin.handle, True, playitem) else: kodiutils.notification('ERROR', kodiutils.get_string(32019).format(drm)) @@ -739,7 +742,7 @@ def play_livestream(livestream_id): @plugin.route('/category/video/') @plugin.route('/category/video//') -def play_video(video_id, channel=''): +def play_video(video_id, channel='', disable_old_format=False): # remove channel number if available if '_' in video_id: video_id = video_id.split('_')[1] @@ -762,20 +765,20 @@ def play_video(video_id, channel=''): sources_request = json.loads(get_url(sources_request_url, critical=True)) log('sources_request: ' + str(sources_request)) protected = sources_request[0]['is_protected'] - mpd_id = 0 - m3u8_id = 0 - ism_id = 0 - mp4_id = 0 + mpd_id = [] + m3u8_id = [] + ism_id = [] + mp4_id = [] protocol = '' for source in sources_request[0]['sources']: if source['mimetype'] == 'text/xml': - ism_id = source['id'] + ism_id.append(source['id']) if source['mimetype'] == 'application/x-mpegURL': - m3u8_id = source['id'] + m3u8_id.append(source['id']) if source['mimetype'] == 'application/dash+xml': - mpd_id = source['id'] + mpd_id.append(source['id']) if source['mimetype'] == 'video/mp4': - mp4_id = source['id'] + mp4_id.append(source['id']) drm = None if not protected: if kodiutils.get_setting_as_int('non_drm_format') == 0: @@ -793,6 +796,11 @@ def play_video(video_id, channel=''): protocol = 'mpd' drm_name = 'widevine' drm = 'com.widevine.alpha' + elif kodiutils.get_setting('drm') == '1': + source_id = mpd_id + protocol = 'mpd' + drm_name = 'playready' + drm = 'com.microsoft.playready' else: source_id = ism_id protocol = 'ism' @@ -812,26 +820,36 @@ def play_video(video_id, channel=''): start = '' end = '' - if protected and kodiutils.get_setting('drm') == '0' and kodiutils.get_setting_as_bool('oldformat'): + if protected and kodiutils.get_setting('drm') == '0' and kodiutils.get_setting_as_bool('oldformat') and not disable_old_format: start = '0' end = '999999999' - - source_url_request_token = get_video_source_request_token(access_token=mdsV2['accessToken'], client_location='null', client_name=mdsV2['clientName'], server_id= server_id, source_id=source_id, video_id=video_id, salt=mdsV2['salt'], start=start, end=end) - source_url_request_url = mdsV2['baseUrl']+'vas/live/v2/videos/%s/sources/url?access_token=%s&client_id=%s&client_location=null&client_name=%s&secure_delivery=true&server_id=%s&source_ids=%s' % (video_id, mdsV2['accessToken'], source_url_request_token, mdsV2['clientName'], server_id, source_id) - - if protected and kodiutils.get_setting('drm') == '0'and kodiutils.get_setting_as_bool('oldformat'): - source_url_request_url += '&subclip_start=0&subclip_end=999999999' - - source_url_request = json.loads(get_url(source_url_request_url, critical=True)) - if not 'status_code' in source_url_request or source_url_request['status_code'] != 0: - log('error on video request: ' + str(source_url_request)) - return sys.exit(0) + source_url_request = {} + for s_id in source_id: + source_url_request_token = get_video_source_request_token(access_token=mdsV2['accessToken'], client_location='null', client_name=mdsV2['clientName'], server_id= server_id, source_id=s_id, video_id=video_id, salt=mdsV2['salt'], start=start, end=end) + source_url_request_url = mdsV2['baseUrl']+'vas/live/v2/videos/%s/sources/url?access_token=%s&client_id=%s&client_location=null&client_name=%s&secure_delivery=true&server_id=%s&source_ids=%s' % (video_id, mdsV2['accessToken'], source_url_request_token, mdsV2['clientName'], server_id, s_id) + + if protected and kodiutils.get_setting('drm') == '0'and kodiutils.get_setting_as_bool('oldformat') and not disable_old_format: + source_url_request_url += '&subclip_start=0&subclip_end=999999999' + + source_url_request = json.loads(get_url(source_url_request_url, critical=True)) + if not 'status_code' in source_url_request or source_url_request['status_code'] != 0: + log('error on video request: ' + str(source_url_request)) + if protected and kodiutils.get_setting('drm') == '0' and kodiutils.get_setting_as_bool('oldformat') and not disable_old_format: + play_video(video_id, channel=channel, disable_old_format=True) + return + kodiutils.notification('ERROR', 'code: {0}'.format(source_url_request['status_code'])) + return sys.exit(0) + if protected: + if drm_name == source_url_request['drm']['type']: + break + else: + break playitem = ListItem('none') log('selected non drm format: ' + kodiutils.get_setting('non_drm_format')) log('media url: ' + source_url_request['sources'][0]['url']) if not protected and (kodiutils.get_setting_as_int('non_drm_format') == 2 or kodiutils.get_setting_as_int('non_drm_format') == 3): - playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=source_url_request['sources'][0]['url']+'|User-Agent=Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36') + playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=source_url_request['sources'][0]['url']+'|User-Agent=' + ids.user_agent_video) setResolvedUrl(plugin.handle, True, playitem) else: is_helper = None @@ -865,12 +883,16 @@ def play_video(video_id, channel=''): xbmcgui.Dialog().ok(heading=kodiutils.get_string(32023), line1=kodiutils.get_string(32024)) setResolvedUrl(plugin.handle, False, ListItem(label='none')) else: - playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=source_url_request['sources'][0]['url']+'|User-Agent=Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36') + playitem = ListItem(label=xbmc.getInfoLabel('Container.ShowTitle'), path=source_url_request['sources'][0]['url']+'|User-Agent=' + ids.user_agent_video) playitem.setProperty('inputstreamaddon', is_helper.inputstream_addon) playitem.setProperty('inputstream.adaptive.manifest_type', protocol) if protected: + log('license url: {0}?token={1}'.format(source_url_request['drm']['licenseAcquisitionUrl'], source_url_request['drm']['token'])) playitem.setProperty('inputstream.adaptive.license_type', drm) - playitem.setProperty('inputstream.adaptive.license_key', source_url_request['drm']['licenseAcquisitionUrl'] + '?token=' + source_url_request['drm']['token'] +'|User-Agent=Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36' +'|R{SSM}|') + if (drm == 'com.microsoft.playready'): + playitem.setProperty('inputstream.adaptive.license_key', source_url_request['drm']['licenseAcquisitionUrl'] + '?token=' + source_url_request['drm']['token'] +'|User-Agent=' + ids.user_agent_video + '&Content-Type=text/xml' +'|R{SSM}|') + else: + playitem.setProperty('inputstream.adaptive.license_key', source_url_request['drm']['licenseAcquisitionUrl'] + '?token=' + source_url_request['drm']['token'] +'|User-Agent=' + ids.user_agent_video +'|R{SSM}|') setResolvedUrl(plugin.handle, True, playitem) else: if drm: @@ -1033,9 +1055,9 @@ def get_url(url, headers={}, cache=False, critical=False): # decompress content buffer = StringIO(request.read()) deflatedContent = gzip.GzipFile(fileobj=buffer) - data = deflatedContent.read() + data = deflatedContent.read().decode('utf-8') else: - data = request.read() + data = request.read().decode('utf-8') if cache: data = xxtea.decryptBase64StringToStringss(data, ids.xxtea_key) @@ -1153,5 +1175,5 @@ def run(): plugin.run() def log(info): - if kodiutils.get_setting_as_bool('debug'): + if kodiutils.get_setting_as_bool('debug') or xbmc.getCondVisibility('System.GetBool(debug.showloginfo)'): logger.warning(info) diff --git a/resources/settings.xml b/resources/settings.xml index fb66516..2ae275d 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -2,8 +2,8 @@ - - + +