예제 #1
0
class Metadata:
    """ Code responsible for the refreshing of the metadata """
    def __init__(self):
        """ Initialise object """
        self._auth = Auth(kodiutils.get_setting('username'),
                          kodiutils.get_setting('password'),
                          kodiutils.get_setting('loginprovider'),
                          kodiutils.get_setting('profile'),
                          kodiutils.get_tokens_path())
        self._api = Api(self._auth)

    def update(self):
        """ Update the metadata with a foreground progress indicator """
        # Create progress indicator
        progress = kodiutils.progress(
            message=kodiutils.localize(30715))  # Updating metadata

        def update_status(i, total):
            """ Update the progress indicator """
            progress.update(
                int(((i + 1) / total) * 100),
                kodiutils.localize(
                    30716, index=i + 1,
                    total=total))  # Updating metadata ({index}/{total})
            return progress.iscanceled()

        self.fetch_metadata(callback=update_status)

        # Close progress indicator
        progress.close()

    def fetch_metadata(self, callback=None):
        """ Fetch the metadata for all the items in the catalog
        :type callback: callable
        """
        # Fetch all items from the catalog
        items = self._api.get_items()
        count = len(items)

        # Loop over all of them and download the metadata
        for index, item in enumerate(items):
            if isinstance(item, Movie):
                self._api.get_movie(item.movie_id)
            elif isinstance(item, Program):
                self._api.get_program(item.program_id)

            # Run callback after every item
            if callback and callback(index, count):
                # Stop when callback returns False
                return False

        return True

    @staticmethod
    def clean():
        """ Clear metadata (called from settings) """
        kodiutils.invalidate_cache()
        kodiutils.set_setting('metadata_last_updated', '0')
        kodiutils.ok_dialog(
            message=kodiutils.localize(30714))  # Local metadata is cleared
