def update_live_stream(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 """ part = item.create_new_empty_media_part() if item.url == "#livetv": url = "https://d34pj260kw1xmk.cloudfront.net/live/l1/tv/index.m3u8" M3u8.update_part_with_m3u8_streams(part, url, encrypted=True, proxy=self.proxy) else: # the audio won't play with the InputStream Adaptive add-on. url = "https://d34pj260kw1xmk.cloudfront.net/live/l1/radio/index.m3u8" for s, b in M3u8.get_streams_from_m3u8(url, self.proxy): part.append_media_stream(s, b) item.complete = True return item
def update_live_stream(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("Updating the live stream") url = "https://rrr.sz.xlcdn.com/?account=atvijf" \ "&file=live&type=live&service=wowza&protocol=https&output=playlist.m3u8" part = item.create_new_empty_media_part() item.complete = \ M3u8.update_part_with_m3u8_streams(part, url, proxy=self.proxy, channel=self) return item
def create_video_item_json(self, result_set): """ Creates a MediaItem of type 'video' using the result_set from the regex. This method creates a new MediaItem from the Regular Expression or Json results <result_set>. The method should be implemented by derived classes and are specific to the channel. If the item is completely processed an no further data needs to be fetched the self.complete property should be set to True. If not set to True, the self.update_video_item method is called if the item is focussed or selected for playback. :param dict result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'video' or 'audio' (despite the method's name). :rtype: MediaItem|None """ Logger.trace(result_set) image_data = result_set.get("media", []) thumb = None url = None for image in image_data: thumb = image.get("imageHigh", image["image"]) url = image.get("url") item = MediaItem(result_set["title"], url) item.type = "video" item.icon = self.icon item.thumb = thumb or self.noImage item.complete = True item.description = result_set.get("text") part = item.create_new_empty_media_part() M3u8.update_part_with_m3u8_streams(part, url, proxy=self.proxy, channel=self) # Let's not do the time now time_stamp = result_set["created"] date_time = DateHelper.get_date_from_posix(time_stamp) item.set_date(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second) 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) xml_data = UriHandler.open(item.url, proxy=self.proxy) # <ref type='adaptive' device='pc' host='http://manifest.us.rtl.nl' href='/rtlxl/network/pc/adaptive/components/videorecorder/27/278629/278630/d009c025-6e8c-3d11-8aba-dc8579373134.ssm/d009c025-6e8c-3d11-8aba-dc8579373134.m3u8' /> m3u8_urls = Regexer.do_regex( "<ref type='adaptive' device='pc' host='([^']+)' href='/([^']+)' />", xml_data) if not m3u8_urls: Logger.warning("No m3u8 data found for: %s", item) return item m3u8_url = "%s/%s" % (m3u8_urls[0][0], m3u8_urls[0][1]) part = item.create_new_empty_media_part() # prevent the "418 I'm a teapot" error part.HttpHeaders[ "user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0" # Remove the Range header to make all streams start at the beginning. # Logger.debug("Setting an empty 'Range' http header to force playback at the start of a stream") # part.HttpHeaders["Range"] = '' item.complete = M3u8.update_part_with_m3u8_streams( part, m3u8_url, proxy=self.proxy, headers=part.HttpHeaders, channel=self) return item
def __update_item_from_video_references(self, item, videos, subtitles=None): # NOSONAR """ :param MediaItem item: The original MediaItem that needs updating. :param list[any] videos: Videos to add. :param dict subtitles: Subtitle information. :return: Updated MediaItem :rtype: MediaItem """ item.MediaItemParts = [] part = item.create_new_empty_media_part() if self.localIP: part.HttpHeaders.update(self.localIP) use_input_stream = AddonSettings.use_adaptive_stream_add_on( channel=self) for video in videos: video_format = video.get("format", "") if not video_format: video_format = video.get("playerType", "") video_format = video_format.lower() if ("dash" in video_format and not video_format == "dash") or "hds" in video_format: Logger.debug("Skipping video format: %s", video_format) continue Logger.debug("Found video item for format: %s", video_format) url = video['url'] if any(filter(lambda s: s.Url == url, part.MediaStreams)): Logger.debug("Skippping duplicate Stream url: %s", url) continue if video_format == "dash" and use_input_stream: stream = part.append_media_stream(video['url'], 1) Mpd.set_input_stream_addon_input(stream, self.proxy) elif "m3u8" in url: alt_index = url.find("m3u8?") if alt_index > 0: url = url[0:alt_index + 4] if "-fmp4.m3u8" in url or "-lowbw.m3u8" in url: Logger.trace("Ignoring: %s", url) continue M3u8.update_part_with_m3u8_streams(part, url, encrypted=False, proxy=self.proxy, headers=part.HttpHeaders, channel=self) elif video["url"].startswith("rtmp"): # just replace some data in the URL part.append_media_stream( self.get_verifiable_video_url(video["url"]).replace( "_definst_", "?slist="), video[1]) else: part.append_media_stream(url, 0) if subtitles: Logger.info("Found subtitles to play") for sub in subtitles: sub_format = sub["format"].lower() url = sub["url"] if sub_format == "websrt": sub_url = url # elif subFormat == "webvtt": # Logger.Info("Found M3u8 subtitle, replacing with WSRT") # start, name, index = sub[-1].rsplit("/", 2) # subUrl = "%s/%s/%s.wsrt" % (start, name, name) else: # look for more continue part.Subtitle = subtitlehelper.SubtitleHelper.download_subtitle( sub_url, format="srt", proxy=self.proxy) # stop when finding one break item.complete = True 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) data = UriHandler.open(item.url, proxy=self.proxy) json_data, _ = self.extract_json(data) video_id = json_data.get_value("versions", 0, "id") stream_data_url = "http://open.live.bbc.co.uk/mediaselector/5/select/version/2.0/mediaset/iptv-all/vpid/{}".format( video_id) # this URL is one from the webbrowser but requires a security part. So NOT: # streamDataUrl = "http://open.live.bbc.co.uk/mediaselector/5/select/version # /2.0/mediaset/pc/vpid/%s" % (vid,) # # but: # streamDataUrl = "http://open.live.bbc.co.uk/mediaselector/5/select/version # /2.0/mediaset/pc/vpid/%s/atk/2214e42b5729dcdd012dfb61a3054d39309ccd31/asn/1/ # And I don't know where that one comes from part = item.create_new_empty_media_part() stream_data = UriHandler.open(stream_data_url, proxy=self.proxy) # Reroute for debugging # from debug.router import Router # streamData = Router.get_via("uk", streamDataUrl, self.proxy) connection_datas = Regexer.do_regex( r'<media bitrate="(\d+)"[^>]+>\W*' r'(<connection[^>]+>\W*)' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?' r'(<connection[^>]+>\W*)?</media>', stream_data) for connection_data in connection_datas: # first the bitrate bitrate = int(connection_data[0]) Logger.trace("Found Media: %s", connection_data) # go through the available connections for connection in connection_data[1:]: if not connection: continue connection_xml = XmlHelper(connection) stream_bitrate = bitrate Logger.trace("Analyzing Connection: %s", connection) supplier = connection_xml.get_tag_attribute( "connection", {"supplier": None}) protocol = connection_xml.get_tag_attribute( "connection", {"protocol": None}) transfer_format = connection_xml.get_tag_attribute( "connection", {"transferFormat": None}) Logger.debug( "Found connection information:\n" "Protocol: %s\n" "TransferFormat: %s\n" "Supplier: %s\n" "Bitrate: %s", protocol, transfer_format, supplier, bitrate) if protocol.startswith("http"): if transfer_format != "hls": # and transfer_format != "dash": Logger.debug("Ignoring TransferFormat: %s", transfer_format) continue if "lime" in supplier or "mf_akamai_uk" in supplier: # Prefer others stream_bitrate -= 1 # Logger.debug("Ignoring Supplier: %s", supplier) # continue url = connection_xml.get_tag_attribute( "connection", {"href": None}) elif protocol.startswith("rtmp"): Logger.warning("Ignoring RTMP for now") continue else: Logger.warning("Unknown protocol: %s", protocol) continue if transfer_format == "hls": item.complete = M3u8.update_part_with_m3u8_streams( part, url, proxy=self.proxy, bitrate=stream_bitrate) elif transfer_format == "dash": strm = part.append_media_stream(url, bitrate) Mpd.set_input_stream_addon_input(strm, self.proxy) # get the subtitle subtitles = Regexer.do_regex( '<connection href="(http://www.bbc.co.uk/iplayer/subtitles/[^"]+/)([^/]+.xml)"', stream_data) if len(subtitles) > 0: subtitle = subtitles[0] subtitle_url = "%s%s" % (subtitle[0], subtitle[1]) part.Subtitle = subtitlehelper.SubtitleHelper.download_subtitle( subtitle_url, subtitle[1], "ttml", proxy=self.proxy) item.complete = True Logger.trace('finishing update_video_item: %s.', item) 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