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 __send_log(self): """ Send log files via Pastbin or Gist. """ from helpers.logsender import LogSender sender_mode = 'hastebin' log_sender = LogSender(Config.logSenderApi, logger=Logger.instance(), mode=sender_mode) try: title = LanguageHelper.get_localized_string( LanguageHelper.LogPostSuccessTitle) url_text = LanguageHelper.get_localized_string( LanguageHelper.LogPostLogUrl) files_to_send = [ Logger.instance().logFileName, Logger.instance().logFileName.replace(".log", ".old.log") ] if sender_mode != "gist": paste_url = log_sender.send_file(Config.logFileNameAddon, files_to_send[0]) else: paste_url = log_sender.send_files(Config.logFileNameAddon, files_to_send) XbmcWrapper.show_dialog(title, url_text % (paste_url, )) except Exception as e: Logger.error("Error sending %s", Config.logFileNameAddon, exc_info=True) title = LanguageHelper.get_localized_string( LanguageHelper.LogPostErrorTitle) error_text = LanguageHelper.get_localized_string( LanguageHelper.LogPostError) error = error_text % (str(e), ) XbmcWrapper.show_dialog(title, error.strip(": "))
def toggle_cloak(self): """ Toggles the cloaking (showing/hiding) of the selected folder. """ item = self._pickler.de_pickle_media_item( self.params[self.keywordPickle]) Logger.info("Cloaking current item: %s", item) c = Cloaker(self.channelObject, AddonSettings.store(LOCAL), logger=Logger.instance()) if c.is_cloaked(item.url): c.un_cloak(item.url) self.refresh() return first_time = c.cloak(item.url) if first_time: XbmcWrapper.show_dialog( LanguageHelper.get_localized_string( LanguageHelper.CloakFirstTime), LanguageHelper.get_localized_string( LanguageHelper.CloakMessage)) del c self.refresh()
def __get_application_key(self): """ Gets the decrypted application key that is used for all the encryption. :return: The decrypted application key that is used for all the encryption. :rtype: bytes """ application_key_encrypted = AddonSettings.get_setting( Vault.__APPLICATION_KEY_SETTING, store=LOCAL) # The key was never in the local store the value was None. It was "" if it was reset. if application_key_encrypted is None: application_key_encrypted = AddonSettings.get_setting( Vault.__APPLICATION_KEY_SETTING, store=KODI) if not application_key_encrypted: return None Logger.info("Moved ApplicationKey to local storage") AddonSettings.set_setting(Vault.__APPLICATION_KEY_SETTING, application_key_encrypted, store=LOCAL) # Still no application key? Then there was no key! if application_key_encrypted == "" or application_key_encrypted is None: return None vault_incorrect_pin = LanguageHelper.get_localized_string( LanguageHelper.VaultIncorrectPin) pin = XbmcWrapper.show_key_board( heading=LanguageHelper.get_localized_string( LanguageHelper.VaultInputPin), hidden=True) if not pin: XbmcWrapper.show_notification("", vault_incorrect_pin, XbmcWrapper.Error) raise RuntimeError("Incorrect Retrospect PIN specified") pin_key = self.__get_pbk(pin) application_key = self.__decrypt(application_key_encrypted, pin_key) if not application_key.startswith(Vault.__APPLICATION_KEY_SETTING): Logger.critical("Invalid Retrospect PIN") XbmcWrapper.show_notification("", vault_incorrect_pin, XbmcWrapper.Error) raise RuntimeError("Incorrect Retrospect PIN specified") application_key_value = application_key[ len(Vault.__APPLICATION_KEY_SETTING) + 1:] Logger.info("Successfully decrypted the ApplicationKey.") if PY2: return application_key_value # We return bytes on Python 3 return application_key_value.encode()
def __update_title_and_description_with_limitations(self): """ Updates the title/name and description with the symbols for DRM, GEO and Paid. :return: (tuple) name postfix, description postfix :rtype: tuple[str,str] """ geo_lock = "º" # º drm_lock = "^" # ^ paid = "ª" # ª cloaked = "¨" # ¨ description_addition = [] title_postfix = [] description = "" title = "" if self.isDrmProtected: title_postfix.append(drm_lock) description_addition.append( LanguageHelper.get_localized_string( LanguageHelper.DrmProtected)) if self.isGeoLocked: title_postfix.append(geo_lock) description_addition.append( LanguageHelper.get_localized_string( LanguageHelper.GeoLockedId)) if self.isPaid: title_postfix.append(paid) description_addition.append( LanguageHelper.get_localized_string( LanguageHelper.PremiumPaid)) if self.isCloaked: title_postfix.append(cloaked) description_addition.append( LanguageHelper.get_localized_string(LanguageHelper.HiddenItem)) # actually update it if description_addition: description_addition = ", ".join(description_addition) description = "\n\n[COLOR gold][I]%s[/I][/COLOR]" % ( description_addition, ) if title_postfix: title = " [COLOR gold]%s[/COLOR]" % ("".join(title_postfix), ) return title, description
def list_dates(self, data): """ Generates a list of the past week days. 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 = [] # https://api.kijk.nl/v2/templates/page/missed/all/20180201 days = [ "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag" ] for i in range(0, 7): date = datetime.datetime.now() - datetime.timedelta(days=i) # https://api.kijk.nl/v2/templates/page/missed/all/20180626 # url = "https://api.kijk.nl/v2/templates/page/missed/all/{0}{1:02d}{2:02d}".format(date.year, date.month, date.day) # https://api.kijk.nl/v1/default/sections/missed-all-20180619 url = "https://api.kijk.nl/v1/default/sections/missed-all-{0}{1:02d}{2:02d}".format( date.year, date.month, date.day) if i == 0: title = LanguageHelper.get_localized_string( LanguageHelper.Today) elif i == 1: title = LanguageHelper.get_localized_string( LanguageHelper.Yesterday) elif i == 2: title = LanguageHelper.get_localized_string( LanguageHelper.DayBeforeYesterday) else: day_name = days[date.weekday()] title = day_name date_item = MediaItem(title, url) date_item.set_date(date.year, date.month, date.day) items.append(date_item) Logger.debug("Pre-Processing finished") return data, items
def __update_video_from_mpd(self, item, mpd_info, use_adaptive_with_encryption): """ Updates an existing MediaItem with more data based on an MPD stream. :param dict[str,str] mpd_info: Stream info retrieved from the stream json. :param bool use_adaptive_with_encryption: Do we use the Adaptive InputStream add-on? :param MediaItem item: The original MediaItem that needs updating. :return: The original item with more data added to it's properties. :rtype: MediaItem """ Logger.debug("Updating streams using BrightCove data.") part = item.create_new_empty_media_part() mpd_manifest_url = "https:{0}".format(mpd_info["mediaLocator"]) mpd_data = UriHandler.open(mpd_manifest_url, proxy=self.proxy) subtitles = Regexer.do_regex(r'<BaseURL>([^<]+\.vtt)</BaseURL>', mpd_data) if subtitles: Logger.debug("Found subtitle: %s", subtitles[0]) subtitle = SubtitleHelper.download_subtitle(subtitles[0], proxy=self.proxy, format="webvtt") part.Subtitle = subtitle if use_adaptive_with_encryption: # We can use the adaptive add-on with encryption Logger.info("Using MPD InputStreamAddon") license_url = Regexer.do_regex('licenseUrl="([^"]+)"', mpd_data)[0] token = "Bearer {0}".format(mpd_info["playToken"]) key_headers = {"Authorization": token} license_key = Mpd.get_license_key(license_url, key_headers=key_headers) stream = part.append_media_stream(mpd_manifest_url, 0) Mpd.set_input_stream_addon_input(stream, self.proxy, license_key=license_key) item.complete = True else: XbmcWrapper.show_dialog( LanguageHelper.get_localized_string(LanguageHelper.DrmTitle), LanguageHelper.get_localized_string( LanguageHelper.WidevineLeiaRequired)) return item
def create_json_page_item(self, result_set): """ Creates a MediaItem of type 'page' 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. :param list[str]|dict[str,str] result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'page'. :rtype: MediaItem|None """ Logger.trace(result_set) if "nextPageUrl" not in result_set: return None title = "\b.: %s :." % (LanguageHelper.get_localized_string( LanguageHelper.MorePages), ) url = "%s%s" % (self.baseUrl, result_set["nextPageUrl"]) item = MediaItem(title, url) item.icon = self.icon item.thumb = self.noImage item.complete = True return item
def __show_first_time_message(self, channel_info): """ Checks if it is the first time a channel is executed and if a first time message is available it will be shown. Shows a message dialog if the message should be shown. Make sure that each line fits in a single line of a Kodi Dialog box (50 chars). :param ChannelInfo channel_info: The ChannelInfo to show a message for. """ hide_first_time = AddonSettings.hide_first_time_messages() if channel_info.firstTimeMessage: if not hide_first_time: Logger.info( "Showing first time message '%s' for channel chn_%s.", channel_info.firstTimeMessage, channel_info.moduleName) title = LanguageHelper.get_localized_string( LanguageHelper.ChannelMessageId) XbmcWrapper.show_dialog( title, channel_info.firstTimeMessage.split("|")) else: Logger.debug( "Not showing first time message due to add-on setting set to '%s'.", hide_first_time) return
def add_live_channel(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] title = LanguageHelper.get_localized_string( LanguageHelper.LiveStreamTitleId) item = MediaItem("\a.: {} :.".format(title), "") item.type = "folder" items.append(item) live_item = MediaItem(title, "#livestream") live_item.type = "video" live_item.isLive = True item.items.append(live_item) Logger.debug("Pre-Processing finished") return data, items
def create_alpha_item(self, result_set): """ Creates a MediaItem of type 'folder' using the Alpha chars available. It uses 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. :param list[str]|dict result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'folder'. :rtype: MediaItem|None """ program_count = result_set.get("availableInternationally", 0) if program_count <= 0: return None title = result_set["title"] url_part = title.lower() if url_part == "0-9": url_part = "$" url = "https://psapi.nrk.no/medium/tv/letters/{}/indexelements?onlyOnDemandRights=false&" \ "apiKey={}".format(url_part, self.__api_key) title = LanguageHelper.get_localized_string( LanguageHelper.StartWith) % (title, ) item = MediaItem(title, url) item.icon = self.icon item.type = 'folder' item.fanart = self.fanart item.thumb = self.noImage return item
def create_page_item(self, result_set): """ Creates a MediaItem of type 'page' 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. :param list[str]|dict[str,str] result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'page'. :rtype: MediaItem|None """ Logger.trace(result_set) next_page = result_set["next"] if not next_page: Logger.debug("No more items available") return None more = LanguageHelper.get_localized_string(LanguageHelper.MorePages) url = "%s=%s" % (self.parentItem.url.rsplit("=", 1)[0], next_page) item = MediaItem(more, url) item.thumb = self.parentItem.thumb item.icon = self.icon item.fanart = self.parentItem.fanart item.complete = True return item
def __get_title(self, name): """ Create the title based on the MediaItems name and type. :param str name: the name to update. :return: an updated name :rtype: str """ if not name: name = self.name if self.type == 'page': # We need to add the Page prefix to the item name = "%s %s" % (LanguageHelper.get_localized_string( LanguageHelper.Page), name) Logger.debug("MediaItem.__get_title :: Adding Page Prefix") elif self.__date != '' and not self.is_playable(): # not playable items should always show date name = "%s [COLOR=dimgray](%s)[/COLOR]" % (name, self.__date) folder_prefix = AddonSettings.get_folder_prefix() if self.type == "folder" and not folder_prefix == "": name = "%s %s" % (folder_prefix, name) return name
def add_live_channel_and_extract_data(self, data): """ Add the live channel and extract the correct data to process further. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] title = LanguageHelper.get_localized_string(LanguageHelper.LiveStreamTitleId) item = MediaItem("\a.: {} :.".format(title), self.liveUrl) item.type = "folder" items.append(item) if not data: return "[]", items json_data = Regexer.do_regex(r"setupBroadcastArchive\('Tv',\s*([^;]+)\);", data) if isinstance(json_data, (tuple, list)) and len(json_data) > 0: Logger.debug("Pre-Processing finished") return json_data[0], items Logger.info("Cannot extract JSON data from HTML.") return data, items
def alpha_listing(self, data): """ Creates a alpha listing with items pointing to the alpha listing on line. :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 an Alpha list for BBC") items = [] # https://www.bbc.co.uk/iplayer/a-z/a title_format = LanguageHelper.get_localized_string( LanguageHelper.StartWith) url_format = "https://www.bbc.co.uk/iplayer/a-z/%s" for char in "abcdefghijklmnopqrstuvwxyz0": if char == "0": char = "0-9" sub_item = MediaItem(title_format % (char.upper(), ), url_format % (char, )) sub_item.complete = True sub_item.icon = self.icon sub_item.thumb = self.noImage sub_item.dontGroup = True sub_item.HttpHeaders = {"X-Requested-With": "XMLHttpRequest"} items.append(sub_item) return data, items
def set_bitrate(self): """ Sets the bitrate for the selected channel via a specific dialog. """ if self.channelObject is None: raise ValueError("Missing channel") # taken from the settings.xml bitrate_options = "Retrospect|100|250|500|750|1000|1500|2000|2500|4000|8000|20000"\ .split("|") current_bitrate = AddonSettings.get_max_channel_bitrate( self.channelObject) Logger.debug("Found bitrate for %s: %s", self.channelObject, current_bitrate) current_bitrate_index = 0 if current_bitrate not in bitrate_options \ else bitrate_options.index(current_bitrate) dialog = xbmcgui.Dialog() heading = LanguageHelper.get_localized_string( LanguageHelper.BitrateSelection) selected_bitrate = dialog.select(heading, bitrate_options, preselect=current_bitrate_index) if selected_bitrate < 0: return Logger.info("Changing bitrate for %s from %s to %s", self.channelObject, bitrate_options[current_bitrate_index], bitrate_options[selected_bitrate]) AddonSettings.set_max_channel_bitrate( self.channelObject, bitrate_options[selected_bitrate]) return
def add_live_streams(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ items = [] if self.parentItem is None: live_item = MediaItem( "\a.: Live TV :.", "https://d5ms27yy6exnf.cloudfront.net/live/omroepflevoland/tv/index.m3u8" ) live_item.icon = self.icon live_item.thumb = self.noImage live_item.type = 'video' live_item.dontGroup = True now = datetime.datetime.now() live_item.set_date(now.year, now.month, now.day, now.hour, now.minute, now.second) items.append(live_item) live_item = MediaItem( "\a.: Live Radio :.", "https://d5ms27yy6exnf.cloudfront.net/live/omroepflevoland/radio/index.m3u8" ) live_item.icon = self.icon live_item.thumb = self.noImage live_item.type = 'video' live_item.dontGroup = True now = datetime.datetime.now() live_item.set_date(now.year, now.month, now.day, now.hour, now.minute, now.second) items.append(live_item) # add "More" more = LanguageHelper.get_localized_string(LanguageHelper.MorePages) current_url = self.parentItem.url if self.parentItem is not None else self.mainListUri url, page = current_url.rsplit("=", 1) url = "{}={}".format(url, int(page) + 1) item = MediaItem(more, url) item.thumb = self.noImage item.icon = self.icon item.fanart = self.fanart item.complete = True items.append(item) return data, items
def create_main_list(self, data): """ Performs pre-process actions for data processing and creates the main menu list Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] live = LanguageHelper.get_localized_string( LanguageHelper.LiveStreamTitleId) live_tv = "{} - TV".format(live) live_radio = "{} - Radio".format(live) links = { live_tv: "https://psapi.nrk.no/tv/live?apiKey={}".format(self.__api_key), live_radio: "https://psapi.nrk.no/radio/live?apiKey={}".format(self.__api_key), "Recommended": "https://psapi.nrk.no/medium/tv/recommendedprograms?maxnumber=100&startRow=0&apiKey={}" .format(self.__api_key), "Popular": "https://psapi.nrk.no/medium/tv/popularprogramssuper?maxnumber=100&startRow=0&apiKey={}" .format(self.__api_key), "Recent": "https://psapi.nrk.no/medium/tv/recentlysentprograms?maxnumber=100&startRow=0&apiKey={}" .format(self.__api_key), "Categories": "https://psapi.nrk.no/medium/tv/categories?apiKey={}".format( self.__api_key), "A - Å": "https://psapi.nrk.no/medium/tv/letters?apiKey={}".format( self.__api_key), "Søk": "#searchSite" } for name, url in links.items(): item = MediaItem(name, url) item.icon = self.icon item.thumb = self.noImage item.complete = True item.HttpHeaders = self.httpHeaders items.append(item) Logger.debug("Pre-Processing finished") return data, items
def log_on(self): """ Logs on to a website, using an url. First checks if the channel requires log on. If so and it's not already logged on, it should handle the log on. That part should be implemented by the specific channel. More arguments can be passed on, but must be handled by custom code. After a successful log on the self.loggedOn property is set to True and True is returned. :return: indication if the login was successful. :rtype: bool """ if self.__idToken: return True # check if there is a refresh token # refresh token: viervijfzes_refresh_token refresh_token = AddonSettings.get_setting("viervijfzes_refresh_token") client = AwsIdp("eu-west-1_dViSsKM5Y", "6s1h851s8uplco5h6mqh1jac8m", proxy=self.proxy, logger=Logger.instance()) if refresh_token: id_token = client.renew_token(refresh_token) if id_token: self.__idToken = id_token return True else: Logger.info("Extending token for VierVijfZes failed.") # username: viervijfzes_username username = AddonSettings.get_setting("viervijfzes_username") # password: viervijfzes_password v = Vault() password = v.get_setting("viervijfzes_password") if not username or not password: XbmcWrapper.show_dialog( title=None, lines=LanguageHelper.get_localized_string( LanguageHelper.MissingCredentials), ) return False id_token, refresh_token = client.authenticate(username, password) if not id_token or not refresh_token: Logger.error("Error getting a new token. Wrong password?") return False self.__idToken = id_token AddonSettings.set_setting("viervijfzes_refresh_token", refresh_token) return True
def process_video_item(self, item): """ Process a video item using the required dataparsers :param MediaItem item: The Item to update :return: An updated item. :rtype: MediaItem """ data_parsers = self.__get_data_parsers(item.url) if not data_parsers: Logger.error("No dataparsers found cannot update item.") return item data_parsers = [d for d in data_parsers if d.Updater is not None] if len(data_parsers) < 1: Logger.warning("No DataParsers with Updaters found.") return item if len(data_parsers) > 1: Logger.warning( "More than 2 DataParsers with Updaters found. Only using first one." ) data_parser = data_parsers[0] if not data_parser.Updater: Logger.error("No videoupdater found cannot update item.") return item if data_parser.LogOnRequired: Logger.info("One or more dataparsers require logging in.") self.loggedOn = self.log_on() if not self.loggedOn: Logger.warning("Could not log on for: %s", self) title = LanguageHelper.get_localized_string( LanguageHelper.LoginErrorTitle) text = LanguageHelper.get_localized_string( LanguageHelper.LoginErrorText) XbmcWrapper.show_dialog(title, text) Logger.debug("Processing Updater from %s", data_parser) return data_parser.Updater(item)
def reset(): """ Resets the Vault and Retrospect Machine key, making all encrypted values useless. :rtype: None """ ok = XbmcWrapper.show_yes_no(LanguageHelper.get_localized_string(LanguageHelper.VaultReset), LanguageHelper.get_localized_string(LanguageHelper.VaultResetConfirm)) if not ok: Logger.debug("Aborting Reset Vault") return Logger.info("Resetting the vault to a new initial state.") AddonSettings.set_setting(Vault.__APPLICATION_KEY_SETTING, "", store=LOCAL) # create a vault instance so we initialize a new one with a new PIN. Vault() return
def set_inputstream_adaptive(self): """ Set the InputStream Adaptive for this channel """ if self.channelObject is None: raise ValueError("Missing channel") if not self.channelObject.adaptiveAddonSelectable: Logger.warning( "Cannot set InputStream Adaptive add-on mode for %s", self.channelObject) return current_mode = AddonSettings.get_adaptive_mode(self.channelObject) mode_values = [None, True, False] current_index = mode_values.index(current_mode) mode_options = [ LanguageHelper.get_localized_string(LanguageHelper.Retrospect), LanguageHelper.get_localized_string(LanguageHelper.Enabled), LanguageHelper.get_localized_string(LanguageHelper.Disabled) ] dialog = xbmcgui.Dialog() heading = LanguageHelper.get_localized_string( LanguageHelper.ChannelAdaptiveMode) selected_index = dialog.select(heading, mode_options, preselect=current_index) if selected_index < 0: return selected_value = mode_values[selected_index] Logger.info("Changing InputStream Adaptive mode for %s from %s to %s", self.channelObject, mode_options[current_index], mode_options[selected_index]) AddonSettings.set_adaptive_mode(self.channelObject, selected_value) # Refresh if we have a video item selected, so the cached urls are removed. if self.keywordPickle in self.params: Logger.debug("Refreshing list to clear URL caches") self.refresh()
def select_channels(self): """ Selects the channels that should be visible. @return: None """ valid_channels = ChannelIndex.get_register().get_channels( include_disabled=True) channels_to_show = [c for c in valid_channels if c.visible] # The old way # channels_to_show = filter(lambda c: c.visible, valid_channels) selected_channels = [c for c in channels_to_show if c.enabled] selected_indices = list( [channels_to_show.index(c) for c in selected_channels]) Logger.debug("Currently selected channels: %s", selected_indices) channel_to_show_names = [ HtmlEntityHelper.convert_html_entities(c.channelName) for c in channels_to_show ] # The old way # channel_to_show_names = list(map(lambda c: HtmlEntityHelper.convert_html_entities(c.channelName), channels_to_show)) dialog = xbmcgui.Dialog() heading = LanguageHelper.get_localized_string( LanguageHelper.ChannelSelection)[:-1] selected_channels = dialog.multiselect(heading, channel_to_show_names, preselect=selected_indices) if selected_channels is None: return selected_channels = list(selected_channels) Logger.debug("New selected channels: %s", selected_channels) indices_to_remove = [ i for i in selected_indices if i not in selected_channels ] indices_to_add = [ i for i in selected_channels if i not in selected_indices ] for i in indices_to_remove: Logger.info("Hiding channel: %s", channels_to_show[i]) AddonSettings.set_channel_visiblity(channels_to_show[i], False) for i in indices_to_add: Logger.info("Showing channel: %s", channels_to_show[i]) AddonSettings.set_channel_visiblity(channels_to_show[i], True) self.refresh() return
def create_page_item(self, result_set): """ Creates a MediaItem of type 'page' 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. :param list[str]|dict result_set: The result_set of the self.episodeItemRegex :return: A new MediaItem of type 'page'. :rtype: MediaItem|None """ if "totalPages" not in result_set: return None Logger.debug("Starting create_page_item") # current page? page_uri_part = "page%5Bnumber%5D=" if page_uri_part not in self.parentItem.url: page = 1 url_format = "{0}&page%5Bnumber%5D={{0:d}}".format( self.parentItem.url) else: base_url, page_part = self.parentItem.url.rsplit(page_uri_part, 1) next_part = page_part.find("&") if next_part < 0: # end page = int(page_part) url_format = "{0}&page%5Bnumber%5D={{0:d}}".format(base_url) else: page = int(page_part[0:next_part]) url_format = "{0}&page%5Bnumber%5D={{0:d}}&{1}".format( base_url, page_part[next_part:]) max_pages = result_set.get("totalPages", 0) Logger.trace("Current Page: %d of %d (%s)", page, max_pages, self.parentItem.url) if page + 1 > max_pages: return None title = LanguageHelper.get_localized_string(LanguageHelper.MorePages) url = url_format.format(page + 1) item = MediaItem(title, url) item.fanart = self.parentItem.fanart item.thumb = self.parentItem.thumb return item
def __show_warnings(self, media_item): """ Show playback warnings for this MediaItem :param MediaItem media_item: The current MediaItem that will be played. """ if (media_item.isDrmProtected or media_item.isPaid) and AddonSettings.show_drm_paid_warning(): if media_item.isDrmProtected: Logger.debug("Showing DRM Warning message") title = LanguageHelper.get_localized_string( LanguageHelper.DrmTitle) message = LanguageHelper.get_localized_string( LanguageHelper.DrmText) XbmcWrapper.show_dialog(title, message) elif media_item.isPaid: Logger.debug("Showing Paid Warning message") title = LanguageHelper.get_localized_string( LanguageHelper.PaidTitle) message = LanguageHelper.get_localized_string( LanguageHelper.PaidText) XbmcWrapper.show_dialog(title, message)
def add_categories(self, data): """ Adds categories to the main listings. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] if self.parentItem and "code" in self.parentItem.metaData: self.__currentChannel = self.parentItem.metaData["code"] Logger.info("Only showing items for channel: '%s'", self.__currentChannel) return data, items cat = MediaItem("\a.: Categoriën :.", "https://www.vrt.be/vrtnu/categorieen/") cat.fanart = self.fanart cat.thumb = self.noImage cat.icon = self.icon cat.dontGroup = True items.append(cat) live = MediaItem("\a.: Live Streams :.", "https://services.vrt.be/videoplayer/r/live.json") live.fanart = self.fanart live.thumb = self.noImage live.icon = self.icon live.dontGroup = True live.isLive = True items.append(live) channel_text = LanguageHelper.get_localized_string(30010) channels = MediaItem("\a.: %s :." % (channel_text, ), "#channels") channels.fanart = self.fanart channels.thumb = self.noImage channels.icon = self.icon channels.dontGroup = True items.append(channels) Logger.debug("Pre-Processing finished") return data, items
def create_folder_item(self, result_set): """ Creates a MediaItem of type 'folder' 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. :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) result_set["title"] = LanguageHelper.get_localized_string(LanguageHelper.MorePages) return chn_class.Channel.create_folder_item(self, result_set)
def set_setting(self, setting_id, setting_name=None, setting_action_id=None): """ Reads a value for a setting from the keyboard and encryptes it in the Kodi Add-on settings. The setttingActionId defaults to <settingId>_set :param str setting_id: The ID for the Kodi Add-on setting to set. :param str setting_name: The name to display in the keyboard. :param str setting_action_id: The name of the action that was called. :rtype: None """ Logger.info("Encrypting value for setting '%s'", setting_id) input_value = XbmcWrapper.show_key_board( "", LanguageHelper.get_localized_string( LanguageHelper.VaultSpecifySetting) % (setting_name or setting_id, )) if input_value is None: Logger.debug("Setting of encrypted value cancelled.") return value = "%s=%s" % (setting_id, input_value) encrypted_value = self.__encrypt(value, Vault.__Key) if setting_action_id is None: setting_action_id = "%s_set" % (setting_id, ) Logger.debug("Updating '%s' and '%s'", setting_id, setting_action_id) AddonSettings.set_setting(setting_id, encrypted_value) if input_value: AddonSettings.set_setting(setting_action_id, "******") else: AddonSettings.set_setting(setting_action_id, "") Logger.info("Successfully encrypted value for setting '%s'", setting_id) return
def add_page_items(self, data): """ Performs pre-process actions for data processing. Accepts an data from the process_folder_list method, BEFORE the items are processed. Allows setting of parameters (like title etc) for the channel. Inside this method the <data> could be changed and additional items can be created. The return values should always be instantiated in at least ("", []). :param str data: The retrieve data that was loaded for the current item and URL. :return: A tuple of the data and a list of MediaItems that were generated. :rtype: tuple[str|JsonHelper,list[MediaItem]] """ Logger.info("Performing Pre-Processing") items = [] json = JsonHelper(data) total_results = json.get_value("totalResults") from_value = json.get_value("from") size_value = json.get_value("size") if from_value + size_value < total_results: more_pages = LanguageHelper.get_localized_string( LanguageHelper.MorePages) url = self.parentItem.url.split('?')[0] url = "%s?size=%s&from=%s&sort=Nieuwste" % ( url, size_value, from_value + size_value) Logger.debug("Adding next-page item from %s to %s", from_value + size_value, from_value + size_value + size_value) next_page = MediaItem(more_pages, url) next_page.icon = self.parentItem.icon next_page.fanart = self.parentItem.fanart next_page.thumb = self.parentItem.thumb next_page.dontGroup = True items.append(next_page) Logger.debug("Pre-Processing finished") return json, items
def get_kodi_item(self): """ Creates an Kodi ListItem object for this channel :return: a Kodi ListItem with all required properties set. :rtype: xbmcgui.ListItem """ name = HtmlEntityHelper.convert_html_entities(self.channelName) description = HtmlEntityHelper.convert_html_entities( self.channelDescription) if self.uses_external_addon: other = LanguageHelper.get_localized_string( LanguageHelper.OtherAddon) name = "{0} {1} [COLOR gold]{2}[/COLOR]".format( name, unichr(187), other) self.icon = self.__get_image_path(self.icon) item = xbmcgui.ListItem(name, description) item.setArt({'thumb': self.icon, 'icon': self.icon}) # http://mirrors.kodi.tv/docs/python-docs/14.x-helix/xbmcgui.html#ListItem-setInfo item.setInfo( "video", { "Title": name, # "Count": self.sortOrderPerCountry, # "TrackNumber": self.sortOrder, "Genre": LanguageHelper.get_full_language(self.language), # "Tagline": description, "Plot": description }) if AddonSettings.hide_fanart(): return item if self.fanart is not None: self.fanart = self.__get_image_path(self.fanart) else: self.fanart = os.path.join(Config.rootDir, "fanart.jpg") item.setArt({'fanart': self.fanart}) return item