Exemplo n.º 1
0
 def __init__(self):
     """ Initialise object """
     self._auth = AuthApi(kodiutils.get_setting('username'),
                          kodiutils.get_setting('password'),
                          kodiutils.get_tokens_path())
     self._api = ContentApi(self._auth,
                            cache_path=kodiutils.get_cache_path())
Exemplo n.º 2
0
    def __init__(self):
        """ Initialise object """
        auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
        self._api = ContentApi(auth, cache_path=kodiutils.get_cache_path())

        # Workaround for Raspberry Pi 3 and older
        kodiutils.set_global_setting('videoplayer.useomxplayer', True)
Exemplo n.º 3
0
    def test_get_stream(self):
        api = ContentApi(self._auth.get_token())
        program = api.get_program('vier', 'auwch')
        episode = program.episodes[0]
        video = api.get_stream(episode.channel, episode.uuid)
        self.assertTrue(video)

        _LOGGER.info('Got video URL: %s', video)
Exemplo n.º 4
0
    def test_episodes(self):
        api = ContentApi(self._auth.get_token())

        for channel, program in [('vier', 'auwch'),
                                 ('vijf', 'zo-man-zo-vrouw')]:
            program = api.get_program(channel, program)
            self.assertIsInstance(program, Program)
            self.assertIsInstance(program.seasons, dict)
            # self.assertIsInstance(program.seasons[0], Season)
            self.assertIsInstance(program.episodes, list)
            self.assertIsInstance(program.episodes[0], Episode)
            _LOGGER.info('Got program: %s', program)
    def test_play_video_from_epg(self):
        epg = EpgApi()
        epg_programs = epg.get_epg('vier', date.today().strftime('%Y-%m-%d'))
        epg_program = [program for program in epg_programs if program.video_url][0]

        # Lookup the Episode data since we don't have an UUID
        api = ContentApi(self._auth.get_token())
        episode = api.get_episode(epg_program.channel, epg_program.video_url)
        self.assertIsInstance(episode, Episode)

        # Get stream based on the Episode's UUID
        video = api.get_stream(episode.channel, episode.uuid)
        self.assertTrue(video)
Exemplo n.º 6
0
class Metadata:
    """ Code responsible for the management of the local cached metadata """
    def __init__(self):
        """ Initialise object """
        self._api = ContentApi(cache_path=kodiutils.get_cache_path())

    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, refresh=True)

        # Close progress indicator
        progress.close()

    def fetch_metadata(self, callback=None, refresh=False):
        """ Fetch the metadata for all the items in the catalog
        :type callback: callable
        :type refresh: bool
        """
        # Fetch all items from the catalog
        items = []
        for channel in list(CHANNELS):
            items.extend(self._api.get_programs(channel, CACHE_PREVENT))
        count = len(items)

        # Loop over all of them and download the metadata
        for index, item in enumerate(items):
            if isinstance(item, Program):
                self._api.get_program(item.channel, item.path,
                                      CACHE_PREVENT if refresh else CACHE_AUTO)

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

        return True
Exemplo n.º 7
0
    def _resolve_stream(uuid):
        """ Resolve the stream for the requested item
        :type uuid: string
        """
        try:
            # Check if we have credentials
            if not kodiutils.get_setting('username') or not kodiutils.get_setting('password'):
                confirm = kodiutils.yesno_dialog(
                    message=kodiutils.localize(30701))  # To watch a video, you need to enter your credentials. Do you want to enter them now?
                if confirm:
                    kodiutils.open_settings()
                kodiutils.end_of_directory()
                return None

            # Fetch an auth token now
            try:
                auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())

                # Get stream information
                resolved_stream = ContentApi(auth).get_stream_by_uuid(uuid)
                return resolved_stream

            except (InvalidLoginException, AuthenticationException) as ex:
                _LOGGER.exception(ex)
                kodiutils.ok_dialog(message=kodiutils.localize(30702, error=str(ex)))
                kodiutils.end_of_directory()
                return None

        except GeoblockedException:
            kodiutils.ok_dialog(message=kodiutils.localize(30710))  # This video is geo-blocked...
            return None

        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable...
            return None
Exemplo n.º 8
0
    def play_from_page(self, channel, path):
        """ Play the requested item.
        :type channel: string
        :type path: string
        """
        # Get episode information
        episode = ContentApi().get_episode(channel, path)

        # Play this now we have the uuid
        self.play(episode.uuid)
