예제 #1
0
    def __show_empty_information(self, items, favs=False):
        """ Adds an empty item to a list or just shows a message.
        @type favs: boolean
        @param items:

        :param list[MediaItem] items:   The list of items.
        :param bool favs:               Indicating that we are dealing with favourites.

        :return: boolean indicating to report the listing as succes or not.
        :rtype: ok

        """

        if favs:
            title = LanguageHelper.get_localized_string(LanguageHelper.NoFavsId)
        else:
            title = LanguageHelper.get_localized_string(LanguageHelper.ErrorNoEpisodes)

        behaviour = AddonSettings.get_empty_list_behaviour()

        Logger.debug("Showing empty info for mode (favs=%s): [%s]", favs, behaviour)
        if behaviour == "error":
            # show error
            ok = False
        elif behaviour == "dummy" and not favs:
            # We should add a dummy items, but not for favs
            empty_list_item = MediaItem("- %s -" % (title.strip("."), ), "", type='video')
            if self.channelObject:
                empty_list_item.icon = self.channelObject.icon
                empty_list_item.thumb = self.channelObject.noImage
                empty_list_item.fanart = self.channelObject.fanart
            else:
                icon = Config.icon
                fanart = Config.fanart
                empty_list_item.icon = icon
                empty_list_item.thumb = fanart
                empty_list_item.fanart = fanart

            empty_list_item.dontGroup = True
            empty_list_item.description = "This listing was left empty intentionally."
            empty_list_item.complete = True
            # add funny stream here?
            # part = empty_list_item.create_new_empty_media_part()
            # for s, b in YouTube.get_streams_from_you_tube("", self.channelObject.proxy):
            #     part.append_media_stream(s, b)

            # if we add one, set OK to True
            ok = True
            items.append(empty_list_item)
        else:
            ok = True

        XbmcWrapper.show_notification(LanguageHelper.get_localized_string(LanguageHelper.ErrorId),
                                      title, XbmcWrapper.Error, 2500)
        return ok
예제 #2
0
    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()
예제 #3
0
    def __show_empty_information(self, items, favs=False):
        """ Adds an empty item to a list or just shows a message.
        @type favs: boolean
        @param items:

        :param list[MediaItem] items:   The list of items.
        :param bool favs:               Indicating that we are dealing with favourites.

        :return: boolean indicating to report the listing as succes or not.
        :rtype: ok

        """

        if favs:
            title = LanguageHelper.get_localized_string(
                LanguageHelper.NoFavsId)
        else:
            title = LanguageHelper.get_localized_string(
                LanguageHelper.ErrorNoEpisodes)

        behaviour = AddonSettings.get_empty_list_behaviour()

        Logger.debug("Showing empty info for mode (favs=%s): [%s]", favs,
                     behaviour)
        if behaviour == "error":
            # show error
            ok = False
        elif behaviour == "dummy" and not favs:
            # We should add a dummy items, but not for favs
            empty_list_item = MediaItem("- %s -" % (title.strip("."), ),
                                        "",
                                        type='video')
            empty_list_item.dontGroup = True
            empty_list_item.complete = True

            # if we add one, set OK to True
            ok = True
            items.append(empty_list_item)
        else:
            ok = True

        XbmcWrapper.show_notification(
            LanguageHelper.get_localized_string(LanguageHelper.ErrorId), title,
            XbmcWrapper.Error, 2500)
        return ok
