def __update_video_from_mpd(self, item, mpd_info, use_adaptive_with_encryption): """ Updates an existing MediaItem with more data based on an MPD stream. :param dict[str,str] mpd_info: Stream info retrieved from the stream json. :param bool use_adaptive_with_encryption: Do we use the Adaptive InputStream add-on? :param MediaItem item: The original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug("Updating streams using BrightCove data.") part = item.create_new_empty_media_part() mpd_manifest_url = "https:{0}".format(mpd_info["mediaLocator"]) mpd_data = UriHandler.open(mpd_manifest_url, proxy=self.proxy) subtitles = Regexer.do_regex(r'<BaseURL>([^<]+\.vtt)</BaseURL>', mpd_data) if subtitles: Logger.debug("Found subtitle: %s", subtitles[0]) subtitle = SubtitleHelper.download_subtitle(subtitles[0], proxy=self.proxy, format="webvtt") part.Subtitle = subtitle if use_adaptive_with_encryption: # We can use the adaptive add-on with encryption Logger.info("Using MPD InputStreamAddon") license_url = Regexer.do_regex('licenseUrl="([^"]+)"', mpd_data)[0] token = "Bearer {0}".format(mpd_info["playToken"]) key_headers = {"Authorization": token} license_key = Mpd.get_license_key(license_url, key_headers=key_headers) stream = part.append_media_stream(mpd_manifest_url, 0) Mpd.set_input_stream_addon_input(stream, self.proxy, license_key=license_key) item.complete = True else: XbmcWrapper.show_dialog( LanguageHelper.get_localized_string(LanguageHelper.DrmTitle), LanguageHelper.get_localized_string( LanguageHelper.WidevineLeiaRequired)) return item
def update_video_item(self, item): # NOSONAR """ Updates an existing MediaItem with more data. Used to update none complete MediaItems (self.complete = False). This could include opening the item's URL to fetch more data and then process that data or retrieve it's real media-URL. The method should at least: * cache the thumbnail to disk (use self.noImage if no thumb is available). * set at least one MediaItemPart with a single MediaStream. * set self.complete = True. if the returned item does not have a MediaItemPart then the self.complete flag will automatically be set back to False. :param MediaItem item: the original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug('Starting update_video_item for %s (%s)', item.name, self.channelName) # we need to fetch the actual url as it might differ for single video items data, secure_url = UriHandler.header(item.url, proxy=self.proxy) # Get the MZID secure_url = secure_url.rstrip("/") secure_url = "%s.mssecurevideo.json" % (secure_url, ) data = UriHandler.open(secure_url, proxy=self.proxy, additional_headers=item.HttpHeaders) secure_data = JsonHelper(data, logger=Logger.instance()) mzid = secure_data.get_value( list(secure_data.json.keys())[0], "videoid") # region New URL retrieval with DRM protection # We need a player token token_data = UriHandler.open( "https://media-services-public.vrt.be/" "vualto-video-aggregator-web/rest/external/v1/tokens", data="", additional_headers={"Content-Type": "application/json"}) token = JsonHelper(token_data).get_value("vrtPlayerToken") asset_url = "https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/" \ "external/v1/videos/{0}?vrtPlayerToken={1}&client=vrtvideo"\ .format(HtmlEntityHelper.url_encode(mzid), HtmlEntityHelper.url_encode(token)) asset_data = UriHandler.open(asset_url, proxy=self.proxy, no_cache=True) asset_data = JsonHelper(asset_data) drm_key = asset_data.get_value("drm") drm_protected = drm_key is not None adaptive_available = AddonSettings.use_adaptive_stream_add_on( with_encryption=drm_protected) part = item.create_new_empty_media_part() srt = None # see if we prefer hls over dash hls_prio = 2 if self._get_setting("hls_over_dash", False) else 0 for target_url in asset_data.get_value("targetUrls"): video_type = target_url["type"] video_url = target_url["url"] if video_type == "hls_aes" and drm_protected and adaptive_available: # no difference in encrypted or not. Logger.debug("Found HLS AES encrypted stream and a DRM key") stream = part.append_media_stream(video_url, 0) M3u8.set_input_stream_addon_input(stream, self.proxy) elif video_type == "hls" and not drm_protected: # no difference in encrypted or not. if adaptive_available: Logger.debug( "Found standard HLS stream and without DRM protection") stream = part.append_media_stream(video_url, hls_prio) M3u8.set_input_stream_addon_input(stream, self.proxy) else: m3u8_data = UriHandler.open(video_url, self.proxy) for s, b, a in M3u8.get_streams_from_m3u8( video_url, self.proxy, play_list_data=m3u8_data, map_audio=True): item.complete = True if a: audio_part = a.rsplit("-", 1)[-1] audio_part = "-%s" % (audio_part, ) s = s.replace(".m3u8", audio_part) part.append_media_stream(s, b) srt = M3u8.get_subtitle(video_url, play_list_data=m3u8_data, proxy=self.proxy) if not srt: continue srt = srt.replace(".m3u8", ".vtt") part.Subtitle = SubtitleHelper.download_subtitle( srt, format="webvtt") elif video_type == "mpeg_dash" and adaptive_available: if not drm_protected: Logger.debug( "Found standard MPD stream and without DRM protection") stream = part.append_media_stream(video_url, 1) Mpd.set_input_stream_addon_input(stream, self.proxy) else: stream = part.append_media_stream(video_url, 1) encryption_json = '{{"token":"{0}","drm_info":[D{{SSM}}],"kid":"{{KID}}"}}'\ .format(drm_key) encryption_key = Mpd.get_license_key( key_url="https://widevine-proxy.drm.technology/proxy", key_type="D", key_value=encryption_json, key_headers={ "Content-Type": "text/plain;charset=UTF-8" }) Mpd.set_input_stream_addon_input( stream, self.proxy, license_key=encryption_key) if video_type.startswith("hls") and srt is None: srt = M3u8.get_subtitle(video_url, proxy=self.proxy) if srt: srt = srt.replace(".m3u8", ".vtt") part.Subtitle = SubtitleHelper.download_subtitle( srt, format="webvtt") item.complete = True # endregion return item
def update_video_item(self, item): """ Updates an existing MediaItem with more data. Used to update none complete MediaItems (self.complete = False). This could include opening the item's URL to fetch more data and then process that data or retrieve it's real media-URL. The method should at least: * cache the thumbnail to disk (use self.noImage if no thumb is available). * set at least one MediaItemPart with a single MediaStream. * set self.complete = True. if the returned item does not have a MediaItemPart then the self.complete flag will automatically be set back to False. :param MediaItem item: the original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug('Starting update_video_item for %s (%s)', item.name, self.channelName) if not AddonSettings.use_adaptive_stream_add_on(with_encryption=False): Logger.error("Cannot playback video without adaptive stream addon") return item # https://www.foxsports.nl/api/video/videodata/2945190 data = UriHandler.open(item.url, proxy=self.proxy, additional_headers=item.HttpHeaders) video_id = Regexer.do_regex(r'data-videoid="(\d+)" ', data)[-1] data = UriHandler.open("https://www.foxsports.nl/api/video/videodata/%s" % (video_id,), proxy=self.proxy, additional_headers=item.HttpHeaders, no_cache=True) stream_id = Regexer.do_regex('<uri>([^>]+)</uri>', data)[-1] # POST https://d3api.foxsports.nl/api/V2/entitlement/tokenize post_data = { "Type": 1, "User": "", "VideoId": "{0}".format(video_id), "VideoSource": "{0}".format(stream_id), "VideoKind": "vod", "AssetState": "3", "PlayerType": "HTML5", "VideoSourceFormat": "DASH", "VideoSourceName": "DASH", # "VideoSourceFormat": "HLS", # "VideoSourceName": "HLS", "DRMType": "widevine", "AuthType": "Token", "ContentKeyData": "", "Other__": "playerName=HTML5-Web-vod|ae755267-8482-455b-9055-529b643ece1d|" "undefined|undefined|undefined|2945541|HTML5|web|diva.MajorVersion=4|" "diva.MinorVersion=2|diva.PatchVersion=13" } data = UriHandler.open("https://d3api.foxsports.nl/api/V2/entitlement/tokenize", json=post_data, no_cache=True, proxy=self.proxy) stream_info = JsonHelper(data) stream_url = stream_info.get_value("ContentUrl") if not stream_url: message = "Protected stream: {0}".format(stream_info.get_value("Message")) XbmcWrapper.show_notification(None, message, notification_type=XbmcWrapper.Error, display_time=5000) license_url = stream_info.get_value("LicenseURL") part = item.create_new_empty_media_part() stream = part.append_media_stream(stream_url, 0) license_key = Mpd.get_license_key(license_url) Mpd.set_input_stream_addon_input(stream, proxy=self.proxy, license_key=license_key) return item
def update_video_for_mzid(self, item, mzid, live=False): # NOSONAR """ Updates a video item based on the MZID :param MediaItem item: the parent item :param str mzid: the MZID """ # region New URL retrieval with DRM protection # We need a player token token_data = UriHandler.open( "https://media-services-public.vrt.be/" "vualto-video-aggregator-web/rest/external/v1/tokens", data="", additional_headers={"Content-Type": "application/json"}) token = JsonHelper(token_data).get_value("vrtPlayerToken") asset_url = "https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/" \ "external/v1/videos/{0}?vrtPlayerToken={1}&client=vrtvideo"\ .format(HtmlEntityHelper.url_encode(mzid), HtmlEntityHelper.url_encode(token)) asset_data = UriHandler.open(asset_url, proxy=self.proxy, no_cache=True) asset_data = JsonHelper(asset_data) drm_key = asset_data.get_value("drm") drm_protected = drm_key is not None adaptive_available = AddonSettings.use_adaptive_stream_add_on( with_encryption=drm_protected, channel=self) part = item.create_new_empty_media_part() srt = None # see if we prefer hls over dash hls_prio = 2 if self._get_setting("hls_over_dash", 'false') == 'true' else 0 for target_url in asset_data.get_value("targetUrls"): video_type = target_url["type"] video_url = target_url["url"] if video_type == "hls_aes" and drm_protected and adaptive_available: # no difference in encrypted or not. Logger.debug("Found HLS AES encrypted stream and a DRM key") stream = part.append_media_stream(video_url, hls_prio) M3u8.set_input_stream_addon_input(stream, self.proxy) elif video_type == "hls" and not drm_protected: # no difference in encrypted or not. if adaptive_available: Logger.debug( "Found standard HLS stream and without DRM protection") stream = part.append_media_stream(video_url, hls_prio) M3u8.set_input_stream_addon_input(stream, self.proxy) else: m3u8_data = UriHandler.open(video_url, self.proxy) for s, b, a in M3u8.get_streams_from_m3u8( video_url, self.proxy, play_list_data=m3u8_data, map_audio=True): item.complete = True if a: audio_part = a.rsplit("-", 1)[-1] audio_part = "-%s" % (audio_part, ) s = s.replace(".m3u8", audio_part) part.append_media_stream(s, b) srt = M3u8.get_subtitle(video_url, play_list_data=m3u8_data, proxy=self.proxy) if not srt or live: # If there is not SRT don't download it. If it a live stream with subs, # don't use it as it is not supported by Kodi continue srt = srt.replace(".m3u8", ".vtt") part.Subtitle = SubtitleHelper.download_subtitle( srt, format="webvtt") elif video_type == "mpeg_dash" and adaptive_available: if not drm_protected: Logger.debug( "Found standard MPD stream and without DRM protection") stream = part.append_media_stream(video_url, 1) Mpd.set_input_stream_addon_input(stream, self.proxy) else: stream = part.append_media_stream(video_url, 1) encryption_json = '{{"token":"{0}","drm_info":[D{{SSM}}],"kid":"{{KID}}"}}'\ .format(drm_key) encryption_key = Mpd.get_license_key( key_url="https://widevine-proxy.drm.technology/proxy", key_type="D", key_value=encryption_json, key_headers={ "Content-Type": "text/plain;charset=UTF-8" }) Mpd.set_input_stream_addon_input( stream, self.proxy, license_key=encryption_key) if video_type.startswith("hls") and srt is None: srt = M3u8.get_subtitle(video_url, proxy=self.proxy) if not srt or live: # If there is not SRT don't download it. If it a live stream with subs, # don't use it as it is not supported by Kodi continue srt = srt.replace(".m3u8", ".vtt") part.Subtitle = SubtitleHelper.download_subtitle( srt, format="webvtt") item.complete = True # endregion return item
def __update_embedded_video(self, item): """ Updates video items that are encrypted. This could be the default for Krypton! :param MediaItem item: The item to update. :return: An updated item. :rtype: MediaItem """ data = UriHandler.open(item.url, proxy=self.proxy) start_needle = "var playerConfig =" start_data = data.index(start_needle) + len(start_needle) end_data = data.index("var talpaPlayer") data = data[start_data:end_data].strip().rstrip(";") json = JsonHelper(data) has_drm_only = True adaptive_available = AddonSettings.use_adaptive_stream_add_on( with_encryption=False, channel=self) adaptive_available_encrypted = AddonSettings.use_adaptive_stream_add_on( with_encryption=True, channel=self) for play_list_entry in json.get_value("playlist"): part = item.create_new_empty_media_part() for source in play_list_entry["sources"]: stream_type = source["type"] stream_url = source["file"] stream_drm = source.get("drm") if not stream_drm: has_drm_only = False if stream_type == "m3u8": Logger.debug("Found non-encrypted M3u8 stream: %s", stream_url) M3u8.update_part_with_m3u8_streams(part, stream_url, proxy=self.proxy, channel=self) item.complete = True elif stream_type == "dash" and adaptive_available: Logger.debug("Found non-encrypted Dash stream: %s", stream_url) stream = part.append_media_stream(stream_url, 1) Mpd.set_input_stream_addon_input(stream, proxy=self.proxy) item.complete = True else: Logger.debug("Unknown stream source: %s", source) else: compatible_drm = "widevine" if compatible_drm not in stream_drm or stream_type != "dash": Logger.debug("Found encrypted %s stream: %s", stream_type, stream_url) continue Logger.debug("Found Widevine encrypted Dash stream: %s", stream_url) license_url = stream_drm[compatible_drm]["url"] pid = stream_drm[compatible_drm]["releasePid"] encryption_json = '{"getRawWidevineLicense":' \ '{"releasePid":"%s", "widevineChallenge":"b{SSM}"}' \ '}' % (pid,) headers = { "Content-Type": "application/json", "Origin": "https://embed.kijk.nl", "Referer": stream_url } encryption_key = Mpd.get_license_key( license_url, key_type=None, key_value=encryption_json, key_headers=headers) stream = part.append_media_stream(stream_url, 0) Mpd.set_input_stream_addon_input( stream, proxy=self.proxy, license_key=encryption_key) item.complete = True subs = [ s['file'] for s in play_list_entry.get("tracks", []) if s.get('kind') == "captions" ] if subs: subtitle = SubtitleHelper.download_subtitle(subs[0], format="webvtt") part.Subtitle = subtitle if has_drm_only and not adaptive_available_encrypted: XbmcWrapper.show_dialog( LanguageHelper.get_localized_string(LanguageHelper.DrmTitle), LanguageHelper.get_localized_string( LanguageHelper.WidevineLeiaRequired)) return item