Exemplo n.º 9
0
    def play(item):
        """ Play the requested item.
        :type item: string
        """

        # Workaround for Raspberry Pi 3 and older
        omxplayer = kodiutils.get_global_setting('videoplayer.useomxplayer')
        if omxplayer is False:
            kodiutils.set_global_setting('videoplayer.useomxplayer', True)

        try:
            # Check if we have credentials
            if not kodiutils.get_setting(
                    'username') or not kodiutils.get_setting('password'):
                confirm = kodiutils.yesno_dialog(
                    message=kodiutils.localize(30701)
                )  # To watch a video, you need to enter your credentials. Do you want to enter them now?
                if confirm:
                    kodiutils.open_settings()
                kodiutils.end_of_directory()
                return

            # Fetch an auth token now
            try:
                auth = AuthApi(kodiutils.get_setting('username'),
                               kodiutils.get_setting('password'),
                               kodiutils.get_tokens_path())

                # Get stream information
                resolved_stream = ContentApi(auth).get_stream_by_uuid(item)

            except (InvalidLoginException, AuthenticationException) as ex:
                _LOGGER.error(ex)
                kodiutils.ok_dialog(
                    message=kodiutils.localize(30702, error=str(ex)))
                kodiutils.end_of_directory()
                return

        except GeoblockedException:
            kodiutils.ok_dialog(heading=kodiutils.localize(30709),
                                message=kodiutils.localize(
                                    30710))  # This video is geo-blocked...
            return

        except UnavailableException:
            kodiutils.ok_dialog(heading=kodiutils.localize(30711),
                                message=kodiutils.localize(
                                    30712))  # The video is unavailable...
            return

        # Play this item
        kodiutils.play(resolved_stream)
Exemplo n.º 10
0
class Player:
    """ Code responsible for playing media """

    def __init__(self):
        """ Initialise object """
        self._auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
        self._api = ContentApi(self._auth.get_token())

    def play_from_page(self, channel, path):
        """ Play the requested item.
        :type channel: string
        :type path: string
        """
        # Get episode information
        episode = self._api.get_episode(channel, path)

        # Play this now we have the uuid
        self.play(channel, episode.uuid)

    def play(self, channel, item):
        """ Play the requested item.
        :type channel: string
        :type item: string
        """
        try:
            # Get stream information
            resolved_stream = self._api.get_stream(channel, item)

        except GeoblockedException:
            kodiutils.ok_dialog(heading=kodiutils.localize(30709), message=kodiutils.localize(30710))  # This video is geo-blocked...
            return

        except UnavailableException:
            kodiutils.ok_dialog(heading=kodiutils.localize(30711), message=kodiutils.localize(30712))  # The video is unavailable...
            return

        # Play this item
        kodiutils.play(resolved_stream)
class SearchApi:
    """ GoPlay Search API """
    API_ENDPOINT = 'https://api.goplay.be/search'

    def __init__(self):
        """ Initialise object """
        self._api = ContentApi(None, cache_path=kodiutils.get_cache_path())
        self._session = requests.session()

    def search(self, query):
        """ Get the stream URL to use for this video.
        :type query: str
        :rtype list[Program]
        """
        if not query:
            return []

        response = self._session.post(self.API_ENDPOINT,
                                      json={
                                          "query": query,
                                          "page": 0,
                                          "mode": "programs"
                                      })
        _LOGGER.debug(response.content)
        response.raise_for_status()

        data = json.loads(response.text)

        results = []
        for hit in data['hits']['hits']:
            if hit['_source']['bundle'] == 'program':
                path = hit['_source']['url'].split('/')[-1]
                program = self._api.get_program(path, cache=CACHE_ONLY)
                if program:
                    results.append(program)
                else:
                    results.append(
                        Program(
                            path=path,
                            title=hit['_source']['title'],
                            description=hit['_source']['intro'],
                            poster=hit['_source']['img'],
                        ))

        return results
