def __fetch_textures(self): textures_to_retrieve = TextureHandler.instance( ).number_of_missing_textures() if textures_to_retrieve > 0: w = None try: # show a blocking or background progress bar if textures_to_retrieve > 4: w = XbmcDialogProgressWrapper( "%s: %s" % (Config.appName, LanguageHelper.get_localized_string( LanguageHelper.InitChannelTitle)), LanguageHelper.get_localized_string( LanguageHelper.FetchTexturesTitle), # Config.textureUrl ) else: w = XbmcDialogProgressBgWrapper( "%s: %s" % (Config.appName, LanguageHelper.get_localized_string( LanguageHelper.FetchTexturesTitle)), Config.textureUrl) TextureHandler.instance().fetch_textures(w.progress_update) except: Logger.error("Error fetching textures", exc_info=True) finally: if w is not None: # always close the progress bar w.close() return
def __initialise_channel_set(self, channel_info): # type: (ChannelInfo) -> None """ Initialises a channelset (.py file) WARNING: these actions are done ONCE per python file, not per channel. Arguments: channelInfo : ChannelInfo - The channelinfo Keyword Arguments: abortOnNew : Boolean - If set to true, channel initialisation will not continue if a new channel was found. This will have to be done later. Returns True if any operations where executed """ Logger.info("Initialising channel set at: %s.", channel_info.path) # now import (required for the PerformFirstTimeActions sys.path.append(channel_info.path) # make sure a pyo or pyc exists # __import__(channelInfo.moduleName) # The debugger won't compile if __import__ is used. So let's use this one. import py_compile py_compile.compile(os.path.join(channel_info.path, "%s.py" % (channel_info.moduleName,))) # purge the texture cache. if TextureHandler.instance(): TextureHandler.instance().purge_texture_cache(channel_info) else: Logger.warning("Could not purge_texture_cache: no TextureHandler available") return
def __update_artwork(self, media_item, channel, use_thumbs_as_fanart): """ Updates the fanart and icon of a MediaItem if thoses are missing. :param MediaItem media_item: The item to update :param Channel channel: A possible selected channel :param bool use_thumbs_as_fanart: Use thumbs for artwork """ if media_item is None: return if channel: # take the channel values fallback_icon = channel.icon fallback_thumb = channel.noImage fallback_fanart = channel.fanart fallback_poster = channel.poster parent_item = channel.parentItem else: # else the Retrospect ones fallback_icon = Config.icon fallback_thumb = Config.fanart fallback_fanart = Config.fanart fallback_poster = None parent_item = None if parent_item is not None: fallback_icon = parent_item.icon or fallback_icon fallback_thumb = parent_item.thumb or fallback_thumb fallback_fanart = parent_item.fanart or fallback_fanart fallback_poster = parent_item.poster or fallback_poster # keep it or use the fallback if not media_item.is_playable( ) and not media_item.poster and not media_item.thumb: # Only set a fallback poster on none-playable items that do not have a poster and # don't have a thumb. Otherwise Kodi will always display the fallback poster. The # thumb is preferred in that case. media_item.poster = fallback_poster media_item.icon = media_item.icon or fallback_icon media_item.thumb = media_item.thumb or fallback_thumb media_item.fanart = media_item.fanart or fallback_fanart if use_thumbs_as_fanart and \ TextureHandler.instance().is_texture_or_empty(media_item.fanart) and \ not TextureHandler.instance().is_texture_or_empty(media_item.thumb): media_item.fanart = media_item.thumb return
def init_channel(self): """Initializes the channel and will call some post processing stuff. This method is called for each add-on call and can be used to do some channel initialisation. """ Logger.debug("Initializing channel (init_channel): %s", self) # Make sure all images are from the correct absolute location self.noImage = TextureHandler.instance().get_texture_uri( self, self.noImage) self.poster = TextureHandler.instance().get_texture_uri( self, self.poster) return
def get_image_location(self, image): """ Returns the path for a specific image name. :param str image: the filename of the requested argument. :return: The full local path to the requested image. :rtype: str """ return TextureHandler.instance().get_texture_uri(self, image)
def __get_image_path(self, image): """ Tries to determine the path of an image Arguments: image : String - The filename (not path) of the image Returns the path of the image. In case of a Kodi skin image it will return just the filename, else the full path. """ return TextureHandler.instance().get_texture_uri(self, image)
def __update_artwork(self, media_item, channel): """ Updates the fanart and icon of a MediaItem if thoses are missing. :param MediaItem media_item: The item to update :param Channel channel: A possible selected channel """ if media_item is None: return if channel: # take the channel values fallback_icon = self.channelObject.icon fallback_thumb = self.channelObject.noImage fallback_fanart = self.channelObject.fanart parent_item = channel.parentItem else: # else the Retrospect ones fallback_icon = Config.icon fallback_thumb = Config.fanart fallback_fanart = Config.fanart parent_item = None if parent_item is not None: fallback_thumb = parent_item.thumb or fallback_thumb fallback_fanart = parent_item.fanart or fallback_fanart # keep it or use the fallback media_item.icon = media_item.icon or fallback_icon media_item.thumb = media_item.thumb or fallback_thumb media_item.fanart = media_item.fanart or fallback_fanart if AddonSettings.use_thumbs_as_fanart() and \ TextureHandler.instance().is_texture_or_empty(media_item.fanart) and \ not TextureHandler.instance().is_texture_or_empty(media_item.thumb): media_item.fanart = media_item.thumb return
def __init__(self, channel_info): """ Initialisation of the class. All class variables should be instantiated here and this method should not be overridden by any derived classes. :param ChannelInfo channel_info: The channel info object to base this channel on. """ Logger.info("Initializing channel (__init__): %s", channel_info) self.mainListItems = [] self.parentItem = None # The proxy to be used for this channel self.proxy = AddonSettings.get_proxy_for_channel(channel_info) self.localIP = AddonSettings.get_local_ip_header_for_channel( channel_info) # More and more API's need a specific set of headers. This set is used for the self.mainListUri, and is set to # all items generated by the chn_class.py. self.httpHeaders = dict() self.loggedOn = False # Initialize channel stuff from ChannelInfo object self.guid = channel_info.guid self.id = channel_info.id self.channelName = channel_info.channelName self.safeName = channel_info.safe_name self.channelCode = channel_info.channelCode self.channelDescription = channel_info.channelDescription self.moduleName = channel_info.moduleName self.compatiblePlatforms = channel_info.compatiblePlatforms self.sortOrder = channel_info.sortOrder self.sortOrderPerCountry = channel_info.sortOrderPerCountry self.category = channel_info.category self.language = channel_info.language self.path = channel_info.path self.version = channel_info.version self.adaptiveAddonSelectable = channel_info.adaptiveAddonSelectable self.hasSettings = channel_info.settings is not None and len( channel_info.settings) > 0 # get the textures from the channelinfo and get their full uri's. self.icon = TextureHandler.instance().get_texture_uri( self, channel_info.icon) self.fanart = TextureHandler.instance().get_texture_uri( self, channel_info.fanart) # ============== Actual channel setup STARTS here and should be overwritten from derived classes =============== self.noImage = "" # set context menu items self.contextMenuItems = [] # configure login stuff self.requiresLogon = False # setup the urls self.mainListUri = "" self.baseUrl = "" self.swfUrl = "" # setup the main parsing data # self.dataHandlers = dict() # self.updateHandlers = dict() self.dataParsers = dict() self.episodeItemRegex = '' # : used for the ParseMainList self.episodeItemJson = None # : used for the ParseMainList self.videoItemRegex = '' # : used for the ParseMainList self.videoItemJson = None # : used for the ParseMainList self.folderItemRegex = '' # : used for the create_folder_item self.folderItemJson = None # : used for the create_folder_item self.mediaUrlRegex = '' # : used for the update_video_item self.mediaUrlJson = None # : used for the update_video_item """ The ProcessPageNavigation method will parse the current data using the pageNavigationRegex. It will create a pageItem using the create_page_item method. If no create_page_item method is in the channel, a default one will be created with the number present in the resultset location specified in the pageNavigationRegexIndex and the url from the combined resultset. If that url does not contain http:// the self.baseUrl will be added. """ self.pageNavigationIndicationRegex = '' self.pageNavigationRegex = '' self.pageNavigationJson = None self.pageNavigationRegexIndex = 0 self.pageNavigationJsonIndex = None #=============================================================================================================== # non standard items #=============================================================================================================== # Test cases: # ====================================== Actual channel setup STOPS here ======================================= return
def __init__(self, channel_info): """ Initialisation of the class. All class variables should be instantiated here and this method should not be overridden by any derived classes. :param ChannelInfo channel_info: The channel info object to base this channel on. """ chn_class.Channel.__init__(self, channel_info) # ============== Actual channel setup STARTS here and should be overwritten from derived classes =============== self.noImage = "vrtnuimage.png" self.mainListUri = "https://www.vrt.be/vrtnu/a-z/" self.baseUrl = "https://www.vrt.be" # first regex is a bit tighter than the second one. episode_regex = r'<nui-tile href="(?<url>/vrtnu[^"]+)"[^>]*>\s*<h3[^>]*>\s*<a[^>]+>' \ r'(?<title>[^<]+)</a>\s*</h3>\s*<div[^>]+>(?:\s*<p>)?(?<description>' \ r'[\w\W]{0,2000}?)(?:</p>)?\W*</div>\s*(?:<p[^>]*' \ r'data-brand="(?<channel>[^"]+)"[^>]*>[^<]+</p>)?\s*(?:<img[\w\W]{0,100}?' \ r'data-responsive-image="(?<thumburl>//[^" ]+)")?' episode_regex = Regexer.from_expresso(episode_regex) self._add_data_parser(self.mainListUri, name="Main A-Z listing", preprocessor=self.add_categories, match_type=ParserData.MatchExact, parser=episode_regex, creator=self.create_episode_item) self._add_data_parser("#channels", name="Main channel name listing", preprocessor=self.list_channels) self._add_data_parser("https://search.vrt.be/suggest?facets[categories]", name="JSON Show Parser", json=True, parser=[], creator=self.create_show_item) self._add_data_parser("https://services.vrt.be/videoplayer/r/live.json", json=True, name="Live streams parser", parser=[], creator=self.create_live_stream) self._add_data_parsers(["http://live.stream.vrt.be/", "https://live-vrt.akamaized.net"], name="Live streams updater", updater=self.update_live_video) self._add_data_parser(r"https://live-[^/]+\.vrtcdn\.be", match_type=ParserData.MatchRegex, name="Live streams updater", updater=self.update_live_video) self._add_data_parser("https://www.vrt.be/vrtnu/categorieen.model.json", name="Category parser", json=True, match_type=ParserData.MatchExact, parser=[":items", "par", ":items", "categories", "items"], creator=self.create_category) folder_regex = r'<li class="vrt-labelnav--item "[^>]*>\s*(?:<h2[^<]*>\s*)?<a[^>]*href="(?<url>[^"]+)"[^>]*>(?:\W*<nui[^>]*>\W*)?(?<title>[^<]+)</' folder_regex = Regexer.from_expresso(folder_regex) self._add_data_parser("*", name="Folder/Season parser", parser=folder_regex, creator=self.create_folder_item) video_regex = r'<a[^>]+href="(?<url>/vrtnu/(?:[^/]+/){2}[^/]*?(?<year2>\d*)/[^"]+)"[^>]*>' \ r'\W*(?<title>[^<]+)(?:<br\s*/>\s*)?</a>\s*</h3>\s*<p[^>]*>\W*(?<channel>[^<]+)' \ r'</p>\s*(?:<p[^<]+</p>\s*)?<div[^>]*class="meta[^>]*>\s*(?:<time[^>]+datetime=' \ r'"(?<year>\d+)-(?<month>\d+)-(?<day>\d+))?[\w\W]{0,1000}?ata-responsive-image=' \ r'"(?<thumburl>[^"]+)' # No need for a subtitle for now as it only includes the textual date video_regex = Regexer.from_expresso(video_regex) self._add_data_parser("*", name="Video item parser", parser=video_regex, creator=self.create_video_item) # needs to be after the standard video item regex single_video_regex = r'<script type="application/ld\+json">\W+({[\w\W]+?})\s*</script' single_video_regex = Regexer.from_expresso(single_video_regex) self._add_data_parser("*", name="Single video item parser", parser=single_video_regex, creator=self.create_single_video_item) self._add_data_parser("*", updater=self.update_video_item, requires_logon=True) # =============================================================================================================== # non standard items self.__hasAlreadyVideoItems = False self.__currentChannel = None # The key is the channel live stream key self.__channelData = { "vualto_mnm": { "title": "MNM", "metaCode": "mnm", "fanart": TextureHandler.instance().get_texture_uri(self, "mnmfanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "mnmimage.jpg"), "icon": TextureHandler.instance().get_texture_uri(self, "mnmicon.png") }, "vualto_stubru": { "title": "Studio Brussel", "metaCode": "stubru", "fanart": TextureHandler.instance().get_texture_uri(self, "stubrufanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "stubruimage.jpg"), "icon": TextureHandler.instance().get_texture_uri(self, "stubruicon.png") }, "vualto_een_geo": { "title": "Eén", "metaCode": "een", "fanart": TextureHandler.instance().get_texture_uri(self, "eenfanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "eenimage.png"), "icon": TextureHandler.instance().get_texture_uri(self, "eenlarge.png"), # "url": "https://live-vrt.akamaized.net/groupc/live/8edf3bdf-7db3-41c3-a318-72cb7f82de66/live_aes.isml/.m3u8" }, "vualto_canvas_geo": { "title": "Canvas", "metaCode": "canvas", "fanart": TextureHandler.instance().get_texture_uri(self, "canvasfanart.png"), "thumb": TextureHandler.instance().get_texture_uri(self, "canvasimage.png"), "icon": TextureHandler.instance().get_texture_uri(self, "canvaslarge.png"), # "url": "https://live-vrt.akamaized.net/groupc/live/14a2c0f6-3043-4850-88a5-7fb062fe7f05/live_aes.isml/.m3u8" }, "vualto_ketnet_geo": { "title": "KetNet", "metaCode": "ketnet", "fanart": TextureHandler.instance().get_texture_uri(self, "ketnetfanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "ketnetimage.jpg"), "icon": TextureHandler.instance().get_texture_uri(self, "ketnetlarge.png"), # "url": "https://live-vrt.akamaized.net/groupc/live/f132f1b8-d04d-404e-90e0-6da1abb4f4fc/live_aes.isml/.m3u8" }, "vualto_sporza_geo": { # not in the channel filter maps, so no metaCode "title": "Sporza", "fanart": TextureHandler.instance().get_texture_uri(self, "sporzafanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "sporzaimage.jpg"), "icon": TextureHandler.instance().get_texture_uri(self, "sporzalarge.png"), # "url": "https://live-vrt.akamaized.net/groupa/live/7d5f0e4a-3429-4861-91d4-aa3229d7ad7b/live_aes.isml/.m3u8" }, "ketnet-jr": { # Not in the live channels "title": "KetNet Junior", "metaCode": "ketnet-jr", "fanart": TextureHandler.instance().get_texture_uri(self, "ketnetfanart.jpg"), "thumb": TextureHandler.instance().get_texture_uri(self, "ketnetimage.jpg"), "icon": TextureHandler.instance().get_texture_uri(self, "ketnetlarge.png") } } # To get the tokens: # POST # Content-Type:application/json # https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/external/v1/tokens # =============================================================================================================== # Test cases: # ====================================== Actual channel setup STOPS here ======================================= return
def __init__(self, channel_info): """ Initialisation of the class. All class variables should be instantiated here and this method should not be overridden by any derived classes. :param ChannelInfo channel_info: The channel info object to base this channel on. """ chn_class.Channel.__init__(self, channel_info) if self.channelCode == "pathejson": # we need to add headers and stuff for the API # self.UriHandlerOpen = UriHandler.open # UriHandler.open = self.__JsonHandlerOpen self.baseUrl = "https://connect.pathe.nl/v1" # set the default headers self.httpHeaders = { "X-Client-Token": "03f9f5feb3734c94831a972c932a7007", "Accept": "application/json" } self.mainListUri = "https://connect.pathe.nl/v1/cinemas" self._add_data_parser("https://connect.pathe.nl/v1/cinemas", json=True, match_type=ParserData.MatchExact, preprocessor=self.add_special_categories, parser=[], creator=self.create_cinema) self._add_data_parsers(["/movies/nowplaying", "https://connect.pathe.nl/v1/movies/comingsoon/", "https://connect.pathe.nl/v1/specials/movies/"], json=True, match_type=ParserData.MatchEnd, parser=[], creator=self.create_movie) self._add_data_parser("https://connect.pathe.nl/v1/movies/lists/", json=True, match_type=ParserData.MatchExact, parser=["comingSoon"], creator=self.create_movie) self._add_data_parser("https://connect.pathe.nl/v1/movies/", json=True, parser=['trailers'], creator=self.create_trailer) self._add_data_parser("/schedules?date=", json=True, match_type=ParserData.MatchContains, preprocessor=self.get_schedule_data, parser=['movies'], creator=self.create_movie) elif self.channelCode == "pathe": self.mainListUri = "https://www.pathe.nl" self.baseUrl = "https://www.pathe.nl" # setup the main parsing data self.episodeItemRegex = '<li><a[^>]+href="(https://www.pathe.nl/bioscoop/[^"]+)"[^>]+>([^<]+)</a></li>' self.folderItemRegex = r'<li class="tab-item[^>]+>\W+<a[^>]+title="\w+ (\d+) (\w+) (\d+)"[^<]+' \ r'href="([^#]+)#schedule[^>]*>(\w+)' self.videoItemRegex = r'<div class="schedule-movie">\W+<a[^>]+href="([^#]+)\#[^>]+"[^>]+' \ r'title="([^"]+)"[^>]+>\W+<div[^>]+>\W+<img[^>]+src="([^"]+)"[^>]+>\W+</div>' \ r'[\w\W]{0,1500}?<table class="table-schedule">([\w\W]{0,5000}?)</table>' self.mediaUrlRegex = 'file: "(http[^"]+)' else: raise NotImplementedError("Code %s is not implemented" % (self.channelCode,)) # ============== Actual channel setup STARTS here and should be overwritten from derived classes =============== self.noImage = "patheimage.png" self.movie_poster = TextureHandler.instance().get_texture_uri(self, "patheposter.jpg") self.scheduleData = None # ====================================== Actual channel setup STOPS here ======================================= return