def create_channel_item(self, channel): """ Creates a MediaItem of type 'video' for a live channel 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 list[str]|dict channel: 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(channel) title = channel["programmeTitle"] episode = channel.get("episodeTitle", None) thumb = self.noImage channel_title = channel["channelName"] description = channel.get("description") channel_id = channel["channel"].lower() if channel_id == "svtbarn": channel_id = "barnkanalen" date_format = "%Y-%m-%dT%H:%M:%S" start_time = DateHelper.get_date_from_string(channel["publishingTime"][:19], date_format) end_time = DateHelper.get_date_from_string(channel["publishingEndTime"][:19], date_format) if episode: title = "%s: %s - %s (%02d:%02d - %02d:%02d)" \ % (channel_title, title, episode, start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min) else: title = "%s: %s (%02d:%02d - %02d:%02d)" \ % (channel_title, title, start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min) channel_item = MediaItem( title, "https://www.svt.se/videoplayer-api/video/ch-%s" % (channel_id.lower(),) ) channel_item.type = "video" channel_item.description = description channel_item.isLive = True channel_item.isGeoLocked = True channel_item.thumb = thumb if "titlePageThumbnailIds" in channel and channel["titlePageThumbnailIds"]: channel_item.thumb = "https://www.svtstatic.se/image/wide/650/%s.jpg" % ( channel["titlePageThumbnailIds"][0],) return channel_item
def pre_process_folder_list(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] if '>Populair<' in data: data = data[data.index('>Populair<'):] if '>L1-kanalen<' in data: data = data[:data.index('>L1-kanalen<')] Logger.debug("Pre-Processing finished") # add live items title = LanguageHelper.get_localized_string(LanguageHelper.LiveStreamTitleId) item = MediaItem("\a.: {} :.".format(title), "") item.type = "folder" items.append(item) live_item = MediaItem("L1VE TV".format(title), "#livetv") live_item.type = "video" live_item.isLive = True item.items.append(live_item) live_item = MediaItem("L1VE Radio".format(title), "#liveradio") live_item.type = "video" live_item.isLive = True item.items.append(live_item) return data, items
def add_missing_live_streams(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] slam = MediaItem( "Slam! TV", "https://hls.slam.nl/streaming/hls/SLAM!/playlist.m3u8") slam.type = "video" slam.isLive = True items.append(slam) slam_fm = MediaItem( "Slam! FM", "https://18973.live.streamtheworld.com/SLAM_AAC.aac" "?ttag=PLAYER%3ANOPREROLL&tdsdk=js-2.9" "&pname=TDSdk&pversion=2.9&banners=none") slam_fm.type = "audio" slam_fm.isLive = True slam_fm.append_single_stream(slam_fm.url) slam_fm.complete = True items.append(slam_fm) data = JsonHelper(data) for e in data.get_value("includes", "Entry"): self.__liveData[e["sys"]["id"]] = e for e in data.get_value("includes", "Asset"): self.__liveData[e["sys"]["id"]] = e return data, items
def add_other_items(self, data): """ Performs pre-process actions for data processing and adds the live channels if present. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] if self.liveUrls: live_title = LanguageHelper.get_localized_string( LanguageHelper.LiveStreamTitleId) live_item = MediaItem("\a{}".format(live_title), "") live_item.dontGroup = True items.append(live_item) for name, url in self.liveUrls.items(): item = MediaItem(name, url) item.type = "video" item.isLive = True live_item.items.append(item) elif self.liveUrl: Logger.debug("Adding live item") live_title = LanguageHelper.get_localized_string( LanguageHelper.LiveStreamTitleId) live_item = MediaItem("\a{}".format(live_title), self.liveUrl) live_item.dontGroup = True items.append(live_item) if self.recentUrl: Logger.debug("Adding recent item") recent_title = LanguageHelper.get_localized_string( LanguageHelper.Recent) recent_item = MediaItem("\a{}".format(recent_title), self.recentUrl) recent_item.dontGroup = True items.append(recent_item) return data, items
def add_live_streams_and_recent(self, data): """ Adds the live streams for RTL-Z. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] # let's add the RTL-Z live stream rtlz_live = MediaItem("RTL Z Live Stream", "") rtlz_live.icon = self.icon rtlz_live.thumb = self.noImage rtlz_live.complete = True rtlz_live.isLive = True rtlz_live.dontGroup = True stream_item = MediaItem( "RTL Z: Live Stream", "http://www.rtl.nl/(config=RTLXLV2,channel=rtlxl,progid=rtlz,zone=inlineplayer.rtl.nl/rtlz,ord=0)/system/video/wvx/components/financien/rtlz/miMedia/livestream/rtlz_livestream.xml/1500.wvx" ) stream_item.icon = self.icon stream_item.thumb = self.noImage stream_item.complete = True stream_item.type = "video" stream_item.dontGroup = True stream_item.append_single_stream("http://mss6.rtl7.nl/rtlzbroad", 1200) stream_item.append_single_stream("http://mss26.rtl7.nl/rtlzbroad", 1200) stream_item.append_single_stream("http://mss4.rtl7.nl/rtlzbroad", 1200) stream_item.append_single_stream("http://mss5.rtl7.nl/rtlzbroad", 1200) stream_item.append_single_stream("http://mss3.rtl7.nl/rtlzbroad", 1200) rtlz_live.items.append(stream_item) items.append(rtlz_live) # Add recent items data, recent_items = self.add_recent_items(data) return data, recent_items
def add_categories(self, data): """ Adds categories to the main listings. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] if self.parentItem and "code" in self.parentItem.metaData: self.__currentChannel = self.parentItem.metaData["code"] Logger.info("Only showing items for channel: '%s'", self.__currentChannel) return data, items cat = MediaItem("\a.: Categoriën :.", "https://www.vrt.be/vrtnu/categorieen.model.json") cat.fanart = self.fanart cat.thumb = self.noImage cat.icon = self.icon cat.dontGroup = True items.append(cat) live = MediaItem("\a.: Live Streams :.", "https://services.vrt.be/videoplayer/r/live.json") live.fanart = self.fanart live.thumb = self.noImage live.icon = self.icon live.dontGroup = True live.isLive = True items.append(live) channel_text = LanguageHelper.get_localized_string(30010) channels = MediaItem("\a.: %s :." % (channel_text, ), "#channels") channels.fanart = self.fanart channels.thumb = self.noImage channels.icon = self.icon channels.dontGroup = True items.append(channels) Logger.debug("Pre-Processing finished") return data, items
def create_live_stream(self, result_set): """ Creates a MediaItem of type 'video' for a live stream 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 list[str]|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 """ items = [] for key_value, stream_value in result_set.items(): Logger.trace(stream_value) # noinspection PyArgumentList channel_data = self.__channelData.get(key_value, None) if not channel_data: continue url = channel_data[ "url"] if "url" in channel_data else stream_value["mpd"] live_item = MediaItem(channel_data["title"], url) live_item.isLive = True live_item.type = 'video' live_item.fanart = channel_data.get("fanart", self.fanart) live_item.thumb = channel_data.get("icon", self.icon) live_item.icon = channel_data.get("icon", self.icon) live_item.metaData["channel_key"] = key_value items.append(live_item) return items
def add_category_and_live_items(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] sub_items = { "\a.: Direct :.": "%s/auvio/direct/" % (self.baseUrl, ), "\a.: Catégories :.": "http://www.rtbf.be/news/api/menu?site=media" } for k, v in sub_items.items(): item = MediaItem(k, v) item.complete = True item.dontGroup = True items.append(item) item.isLive = v.endswith('/direct/') Logger.debug("Pre-Processing finished") return data, items
def add_live_channels_and_folders(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Generating Live channels") items = [] live_channels = [ {"name": "BBC 1 HD", "code": "bbc_one_hd", "image": "bbc1large.png"}, {"name": "BBC 2 HD", "code": "bbc_two_hd", "image": "bbc2large.png"}, {"name": "BBC 3 HD", "code": "bbc_three_hd", "image": "bbc3large.png"}, {"name": "BBC 4 HD", "code": "bbc_four_hd", "image": "bbc4large.png"}, {"name": "CBBC", "code": "cbbc_hd", "image": "cbbclarge.png"}, {"name": "CBeebies", "code": "cbeebies_hd", "image": "cbeebieslarge.png"}, {"name": "BBC News Channel", "code": "bbc_news24", "image": "bbcnewslarge.png"}, {"name": "BBC Parliament", "code": "bbc_parliament", "image": "bbcparliamentlarge.png"}, {"name": "Alba", "code": "bbc_alba", "image": "bbcalbalarge.png"}, {"name": "S4C", "code": "s4cpbs", "image": "bbchdlarge.png"}, {"name": "BBC One London", "code": "bbc_one_london", "image": "bbchdlarge.png"}, {"name": "BBC One Scotland", "code": "bbc_one_scotland_hd", "image": "bbchdlarge.png"}, {"name": "BBC One Northern Ireland", "code": "bbc_one_northern_ireland_hd", "image": "bbchdlarge.png"}, {"name": "BBC One Wales", "code": "bbc_one_wales_hd", "image": "bbchdlarge.png"}, {"name": "BBC Two Scotland", "code": "bbc_two_scotland", "image": "bbchdlarge.png"}, {"name": "BBC Two Northern Ireland", "code": "bbc_two_northern_ireland_digital", "image": "bbchdlarge.png"}, {"name": "BBC Two Wales", "code": "bbc_two_wales_digital", "image": "bbchdlarge.png"}, ] live = MediaItem("Live Channels", "") live.dontGroup = True live.type = "folder" items.append(live) for channel in live_channels: url = "http://a.files.bbci.co.uk/media/live/manifesto/audio_video/simulcast/hds/uk/pc/ak/%(code)s.f4m" % channel item = MediaItem(channel["name"], url) item.isGeoLocked = True item.isLive = True item.type = "video" item.complete = False item.thumb = self.get_image_location(channel["image"]) live.items.append(item) extra = MediaItem("Shows (A-Z)", "#alphalisting") extra.complete = True extra.description = "Alphabetical show listing of BBC shows" extra.dontGroup = True items.append(extra) return data, items
def process_live_items(self, data): # NOSONAR """ Performs pre-process actions that either return multiple live channels that are present in the live url or an actual list item if a single live stream is present. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] Logger.info("Adding Live Streams") if self.liveUrl.endswith(".m3u8"): # We actually have a single stream. title = "{} - {}".format( self.channelName, LanguageHelper.get_localized_string( LanguageHelper.LiveStreamTitleId)) live_item = MediaItem(title, self.liveUrl) live_item.type = 'video' live_item.isLive = True if self.channelCode == "rtvdrenthe": # RTV Drenthe actually has a buggy M3u8 without master index. live_item.append_single_stream(live_item.url, 0) live_item.complete = True items.append(live_item) return "", items # we basically will check for live channels json_data = JsonHelper(data, logger=Logger.instance()) live_streams = json_data.get_value() Logger.trace(live_streams) if "videos" in live_streams: Logger.debug("Multiple streams found") live_streams = live_streams["videos"] elif not isinstance(live_streams, (list, tuple)): Logger.debug("Single streams found") live_streams = (live_streams, ) else: Logger.debug("List of stream found") live_stream_value = None for streams in live_streams: Logger.debug("Adding live stream") title = streams.get( 'name') or "%s - Live TV" % (self.channelName, ) live_item = MediaItem(title, self.liveUrl) live_item.type = 'video' live_item.complete = True live_item.isLive = True part = live_item.create_new_empty_media_part() for stream in streams: Logger.trace(stream) bitrate = None # used in Omrop Fryslan if stream == "android" or stream == "iPhone": bitrate = 250 url = streams[stream]["videoLink"] elif stream == "iPad": bitrate = 1000 url = streams[stream]["videoLink"] # used in RTV Utrecht elif stream == "androidLink" or stream == "iphoneLink": bitrate = 250 url = streams[stream] elif stream == "ipadLink": bitrate = 1000 url = streams[stream] elif stream == "tabletLink": bitrate = 300 url = streams[stream] # These windows stream won't work # elif stream == "windowsLink": # bitrate = 1200 # url = streams[stream] # elif stream == "wpLink": # bitrate = 1200 # url = streams[stream] elif stream == "name": Logger.trace("Ignoring stream '%s'", stream) else: Logger.warning("No url found for type '%s'", stream) # noinspection PyUnboundLocalVariable if "livestreams.omroep.nl/live/" in url and url.endswith( "m3u8"): Logger.info("Found NPO Stream, adding ?protection=url") url = "%s?protection=url" % (url, ) if bitrate: part.append_media_stream(url, bitrate) if url == live_stream_value and ".m3u8" in url: # if it was equal to the previous one, assume we have a m3u8. Reset the others. Logger.info( "Found same M3u8 stream for all streams for this Live channel, using that one: %s", url) live_item.MediaItemParts = [] live_item.url = url live_item.complete = False break elif "playlist.m3u8" in url: # if we have a playlist, use that one. Reset the others. Logger.info( "Found M3u8 playlist for this Live channel, using that one: %s", url) live_item.MediaItemParts = [] live_item.url = url live_item.complete = False break else: # add it to the possibilities live_stream_value = url items.append(live_item) return "", items
def add_categories_and_specials(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] extras = { LanguageHelper.get_localized_string(LanguageHelper.Search): ("searchSite", None, False), LanguageHelper.get_localized_string(LanguageHelper.TvShows): ( "https://www.tv4play.se/alla-program", None, False ), LanguageHelper.get_localized_string(LanguageHelper.Categories): ( "https://graphql.tv4play.se/graphql?query=query%7Btags%7D", None, False ), LanguageHelper.get_localized_string(LanguageHelper.CurrentlyPlayingEpisodes): ( self.__get_api_url("LiveVideos", "9b3d0d2f039089311cde2989760744844f7c4bb5033b0ce5643676ee60cb0901"), None, False ) } # No more extras # today = datetime.datetime.now() # days = [LanguageHelper.get_localized_string(LanguageHelper.Monday), # LanguageHelper.get_localized_string(LanguageHelper.Tuesday), # LanguageHelper.get_localized_string(LanguageHelper.Wednesday), # LanguageHelper.get_localized_string(LanguageHelper.Thursday), # LanguageHelper.get_localized_string(LanguageHelper.Friday), # LanguageHelper.get_localized_string(LanguageHelper.Saturday), # LanguageHelper.get_localized_string(LanguageHelper.Sunday)] # for i in range(0, 7, 1): # start_date = today - datetime.timedelta(i) # end_date = start_date + datetime.timedelta(1) # # day = days[start_date.weekday()] # if i == 0: # day = LanguageHelper.get_localized_string(LanguageHelper.Today) # elif i == 1: # day = LanguageHelper.get_localized_string(LanguageHelper.Yesterday) # # Logger.trace("Adding item for: %s - %s", start_date, end_date) # url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=" \ # "&platform=tablet&is_live=false&product_groups=2&type=episode&per_page=100" # url = "%s&broadcast_from=%s&broadcast_to=%s&" % (url, start_date.strftime("%Y%m%d"), end_date.strftime("%Y%m%d")) # extras[day] = (url, start_date, False) # # extras[LanguageHelper.get_localized_string(LanguageHelper.CurrentlyPlayingEpisodes)] = ( # "https://api.tv4play.se/play/video_assets?exclude_node_nids=&platform=tablet&" # "is_live=true&product_groups=2&type=episode&per_page=100", None, False) # Actually add the extra items for name in extras: title = name url, date, is_live = extras[name] # type: str, datetime.datetime, bool item = MediaItem(title, url) item.dontGroup = True item.complete = True item.HttpHeaders = self.httpHeaders item.isLive = is_live if date is not None: item.set_date(date.year, date.month, date.day, 0, 0, 0, text=date.strftime("%Y-%m-%d")) items.append(item) Logger.debug("Pre-Processing finished") return data, items
def load_programs(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] # fetch al pages p = 1 url_format = "https://{0}/content/shows?" \ "include=images" \ "&page%5Bsize%5D=100&page%5Bnumber%5D={{0}}".format(self.baseUrlApi) # "include=images%2CprimaryChannel" \ url = url_format.format(p) data = UriHandler.open(url, proxy=self.proxy) json = JsonHelper(data) pages = json.get_value("meta", "totalPages") programs = json.get_value("data") or [] # extract the images self.__update_image_lookup(json) for p in range(2, pages + 1, 1): url = url_format.format(p) Logger.debug("Loading: %s", url) data = UriHandler.open(url, proxy=self.proxy) json = JsonHelper(data) programs += json.get_value("data") or [] # extract the images self.__update_image_lookup(json) Logger.debug("Found a total of %s items over %s pages", len(programs), pages) for p in programs: item = self.create_program_item(p) if item is not None: items.append(item) if self.recentUrl: recent_text = LanguageHelper.get_localized_string( LanguageHelper.Recent) recent = MediaItem("\b.: {} :.".format(recent_text), self.recentUrl) recent.dontGroup = True items.append(recent) # live items if self.liveUrl: live = MediaItem("\b.: Live :.", self.liveUrl) live.type = "video" live.dontGroup = True live.isGeoLocked = True live.isLive = True items.append(live) search = MediaItem("\a.: Sök :.", "searchSite") search.type = "folder" search.dontGroup = True items.append(search) return data, items
def add_categories_and_specials(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] # TV4 Group specific items query = 'query{programSearch(per_page:1000){__typename,programs' \ '%s,' \ 'totalHits}}' % (self.__program_fields,) query = HtmlEntityHelper.url_encode(query) tv_shows_url = "https://graphql.tv4play.se/graphql?query={}".format( query) extras = { LanguageHelper.get_localized_string(LanguageHelper.Search): ("searchSite", None, False), LanguageHelper.get_localized_string(LanguageHelper.TvShows): (tv_shows_url, None, False), LanguageHelper.get_localized_string(LanguageHelper.Categories): ("https://graphql.tv4play.se/graphql?query=query%7Btags%7D", None, False), LanguageHelper.get_localized_string(LanguageHelper.MostViewedEpisodes): ("https://api.tv4play.se/play/video_assets/most_viewed?type=episode" "&platform=tablet&is_live=false&per_page=%s&start=0" % (self.__maxPageSize, ), None, False), } today = datetime.datetime.now() days = [ LanguageHelper.get_localized_string(LanguageHelper.Monday), LanguageHelper.get_localized_string(LanguageHelper.Tuesday), LanguageHelper.get_localized_string(LanguageHelper.Wednesday), LanguageHelper.get_localized_string(LanguageHelper.Thursday), LanguageHelper.get_localized_string(LanguageHelper.Friday), LanguageHelper.get_localized_string(LanguageHelper.Saturday), LanguageHelper.get_localized_string(LanguageHelper.Sunday) ] for i in range(0, 7, 1): start_date = today - datetime.timedelta(i) end_date = start_date + datetime.timedelta(1) day = days[start_date.weekday()] if i == 0: day = LanguageHelper.get_localized_string(LanguageHelper.Today) elif i == 1: day = LanguageHelper.get_localized_string( LanguageHelper.Yesterday) Logger.trace("Adding item for: %s - %s", start_date, end_date) url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=" \ "&platform=tablet&is_live=false&product_groups=2&type=episode&per_page=100" url = "%s&broadcast_from=%s&broadcast_to=%s&" % ( url, start_date.strftime("%Y%m%d"), end_date.strftime("%Y%m%d")) extras[day] = (url, start_date, False) extras[LanguageHelper.get_localized_string( LanguageHelper.CurrentlyPlayingEpisodes )] = ( "https://api.tv4play.se/play/video_assets?exclude_node_nids=&platform=tablet&" "is_live=true&product_groups=2&type=episode&per_page=100", None, False) # Actually add the extra items for name in extras: title = name url, date, is_live = extras[name] item = MediaItem(title, url) item.dontGroup = True item.complete = True item.HttpHeaders = self.httpHeaders item.isLive = is_live if date is not None: item.set_date(date.year, date.month, date.day, 0, 0, 0, text=date.strftime("%Y-%m-%d")) items.append(item) Logger.debug("Pre-Processing finished") return data, items
def add_live_stream(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] item = MediaItem("\a.: TWiT.TV Live :.", "http://live.twit.tv/") item.complete = True playback_item = MediaItem("Play Live", "http://live.twit.tv/") playback_item.type = "playlist" playback_item.isLive = True playback_part = playback_item.create_new_empty_media_part() # noinspection PyStatementEffect """ BitGravity There are two streams available from BitGravity; a 512 kbps low-bandwidth stream and a 1 Mbps high-bandwidth stream. UStream This is the default stream. The UStream stream is a variable stream that maxes at 2.2 Mbps and adjusts down based on your bandwidth. Justin.tv The Justin.tv stream is a 2.2 mbps high-bandwidth stream that will adjust to lower bandwidth and resolutions. Flosoft.biz The Flosoft.biz stream is a 5 resolution/bitrate HLS stream, intended for our app developers. Please see Flosoft Developer Section. This stream is hosted by TWiT through Flosoft.biz """ # http://wiki.twit.tv/wiki/TWiT_Live#Direct_links_to_TWiT_Live_Video_Streams media_urls = { # Justin TV # "2000": "http://usher.justin.tv/stream/multi_playlist/twit.m3u8", # Flosoft (http://wiki.twit.tv/wiki/Developer_Guide#Flosoft.biz) "264": "http://hls.cdn.flosoft.biz/flosoft/mp4:twitStream_240/playlist.m3u8", "512": "http://hls.cdn.flosoft.biz/flosoft/mp4:twitStream_360/playlist.m3u8", "1024": "http://hls.cdn.flosoft.biz/flosoft/mp4:twitStream_480/playlist.m3u8", "1475": "http://hls.cdn.flosoft.biz/flosoft/mp4:twitStream_540/playlist.m3u8", "1778": "http://hls.cdn.flosoft.biz/flosoft/mp4:twitStream_720/playlist.m3u8", # UStream "1524": "http://iphone-streaming.ustream.tv/ustreamVideo/1524/streams/live/playlist.m3u8", # BitGravity # "512": "http://209.131.99.99/twit/live/low", # "1024": "http://209.131.99.99/twit/live/high", #"512": "http://64.185.191.180/cdn-live-s1/_definst_/twit/live/low/playlist.m3u8", #"1024": "http://64.185.191.180/cdn-live-s1/_definst_/twit/live/high/playlist.m3u8", } for bitrate in media_urls: playback_part.append_media_stream(media_urls[bitrate], bitrate) Logger.debug("Streams: %s", playback_part) playback_item.complete = True item.items.append(playback_item) Logger.debug("Appended: %s", playback_item) items.append(item) return data, items
def create_channel_item(self, channel): """ Creates a MediaItem of type 'video' for a live channel 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 channel: 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(channel) # Channel data channel_title = channel["name"] channel_id = channel["id"] if channel_id == "svtb": channel_id = "barnkanalen" elif channel_id == "svtk": channel_id = "kunskapskanalen" # Running data running = channel["running"] title = running["name"] episode = running.get("subHeading", None) thumb = self.__get_thumb(running["image"], width=720) date_format = "%Y-%m-%dT%H:%M:%S" start_time = DateHelper.get_date_from_string(running["start"][:19], date_format) end_time = DateHelper.get_date_from_string(running["end"][:19], date_format) description = running.get("description") if episode: title = "%s: %s - %s (%02d:%02d - %02d:%02d)" \ % (channel_title, title, episode, start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min) description = "{:02d}:{:02d} - {:02d}:{:02d}: {} - {}\n\n{}".format( start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min, title, episode or "", description) else: title = "%s: %s (%02d:%02d - %02d:%02d)" \ % (channel_title, title, start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min) description = "{:02d}:{:02d} - {:02d}:{:02d}: {}\n\n{}".format( start_time.tm_hour, start_time.tm_min, end_time.tm_hour, end_time.tm_min, title, description) channel_item = MediaItem( title, "https://www.svt.se/videoplayer-api/video/%s" % (channel_id.lower(),) ) channel_item.type = "video" channel_item.isLive = True channel_item.isGeoLocked = True channel_item.description = description channel_item.thumb = thumb if "episodeThumbnailIds" in channel and channel["episodeThumbnailIds"]: channel_item.thumb = "https://www.svtstatic.se/image/wide/650/%s.jpg" % ( channel["episodeThumbnailIds"][0],) return channel_item
def add_categories_and_specials(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] extras = { LanguageHelper.get_localized_string(LanguageHelper.Search): ("searchSite", None, False), LanguageHelper.get_localized_string(LanguageHelper.TvShows): ("https://api.tv4play.se/play/programs?is_active=true&platform=tablet" "&per_page=1000&fl=nid,name,program_image,is_premium,updated_at,channel&start=0", None, False) } # Channel 4 specific items if self.channelCode == "tv4se": extras.update({ LanguageHelper.get_localized_string(LanguageHelper.Categories): ("https://api.tv4play.se/play/categories.json", None, False), LanguageHelper.get_localized_string(LanguageHelper.MostViewedEpisodes): ("https://api.tv4play.se/play/video_assets/most_viewed?type=episode" "&platform=tablet&is_live=false&per_page=%s&start=0" % (self.maxPageSize, ), None, False), }) today = datetime.datetime.now() days = [ LanguageHelper.get_localized_string(LanguageHelper.Monday), LanguageHelper.get_localized_string(LanguageHelper.Tuesday), LanguageHelper.get_localized_string(LanguageHelper.Wednesday), LanguageHelper.get_localized_string(LanguageHelper.Thursday), LanguageHelper.get_localized_string(LanguageHelper.Friday), LanguageHelper.get_localized_string(LanguageHelper.Saturday), LanguageHelper.get_localized_string(LanguageHelper.Sunday) ] for i in range(0, 7, 1): start_date = today - datetime.timedelta(i) end_date = start_date + datetime.timedelta(1) day = days[start_date.weekday()] if i == 0: day = LanguageHelper.get_localized_string( LanguageHelper.Today) elif i == 1: day = LanguageHelper.get_localized_string( LanguageHelper.Yesterday) Logger.trace("Adding item for: %s - %s", start_date, end_date) # Old URL: # url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=" \ # "nyheterna,v%C3%A4der,ekonomi,lotto,sporten,nyheterna-blekinge,nyheterna-bor%C3%A5s," \ # "nyheterna-dalarna,nyheterna-g%C3%A4vle,nyheterna-g%C3%B6teborg,nyheterna-halland," \ # "nyheterna-helsingborg,nyheterna-j%C3%B6nk%C3%B6ping,nyheterna-kalmar,nyheterna-link%C3%B6ping," \ # "nyheterna-lule%C3%A5,nyheterna-malm%C3%B6,nyheterna-norrk%C3%B6ping,nyheterna-skaraborg," \ # "nyheterna-skellefte%C3%A5,nyheterna-stockholm,nyheterna-sundsvall,nyheterna-ume%C3%A5," \ # "nyheterna-uppsala,nyheterna-v%C3%A4rmland,nyheterna-v%C3%A4st,nyheterna-v%C3%A4ster%C3%A5s," \ # "nyheterna-v%C3%A4xj%C3%B6,nyheterna-%C3%B6rebro,nyheterna-%C3%B6stersund,tv4-tolken," \ # "fotbollskanalen-europa" \ # "&platform=tablet&per_page=32&is_live=false&product_groups=2&type=episode&per_page=100" url = "https://api.tv4play.se/play/video_assets?exclude_node_nids=" \ "&platform=tablet&per_page=32&is_live=false&product_groups=2&type=episode&per_page=100" url = "%s&broadcast_from=%s&broadcast_to=%s&" % ( url, start_date.strftime("%Y%m%d"), end_date.strftime("%Y%m%d")) extras[day] = (url, start_date, False) extras[LanguageHelper.get_localized_string( LanguageHelper.CurrentlyPlayingEpisodes )] = ( "https://api.tv4play.se/play/video_assets?exclude_node_nids=&platform=tablet&" "per_page=32&is_live=true&product_groups=2&type=episode&per_page=100", None, False) for name in extras: title = name url, date, is_live = extras[name] item = MediaItem(title, url) item.dontGroup = True item.complete = True item.HttpHeaders = self.httpHeaders item.isLive = is_live if date is not None: item.set_date(date.year, date.month, date.day, 0, 0, 0, text=date.strftime("%Y-%m-%d")) items.append(item) if not self.channelCode == "tv4se": return data, items # Add Live TV # live = MediaItem("\a.: Live-TV :.", # "http://tv4events1-lh.akamaihd.net/i/EXTRAEVENT5_1@324055/master.m3u8", # type="video") # live.dontGroup = True # # live.isDrmProtected = True # live.isGeoLocked = True # live.isLive = True # items.append(live) Logger.debug("Pre-Processing finished") return data, items
def create_video_item(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 list[str]|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('starting FormatVideoItem for %s', self.channelName) # Logger.Trace(result_set) # the vmanProgramId (like 1019976) leads to http://anytime.tv4.se/webtv/metafileFlash.smil?p=1019976&bw=1000&emulate=true&sl=true program_id = result_set["id"] # Logger.Debug("ProgId = %s", programId) # We can either use M3u8 or Dash # url = "https://playback-api.b17g.net/media/%s?service=tv4&device=browser&protocol=hls" % (program_id,) url = "https://playback-api.b17g.net/media/%s?service=tv4&device=browser&protocol=dash" % ( program_id, ) name = result_set["title"] season = result_set.get("season", 0) episode = result_set.get("episode", 0) is_episodic = 0 < season < 1900 and not episode == 0 if is_episodic: episode_text = None if " del " in name: name, episode_text = name.split(" del ", 1) episode_text = episode_text.lstrip("0123456789") if episode_text: episode_text = episode_text.lstrip(" -") name = "{} - s{:02d}e{:02d} - {}".format( name, season, episode, episode_text) else: name = "{} - s{:02d}e{:02d}".format(name, season, episode) item = MediaItem(name, url) item.description = result_set["description"] if item.description is None: item.description = item.name if is_episodic: item.set_season_info(season, episode) # premium_expire_date_time=2099-12-31T00:00:00+01:00 expire_date = result_set.get("expire_date_time") if bool(expire_date): self.__set_expire_time(expire_date, item) date = result_set["broadcast_date_time"] (date_part, time_part) = date.split("T") (year, month, day) = date_part.split("-") (hour, minutes, rest1, zone) = time_part.split(":") item.set_date(year, month, day, hour, minutes, 00) broadcast_date = datetime.datetime(int(year), int(month), int(day), int(hour), int(minutes)) item.fanart = result_set.get("program_image", self.parentItem.fanart) thumb_url = result_set.get("image", result_set.get("program_image")) # some images need to come via a proxy: if thumb_url and "://img.b17g.net/" in thumb_url: item.thumb = "https://imageproxy.b17g.services/?format=jpg&shape=cut" \ "&quality=90&resize=520x293&source={}"\ .format(HtmlEntityHelper.url_encode(thumb_url)) else: item.thumb = thumb_url availability = result_set["availability"] # noinspection PyTypeChecker free_period = availability["availability_group_free"] # noinspection PyTypeChecker premium_period = availability["availability_group_premium"] now = datetime.datetime.now() if False and not premium_period == "0": # always premium free_expired = now - datetime.timedelta(days=99 * 365) elif free_period == "30+" or free_period is None: free_expired = broadcast_date + datetime.timedelta(days=99 * 365) else: free_expired = broadcast_date + datetime.timedelta( days=int(free_period)) Logger.trace( "Premium info for: %s\nPremium state: %s\nFree State: %s\nBroadcast %s vs Expired %s", name, premium_period, free_period, broadcast_date, free_expired) if now > free_expired: item.isPaid = True item.type = "video" item.complete = False item.isGeoLocked = result_set["is_geo_restricted"] item.isDrmProtected = result_set["is_drm_protected"] item.isLive = result_set.get("is_live", False) if item.isLive: item.name = "{}:{} - {}".format(hour, minutes, name) item.url = "{0}&is_live=true".format(item.url) if item.isDrmProtected: item.url = "{}&drm=widevine&is_drm=true".format(item.url) item.set_info_label("duration", int(result_set.get("duration", 0))) return item
def create_api_video_asset_type(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 list[str]|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('starting FormatVideoItem for %s', self.channelName) program_id = result_set["id"] url = "https://playback-api.b17g.net/media/{}?service=tv4&device=browser&protocol=dash".\ format(program_id) name = result_set["title"] season = result_set.get("season", 0) episode = result_set.get("episode", 0) is_episodic = 0 < season < 1900 and not episode == 0 if is_episodic: episode_text = None if " del " in name: name, episode_text = name.split(" del ", 1) episode_text = episode_text.lstrip("0123456789") if episode_text: episode_text = episode_text.lstrip(" -") name = "{} - s{:02d}e{:02d} - {}".format(name, season, episode, episode_text) else: name = "{} - s{:02d}e{:02d}".format(name, season, episode) item = MediaItem(name, url) item.description = result_set["description"] if item.description is None: item.description = item.name if is_episodic: item.set_season_info(season, episode) # premium_expire_date_time=2099-12-31T00:00:00+01:00 expire_in_days = result_set.get("daysLeftInService", 0) if 0 < expire_in_days < 10000: item.set_expire_datetime( timestamp=datetime.datetime.now() + datetime.timedelta(days=expire_in_days)) date = result_set["broadcastDateTime"] broadcast_date = DateHelper.get_datetime_from_string(date, "%Y-%m-%dT%H:%M:%SZ", "UTC") broadcast_date = broadcast_date.astimezone(self.__timezone) item.set_date(broadcast_date.year, broadcast_date.month, broadcast_date.day, broadcast_date.hour, broadcast_date.minute, 0) item.fanart = result_set.get("program_image", self.parentItem.fanart) thumb_url = result_set.get("image", result_set.get("program_image")) # some images need to come via a proxy: if thumb_url and "://img.b17g.net/" in thumb_url: item.thumb = "https://imageproxy.b17g.services/?format=jpg&shape=cut" \ "&quality=70&resize=520x293&source={}" \ .format(HtmlEntityHelper.url_encode(thumb_url)) else: item.thumb = thumb_url item.type = "video" item.complete = False item.isGeoLocked = True # For now, none are paid. # item.isPaid = not result_set.get("freemium", False) if "drmProtected" in result_set: item.isDrmProtected = result_set["drmProtected"] elif "is_drm_protected" in result_set: item.isDrmProtected = result_set["is_drm_protected"] item.isLive = result_set.get("live", False) if item.isLive: item.name = "{:02d}:{:02d} - {}".format(broadcast_date.hour, broadcast_date.minute, name) item.url = "{0}&is_live=true".format(item.url) if item.isDrmProtected: item.url = "{}&drm=widevine&is_drm=true".format(item.url) item.set_info_label("duration", int(result_set.get("duration", 0))) return item