예제 #4
0
    def play_video_item(self):
        """ Starts the videoitem using a playlist. """

        from resources.lib import player

        Logger.debug("Playing videoitem using PlayListMethod")

        try:
            media_item = self.media_item

            if not media_item.complete:
                media_item = self.channelObject.process_video_item(media_item)

            # Any warning to show
            self.__show_warnings(media_item)

            # validated the updated media_item
            if not media_item.complete or not media_item.has_media_item_parts(
            ):
                Logger.warning(
                    "process_video_item returned an MediaItem that had MediaItem.complete = False:\n%s",
                    media_item)

            if not media_item.has_media_item_parts():
                # the update failed or no items where found. Don't play
                XbmcWrapper.show_notification(
                    LanguageHelper.get_localized_string(
                        LanguageHelper.ErrorId),
                    LanguageHelper.get_localized_string(
                        LanguageHelper.NoStreamsId), XbmcWrapper.Error)
                Logger.warning(
                    "Could not start playback due to missing streams. Item:\n%s",
                    media_item)
                xbmcplugin.endOfDirectory(self.handle, False)
                return

            kodi_items = media_item.get_kodi_play_list_data(
                AddonSettings.get_max_stream_bitrate(self.channelObject),
                self.channelObject.proxy)

            Logger.debug("Continuing playback in plugin.py")
            if not bool(kodi_items):
                Logger.warning("play_video_item did not return valid playdata")
                xbmcplugin.endOfDirectory(self.handle, False)
                return

            # Now we force the busy dialog to close, else the video will not play and the
            # setResolved will not work.
            LockWithDialog.close_busy_dialog()

            # Append it to the Kodi playlist in a smart way.
            start_url = self.__append_kodi_play_list(kodi_items)

            # Set the mode (if the InputStream Adaptive add-on is used, we also need to set it)
            show_subs = AddonSettings.show_subtitles()

            # TODO: Apparently if we use the InputStream Adaptive, using the setSubtitles() causes sync issues.
            available_subs = [p.Subtitle for p in media_item.MediaItemParts]

            # Get the Kodi Player instance (let Kodi decide what player, see
            # http://forum.kodi.tv/showthread.php?tid=173887&pid=1516662#pid1516662)
            kodi_player = player.Player(show_subs=show_subs,
                                        subs=available_subs)
            kodi_player.waitForPlayBack(url=start_url, time_out=10)

            xbmcplugin.endOfDirectory(self.handle, True)
        except:
            XbmcWrapper.show_notification(
                LanguageHelper.get_localized_string(LanguageHelper.ErrorId),
                LanguageHelper.get_localized_string(
                    LanguageHelper.NoPlaybackId), XbmcWrapper.Error)
            Logger.critical("Could not playback the url", exc_info=True)

            # We need to single Kodi that it failed and it should not wait longer. Either using a
            # `raise` or with `xbmcplugin.endOfDirectory`. Doing the latter for now although we are
            # not really playing.
            xbmcplugin.endOfDirectory(self.handle, False)

        return
예제 #5
0
    def process_folder_list(self, favorites=None):
        """Wraps the channel.process_folder_list

        :param list[MediaItem]|None favorites:

        """

        Logger.info("Plugin::process_folder_list Doing process_folder_list")
        try:
            ok = True

            # read the item from the parameters
            selected_item = self.media_item

            # determine the parent guid
            parent_guid = self._get_parent_guid(self.channelObject,
                                                selected_item)

            if favorites is None:
                watcher = StopWatch("Plugin process_folder_list",
                                    Logger.instance())
                media_items = self.channelObject.process_folder_list(
                    selected_item)
                watcher.lap("Class process_folder_list finished")
            else:
                watcher = StopWatch("Plugin process_folder_list With Items",
                                    Logger.instance())
                media_items = favorites

            if len(media_items) == 0:
                Logger.warning("process_folder_list returned %s items",
                               len(media_items))
                ok = self.__show_empty_information(media_items,
                                                   favs=favorites is not None)
            else:
                Logger.debug("process_folder_list returned %s items",
                             len(media_items))

            kodi_items = []

            for media_item in media_items:  # type: MediaItem
                self.__update_artwork(media_item, self.channelObject)

                if media_item.type == 'folder' or media_item.type == 'append' or media_item.type == "page":
                    action = self.actionListFolder
                    folder = True
                elif media_item.is_playable():
                    action = self.actionPlayVideo
                    folder = False
                else:
                    Logger.critical(
                        "Plugin::process_folder_list: Cannot determine what to add"
                    )
                    continue

                # Get the Kodi item
                kodi_item = media_item.get_kodi_item()
                self.__set_kodi_properties(kodi_item,
                                           media_item,
                                           folder,
                                           is_favourite=favorites is not None)

                # Get the context menu items
                context_menu_items = self.__get_context_menu_items(
                    self.channelObject, item=media_item)
                kodi_item.addContextMenuItems(context_menu_items)

                # Get the action URL
                url = media_item.actionUrl
                if url is None:
                    url = self._create_action_url(self.channelObject,
                                                  action=action,
                                                  item=media_item,
                                                  store_id=parent_guid)

                # Add them to the list of Kodi items
                kodi_items.append((url, kodi_item, folder))

            watcher.lap("Kodi Items generated")

            # add items but if OK was False, keep it like that
            ok = ok and xbmcplugin.addDirectoryItems(self.handle, kodi_items,
                                                     len(kodi_items))
            watcher.lap("items send to Kodi")

            if ok and parent_guid is not None:
                self._pickler.store_media_items(parent_guid, selected_item,
                                                media_items)

            watcher.stop()

            self.__add_sort_method_to_handle(self.handle, media_items)
            self.__add_breadcrumb(self.handle, self.channelObject,
                                  selected_item)

            # set the content. It needs to be "episodes" to make the MediaItem.set_season_info() work
            xbmcplugin.setContent(handle=self.handle, content="episodes")

            xbmcplugin.endOfDirectory(self.handle, ok)
        except Exception:
            Logger.error("Plugin::Error Processing FolderList", exc_info=True)
            XbmcWrapper.show_notification(
                LanguageHelper.get_localized_string(LanguageHelper.ErrorId),
                LanguageHelper.get_localized_string(LanguageHelper.ErrorList),
                XbmcWrapper.Error, 4000)
            xbmcplugin.endOfDirectory(self.handle, False)