Exemplo n.º 12
0
class Catalog:
    """ Menu code related to the catalog """
    def __init__(self):
        """ Initialise object """
        self._auth = AuthApi(kodiutils.get_setting('username'),
                             kodiutils.get_setting('password'),
                             kodiutils.get_tokens_path())
        self._api = ContentApi(self._auth.get_token())
        self._menu = Menu()

    def show_catalog(self):
        """ Show all the programs of all channels """
        try:
            items = []
            for channel in list(CHANNELS):
                items.extend(self._api.get_programs(channel))
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

        listing = [self._menu.generate_titleitem(item) for item in items]

        # 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='tvshows', sort='label')

    def show_catalog_channel(self, channel):
        """ Show the programs of a specific channel
        :type channel: str
        """
        try:
            items = self._api.get_programs(channel)
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

        listing = []
        for item in items:
            listing.append(self._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='tvshows', sort='label')

    def show_program(self, channel, program_id):
        """ Show a program from the catalog
        :type channel: str
        :type program_id: str
         """
        try:
            program = self._api.get_program(channel, program_id)
        except UnavailableException:
            kodiutils.ok_dialog(
                message=kodiutils.localize(30717)
            )  # This program is not available in the Vier/Vijf/Zes catalogue.
            kodiutils.end_of_directory()
            return

        if not program.episodes:
            kodiutils.ok_dialog(
                message=kodiutils.localize(30717)
            )  # This program is not available in the Vier/Vijf/Zes catalogue.
            kodiutils.end_of_directory()
            return

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

        studio = CHANNELS.get(program.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',
                                           channel=channel,
                                           program=program_id,
                                           season=-1),
                    art_dict={
                        'thumb': program.cover,
                        'fanart': program.background,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(30204),  # All seasons
                        'plot': program.description,
                        'set': program.title,
                        'studio': studio,
                    }))

        # Add the seasons
        for s in list(program.seasons.values()):
            listing.append(
                TitleItem(
                    title=s.
                    title,  # kodiutils.localize(30205, season=s.number),  # Season {season}
                    path=kodiutils.url_for('show_catalog_program_season',
                                           channel=channel,
                                           program=program_id,
                                           season=s.number),
                    art_dict={
                        'thumb': s.cover,
                        'fanart': program.background,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title':
                        kodiutils.localize(30205,
                                           season=s.number),  # Season {season}
                        'plot': s.description,
                        'set': program.title,
                        'studio': studio,
                    }))

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

    def show_program_season(self, channel, program_id, season):
        """ Show the episodes of a program from the catalog
        :type channel: str
        :type program_id: str
        :type season: int
        """
        try:
            program = self._api.get_program(channel, program_id)
        except UnavailableException:
            kodiutils.ok_dialog(
                message=kodiutils.localize(30717)
            )  # This program is not available in the Vier/Vijf/Zes catalogue.
            kodiutils.end_of_directory()
            return

        if season == -1:
            # Show all episodes
            episodes = program.episodes
        else:
            # Show the episodes of the season that was selected
            episodes = [e for e in program.episodes if e.season == season]

        listing = [
            self._menu.generate_titleitem(episode) for episode in episodes
        ]

        # Sort by episode number by default. Takes seasons into account.
        kodiutils.show_listing(listing,
                               30003,
                               content='episodes',
                               sort=['episode', 'duration'])
