def create_episode_item(self, result_set): """ Creates a new MediaItem for an episode. 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. :param list[str]|dict[str,str] result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None """ Logger.trace(result_set) genres = result_set[0] if self.__genre and self.__genre not in genres: Logger.debug("Item '%s' filtered due to genre: %s", result_set[2], genres) return None url = result_set[1] if "&" in url: url = HtmlEntityHelper.convert_html_entities(url) if not url.startswith("http:"): url = "%s%s" % (self.baseUrl, url) # get the ajax page for less bandwidth url = "%s?sida=1&sort=tid_stigande&embed=true" % (url, ) item = MediaItem(result_set[2], url) item.complete = True item.isGeoLocked = True return item
def create_api_single_type(self, result_set): """ Creates a MediaItem of type 'video' using the result_set from the API. 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 This works for: __typename=Episode """ title = result_set['name'] url = '{}{}'.format(self.baseUrl, result_set['urls']['svtplay']) item = MediaItem(title, url) item.type = "video" item.description = result_set.get('longDescription') image_info = result_set.get("image") if image_info: item.thumb = self.__get_thumb(image_info, width=720) item.fanart = self.__get_thumb(image_info) item.isGeoLocked = result_set['restrictions']['onlyAvailableInSweden'] return item
def create_api_tvshow_type(self, result_set): """ Creates a new MediaItem for an episode. 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. :param list[str]|dict result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None This works for: __typename=TvShow, KidsTvShow, Single """ url = result_set["urls"]["svtplay"] item = MediaItem(result_set['name'], "#program_item") item.metaData["slug"] = url item.icon = self.icon item.isGeoLocked = result_set.get('restrictions', {}).get('onlyAvailableInSweden', False) item.description = result_set.get('description') image_info = result_set.get("image") if image_info: item.thumb = self.__get_thumb(image_info) return item
def create_live_channel_item(self, result_set): """ Creates a MediaItem of type 'video' for live 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[str,dict|str] 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 """ # noinspection PyTypeChecker url = "{}{}?apiKey={}".format(self.baseUrl, result_set["_links"]["manifest"]["href"], self.__api_key) live_data = result_set["_embedded"]["playback"] # type: dict item = MediaItem(live_data["title"], url) item.type = "video" item.isLive = True item.isGeoLocked = live_data.get("isGeoBlocked") # noinspection PyTypeChecker self.__get_image(live_data["posters"][0]["image"]["items"], "pixelWidth", "url") return item
def create_live_channel(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[str,str] 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) item = MediaItem(result_set[0], result_set[1]) item.type = "video" item.isGeoLocked = result_set[3].lower() == "true" date_time = DateHelper.get_date_from_posix(int(result_set[2]) * 1 / 1000) item.set_date(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second) thumb = result_set[4] if not thumb.startswith("http"): thumb = "%s%s" % (self.baseUrl, thumb) item.thumb = thumb return item
def create_json_episode_item(self, result_set): """ Creates a new MediaItem for an episode. 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. :param dict result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None """ Logger.trace(result_set) meta = result_set["meta"] name = meta["header"]["title"] url = "{}{}".format(self.baseUrl, result_set["url"]) item = MediaItem(name, url) item.description = meta.get("description") item.isGeoLocked = True media_info = result_set.get("media") if media_info is not None: item.thumb = media_info.get("image", {}).get("url") return item
def create_episode_item_api(self, result_set): """ Creates a new MediaItem for an episode. 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. :param list[str] result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None """ if not isinstance(result_set, dict): json_data = result_set[1].replace(""", "\"") result_set = JsonHelper(json_data) result_set = result_set.json brand = result_set["brand"] if brand != self.__channel_brand: return None title = result_set["title"] url = "{}{}".format(self.baseUrl, result_set["link"]) item = MediaItem(title, url) item.description = result_set["description"] item.isGeoLocked = True images = result_set["images"] item.poster = HtmlEntityHelper.convert_html_entities(images.get("poster")) item.thumb = HtmlEntityHelper.convert_html_entities(images.get("teaser")) return item
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["displayName"] description = channel.get("longDescription") channel_id = channel["urlName"] if channel_id == "svtb": channel_id = "barnkanalen" elif channel_id == "svtk": channel_id = "kunskapskanalen" 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 "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 get_live_items(self, data): """ Adds live stream items. 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("Fetching episode items") items = [] live_items = MediaItem("\a.: Live TV :.", "") items.append(live_items) live_base = "http://il.srgssr.ch/integrationlayer/1.0/ue/srf/video/play/%s.json" live_channels = { "SRF 1 live": ("c4927fcf-e1a0-0001-7edd-1ef01d441651", "srf1.png"), "SRF zwei live": ("c49c1d64-9f60-0001-1c36-43c288c01a10", "srf2.png"), "SRF info live": ("c49c1d73-2f70-0001-138a-15e0c4ccd3d0", "srfinfo.png") } for live_item in live_channels.keys(): item = MediaItem(live_item, live_base % (live_channels[live_item][0], )) item.thumb = self.get_image_location(live_channels[live_item][1]) item.isGeoLocked = True item.type = "video" live_items.items.append(item) return data, items
def create_recent_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(result_set) show_title = result_set["abstract_name"] episode_title = result_set["title"] title = "{} - {}".format(show_title, episode_title) description = result_set.get("synopsis") uuid = result_set["uuid"] url = "https://api.rtl.nl/watch/play/api/play/xl/%s?device=web&drm=widevine&format=dash" % (uuid,) item = MediaItem(title.title(), url) item.type = "video" item.description = description item.thumb = "%s%s" % (self.posterBase, uuid,) audience = result_set.get("audience") Logger.debug("Found audience: %s", audience) item.isGeoLocked = audience == "ALLEEN_NL" # We can play the DRM stuff # item.isDrmProtected = audience == "DRM" station = result_set.get("station", None) if station: item.name = "{} ({})".format(item.name, station) icon = self.largeIconSet.get(station.lower(), None) if icon: Logger.trace("Setting icon to: %s", icon) item.icon = icon # 2018-12-05T19:30:00.000Z date_time = result_set.get("dateTime", None) if date_time: date_time = DateHelper.get_date_from_string(date_time[:-5], "%Y-%m-%dT%H:%M:%S") # The time is in UTC, and the show as at UTC+1 date_time = datetime.datetime(*date_time[:6]) + datetime.timedelta(hours=1) item.name = "{:02d}:{:02d}: {}".format(date_time.hour, date_time.minute, item.name) item.set_date(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second) return item
def create_instalment_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[str,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 """ title = result_set["titles"]["title"] sub_title = result_set["titles"]["subtitle"] # noinspection PyTypeChecker if result_set.get("availability", {}).get("status", "available") != "available": Logger.debug("Found '%s' with a non-available status", title) return None url = "https://psapi.nrk.no/programs/{}?apiKey={}".format( result_set["prfId"], self.__api_key) item = MediaItem(title, url) item.type = 'video' item.thumb = self.__get_image(result_set["image"], "width", "url") item.fanart = self.parentItem.fanart # noinspection PyTypeChecker item.isGeoLocked = result_set.get("usageRights", {}).get( "geoBlock", {}).get("isGeoBlocked", False) if sub_title and sub_title.strip(): item.description = sub_title if "firstTransmissionDateDisplayValue" in result_set: Logger.trace("Using 'firstTransmissionDateDisplayValue' for date") day, month, year = result_set[ "firstTransmissionDateDisplayValue"].split(".") item.set_date(year, month, day) elif "usageRights" in result_set and "from" in result_set[ "usageRights"] and result_set["usageRights"][ "from"] is not None: Logger.trace("Using 'usageRights.from.date' for date") # noinspection PyTypeChecker date_value = result_set["usageRights"]["from"]["date"].split( "+")[0] time_stamp = DateHelper.get_date_from_string( date_value, date_format="%Y-%m-%dT%H:%M:%S") item.set_date(*time_stamp[0:6]) return item
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 dict[str,dict|None] 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) title = result_set["title"] if "subTitle" in result_set: title = "%s - %s" % (title, result_set["subTitle"]) mgid = result_set["id"].split(":")[-1] url = "http://feeds.mtvnservices.com/od/feed/intl-mrss-player-feed" \ "?mgid=mgid:arc:episode:mtvplay.com:%s" \ "&ep=%s" \ "&episodeType=segmented" \ "&imageEp=android.playplex.mtv.%s" \ "&arcEp=android.playplex.mtv.%s" \ % (mgid, self.__backgroundServiceEp, self.__region.lower(), self.__region.lower()) item = MediaItem(title, url) item.type = "video" item.description = result_set.get("description", None) item.isGeoLocked = True images = result_set.get("images", []) if images: # mgid:file:gsp:scenic:/international/mtv.nl/playplex/dutch-ridiculousness/Dutch_Ridiculousness_Landscape.png # http://playplex.mtvnimages.com/uri/mgid:file:gsp:scenic:/international/mtv.nl/playplex/dutch-ridiculousness/Dutch_Ridiculousness_Landscape.png for image in images: if image["width"] > 500: pass # no fanart here else: item.thumb = "http://playplex.mtvnimages.com/uri/%(url)s" % image date = result_set.get("originalAirDate", None) if not date: date = result_set.get("originalPublishDate", None) if date: time_stamp = date["timestamp"] 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 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[str,str] 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) thumb_url = result_set[0] if thumb_url.startswith("//"): thumb_url = "http:%s" % (thumb_url, ) elif not thumb_url.startswith("http"): thumb_url = "%s%s" % (self.baseUrl, thumb_url) Logger.trace(thumb_url) season = result_set[1] if season: name = "%s - %s" % (season, result_set[2]) else: name = result_set[2] video_id = result_set[4] url = "http://www.oppetarkiv.se/video/%s?output=json" % (video_id, ) item = MediaItem(name, url) item.type = 'video' item.icon = self.icon item.thumb = thumb_url date = result_set[5] date_key = 'datetime="' if date_key in date: date = date[date.index(date_key) + len(date_key):date.index("T")] date = date.split("-") year = date[0] month = date[1] day = date[2] Logger.trace("%s - %s-%s-%s", date, year, month, day) item.set_date(year, month, day) else: Logger.debug("No date found") item.complete = False item.isGeoLocked = True return item
def __create_json_episode_item(self, result_set, check_channel=True): """ Creates a new MediaItem for an episode. 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. :param dict[str,any] result_set: The result_set of the self.episodeItemRegex :param bool check_channel: Compare channel ID's and ignore that that do not match. :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None """ Logger.trace(result_set) # make sure we use ID as GUID if "id" in result_set: result_set["guid"] = result_set["id"] if check_channel and self.channelId is not None: channels = [int(c["guid"]) for c in result_set.get("channels", [])] valid_channel_found = any( [c for c in channels if c in self.channelId]) if not valid_channel_found: Logger.trace("Found item for wrong channel %s instead of %s", channels, self.channelId) return None # For now we keep using the API, otherwise we need to do more complex VideoItem parsing if self.useNewPages: raise NotImplementedError("The 'slug' part is no longer working") # So this no longer works # category_slug = self.__categories[result_set["category"]]["guid"] # url = "%s/%s/%s" % (self.baseUrl, category_slug, result_set['slug']) else: url = "http://playapi.mtgx.tv/v3/videos?format=%(guid)s&order=-airdate&type=program" % result_set item = MediaItem(result_set['title'], url) # Find the possible images if "images" in result_set and "landscape" in result_set["images"]: image_url = result_set["images"]["landscape"]["href"] item.thumb = self.__get_thumb_image(image_url) item.fanart = self.__get_thumb_image(image_url, True) elif "image" in result_set: item.thumb = self.__get_thumb_image(result_set["image"]) elif "_links" in result_set and "image" in result_set["_links"]: thumb_data = result_set["_links"]["image"] item.thumb = self.__get_thumb_image(thumb_data['href']) item.fanart = self.__get_thumb_image(thumb_data['href'], True) item.isGeoLocked = result_set.get('onlyAvailableInSweden', False) return item
def create_generic_item(self, result_set, program_type): """ Creates a MediaItem of type 'video' or 'folder' using the result_set from the regex and a basic set of values. 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 """ title = result_set["title"] if not result_set.get("hasOndemandRights", True): Logger.debug("Item '%s' has no on-demand rights", title) return None item_id = result_set["id"] if program_type == "programme": url = "https://psapi.nrk.no/programs/{}?apiKey={}".format(item_id, self.__api_key) item = MediaItem(title, url) item.type = 'video' else: use_old_series_api = False if use_old_series_api: url = "https://psapi.nrk.no/series/{}?apiKey={}".format(item_id, self.__api_key) else: url = "https://psapi.nrk.no/tv/catalog/series/{}?apiKey={}".format(item_id, self.__api_key) item = MediaItem(title, url) item.type = 'folder' item.isGeoLocked = result_set.get("isGeoBlocked", result_set.get("usageRights", {}).get("isGeoBlocked", False)) description = result_set.get("description") if description and description.lower() != "no description": item.description = description if "image" not in result_set or "webImages" not in result_set["image"]: return item # noinspection PyTypeChecker item.thumb = self.__get_image(result_set["image"]["webImages"], "pixelWidth", "imageUrl") # see if there is a date? self.__set_date(result_set, item) 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) # get the title title = result_set["title"] mgid = result_set["id"] url = "http://media.mtvnservices.com/pmt/e1/access/index.html" \ "?uri=mgid:arc:video:{}:{}&configtype=edge".format(self.__country_id, mgid) item = MediaItem(title, url) item.type = "video" item.description = result_set["description"] if "images" in result_set: item.thumb = result_set["images"]["url"] air_date = int(result_set["publishDate"]) date_stamp = DateHelper.get_date_from_posix(air_date, self.__timezone_utc) item.set_date(date_stamp.year, date_stamp.month, date_stamp.day) episode = result_set.get("episode") season = result_set.get("season") if season and episode: item.set_season_info(season, episode) duration = result_set.get("duration", "0:00") duration = duration.split(":") duration = int(duration[1]) + 60 * int(duration[0]) item.set_info_label("duration", duration) # store season info item.metaData["season_id"] = result_set.get("seasonId") item.isGeoLocked = True return item
def create_api_clip_type(self, result_set): """ Creates a MediaItem of type 'video' using the result_set from the API. 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 This works for: __typename=Episode """ if not self.__show_videos: return None if not self.__show_videos: return None title = result_set['name'] svt_video_id = result_set.get("videoSvtId", result_set.get("svtId", None)) if svt_video_id: # API style url = "https://api.svt.se/videoplayer-api/video/{}".format( svt_video_id) else: # HTML style url = "{}{}".format(self.baseUrl, result_set['urls']['svtplay']) item = MediaItem(title, url) item.type = "video" item.description = result_set.get('longDescription') item.isGeoLocked = result_set['restrictions']['onlyAvailableInSweden'] image_info = result_set.get("image") if image_info: item.thumb = self.__get_thumb(image_info) duration = int(result_set.get("duration", 0)) item.set_info_label("duration", duration) return item
def create_epg_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 dict[str,] 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 """ if "video_node" not in result_set: return None # Could be: title = result_set['episodeTitle'] program_title = result_set['program_title'] episode_title = result_set['episode_title'] time_value = result_set["time_string"] if episode_title: title = "{}: {} - {}".format(time_value, program_title, episode_title) else: title = "{}: {}".format(time_value, program_title) video_info = result_set["video_node"] url = "{}{}".format(self.baseUrl, video_info["url"]) item = MediaItem(title, url) item.type = "video" item.description = video_info["description"] item.thumb = video_info["image"] item.isGeoLocked = result_set.get("isProtected") item.set_info_label("duration", video_info["duration"]) # 2021-01-27 time_stamp = DateHelper.get_date_from_string(result_set["date_string"], date_format="%Y-%m-%d") item.set_date(*time_stamp[0:6]) item.set_info_label("duration", result_set["duration"]) if "episode_nr" in result_set and "season" in result_set and "-" not in result_set["season"]: item.set_season_info(result_set["season"], result_set["episode_nr"]) return item
def create_json_video(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[str,any|None] 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 """ video_id = result_set['id'] # Categories to use # category = result_set["maincategory"].title() # subcategory = result_set["subcategory"].title() url = "https://api.nos.nl/mobile/video/%s/phone.json" % (video_id, ) item = MediaItem(result_set['title'], url, type="video") item.icon = self.icon if 'image' in result_set: images = result_set['image']["formats"] matched_image = images[-1] for image in images: if image["width"] >= 720: matched_image = image break item.thumb = matched_image["url"].values()[0] item.description = result_set["description"] item.complete = False item.isGeoLocked = result_set.get("geoprotection", False) # set the date and time date = result_set["published_at"] time_stamp = DateHelper.get_date_from_string( date, date_format="%Y-%m-%dT%H:%M:%S+{0}".format(date[-4:])) item.set_date(*time_stamp[0:6]) return item
def create_json_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 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) meta = result_set["meta"] name = meta["header"]["title"] if isinstance(name, dict): name = name["text"] sub_heading = meta.get("subHeader") if sub_heading: name = "{} - {}".format(name, sub_heading) url = "{}{}".format(self.baseUrl, result_set["url"]) item = MediaItem(name, url) item.type = "video" item.description = meta.get("description") item.thumb = result_set.get("media", {}).get("image", {}).get("url") item.isGeoLocked = True date_value = meta["date"] if "." in date_value: date = DateHelper.get_date_from_string(date_value, date_format="%d.%m.%Y") else: date = DateHelper.get_date_from_string(date_value, date_format="%d/%m/%Y") item.set_date(*date[0:6]) return item
def create_series_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 """ title = result_set["title"] sub_title = result_set.get("episodeTitle", None) if sub_title: title = "{} - {}".format(title, sub_title) if not result_set["usageRights"].get("hasRightsNow", True): Logger.debug("Found '%s' without 'usageRights'", title) return None url = "https://psapi.nrk.no/programs/{}?apiKey={}".format( result_set["id"], self.__api_key) item = MediaItem(title, url) item.type = 'video' # noinspection PyTypeChecker item.thumb = self.__get_image(result_set["image"]["webImages"], "pixelWidth", "imageUrl") item.description = result_set.get("longDescription", "") if not item.description: item.description = result_set.get("shortDescription", "") item.isGeoLocked = result_set.get("usageRights", {}).get("isGeoBlocked", False) self.__set_date(result_set, item) return item
def __create_generic_item(self, result_set, expected_item_type, url_format): video_info = result_set["attributes"] name = video_info["name"] if expected_item_type != result_set["type"]: Logger.warning("Not %s, excluding %s", expected_item_type, name) return None channel_id = int(result_set["relationships"]["primaryChannel"]["data"]["id"]) if self.primaryChannelId is not None and channel_id != self.primaryChannelId: return None item_id = result_set["id"] # Show the slug? # showSlug = video_info["alternateId"] url = url_format.format(item_id) item = MediaItem(name, url) item.description = video_info.get("description") geo_info = video_info.get("geoRestrictions", {"countries": ["world"]}) item.isGeoLocked = "world" not in geo_info.get("countries") # set the images if "images" in result_set["relationships"]: thumb_id = result_set["relationships"]["images"]["data"][0]["id"] item.thumb = self.imageLookup.get(thumb_id, self.noImage) if item.thumb == self.noImage: Logger.warning("No thumb found for %s", thumb_id) # paid or not? if "contentPackages" in result_set["relationships"]: item.isPaid = not any( filter( lambda p: p["id"].lower() == "free", result_set["relationships"]["contentPackages"]["data"] ) ) else: item.isPaid = False return item
def create_video_item_api(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[str,str] 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 """ # Could be: title = result_set['episodeTitle'] title = result_set['title'] url = "https://api.viervijfzes.be/content/{}".format( result_set['videoUuid']) item = MediaItem(title, url) item.type = "video" item.description = HtmlHelper.to_text( result_set.get("description").replace(">\r\n", ">")) item.thumb = result_set["image"] item.isGeoLocked = result_set.get("isProtected") date_time = DateHelper.get_date_from_posix(result_set["createdDate"]) item.set_date(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second) item.set_info_label("duration", result_set["duration"]) if "epsiodeNumber" in result_set and "seasonNumber" in result_set: item.set_season_info(result_set["seasonNumber"], result_set["epsiodeNumber"]) return item
def __create_generic_item(self, result_set, expected_item_type, url_format): video_info = result_set["attributes"] name = video_info["name"] if expected_item_type != result_set["type"]: Logger.warning("Not %s, excluding %s", expected_item_type, name) return None channel_id = int( result_set["relationships"]["primaryChannel"]["data"]["id"]) if self.primaryChannelId is not None and channel_id != self.primaryChannelId: return None item_id = result_set["id"] # Show the slug? # showSlug = video_info["alternateId"] url = url_format.format(item_id) item = MediaItem(name, url) item.description = video_info.get("description") geo_info = video_info.get("geoRestrictions", {"countries": ["world"]}) item.isGeoLocked = "world" not in geo_info.get("countries") # set the images if "images" in result_set["relationships"]: thumb_id = result_set["relationships"]["images"]["data"][0]["id"] item.thumb = self.imageLookup.get(thumb_id, self.noImage) if item.thumb == self.noImage: Logger.warning("No thumb found for %s", thumb_id) # paid or not? item.isPaid, logon_required = self._is_paid_or_logged_on_item( result_set) if logon_required: item.metaData[self.__REQUIRES_LOGIN] = True return item
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_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 create_api_episode_type(self, result_set, add_parent_title=False): """ Creates a MediaItem of type 'video' using the result_set from the API. 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 :param bool add_parent_title: Should the parent's title be included? :return: A new MediaItem of type 'video' or 'audio' (despite the method's name). :rtype: MediaItem|None This works for: __typename=Episode """ svt_video_id = result_set.get("videoSvtId", result_set.get("svtId", None)) if svt_video_id: # API style url = "https://api.svt.se/videoplayer-api/video/{}".format( svt_video_id) else: # HTML style url = "{}{}".format(self.baseUrl, result_set['urls']['svtplay']) title = result_set.get("name", "") if "parent" in result_set and add_parent_title: title = "{} - {}".format(result_set["parent"]["name"], title) item = MediaItem(title, url) item.description = result_set.get("longDescription") item.type = "video" item.set_info_label("duration", int(result_set.get("duration", 0))) item.isGeoLocked = result_set.get("restrictions", {}).get("onlyAvailableInSweden", False) parent_images = result_set.get(self.__parent_images) if bool(parent_images): item.fanart = self.__get_thumb(parent_images) if "image" in result_set: item.thumb = self.__get_thumb(result_set["image"], width=720) if not bool(item.fanart): item.fanart = self.__get_thumb(result_set["image"]) valid_to = result_set.get("validTo", None) if valid_to: self.__set_expire_time(valid_to, item) live_data = result_set.get("live") if live_data: is_live_now = live_data["liveNow"] if is_live_now: item.name = "{} [COLOR gold](live)[/COLOR]".format(item.name) start = live_data["start"] if start.endswith("Z"): start_time = DateHelper.get_datetime_from_string( start, "%Y-%m-%dT%H:%M:%SZ", "UTC") start_time = start_time.astimezone(self.__timezone) item.set_date(start_time.year, start_time.month, start_time.day, start_time.hour, start_time.minute, start_time.second) hour = start_time.hour minute = start_time.minute else: start = start.split('.')[0].split("+")[0] start_time = DateHelper.get_date_from_string( start, "%Y-%m-%dT%H:%M:%S") item.set_date(*start_time[0:6]) hour = start_time.tm_hour minute = start_time.tm_min item.name = "{:02}:{:02} - {}".format(hour, minute, item.name) return item
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 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[str,dict[str,str] 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) drm_locked = False geo_blocked = result_set["is_geo_blocked"] title = result_set["title"] if ("_links" not in result_set or "stream" not in result_set["_links"] or "href" not in result_set["_links"]["stream"]): Logger.warning("No streams found for %s", title) return None # the description description = result_set["description"].strip() # The long version summary = result_set["summary"].strip() # The short version # Logger.Trace("Comparing:\nDesc: %s\nSumm:%s", description, summary) if not description.startswith(summary): # the descripts starts with the summary. Don't show description = "%s\n\n%s" % (summary, description) video_type = result_set["type"] if not video_type == "program": title = "%s (%s)" % (title, video_type.title()) elif result_set["format_position"][ "is_episodic"]: # and resultSet["format_position"]["episode"] != "0": # make sure we show the episodes and seaso season = result_set["format_position"].get("season", 0) episode = int(result_set["format_position"]["episode"] or "0") # Was it a webisode? # webisode = result_set.get("webisode", False) # if the name had the episode in it, translate it if episode > 0 and season > 0: # and not webisode: description = "%s\n\n%s" % (title, description) title = "{0} - s{1:02d}e{2:02d}".format( result_set["format_title"], season, episode) else: Logger.debug( "Found episode '0' or websido '%s': using name instead of episode number", title) mpx_guid = result_set.get('mpx_guid') if mpx_guid is None: url = result_set["_links"]["stream"]["href"] else: # we can use mpx_guid and https://viafree.mtg-api.com/stream-links/viafree/web/se/clear-media-guids/{}/streams url = "https://viafree.mtg-api.com/stream-links/viafree/web/{}/clear-media-guids/{}/streams".format( self.language, mpx_guid) item = MediaItem(title, url) date_info = None date_format = "%Y-%m-%dT%H:%M:%S" if "broadcasts" in result_set and len(result_set["broadcasts"]) > 0: date_info = result_set["broadcasts"][0]["air_at"] Logger.trace("Date set from 'air_at'") if "playable_from" in result_set["broadcasts"][0]: start_date = result_set["broadcasts"][0]["playable_from"] playable_from = DateHelper.get_date_from_string( start_date[0:-6], date_format) playable_from = datetime.datetime(*playable_from[0:6]) if playable_from > datetime.datetime.now(): drm_locked = True elif "publish_at" in result_set: date_info = result_set["publish_at"] Logger.trace("Date set from 'publish_at'") if date_info is not None: # publish_at=2007-09-02T21:55:00+00:00 info = date_info.split("T") date_info = info[0] time_info = info[1] date_info = date_info.split("-") time_info = time_info.split(":") item.set_date(date_info[0], date_info[1], date_info[2], time_info[0], time_info[1], 0) item.type = "video" item.complete = False item.isGeoLocked = geo_blocked item.isDrmProtected = drm_locked thumb_data = result_set['_links'].get('image', None) if thumb_data is not None: # Older version # item.thumbUrl = thumb_data['href'].replace("{size}", "thumb") item.thumb = self.__get_thumb_image(thumb_data['href']) item.description = description # unpublish_at=2099-12-31T00:00:00+01:00 expire_date = result_set["unpublish_at"] if bool(expire_date): self.__set_expire_time(expire_date, item) srt = result_set.get("sami_path") if not srt: srt = result_set.get("subtitles_webvtt") if srt: Logger.debug("Storing SRT/WebVTT path: %s", srt) part = item.create_new_empty_media_part() part.Subtitle = srt item.set_info_label("duration", int(result_set.get("duration", 0))) return item
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