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 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 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 __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_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