Exemplo n.º 13
0
class Player:
    """ Code responsible for playing media """

    def __init__(self):
        """ Initialise object """
        auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
        self._api = ContentApi(auth, cache_path=kodiutils.get_cache_path())

        # Workaround for Raspberry Pi 3 and older
        kodiutils.set_global_setting('videoplayer.useomxplayer', True)

    @staticmethod
    def live(channel):
        """ Play the live channel.
        :type channel: string
        """
        # TODO: this doesn't work correctly, playing a live program from the PVR won't play something from the beginning
        # Lookup current program
        # broadcast = self._epg.get_broadcast(channel, datetime.datetime.now().isoformat())
        # if broadcast and broadcast.video_url:
        #     self.play_from_page(broadcast.video_url)
        #     return

        channel_name = CHANNELS.get(channel, dict(name=channel))
        kodiutils.ok_dialog(message=kodiutils.localize(30718, channel=channel_name.get('name')))  # There is no live stream available for {channel}.
        kodiutils.end_of_directory()

    def play_from_page(self, path):
        """ Play the requested item.
        :type path: string
        """
        if not path:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable...
            return

        # Get episode information
        episode = self._api.get_episode(path, cache=CACHE_PREVENT)
        resolved_stream = None

        if episode is None:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))
            return

        if episode.stream:
            # We already have a resolved stream. Nice!
            # We don't need credentials for these streams.
            resolved_stream = ResolvedStream(
                uuid=episode.uuid,
                url=episode.stream,
            )
            _LOGGER.debug('Already got a resolved stream: %s', resolved_stream)

        if episode.uuid:
            # Lookup the stream
            resolved_stream = self._resolve_stream(episode.uuid)
            _LOGGER.debug('Resolved stream: %s', resolved_stream)

        if resolved_stream:
            titleitem = Menu.generate_titleitem(episode)
            if resolved_stream.license_url:
                # Generate license key
                license_key = self.create_license_key(resolved_stream.license_url,
                                                      key_headers=dict(
                                                          customdata=resolved_stream.auth,
                                                      ))
            else:
                license_key = None

            kodiutils.play(resolved_stream.url,
                           resolved_stream.stream_type,
                           license_key,
                           info_dict=titleitem.info_dict,
                           art_dict=titleitem.art_dict,
                           prop_dict=titleitem.prop_dict)

    def play(self, uuid):
        """ Play the requested item.
        :type uuid: string
        """
        if not uuid:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable...
            return

        # Lookup the stream
        resolved_stream = self._resolve_stream(uuid)
        if resolved_stream.license_url:
            # Generate license key
            license_key = self.create_license_key(resolved_stream.license_url, key_headers=dict(
                customdata=resolved_stream.auth,
            ))
        else:
            license_key = None

        kodiutils.play(resolved_stream.url, resolved_stream.stream_type, license_key)

    @staticmethod
    def _resolve_stream(uuid):
        """ Resolve the stream for the requested item
        :type uuid: string
        """
        try:
            # Check if we have credentials
            if not kodiutils.get_setting('username') or not kodiutils.get_setting('password'):
                confirm = kodiutils.yesno_dialog(
                    message=kodiutils.localize(30701))  # To watch a video, you need to enter your credentials. Do you want to enter them now?
                if confirm:
                    kodiutils.open_settings()
                kodiutils.end_of_directory()
                return None

            # Fetch an auth token now
            try:
                auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())

                # Get stream information
                resolved_stream = ContentApi(auth).get_stream_by_uuid(uuid)
                return resolved_stream

            except (InvalidLoginException, AuthenticationException) as ex:
                _LOGGER.exception(ex)
                kodiutils.ok_dialog(message=kodiutils.localize(30702, error=str(ex)))
                kodiutils.end_of_directory()
                return None

        except GeoblockedException:
            kodiutils.ok_dialog(message=kodiutils.localize(30710))  # This video is geo-blocked...
            return None

        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30712))  # The video is unavailable...
            return None

    @staticmethod
    def create_license_key(key_url, key_type='R', key_headers=None, key_value=None):
        """ Create a license key string that we need for inputstream.adaptive.

        :param str key_url:
        :param str key_type:
        :param dict[str, str] key_headers:
        :param str key_value:
        :rtype: str
        """
        header = ''
        if key_headers:
            header = urlencode(key_headers)

        if key_type in ('A', 'R', 'B'):
            key_value = key_type + '{SSM}'
        elif key_type == 'D':
            if 'D{SSM}' not in key_value:
                raise ValueError('Missing D{SSM} placeholder')
            key_value = quote(key_value)

        return '%s|%s|%s|' % (key_url, header, key_value)
Exemplo n.º 14
0
    def test_programs(self):
        api = ContentApi(self._auth.get_token())

        for channel in ['vier', 'vijf', 'zes']:
            channels = api.get_programs(channel)
            self.assertIsInstance(channels, list)
Exemplo n.º 15
0
 def __init__(self):
     """ Initialise object """
     self._api = ContentApi(cache_path=kodiutils.get_cache_path())