예제 #2
0
class Catalog:
    """ Menu code related to the catalog """

    def __init__(self):
        """ Initialise object """
        self._auth = Auth(kodiutils.get_setting('username'),
                          kodiutils.get_setting('password'),
                          kodiutils.get_setting('loginprovider'),
                          kodiutils.get_setting('profile'),
                          kodiutils.get_tokens_path())
        self._api = Api(self._auth)

    def show_catalog(self):
        """ Show the catalog. """
        categories = self._api.get_categories()

        listing = []
        for cat in categories:
            listing.append(TitleItem(
                title=cat.title,
                path=kodiutils.url_for('show_catalog_category', category=cat.category_id),
                info_dict=dict(
                    plot='[B]{category}[/B]'.format(category=cat.title),
                ),
            ))

        # Sort categories by default like in Streamz.
        kodiutils.show_listing(listing, 30003, content='files')

    def show_catalog_category(self, category=None):
        """ Show a category in the catalog.

        :type category: str
        """
        items = self._api.get_items(category)

        listing = []
        for item in items:
            listing.append(Menu.generate_titleitem(item))

        # Sort items by label, but don't put folders at the top.
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30003, content='movies' if category == 'films' else 'tvshows', sort=['label', 'year', 'duration'])

    def show_program(self, program):
        """ Show a program from the catalog.

        :type program: str
         """
        try:
            program_obj = self._api.get_program(program, cache=CACHE_PREVENT)  # Use CACHE_PREVENT since we want fresh data
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable and can't be played right now.
            kodiutils.end_of_directory()
            return

        # Go directly to the season when we have only one season
        if len(program_obj.seasons) == 1:
            self.show_program_season(program, list(program_obj.seasons.values())[0].number)
            return

        # studio = CHANNELS.get(program_obj.channel, {}).get('studio_icon')

        listing = []

        # Add an '* All seasons' entry when configured in Kodi
        if kodiutils.get_global_setting('videolibrary.showallitems') is True:
            listing.append(TitleItem(
                title='* %s' % kodiutils.localize(30204),  # * All seasons
                path=kodiutils.url_for('show_catalog_program_season', program=program, season=-1),
                art_dict=dict(
                    thumb=program_obj.cover,
                    fanart=program_obj.cover,
                ),
                info_dict=dict(
                    tvshowtitle=program_obj.name,
                    title=kodiutils.localize(30204),  # All seasons
                    tagline=program_obj.description,
                    set=program_obj.name,
                    # studio=studio,
                    mpaa=', '.join(program_obj.legal) if hasattr(program_obj, 'legal') and program_obj.legal else kodiutils.localize(30216),  # All ages
                ),
            ))

        # Add the seasons
        for season in list(program_obj.seasons.values()):
            listing.append(TitleItem(
                title=kodiutils.localize(30205, season=season.number),  # Season {season}
                path=kodiutils.url_for('show_catalog_program_season', program=program, season=season.number),
                art_dict=dict(
                    thumb=season.cover,
                    fanart=program_obj.cover,
                ),
                info_dict=dict(
                    tvshowtitle=program_obj.name,
                    title=kodiutils.localize(30205, season=season.number),  # Season {season}
                    tagline=program_obj.description,
                    set=program_obj.name,
                    # studio=studio,
                    mpaa=', '.join(program_obj.legal) if hasattr(program_obj, 'legal') and program_obj.legal else kodiutils.localize(30216),  # All ages
                ),
            ))

        # Sort by label. Some programs return seasons unordered.
        kodiutils.show_listing(listing, 30003, content='tvshows', sort=['label'])

    def show_program_season(self, program, season):
        """ Show the episodes of a program from the catalog.

        :type program: str
        :type season: int
        """
        try:
            program_obj = self._api.get_program(program)  # Use CACHE_AUTO since the data is just refreshed in show_program
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable and can't be played right now.
            kodiutils.end_of_directory()
            return

        if season == -1:
            # Show all seasons
            seasons = list(program_obj.seasons.values())
        else:
            # Show the season that was selected
            seasons = [program_obj.seasons[season]]

        listing = [Menu.generate_titleitem(e) for s in seasons for e in list(s.episodes.values())]

        # Sort by episode number by default. Takes seasons into account.
        kodiutils.show_listing(listing, 30003, content='episodes', sort=['episode', 'duration'])

    def show_recommendations(self, storefront):
        """ Show the recommendations.

        :type storefront: str
        """
        recommendations = self._api.get_recommendations(storefront)

        listing = []
        for cat in recommendations:
            listing.append(TitleItem(
                title=cat.title,
                path=kodiutils.url_for('show_recommendations_category', storefront=storefront, category=cat.category_id),
                info_dict=dict(
                    plot='[B]{category}[/B]'.format(category=cat.title),
                ),
            ))

        # Sort categories by default like in Streamz.
        kodiutils.show_listing(listing, 30015, content='files')

    def show_recommendations_category(self, storefront, category):
        """ Show the items in a recommendations category.

        :type storefront: str
        :type category: str
        """
        recommendations = self._api.get_recommendations(storefront)

        listing = []
        for cat in recommendations:
            # Only show the requested category
            if cat.category_id != category:
                continue

            for item in cat.content:
                listing.append(Menu.generate_titleitem(item))

        # Sort categories by default like in Streamz.
        kodiutils.show_listing(listing, 30015, content='tvshows', sort=['unsorted', 'label', 'year', 'duration'])

    def show_mylist(self):
        """ Show the items in "My List". """
        mylist = self._api.get_swimlane('my-list')

        listing = []
        for item in mylist:
            item.my_list = True
            listing.append(Menu.generate_titleitem(item))

        # Sort categories by default like in Streamz.
        kodiutils.show_listing(listing, 30017, content='tvshows', sort=['unsorted', 'label', 'year', 'duration'])

    def mylist_add(self, video_type, content_id):
        """ Add an item to "My List".

        :type video_type: str
        :type content_id: str
         """
        self._api.add_mylist(video_type, content_id)
        kodiutils.end_of_directory()

    def mylist_del(self, video_type, content_id):
        """ Remove an item from "My List".

        :type video_type: str
        :type content_id: str
        """
        self._api.del_mylist(video_type, content_id)
        kodiutils.end_of_directory()

    def show_continuewatching(self):
        """ Show the items in "Continue Watching". """
        mylist = self._api.get_swimlane('continue-watching')

        listing = []
        for item in mylist:
            titleitem = Menu.generate_titleitem(item, progress=True)

            # Add Program Name to title since this list contains episodes from multiple programs
            title = '%s - %s' % (
                titleitem.info_dict.get('tvshowtitle'),
                titleitem.info_dict.get('title'))
            titleitem.info_dict['title'] = title
            listing.append(titleitem)

        # Sort categories by default like in Streamz.
        kodiutils.show_listing(listing, 30019, content='episodes', sort='label')