예제 #6
0
    def __init__(self, addon_name, params, handle=0):  # NOSONAR complexity
        """ Initialises the plugin with given arguments.

        :param str addon_name:      The add-on name.
        :param str params:          The input parameters from the query string.
        :param int handle:          The Kodi directory handle.

        """

        Logger.info("******** Starting %s add-on version %s/repo *********",
                    Config.appName, Config.version)
        # noinspection PyTypeChecker

        super(Plugin, self).__init__(addon_name, handle, params)
        Logger.debug(self)

        # Container Properties
        self.propertyRetrospect = "Retrospect"
        self.propertyRetrospectChannel = "RetrospectChannel"
        self.propertyRetrospectChannelSetting = "RetrospectChannelSettings"
        self.propertyRetrospectFolder = "RetrospectFolder"
        self.propertyRetrospectVideo = "RetrospectVideo"
        self.propertyRetrospectCloaked = "RetrospectCloaked"
        self.propertyRetrospectCategory = "RetrospectCategory"
        self.propertyRetrospectFavorite = "RetrospectFavorite"
        self.propertyRetrospectAdaptive = "RetrospectAdaptive"

        # channel objects
        self.channelObject = None
        self.channelFile = ""
        self.channelCode = None

        self.methodContainer = dict(
        )  # : storage for the inspect.getmembers(channel) method. Improves performance

        # are we in session?
        session_active = SessionHelper.is_session_active(Logger.instance())

        # fetch some environment settings
        env_ctrl = envcontroller.EnvController(Logger.instance())

        if not session_active:
            # do add-on start stuff
            Logger.info("Add-On start detected. Performing startup actions.")

            # print the folder structure
            env_ctrl.print_retrospect_settings_and_folders(
                Config, AddonSettings)

            # show notification
            XbmcWrapper.show_notification(None,
                                          LanguageHelper.get_localized_string(
                                              LanguageHelper.StartingAddonId) %
                                          (Config.appName, ),
                                          fallback=False,
                                          logger=Logger)

            # check for updates. Using local import for performance
            from resources.lib.updater import Updater
            up = Updater(Config.updateUrl, Config.version,
                         UriHandler.instance(), Logger.instance(),
                         AddonSettings.get_release_track())

            if up.is_new_version_available():
                Logger.info("Found new version online: %s vs %s",
                            up.currentVersion, up.onlineVersion)
                notification = LanguageHelper.get_localized_string(
                    LanguageHelper.NewVersion2Id)
                notification = notification % (Config.appName,
                                               up.onlineVersion)
                XbmcWrapper.show_notification(None,
                                              lines=notification,
                                              display_time=20000)

            # check for cache folder
            env_ctrl.cache_check()

            # do some cache cleanup
            env_ctrl.cache_clean_up(Config.cacheDir, Config.cacheValidTime)

            # empty picklestore
            self._pickler.purge_store(Config.addonId)

        # create a session
        SessionHelper.create_session(Logger.instance())

        #===============================================================================
        #        Start the plugin version of progwindow
        #===============================================================================
        if len(self.params) == 0:

            # Show initial start if not in a session
            # now show the list
            if AddonSettings.show_categories():
                self.show_categories()
            else:
                self.show_channel_list()

        #===============================================================================
        #        Start the plugin verion of the episode window
        #===============================================================================
        else:
            # Determine what stage we are in. Check that there are more than 2 Parameters
            if len(self.params) > 1 and self.keywordChannel in self.params:
                # retrieve channel characteristics
                self.channelFile = os.path.splitext(
                    self.params[self.keywordChannel])[0]
                self.channelCode = self.params[self.keywordChannelCode]
                Logger.debug(
                    "Found Channel data in URL: channel='%s', code='%s'",
                    self.channelFile, self.channelCode)

                # import the channel
                channel_register = ChannelIndex.get_register()
                channel = channel_register.get_channel(self.channelFile,
                                                       self.channelCode)

                if channel is not None:
                    self.channelObject = channel
                else:
                    Logger.critical(
                        "None or more than one channels were found, unable to continue."
                    )
                    return

                # init the channel as plugin
                self.channelObject.init_channel()
                Logger.info("Loaded: %s", self.channelObject.channelName)

            elif self.keywordCategory in self.params \
                    or self.keywordAction in self.params and (
                        self.params[self.keywordAction] == self.actionAllFavourites or
                        self.params[self.keywordAction] == self.actionRemoveFavourite):
                # no channel needed for these favourites actions.
                pass

            # ===============================================================================
            # Vault Actions
            # ===============================================================================
            elif self.keywordAction in self.params and \
                    self.params[self.keywordAction] in \
                    (
                        self.actionSetEncryptedValue,
                        self.actionSetEncryptionPin,
                        self.actionResetVault
                    ):
                try:
                    # Import vault here, as it is only used here or in a channel
                    # that supports it
                    from resources.lib.vault import Vault

                    action = self.params[self.keywordAction]
                    if action == self.actionResetVault:
                        Vault.reset()
                        return

                    v = Vault()
                    if action == self.actionSetEncryptionPin:
                        v.change_pin()
                    elif action == self.actionSetEncryptedValue:
                        v.set_setting(
                            self.params[self.keywordSettingId],
                            self.params.get(self.keywordSettingName, ""),
                            self.params.get(self.keywordSettingActionId, None))
                finally:
                    if self.keywordSettingTabFocus in self.params:
                        AddonSettings.show_settings(
                            self.params[self.keywordSettingTabFocus],
                            self.params.get(self.keywordSettingSettingFocus,
                                            None))
                return

            elif self.keywordAction in self.params and \
                    self.actionPostLog in self.params[self.keywordAction]:
                self.__send_log()
                return

            elif self.keywordAction in self.params and \
                    self.actionProxy in self.params[self.keywordAction]:

                # do this here to not close the busy dialog on the SetProxy when
                # a confirm box is shown
                title = LanguageHelper.get_localized_string(
                    LanguageHelper.ProxyChangeConfirmTitle)
                content = LanguageHelper.get_localized_string(
                    LanguageHelper.ProxyChangeConfirm)
                if not XbmcWrapper.show_yes_no(title, content):
                    Logger.warning(
                        "Stopping proxy update due to user intervention")
                    return

                language = self.params.get(self.keywordLanguage, None)
                proxy_id = self.params.get(self.keywordProxy, None)
                local_ip = self.params.get(self.keywordLocalIP, None)
                self.__set_proxy(language, proxy_id, local_ip)
                return

            else:
                Logger.critical("Error determining Plugin action")
                return

            #===============================================================================
            # See what needs to be done.
            #===============================================================================
            if self.keywordAction not in self.params:
                Logger.critical(
                    "Action parameters missing from request. Parameters=%s",
                    self.params)
                return

            elif self.params[self.keywordAction] == self.actionListCategory:
                self.show_channel_list(self.params[self.keywordCategory])

            elif self.params[
                    self.keywordAction] == self.actionConfigureChannel:
                self.__configure_channel(self.channelObject)

            elif self.params[self.keywordAction] == self.actionFavourites:
                # we should show the favourites
                self.show_favourites(self.channelObject)

            elif self.params[self.keywordAction] == self.actionAllFavourites:
                self.show_favourites(None)

            elif self.params[self.keywordAction] == self.actionListFolder:
                # channelName and URL is present, Parse the folder
                self.process_folder_list()

            elif self.params[self.keywordAction] == self.actionPlayVideo:
                self.play_video_item()

            elif not self.params[self.keywordAction] == "":
                self.on_action_from_context_menu(
                    self.params[self.keywordAction])

            else:
                Logger.warning(
                    "Number of parameters (%s) or parameter (%s) values not implemented",
                    len(self.params), self.params)

        self.__fetch_textures()
        return