Exemplo n.º 16
0
class Metadata:
    """ Code responsible for the management of the local cached metadata """
    def __init__(self):
        """ Initialise object """
        self._api = ContentApi(cache_path=kodiutils.get_cache_path())

    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, refresh=True)

        # Close progress indicator
        progress.close()

    def fetch_metadata(self, callback=None, refresh=False):
        """ Fetch the metadata for all the items in the catalog
        :type callback: callable
        :type refresh: bool
        """
        # Fetch all items from the catalog
        items = []
        for channel in list(CHANNELS):
            items.extend(self._api.get_programs(channel, CACHE_PREVENT))
        count = len(items)

        # Loop over all of them and download the metadata
        for index, item in enumerate(items):
            if isinstance(item, Program):
                self._api.get_program(item.channel, item.path,
                                      CACHE_PREVENT if refresh else CACHE_AUTO)

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

        return True

    @staticmethod
    def clean():
        """ Clear metadata (called from settings) """
        cache_path = kodiutils.get_cache_path()
        _, files = kodiutils.listdir(cache_path)
        for filename in files:
            if not kodiutils.delete(os.path.join(cache_path, filename)):
                return kodiutils.ok_dialog(message=kodiutils.localize(
                    30721))  # Clearing local metadata failed

        kodiutils.set_setting('metadata_last_updated', '0')
        return kodiutils.ok_dialog(
            message=kodiutils.localize(30714))  # Local metadata is cleared
 def __init__(self):
     """ Initialise object """
     self._api = ContentApi(None, cache_path=kodiutils.get_cache_path())
     self._session = requests.session()