class Library:
    """ Menu code related to the catalog """

    def __init__(self):
        """ Initialise object """
        self._auth = Auth(kodiutils.get_setting('username'),
                          kodiutils.get_setting('password'),
                          kodiutils.get_setting('loginprovider'),
                          kodiutils.get_setting('profile'),
                          kodiutils.get_tokens_path())
        self._api = Api(self._auth)

    def show_library_movies(self, movie=None):
        """ Return a list of the movies that should be exported. """
        if movie is None:
            if kodiutils.get_setting_int('library_movies') == LIBRARY_FULL_CATALOG:
                # Full catalog
                # Use cache if available, fetch from api otherwise so we get rich metadata for new content
                items = self._api.get_items(content_filter=Movie, cache=CACHE_AUTO)
            else:
                # Only favourites, use cache if available, fetch from api otherwise
                items = self._api.get_swimlane('my-list', content_filter=Movie)
        else:
            items = [self._api.get_movie(movie)]

        listing = []
        for item in items:
            title_item = Menu.generate_titleitem(item)
            # title_item.path = kodiutils.url_for('library_movies', movie=item.movie_id)  # We need a trailing /
            title_item.path = 'plugin://plugin.video.streamz/library/movies/?movie=%s' % item.movie_id
            listing.append(title_item)

        kodiutils.show_listing(listing, 30003, content='movies', sort=['label', 'year', 'duration'])

    def show_library_tvshows(self, program=None):
        """ Return a list of the series that should be exported. """
        if program is None:
            if kodiutils.get_setting_int('library_tvshows') == LIBRARY_FULL_CATALOG:
                # Full catalog
                # Use cache if available, fetch from api otherwise so we get rich metadata for new content
                # NOTE: We should probably use CACHE_PREVENT here, so we can pick up new episodes, but we can't since that would
                #       require a massive amount of API calls for each update. We do this only for programs in 'My list'.
                items = self._api.get_items(content_filter=Program, cache=CACHE_AUTO)
            else:
                # Only favourites, don't use cache, fetch from api
                # If we use CACHE_AUTO, we will miss updates until the user manually opens the program in the Add-on
                items = self._api.get_swimlane('my-list', content_filter=Program, cache=CACHE_PREVENT)
        else:
            # Fetch only a single program
            items = [self._api.get_program(program, cache=CACHE_PREVENT)]

        listing = []
        for item in items:
            title_item = Menu.generate_titleitem(item)
            # title_item.path = kodiutils.url_for('library_tvshows', program=item.program_id)  # We need a trailing /
            title_item.path = 'plugin://plugin.video.streamz/library/tvshows/?program={program_id}'.format(program_id=item.program_id)
            listing.append(title_item)

        kodiutils.show_listing(listing, 30003, content='tvshows', sort=['label', 'year', 'duration'])

    def show_library_tvshows_program(self, program):
        """ Return a list of the episodes that should be exported. """
        program_obj = self._api.get_program(program)

        listing = []
        for season in list(program_obj.seasons.values()):
            for item in list(season.episodes.values()):
                title_item = Menu.generate_titleitem(item)
                # title_item.path = kodiutils.url_for('library_tvshows', program=item.program_id, episode=item.episode_id)
                title_item.path = 'plugin://plugin.video.streamz/library/tvshows/?program={program_id}&episode={episode_id}'.format(program_id=item.program_id,
                                                                                                                                    episode_id=item.episode_id)
                listing.append(title_item)

        # Sort by episode number by default. Takes seasons into account.
        kodiutils.show_listing(listing, 30003, content='episodes', sort=['episode', 'duration'])

    def check_library_movie(self, movie):
        """ Check if the given movie is still available. """
        _LOGGER.debug('Checking if movie %s is still available', movie)

        # Our parent path always exists
        if movie is None:
            kodiutils.library_return_status(True)
            return

        if kodiutils.get_setting_int('library_movies') == LIBRARY_FULL_CATALOG:
            id_list = self._api.get_catalog_ids()
        else:
            id_list = self._api.get_mylist_ids()

        kodiutils.library_return_status(movie in id_list)

    def check_library_tvshow(self, program):
        """ Check if the given program is still available. """
        _LOGGER.debug('Checking if program %s is still available', program)

        # Our parent path always exists
        if program is None:
            kodiutils.library_return_status(True)
            return

        if kodiutils.get_setting_int('library_tvshows') == LIBRARY_FULL_CATALOG:
            id_list = self._api.get_catalog_ids()
        else:
            id_list = self._api.get_mylist_ids()

        kodiutils.library_return_status(program in id_list)

    @staticmethod
    def mylist_added(video_type, content_id):
        """ Something has been added to My List. We want to index this. """
        if video_type == CONTENT_TYPE_MOVIE:
            if kodiutils.get_setting_int('library_movies') != LIBRARY_ONLY_MYLIST:
                return
            # This unfortunately adds the movie to the database with the wrong parent path:
            # Library().update('plugin://plugin.video.streamz/library/movies/?movie=%s&kodi_action=refresh_info' % content_id)
            Library().update('plugin://plugin.video.streamz/library/movies/')

        elif video_type == CONTENT_TYPE_PROGRAM:
            if kodiutils.get_setting_int('library_tvshows') != LIBRARY_ONLY_MYLIST:
                return
            Library().update('plugin://plugin.video.streamz/library/tvshows/?program=%s&kodi_action=refresh_info' % content_id)

    @staticmethod
    def mylist_removed(video_type, content_id):
        """ Something has been removed from My List. We want to de-index this. """
        if video_type == CONTENT_TYPE_MOVIE:
            if kodiutils.get_setting_int('library_movies') != LIBRARY_ONLY_MYLIST:
                return
            Library().clean('plugin://plugin.video.streamz/library/movies/?movie=%s' % content_id)

        elif video_type == CONTENT_TYPE_PROGRAM:
            if kodiutils.get_setting_int('library_tvshows') != LIBRARY_ONLY_MYLIST:
                return
            Library().clean('plugin://plugin.video.streamz/library/tvshows/?program=%s' % content_id)

    @staticmethod
    def configure():
        """ Configure the library integration. """
        # There seems to be no way to add sources automatically.
        # * https://forum.kodi.tv/showthread.php?tid=228840

        # Open the sources view
        kodiutils.execute_builtin('ActivateWindow(Videos,sources://video/)')

    @staticmethod
    def update(path=None):
        """ Update the library integration. """
        _LOGGER.debug('Scanning %s', path)
        if path:
            # We can use this to instantly add something to the library when we've added it to 'My List'.
            kodiutils.jsonrpc(method='VideoLibrary.Scan', params=dict(
                directory=path,
                showdialogs=False,
            ))
        else:
            kodiutils.jsonrpc(method='VideoLibrary.Scan')

    @staticmethod
    def clean(path=None):
        """ Cleanup the library integration. """
        _LOGGER.debug('Cleaning %s', path)
        if path:
            # We can use this to instantly remove something from the library when we've removed it from 'My List'.
            # This only works from Kodi 19 however. See https://github.com/xbmc/xbmc/pull/18562
            if kodiutils.kodi_version_major() > 18:
                kodiutils.jsonrpc(method='VideoLibrary.Clean', params=dict(
                    directory=path,
                    showdialogs=False,
                ))
            else:
                kodiutils.jsonrpc(method='VideoLibrary.Clean', params=dict(
                    showdialogs=False,
                ))
        else:
            kodiutils.jsonrpc(method='VideoLibrary.Clean')