예제 #7
0
    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 = []

        channels_updated = False
        country_visibility = {}

        channel_path = os.path.join(Config.rootDir, self.__INTERNAL_CHANNEL_PATH)
        for channel_pack in os.listdir(channel_path):
            if not channel_pack.startswith("channel."):
                continue

            for channel_set in os.listdir(os.path.join(channel_path, channel_pack)):
                channel_set_path = os.path.join(channel_path, channel_pack, channel_set)
                if not os.path.isdir(channel_set_path):
                    continue

                channel_set_info_path = os.path.join(channel_set_path, "chn_{}.json".format(channel_set))
                channel_infos = ChannelInfo.from_json(channel_set_info_path)

                # Check if the channel was updated
                if self.__is_channel_set_updated(channel_infos[0]):
                    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)

                    if channel_info.ignore:
                        Logger.warning("Not loading: %s -> ignored in the channel set", channel_info)
                        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
예제 #8
0
    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
예제 #9
0
    def __init__(self, addon_name, params, handle=0):
        """ Initialises the plugin with given arguments.

        :param str addon_name:      The add-on name.
        :param str params:          The input parameters from the query string.
        :param int|str handle:      The Kodi directory handle.

        """

        Logger.info("******** Starting %s add-on version %s/repo *********",
                    Config.appName, Config.version)
        # noinspection PyTypeChecker

        super(Plugin, self).__init__(addon_name, handle, params)
        Logger.debug(self)

        # are we in session?
        session_active = SessionHelper.is_session_active(Logger.instance())

        # fetch some environment settings
        env_ctrl = envcontroller.EnvController(Logger.instance())

        if not session_active:
            # do add-on start stuff
            Logger.info("Add-On start detected. Performing startup actions.")

            # print the folder structure
            env_ctrl.print_retrospect_settings_and_folders(
                Config, AddonSettings)

            # show notification
            XbmcWrapper.show_notification(None,
                                          LanguageHelper.get_localized_string(
                                              LanguageHelper.StartingAddonId) %
                                          (Config.appName, ),
                                          fallback=False,
                                          logger=Logger)

            # check for updates. Using local import for performance
            from resources.lib.updater import Updater
            up = Updater(Config.updateUrl, Config.version,
                         UriHandler.instance(), Logger.instance(),
                         AddonSettings.get_release_track())

            if up.is_new_version_available():
                Logger.info("Found new version online: %s vs %s",
                            up.currentVersion, up.onlineVersion)
                notification = LanguageHelper.get_localized_string(
                    LanguageHelper.NewVersion2Id)
                notification = notification % (Config.appName,
                                               up.onlineVersion)
                XbmcWrapper.show_notification(None,
                                              lines=notification,
                                              display_time=20000)

            # check for cache folder
            env_ctrl.cache_check()

            # do some cache cleanup
            env_ctrl.cache_clean_up(Config.cacheDir, Config.cacheValidTime)

            # empty picklestore
            self.pickler.purge_store(Config.addonId)

        # create a session
        SessionHelper.create_session(Logger.instance())