Exemplo n.º 18
0
class Catalog:
    """ Menu code related to the catalog """
    def __init__(self):
        """ Initialise object """
        self._auth = AuthApi(kodiutils.get_setting('username'),
                             kodiutils.get_setting('password'),
                             kodiutils.get_tokens_path())
        self._api = ContentApi(self._auth,
                               cache_path=kodiutils.get_cache_path())

    def show_catalog(self):
        """ Show all the programs of all channels """
        try:
            items = self._api.get_programs()
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

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

        # Sort items by title
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

    def show_catalog_channel(self, channel):
        """ Show the programs of a specific channel
        :type channel: str
        """
        try:
            items = self._api.get_programs(channel)
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

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

        # Sort items by title
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

    def show_program(self, program_id):
        """ Show a program from the catalog
        :type program_id: str
         """
        try:
            program = self._api.get_program(
                program_id, extract_clips=True, cache=CACHE_PREVENT
            )  # Use CACHE_PREVENT since we want fresh data
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(
                30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        if not program.episodes and not program.clips:
            kodiutils.ok_dialog(message=kodiutils.localize(
                30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        # Go directly to the season when we have only one season and no clips
        if not program.clips and len(program.seasons) == 1:
            self.show_program_season(program_id,
                                     list(program.seasons.values())[0].uuid)
            return

        listing = []

        # Add an '* All seasons' entry when configured in Kodi
        if program.seasons and 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_id,
                                           season='-1'),
                    art_dict={
                        'fanart': program.fanart,
                        'poster': program.poster,
                        'landscape': program.thumb,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(30204),  # All seasons
                        'plot': program.description,
                        'set': program.title,
                    }))

        # Add the seasons
        for season in list(program.seasons.values()):
            listing.append(
                TitleItem(
                    title=season.
                    title,  # kodiutils.localize(30205, season=season.number),  # Season {season}
                    path=kodiutils.url_for('show_catalog_program_season',
                                           program=program_id,
                                           season=season.uuid),
                    art_dict={
                        'fanart': program.fanart,
                        'poster': program.poster,
                        'landscape': program.thumb,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(
                            30205, season=season.number),  # Season {season}
                        'plot': season.description or program.description,
                        'set': program.title,
                    }))

        # Add Clips
        if program.clips:
            listing.append(
                TitleItem(
                    title=kodiutils.localize(
                        30059, program=program.title),  # Clips for {program}
                    path=kodiutils.url_for('show_catalog_program_clips',
                                           program=program_id),
                    art_dict={
                        'fanart': program.fanart,
                        'poster': program.poster,
                        'landscape': program.thumb,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(
                            30059,
                            program=program.title),  # Clips for {program}
                        'plot':
                        kodiutils.localize(30060, program=program.title
                                           ),  # Watch short clips of {program}
                        'set': program.title,
                    }))

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

    def show_program_season(self, program_id, season_uuid):
        """ Show the episodes of a program from the catalog
        :type program_id: str
        :type season_uuid: str
        """
        try:
            program = self._api.get_program(program_id)
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(
                30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        if season_uuid == "-1":
            # Show all episodes
            episodes = program.episodes
        else:
            # Show the episodes of the season that was selected
            episodes = [
                e for e in program.episodes if e.season_uuid == season_uuid
            ]

        listing = [Menu.generate_titleitem(episode) for episode in episodes]

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

    def show_program_clips(self, program_id):
        """ Show the clips of a program from the catalog
        :type program_id: str
        """
        try:
            # We need to query the backend, since we don't cache clips.
            program = self._api.get_program(program_id,
                                            extract_clips=True,
                                            cache=CACHE_PREVENT)
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(
                30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        listing = [
            Menu.generate_titleitem(episode) for episode in program.clips
        ]

        # Sort like we get our results back.
        kodiutils.show_listing(listing, 30003, content='episodes')

    def show_categories(self):
        """ Shows the categories """
        categories = self._api.get_categories()

        listing = []
        for category in categories:
            listing.append(
                TitleItem(title=category.title,
                          path=kodiutils.url_for('show_category',
                                                 category=category.uuid),
                          info_dict={
                              'title': category.title,
                          }))

        kodiutils.show_listing(listing, 30003, sort=['title'])

    def show_category(self, uuid):
        """ Shows a category """
        programs = self._api.get_category_content(int(uuid))

        listing = [Menu.generate_titleitem(program) for program in programs]

        kodiutils.show_listing(listing, 30003, content='tvshows')

    def show_recommendations(self):
        """ Shows the recommendations """
        # "Meest bekeken" has a specific API endpoint, the other categories are scraped from the website.
        listing = [
            TitleItem(title='Meest bekeken',
                      path=kodiutils.url_for('show_recommendations_category',
                                             category='meest-bekeken'),
                      info_dict={
                          'title': 'Meest bekeken',
                      })
        ]

        recommendations = self._api.get_recommendation_categories()
        for category in recommendations:
            listing.append(
                TitleItem(title=category.title,
                          path=kodiutils.url_for(
                              'show_recommendations_category',
                              category=category.uuid),
                          info_dict={
                              'title': category.title,
                          }))

        kodiutils.show_listing(listing, 30005, content='tvshows')

    def show_recommendations_category(self, uuid):
        """ Shows the a category of the recommendations """
        if uuid == 'meest-bekeken':
            programs = self._api.get_popular_programs()
            episodes = []
        else:
            recommendations = self._api.get_recommendation_categories()
            category = next(category for category in recommendations
                            if category.uuid == uuid)
            programs = category.programs
            episodes = category.episodes

        listing = []
        for episode in episodes:
            title_item = Menu.generate_titleitem(episode)
            title_item.info_dict[
                'title'] = episode.program_title + ' - ' + title_item.title
            listing.append(title_item)

        for program in programs:
            listing.append(Menu.generate_titleitem(program))

        kodiutils.show_listing(listing, 30005, content='tvshows')

    def show_mylist(self):
        """ Show all the programs of all channels """
        try:
            mylist, _ = self._auth.get_dataset('myList', 'myList')
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

        items = []
        if mylist:
            for item in mylist:
                program = self._api.get_program_by_uuid(item.get('id'))
                if program:
                    program.my_list = True
                    items.append(program)

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

        # Sort items by title
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30011, content='tvshows', sort='title')

    def mylist_add(self, uuid):
        """ Add a program to My List """
        if not uuid:
            kodiutils.end_of_directory()
            return

        mylist, sync_info = self._auth.get_dataset('myList', 'myList')

        if not mylist:
            mylist = []

        if uuid not in [item.get('id') for item in mylist]:
            # Python 2.7 doesn't support .timestamp(), and windows doesn't do '%s', so we need to calculate it ourself
            epoch = datetime(1970, 1, 1, tzinfo=dateutil.tz.gettz('UTC'))
            now = datetime.now(tz=dateutil.tz.gettz('UTC'))
            timestamp = int((now - epoch).total_seconds()) * 1000

            mylist.append({
                'id': uuid,
                'timestamp': timestamp,
            })

            self._auth.put_dataset('myList', 'myList', mylist, sync_info)

        kodiutils.end_of_directory()

    def mylist_del(self, uuid):
        """ Remove a program from My List """
        if not uuid:
            kodiutils.end_of_directory()
            return

        mylist, sync_info = self._auth.get_dataset('myList', 'myList')

        if not mylist:
            mylist = []

        new_mylist = [item for item in mylist if item.get('id') != uuid]
        self._auth.put_dataset('myList', 'myList', new_mylist, sync_info)

        kodiutils.end_of_directory()
Exemplo n.º 19
0
class Catalog:
    """ Menu code related to the catalog """

    def __init__(self):
        """ Initialise object """
        auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
        self._api = ContentApi(auth, cache_path=kodiutils.get_cache_path())

    def show_catalog(self):
        """ Show all the programs of all channels """
        try:
            items = []
            for channel in list(CHANNELS):
                items.extend(self._api.get_programs(channel))
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

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

        # Sort items by title
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

    def show_catalog_channel(self, channel):
        """ Show the programs of a specific channel
        :type channel: str
        """
        try:
            items = self._api.get_programs(channel)
        except Exception as ex:
            kodiutils.notification(message=str(ex))
            raise

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

        # Sort items by title
        # Used for A-Z listing or when movies and episodes are mixed.
        kodiutils.show_listing(listing, 30003, content='tvshows', sort='title')

    def show_program(self, channel, program_id):
        """ Show a program from the catalog
        :type channel: str
        :type program_id: str
         """
        try:
            program = self._api.get_program(channel, program_id, extract_clips=True, cache=CACHE_PREVENT)  # Use CACHE_PREVENT since we want fresh data
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        if not program.episodes and not program.clips:
            kodiutils.ok_dialog(message=kodiutils.localize(30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        # Go directly to the season when we have only one season and no clips
        if not program.clips and len(program.seasons) == 1:
            self.show_program_season(channel, program_id, list(program.seasons.values())[0].uuid)
            return

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

        listing = []

        # Add an '* All seasons' entry when configured in Kodi
        if program.seasons and 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', channel=channel, program=program_id, season='-1'),
                    art_dict={
                        'fanart': program.background,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(30204),  # All seasons
                        'plot': program.description,
                        'set': program.title,
                        'studio': studio,
                    }
                )
            )

        # Add the seasons
        for season in list(program.seasons.values()):
            listing.append(
                TitleItem(
                    title=season.title,  # kodiutils.localize(30205, season=season.number),  # Season {season}
                    path=kodiutils.url_for('show_catalog_program_season', channel=channel, program=program_id, season=season.uuid),
                    art_dict={
                        'fanart': program.background,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(30205, season=season.number),  # Season {season}
                        'plot': season.description,
                        'set': program.title,
                        'studio': studio,
                    }
                )
            )

        # Add Clips
        if program.clips:
            listing.append(
                TitleItem(
                    title=kodiutils.localize(30059, program=program.title),  # Clips for {program}
                    path=kodiutils.url_for('show_catalog_program_clips', channel=channel, program=program_id),
                    art_dict={
                        'fanart': program.background,
                    },
                    info_dict={
                        'tvshowtitle': program.title,
                        'title': kodiutils.localize(30059, program=program.title),  # Clips for {program}
                        'plot': kodiutils.localize(30060, program=program.title),  # Watch short clips of {program}
                        'set': program.title,
                        'studio': studio,
                    }
                )
            )

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

    def show_program_season(self, channel, program_id, season_uuid):
        """ Show the episodes of a program from the catalog
        :type channel: str
        :type program_id: str
        :type season_uuid: str
        """
        try:
            program = self._api.get_program(channel, program_id)
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        if season_uuid == "-1":
            # Show all episodes
            episodes = program.episodes
        else:
            # Show the episodes of the season that was selected
            episodes = [e for e in program.episodes if e.season_uuid == season_uuid]

        listing = [Menu.generate_titleitem(episode) for episode in episodes]

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

    def show_program_clips(self, channel, program_id):
        """ Show the clips of a program from the catalog
        :type channel: str
        :type program_id: str
        """
        try:
            # We need to query the backend, since we don't cache clips.
            program = self._api.get_program(channel, program_id, extract_clips=True, cache=CACHE_PREVENT)
        except UnavailableException:
            kodiutils.ok_dialog(message=kodiutils.localize(30717))  # This program is not available in the catalogue.
            kodiutils.end_of_directory()
            return

        listing = [Menu.generate_titleitem(episode) for episode in program.clips]

        # Sort like we get our results back.
        kodiutils.show_listing(listing, 30003, content='episodes')
Exemplo n.º 20
0
 def test_notifications(self):
     api = ContentApi(self._auth.get_token())
     notifications = api.get_notifications()
     self.assertIsInstance(notifications, list)