def get_channel(self, class_name, channel_code, info_only=False): """ Fetches a single channel for a given className and channelCode If updated channels are found, the those channels are indexed and the channel index is rebuild. :param str|unicode class_name: The chn_<name> class name. :param str|unicode channel_code: A possible channel code within the channel set. :param bool info_only: Only return the ChannelInfo. :return: a Channel object :rtype: Channel """ channel_set = self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY].get(class_name, None) if channel_set is None: Logger.error("Could not find info for channelClass '%s'.", class_name) return None channel_set_info_path = channel_set[self.__CHANNEL_INDEX_CHANNEL_INFO_KEY] channel_set_version = channel_set[self.__CHANNEL_INDEX_CHANNEL_VERSION_KEY] if not os.path.isfile(channel_set_info_path) and not self.__reindexed: Logger.warning("Missing channel_set file: %s.", channel_set_info_path) self.__rebuild_index() return self.get_channel(class_name, channel_code) channel_infos = ChannelInfo.from_json(channel_set_info_path, channel_set_version) if channel_code is None: channel_infos = [ci for ci in channel_infos if ci.channelCode is None] else: channel_infos = [ci for ci in channel_infos if ci.channelCode == channel_code] if len(channel_infos) != 1: Logger.error("Found none or more than 1 matches for '%s' and '%s' in the channel index.", class_name, channel_code or "None") return None else: Logger.debug("Found single channel in the channel index: %s.", channel_infos[0]) if self.__is_channel_set_updated(channel_infos[0]): # let's see if the index has already been updated this section, of not, do it and # restart the ChannelRetrieval. if not self.__reindexed: # rebuild and restart Logger.warning("Re-index channel index due to channel_set update: %s.", channel_set_info_path) self.__rebuild_index() else: Logger.warning("Found updated channel_set: %s.", channel_set_info_path) # new we should init all channels by loading them all, just to be shure that all is ok Logger.debug("Going to fetching all channels to init them all.") self.get_channels() return self.get_channel(class_name, channel_code) if info_only: return channel_infos[0] return channel_infos[0].get_channel()
def GetChannel(self, className, channelCode): """ Fetches a single channel for a given className and channelCode If updated channels are found, the those channels are indexed and the channel index is rebuild. @param className: the chn_<name> class name @param channelCode: a possible channel code within the channel set @return: a ChannelInfo object """ channelSet = self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY].get(className, None) if channelSet is None: Logger.Error("Could not find info for channelClass '%s'.", className) return None channelSetInfoPath = channelSet[self.__CHANNEL_INDEX_CHANNEL_INFO_KEY] channelSetVersion = channelSet[self.__CHANNEL_INDEX_CHANNEL_VERSION_KEY] if not os.path.isfile(channelSetInfoPath) and not self.__reindexed: Logger.Warning("Missing channelSet file: %s.", channelSetInfoPath) self.__RebuildIndex() return self.GetChannel(className, channelCode) channelInfos = ChannelInfo.FromJson(channelSetInfoPath, channelSetVersion) if channelCode is None: channelInfos = filter(lambda ci: ci.channelCode is None, channelInfos) else: channelInfos = filter(lambda ci: ci.channelCode == channelCode, channelInfos) if len(channelInfos) != 1: Logger.Error("Found none or more than 1 matches for '%s' and '%s' in the channel index.", className, channelCode or "None") return None else: Logger.Debug("Found single channel in the channel index: %s.", channelInfos[0]) if self.__IsChannelSetUpdated(channelInfos[0]): # let's see if the index has already been updated this section, of not, do it and # restart the ChannelRetrieval. if not self.__reindexed: # rebuild and restart Logger.Warning("Re-index channel index due to channelSet update: %s.", channelSetInfoPath) self.__RebuildIndex() else: Logger.Warning("Found updated channelSet: %s.", channelSetInfoPath) # new we should init all channels by loading them all, just to be shure that all is ok Logger.Debug("Going to fetching all channels to init them all.") self.GetChannels() return self.GetChannel(className, channelCode) return channelInfos[0].GetChannel()
def get_channels(self, include_disabled=False, **kwargs): # NOSONAR """ Retrieves all enabled channels within Retrospect. If updated channels are found, the those channels are indexed and the channel index is rebuild. :param bool include_disabled: Boolean to indicate if we should include those channels that are explicitly disabled from the settings. :param dict kwargs: Here for backward compatibility. :return: a list of ChannelInfo objects of enabled channels. :rtype: list[ChannelInfo] """ sw = StopWatch("ChannelIndex.get_channels Importer", Logger.instance()) Logger.info("Fetching all enabled channels.") self.__allChannels = [] valid_channels = [] # What platform are we platform = envcontroller.EnvController.get_platform() channels_updated = False country_visibility = {} for channel_set in self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY]: channel_set = self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY][channel_set] channel_set_info_path = channel_set[self.__CHANNEL_INDEX_CHANNEL_INFO_KEY] channel_set_version = channel_set[self.__CHANNEL_INDEX_CHANNEL_VERSION_KEY] # Check if file exists. If not, rebuild index if not os.path.isfile(channel_set_info_path) and not self.__reindexed: Logger.warning("Missing channelSet file: %s.", channel_set_info_path) self.__rebuild_index() return self.get_channels() channel_infos = ChannelInfo.from_json(channel_set_info_path, channel_set_version) # Check if the channel was updated if self.__is_channel_set_updated(channel_infos[0]): # let's see if the index has already been updated this section, of not, do it and # restart the ChannelRetrieval. if not self.__reindexed: # rebuild and restart Logger.warning("Re-index channel index due to channelSet update: %s.", channel_set_info_path) self.__rebuild_index() return self.get_channels() else: Logger.warning("Found updated channelSet: %s.", channel_set_info_path) if not channels_updated: # this was the first update found (otherwise channelsUpdated was True) show a message: title = LanguageHelper.get_localized_string(LanguageHelper.InitChannelTitle) text = LanguageHelper.get_localized_string(LanguageHelper.InitChannelText) XbmcWrapper.show_notification(title, text, display_time=15000, logger=Logger.instance()) channels_updated |= True # Initialise the channelset. self.__initialise_channel_set(channel_infos[0]) # And perform all first actions for the included channels in the set for channel_info in channel_infos: self.__initialise_channel(channel_info) # Check the channel validity for channel_info in channel_infos: if not self.__channel_is_correct(channel_info): continue self.__allChannels.append(channel_info) # valid channel for this platform ? if not channel_info.compatiblePlatforms & platform == platform: Logger.warning("Not loading: %s -> platform '%s' is not compatible.", channel_info, Environments.name(platform)) continue valid_channels.append(channel_info) # was the channel hidden based on language settings? We do some caching to speed # things up. if channel_info.language not in country_visibility: country_visibility[channel_info.language] = AddonSettings.show_channel_with_language(channel_info.language) channel_info.visible = country_visibility[channel_info.language] # was the channel explicitly disabled from the settings? channel_info.enabled = AddonSettings.get_channel_visibility(channel_info) Logger.debug("Found channel: %s", channel_info) if channels_updated: Logger.info("New or updated channels found. Updating add-on configuration for all channels and user agent.") AddonSettings.update_add_on_settings_with_channels(valid_channels, Config) AddonSettings.update_user_agent() else: Logger.debug("No channel changes found. Skipping add-on configuration for channels.") # TODO: perhaps we should check that the settings.xml is correct and not broken? valid_channels.sort(key=lambda c: c.sort_key) visible_channels = [ci for ci in valid_channels if ci.visible and ci.enabled] Logger.info("Fetch a total of %d channels of which %d are visible.", len(valid_channels), len(visible_channels)) sw.stop() if include_disabled: return valid_channels return visible_channels
def GetChannels(self, includeDisabled=False, **kwargs): # type: (object) -> list """ Retrieves all enabled channels within Retrospect. If updated channels are found, the those channels are indexed and the channel index is rebuild. @type kwargs: here for backward compatibility @return: a list of ChannelInfo objects of enabled channels. """ sw = StopWatch("ChannelIndex.GetChannels Importer", Logger.Instance()) Logger.Info("Fetching all enabled channels.") self.__enabledChannels = [] self.__allChannels = [] self.__validChannels = [] # What platform are we platform = envcontroller.EnvController.GetPlatform() channelsUpdated = False for channelSet in self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY]: channelSet = self.__channelIndex[self.__CHANNEL_INDEX_CHANNEL_KEY][channelSet] channelSetInfoPath = channelSet[self.__CHANNEL_INDEX_CHANNEL_INFO_KEY] channelSetVersion = channelSet[self.__CHANNEL_INDEX_CHANNEL_VERSION_KEY] # Check if file exists. If not, rebuild index if not os.path.isfile(channelSetInfoPath) and not self.__reindexed: Logger.Warning("Missing channelSet file: %s.", channelSetInfoPath) self.__RebuildIndex() return self.GetChannels() channelInfos = ChannelInfo.FromJson(channelSetInfoPath, channelSetVersion) # Check if the channel was updated if self.__IsChannelSetUpdated(channelInfos[0]): # let's see if the index has already been updated this section, of not, do it and # restart the ChannelRetrieval. if not self.__reindexed: # rebuild and restart Logger.Warning("Re-index channel index due to channelSet update: %s.", channelSetInfoPath) self.__RebuildIndex() return self.GetChannels() else: Logger.Warning("Found updated channelSet: %s.", channelSetInfoPath) if not channelsUpdated: # this was the first update found (otherwise channelsUpdated was True) show a message: title = LanguageHelper.GetLocalizedString(LanguageHelper.InitChannelTitle) text = LanguageHelper.GetLocalizedString(LanguageHelper.InitChannelText) XbmcWrapper.ShowNotification(title, text, displayTime=15000, logger=Logger.Instance()) channelsUpdated |= True # Initialise the channelset. self.__InitialiseChannelSet(channelInfos[0]) # And perform all first actions for the included channels in the set for channelInfo in channelInfos: self.__InitialiseChannel(channelInfo) # Check the channel validity for channelInfo in channelInfos: if not self.__ChannelIsCorrect(channelInfo): continue self.__allChannels.append(channelInfo) # valid channel for this platform ? if not channelInfo.compatiblePlatforms & platform == platform: Logger.Warning("Not loading: %s -> platform '%s' is not compatible.", channelInfo, Environments.Name(platform)) continue self.__validChannels.append(channelInfo) # was the channel disabled? if not (AddonSettings.ShowChannel( channelInfo) and AddonSettings.ShowChannelWithLanguage( channelInfo.language)): Logger.Warning("Not loading: %s -> Channel was disabled from settings.", channelInfo) continue self.__enabledChannels.append(channelInfo) Logger.Debug("Loading: %s", channelInfo) if channelsUpdated: Logger.Info("New or updated channels found. Updating add-on configuration for all channels and user agent.") AddonSettings.UpdateAddOnSettingsWithChannels(self.__validChannels, Config) AddonSettings.UpdateUserAgent() else: Logger.Debug("No channel changes found. Skipping add-on configuration for channels.") # TODO: perhaps we should check that the settings.xml is correct and not broken? self.__enabledChannels.sort() Logger.Info("Fetch a total of %d channels of which %d are enabled.", len(self.__allChannels), len(self.__enabledChannels)) sw.Stop() if includeDisabled: return self.__validChannels return self.__enabledChannels
def __ImportChannels(self): # , className = None): """Import the available channels This method will: - iterate through the Addons folder and find all the folders name <basename>.channel.<channelname>. - then adds all the subfolders into a list (with paths). - then all paths are added to the system path, so they can be imported. - then read all the chn_<name>.xml metadata files and add the ChannelInfo objects to the self.__channelsToImport - then the channels in the self.__channelsToImport list are instantiated into the self.channels list. """ Logger.Debug("Importing available channels") # import each channelPath. On import, the channelPath will call the RegisterChannel Method try: # clear a possible previous import self.__enabledChannels = [] self.__allChannels = [] self.__validChannels = [] self.__channelVersions = [] # first find all folders with channels that we might need to import channelImport = [] importTimer = StopWatch("ChannelImporter :: importing channels", Logger.Instance()) addonPath = self.__GetAddonPath() channelPathStart = "%s.channel" % (Config.addonDir,) for directory in os.listdir(addonPath): if channelPathStart in directory and "BUILD" not in directory: path = os.path.join(addonPath, directory) channelVersion = self.__ParseChannelVersionInfo(path) if channelVersion: self.__channelVersions.append(channelVersion) else: # no info was returned, so we will not include the channel continue # get all nested channels subDirs = os.listdir(path) channelImport.extend( [os.path.abspath(os.path.join(path, weapon)) for weapon in subDirs]) channelImport.sort() importTimer.Lap("Directories scanned for .channel") # we need to make sure we don't load multiple channel classes and track if we found updates channelsUpdated = False loadedChannels = [] channelsToImport = [] # now import the channels for channelPath in channelImport: if not os.path.isdir(channelPath): continue # determine channelname channelName = os.path.split(channelPath)[-1] if channelName == self.__updateChannelPath: Logger.Trace("Update path found and skipping: %s", channelName) continue # if loadedChannels.count(channelName) > 0: if channelName in loadedChannels: Logger.Warning( "Not loading: chn_%s.xml in %s because there is already a path with " "name '%s' that name loaded", channelName, channelPath, channelName) continue if channelName.startswith("."): continue # now we can continue loadedChannels.append(channelName) fileName = os.path.join(channelPath, "chn_" + channelName + ".json") Logger.Trace("Loading info for chn_%s @ %s", channelName, fileName) if os.path.isfile(fileName): try: ci = ChannelInfo.FromJson(fileName) if len(ci) <= 0: Logger.Warning("No channels found in '%s'", fileName) continue # Add them to the list to import channelsToImport += ci if self.__IsChannelSetUpdated(ci[0]): if not channelsUpdated: # this was the first update found (otherwise channelsUpdated was True) show a message: title = LanguageHelper.GetLocalizedString( LanguageHelper.InitChannelTitle) text = LanguageHelper.GetLocalizedString( LanguageHelper.InitChannelText) XbmcWrapper.ShowNotification(title, text, displayTime=15000) # set the updates found bit channelsUpdated |= True # Initialise the channelset. self.__InitialiseChannelSet(ci[0]) # And perform all first actions for the included channels in the set for channelInfo in ci: self.__FirstTimeChannelActions(channelInfo) except: Logger.Error("Error import chn_%s.json", channelName, exc_info=True) importTimer.Lap() # What platform are we platform = envcontroller.EnvController.GetPlatform() # instantiate the registered channels for channelInfo in channelsToImport: # noinspection PyUnusedLocal isValid = self.__ValidateChannelInfo(channelInfo, platform) # sort the channels self.__enabledChannels.sort() if channelsUpdated: Logger.Info("New or updated channels found. Updating add-on configuration for all " "channels and user agent") AddonSettings.UpdateAddOnSettingsWithChannels(self.__validChannels, Config) AddonSettings.UpdateUserAgent() else: Logger.Debug("No channel changes found. Skipping add-on configuration for channels") # Should we update the channel index? if channelsUpdated or not os.path.isfile(self.__CHANNEL_INDEX): self.__CreateChannelIndex() Logger.Info("Imported %s channels from which %s are enabled", len(self.__allChannels), len(self.__enabledChannels)) importTimer.Stop() except: Logger.Critical("Error loading channel modules", exc_info=True)
def GetSingleChannel(self, className, channelCode): """Imports a single channel Arguments: className : string - class name of the channel to import. Returns: The channels in the requested class. So that could be more, but they can be distinguished using the channelcode. Returns an empty list if no channels were found. """ if not className: raise ValueError("className should be specified.") Logger.Info("Loading channels for class '%s' and channelCode '%s'", className, channelCode) self.__enabledChannels = [] self.__allChannels = [] self.__validChannels = [] self.__channelVersions = [] # noinspection PyUnusedLocal classPath = None channelPath = None classBaseName = className[4:] if os.path.isfile(self.__CHANNEL_INDEX): Logger.Debug("Using ChannelIndex for channel lookup: %s", self.__CHANNEL_INDEX) fd = None try: fd = open(self.__CHANNEL_INDEX) data = fd.read() finally: if fd is not None and not fd.closed: fd.close() channelIndex = JsonHelper(data) classPath = channelIndex.GetValue(className, channelCode or "null") if classPath is not None: if not os.path.isdir(classPath): Logger.Warning("Missing channel class path '%s' found. Rebuilding the ChannelIndex.", classPath) # remove the old one os.remove(self.__CHANNEL_INDEX) # return self.GetSingleChannel(className, channelCode) return self.__ImportChannel(className, channelCode) channelPath = os.path.join(classPath, "..") else: Logger.Warning("Missing ChannelIndex. Rebuilding the ChannelIndex.") return self.__ImportChannel(className, channelCode) # Logger.Warning("Falling back to classic find pattern") # # # walk the channel dirs to find the one that has the channel # addonPath = self.__GetAddonPath() # channelPathStart = "%s.channel" % (Config.addonDir,) # # # list all add-ons # for directory in os.listdir(addonPath): # # find the xot ones # if channelPathStart in directory and "BUILD" not in directory: # channelPath = os.path.join(addonPath, directory) # # # list the subfolders for the requested folder to find the one we need # if classBaseName not in os.listdir(channelPath): # continue # # # we perhaps found it. # classPath = os.path.join(channelPath, classBaseName) if classPath is None: Logger.Error("No Channel found for class '%s' and channelCode '%s'", className, channelCode) return None Logger.Debug("Found possible channel folder in %s", classPath) # check the addon.xml with self.__ParseChannelVersionInfo(path) channelVersion = self.__ParseChannelVersionInfo(channelPath) if channelVersion: self.__channelVersions.append(channelVersion) else: # no info was returned, so we will not include the channel Logger.Error("Match in %s has incorrect version", classPath) return None # create ChannelInfo objects from the xml file and get the correct ChannelInfo object. It coulde that none # is found and we might need to continue (in case there were duplicate channel names fileName = os.path.join(classPath, "chn_" + classBaseName + ".json") Logger.Debug("Loading info for chn_%s @ %s", classBaseName, fileName) if not os.path.isfile(fileName): Logger.Error("Could not load %s", fileName) return None cis = ChannelInfo.FromJson(fileName) ci = filter(lambda c: c.moduleName == className and (c.channelCode == channelCode or c.channelCode is channelCode), cis) if not ci or len(ci) > 1: Logger.Error("Could not load channel with className=%s and channelCode=%s from %s", className, channelCode, fileName) return None ci = ci[0] if self.__IsChannelSetUpdated(ci): # apparently a new channel was found, so we need to do it all Logger.Info("Found a new channel, we need to reload all channels") return self.__ImportChannel(className, channelCode) # What platform are we platform = envcontroller.EnvController.GetPlatform() # check if it is enabled or not if self.__ValidateChannelInfo(ci, platform): return ci.GetChannel() else: Logger.Error("Invalid Channel found for class '%s' and channelCode '%s'", className, channelCode) return None
def GetSingleChannel_old(self, className, channelCode): """Imports a single channel Arguments: className : string - class name of the channel to import. Returns: The channels in the requested class. So that could be more, but they can be distinguished using the channelcode. Returns an empty list if no channels were found. """ if not className: raise ValueError("className should be specified.") Logger.Info("Loading channels for class '%s' and channelCode '%s'", className, channelCode) self.__enabledChannels = [] self.__allChannels = [] self.__validChannels = [] self.__channelVersions = [] channel = None # walk the channel dirs to find the one that has the channel addonPath = self.__GetAddonPath() channelPathStart = "%s.channel" % (Config.addonDir, ) folderToFind = className[4:] # list all add-ons for directory in os.listdir(addonPath): # find the xot ones if channelPathStart in directory and "BUILD" not in directory: channelPath = os.path.join(addonPath, directory) # list the subfolders for the requested folder to find the one we need if folderToFind not in os.listdir(channelPath): continue # we perhaps found it. classPath = os.path.join(channelPath, folderToFind) Logger.Debug("Found possible channel folder in %s", classPath) # check the addon.xml with self.__ParseChannelVersionInfo(path) channelVersion = self.__ParseChannelVersionInfo(channelPath) if channelVersion: self.__channelVersions.append(channelVersion) else: # no info was returned, so we will not include the channel Logger.Warning("Match in %s has incorrect version", classPath) continue # create ChannelInfo objects from the xml file and get the correct ChannelInfo object. It coulde that none # is found and we might need to continue (in case there were duplicate channel names fileName = os.path.join(classPath, "chn_" + folderToFind + ".json") Logger.Debug("Loading info for chn_%s @ %s", folderToFind, fileName) if not os.path.isfile(fileName): Logger.Warning("Could not load %s", fileName) continue cis = ChannelInfo.FromJson(fileName) ci = filter(lambda c: c.moduleName == className and (c.channelCode == channelCode or c.channelCode is channelCode), cis) if not ci or len(ci) > 1: Logger.Warning("Could not load channel with className=%s and channelCode=%s from %s", className, channelCode, fileName) continue ci = ci[0] if self.__IsChannelSetUpdated(ci): # apparently a new channel was found, so we need to do it all Logger.Info("Found a new channel, we need to reload all channels") return self.__ImportChannel(className, channelCode) # What platform are we platform = envcontroller.EnvController.GetPlatform() # check if it is enabled or not if self.__ValidateChannelInfo(ci, platform): return ci.GetChannel() else: continue Logger.Error("No Channel found for class '%s' and channelCode '%s'", className, channelCode) return channel