예제 #10
0
    def execute(self):
        Logger.info("Plugin::process_folder_list Doing process_folder_list")
        try:
            ok = True

            # read the item from the parameters
            selected_item = self.__media_item

            # determine the parent guid
            parent_guid = self.parameter_parser.get_parent_guid(
                self.__channel, selected_item)

            if self.__favorites is None:
                watcher = StopWatch("Plugin process_folder_list",
                                    Logger.instance())
                media_items = self.__channel.process_folder_list(selected_item)
                watcher.lap("Class process_folder_list finished")
            else:
                parent_guid = "{}.fav".format(parent_guid)
                watcher = StopWatch("Plugin process_folder_list With Items",
                                    Logger.instance())
                media_items = self.__favorites

            if len(media_items) == 0:
                Logger.warning("process_folder_list returned %s items",
                               len(media_items))
                ok = self.__show_empty_information(media_items,
                                                   favs=self.__favorites
                                                   is not None)
            else:
                Logger.debug("process_folder_list returned %s items",
                             len(media_items))

            kodi_items = []

            use_thumbs_as_fanart = AddonSettings.use_thumbs_as_fanart()
            for media_item in media_items:  # type: MediaItem
                self.__update_artwork(media_item, self.__channel,
                                      use_thumbs_as_fanart)

                if media_item.type == 'folder' or media_item.type == 'append' or media_item.type == "page":
                    action_value = action.LIST_FOLDER
                    folder = True
                elif media_item.is_playable():
                    action_value = action.PLAY_VIDEO
                    folder = False
                else:
                    Logger.critical(
                        "Plugin::process_folder_list: Cannot determine what to add"
                    )
                    continue

                # Get the Kodi item
                kodi_item = media_item.get_kodi_item()
                self.__set_kodi_properties(kodi_item,
                                           media_item,
                                           folder,
                                           is_favourite=self.__favorites
                                           is not None)

                # Get the context menu items
                context_menu_items = self._get_context_menu_items(
                    self.__channel, item=media_item)
                kodi_item.addContextMenuItems(context_menu_items)

                # Get the action URL
                url = media_item.actionUrl
                if url is None:
                    url = self.parameter_parser.create_action_url(
                        self.__channel,
                        action=action_value,
                        item=media_item,
                        store_id=parent_guid)

                # Add them to the list of Kodi items
                kodi_items.append((url, kodi_item, folder))

            watcher.lap("Kodi Items generated")

            # add items but if OK was False, keep it like that
            ok = ok and xbmcplugin.addDirectoryItems(self.handle, kodi_items,
                                                     len(kodi_items))
            watcher.lap("items send to Kodi")

            if ok and parent_guid is not None:
                self.parameter_parser.pickler.store_media_items(
                    parent_guid, selected_item, media_items)

            watcher.stop()

            self.__add_sort_method_to_handle(self.handle, media_items)
            self.__add_breadcrumb(self.handle, self.__channel, selected_item)
            self.__add_content_type(self.handle, self.__channel, selected_item)

            xbmcplugin.endOfDirectory(self.handle, ok)
        except Exception:
            Logger.error("Plugin::Error Processing FolderList", exc_info=True)
            XbmcWrapper.show_notification(
                LanguageHelper.get_localized_string(LanguageHelper.ErrorId),
                LanguageHelper.get_localized_string(LanguageHelper.ErrorList),
                XbmcWrapper.Error, 4000)
            xbmcplugin.endOfDirectory(self.handle, False)
예제 #11
0
    def change_pin(self, application_key=None):
        """ Stores an existing ApplicationKey using a new PIN.

        :param bytes application_key: an existing ApplicationKey that will be stored. If none
                                      specified, the existing ApplicationKey of the Vault will
                                      be used.

        :return: Indication of success.
        :rtype: bool

        """

        Logger.info("Updating the ApplicationKey with a new PIN")

        if self.__newKeyGeneratedInConstructor:
            Logger.info("A key was just generated, no need to change PINs.")
            return True

        if application_key is None:
            Logger.debug("Using the ApplicationKey from the vault.")
            application_key = Vault.__Key
        else:
            Logger.debug("Using the ApplicationKey from the input parameter.")

        if not application_key:
            raise ValueError("No ApplicationKey specified.")

        # Now we get a new PIN and (re)encrypt

        pin = XbmcWrapper.show_key_board(
            heading=LanguageHelper.get_localized_string(
                LanguageHelper.VaultNewPin),
            hidden=True)
        if not pin:
            XbmcWrapper.show_notification(
                "",
                LanguageHelper.get_localized_string(LanguageHelper.VaultNoPin),
                XbmcWrapper.Error)
            return False

        pin2 = XbmcWrapper.show_key_board(
            heading=LanguageHelper.get_localized_string(
                LanguageHelper.VaultRepeatPin),
            hidden=True)
        if pin != pin2:
            Logger.critical("Mismatch in PINs")
            XbmcWrapper.show_notification(
                "",
                LanguageHelper.get_localized_string(
                    LanguageHelper.VaultPinsDontMatch), XbmcWrapper.Error)
            return False

        if PY2:
            encrypted_key = "%s=%s" % (self.__APPLICATION_KEY_SETTING,
                                       application_key)
        else:
            # make it text to store
            encrypted_key = "%s=%s" % (self.__APPLICATION_KEY_SETTING,
                                       application_key.decode())

        # let's generate a pin using the scrypt password-based key derivation
        pin_key = self.__get_pbk(pin)
        encrypted_key = self.__encrypt(encrypted_key, pin_key)
        AddonSettings.set_setting(Vault.__APPLICATION_KEY_SETTING,
                                  encrypted_key,
                                  store=LOCAL)
        Logger.info("Successfully updated the Retrospect PIN")
        return True
    def update_video_item(self, item):
        """ Updates an existing MediaItem with more data.

        Used to update none complete MediaItems (self.complete = False). This
        could include opening the item's URL to fetch more data and then process that
        data or retrieve it's real media-URL.

        The method should at least:
        * cache the thumbnail to disk (use self.noImage if no thumb is available).
        * set at least one MediaItemPart with a single MediaStream.
        * set self.complete = True.

        if the returned item does not have a MediaItemPart then the self.complete flag
        will automatically be set back to False.

        :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('Starting update_video_item for %s (%s)', item.name,
                     self.channelName)

        if not AddonSettings.use_adaptive_stream_add_on(with_encryption=False):
            Logger.error("Cannot playback video without adaptive stream addon")
            return item

        # https://www.foxsports.nl/api/video/videodata/2945190
        data = UriHandler.open(item.url,
                               proxy=self.proxy,
                               additional_headers=item.HttpHeaders)
        video_id = Regexer.do_regex(r'data-videoid="(\d+)" ', data)[-1]
        data = UriHandler.open(
            "https://www.foxsports.nl/api/video/videodata/%s" % (video_id, ),
            proxy=self.proxy,
            additional_headers=item.HttpHeaders,
            no_cache=True)
        stream_id = Regexer.do_regex('<uri>([^>]+)</uri>', data)[-1]

        # POST https://d3api.foxsports.nl/api/V2/entitlement/tokenize
        post_data = {
            "Type":
            1,
            "User":
            "",
            "VideoId":
            "{0}".format(video_id),
            "VideoSource":
            "{0}".format(stream_id),
            "VideoKind":
            "vod",
            "AssetState":
            "3",
            "PlayerType":
            "HTML5",
            "VideoSourceFormat":
            "DASH",
            "VideoSourceName":
            "DASH",
            # "VideoSourceFormat": "HLS",
            # "VideoSourceName": "HLS",
            "DRMType":
            "widevine",
            "AuthType":
            "Token",
            "ContentKeyData":
            "",
            "Other__":
            "playerName=HTML5-Web-vod|ae755267-8482-455b-9055-529b643ece1d|"
            "undefined|undefined|undefined|2945541|HTML5|web|diva.MajorVersion=4|"
            "diva.MinorVersion=2|diva.PatchVersion=13"
        }

        data = UriHandler.open(
            "https://d3api.foxsports.nl/api/V2/entitlement/tokenize",
            json=post_data,
            no_cache=True,
            proxy=self.proxy)
        stream_info = JsonHelper(data)
        stream_url = stream_info.get_value("ContentUrl")
        if not stream_url:
            message = "Protected stream: {0}".format(
                stream_info.get_value("Message"))
            XbmcWrapper.show_notification(None,
                                          message,
                                          notification_type=XbmcWrapper.Error,
                                          display_time=5000)

        license_url = stream_info.get_value("LicenseURL")
        part = item.create_new_empty_media_part()
        stream = part.append_media_stream(stream_url, 0)
        license_key = Mpd.get_license_key(license_url)
        Mpd.set_input_stream_addon_input(stream,
                                         proxy=self.proxy,
                                         license_key=license_key)
        return item