Ejemplo n.º 1
0
 def _get_login_json(self):
     """Get login json"""
     payload = dict(
         loginID=from_unicode(get_setting('username')),
         password=from_unicode(get_setting('password')),
         sessionExpiration='-2',
         APIKey=self._API_KEY,
         targetEnv='jssdk',
     )
     data = urlencode(payload).encode()
     return get_url_json(self._LOGIN_URL, data=data, fail={})
def log_error(message, **kwargs):
    """Log error messages to Kodi"""
    if kwargs:
        from string import Formatter
        message = Formatter().vformat(message, (), SafeDict(**kwargs))
    message = '[{addon}] {message}'.format(addon=addon_id(), message=message)
    xbmc.log(from_unicode(message), 4)
Ejemplo n.º 3
0
def unwatchlater(asset_id, title, url):
    """The API interface to unwatch an episode used by the context menu"""
    from resumepoints import ResumePoints
    ResumePoints().unwatchlater(asset_id=asset_id,
                                title=to_unicode(
                                    unquote_plus(from_unicode(title))),
                                url=url)
Ejemplo n.º 4
0
def unfollow(program, title):
    """The API interface to unfollow a program used by the context menu"""
    move_down = bool(plugin.args.get('move_down'))
    from favorites import Favorites
    Favorites().unfollow(program=program,
                         title=to_unicode(unquote_plus(from_unicode(title))),
                         move_down=move_down)
Ejemplo n.º 5
0
 def _get_xvrttoken(self, login_json=None):
     """Get a one year valid X-VRT-Token"""
     from json import dumps
     if not login_json:
         login_json = self._get_login_json()
     login_token = login_json.get('sessionInfo', {}).get('login_token')
     if not login_token:
         return None
     login_cookie = 'glt_{api_key}={token}'.format(api_key=self._API_KEY,
                                                   token=login_token)
     payload = dict(uid=login_json.get('UID'),
                    uidsig=login_json.get('UIDSignature'),
                    ts=login_json.get('signatureTimestamp'),
                    email=from_unicode(get_setting('username')))
     data = dumps(payload).encode()
     headers = {'Content-Type': 'application/json', 'Cookie': login_cookie}
     response = open_url(self._TOKEN_GATEWAY_URL,
                         data=data,
                         headers=headers)
     if response is None:
         return None
     setcookie_header = response.info().get('Set-Cookie')
     xvrttoken = TokenResolver._create_token_dictionary(setcookie_header)
     if xvrttoken is None:
         return None
     notification(message=localize(30952))  # Login succeeded.
     return xvrttoken
Ejemplo n.º 6
0
    def _get_new_xvrttoken(self, login_json, token_variant=None):
        """Get new X-VRT-Token from VRT NU website"""

        if token_variant == 'roaming':
            xvrttoken = self._get_roaming_xvrttoken()
        else:
            login_token = login_json.get('sessionInfo', {}).get('login_token')
            if not login_token:
                return None

            from json import dumps
            login_cookie = 'glt_{api_key}={token}'.format(api_key=self._API_KEY, token=login_token)
            payload = dict(
                uid=login_json.get('UID'),
                uidsig=login_json.get('UIDSignature'),
                ts=login_json.get('signatureTimestamp'),
                email=from_unicode(get_setting('username')),
            )
            data = dumps(payload).encode()
            headers = {'Content-Type': 'application/json', 'Cookie': login_cookie}
            log(2, 'URL post: {url}', url=unquote(self._TOKEN_GATEWAY_URL))
            req = Request(self._TOKEN_GATEWAY_URL, data=data, headers=headers)
            try:  # Python 3
                setcookie_header = urlopen(req).info().get('Set-Cookie')
            except AttributeError:  # Python 2
                setcookie_header = urlopen(req).info().getheader('Set-Cookie')
            xvrttoken = TokenResolver._create_token_dictionary(setcookie_header)

        if xvrttoken is None:
            return None
        self._set_cached_token(xvrttoken, token_variant)
        notification(message=localize(30952))  # Login succeeded.
        return xvrttoken.get('X-VRT-Token')
def log(level=1, message='', **kwargs):
    """Log info messages to Kodi"""
    debug_logging = get_global_setting(
        'debug.showloginfo')  # Returns a boolean
    max_log_level = get_setting_int('max_log_level', default=0)
    if not debug_logging and not (level <= max_log_level
                                  and max_log_level != 0):
        return
    if kwargs:
        from string import Formatter
        message = Formatter().vformat(message, (), SafeDict(**kwargs))
    message = '[{addon}] {message}'.format(addon=addon_id(), message=message)
    xbmc.log(from_unicode(message), level % 3 if debug_logging else 2)
Ejemplo n.º 8
0
    def get_properties(self, api_data):
        """Get properties from single item json api data"""
        properties = {}

        # Only fill in properties when using VRT NU resumepoints because setting resumetime/totaltime breaks standard Kodi watched status
        if self._resumepoints.is_activated():
            asset_id = self.get_asset_id(api_data)
            if asset_id:
                # We need to ensure forward slashes are quoted
                program_title = to_unicode(
                    quote_plus(from_unicode(api_data.get('program'))))

                url = reformat_url(api_data.get('url', ''), 'medium')
                properties.update(asset_id=asset_id,
                                  url=url,
                                  title=program_title)

                position = self._resumepoints.get_position(asset_id)
                total = self._resumepoints.get_total(asset_id)
                # Master over Kodi watch status
                if position and total and SECONDS_MARGIN < position < total - SECONDS_MARGIN:
                    properties['resumetime'] = position
                    properties['totaltime'] = total
                    log(2,
                        '[Metadata] manual resumetime set to {position}',
                        position=position)

            episode = self.get_episode(api_data)
            season = self.get_season(api_data)
            if episode and season:
                properties['episodeno'] = 's%se%s' % (season, episode)

            year = self.get_year(api_data)
            if year:
                properties['year'] = year

        return properties
def set_property(key, value, window_id=10000):
    """Set a Window property"""
    from xbmcgui import Window
    return Window(window_id).setProperty(key, from_unicode(value))
Ejemplo n.º 10
0
def set_setting(key, value):
    """Set an add-on setting"""
    return ADDON.setSetting(key, from_unicode(str(value)))
Ejemplo n.º 11
0
def show_listing(list_items,
                 category=None,
                 sort='unsorted',
                 ascending=True,
                 content=None,
                 cache=None,
                 selected=None):
    """Show a virtual directory in Kodi"""
    from xbmcgui import ListItem
    from addon import plugin

    set_property('container.url', 'plugin://' + addon_id() + plugin.path)
    xbmcplugin.setPluginFanart(handle=plugin.handle,
                               image=from_unicode(addon_fanart()))

    usemenucaching = get_setting_bool('usemenucaching', default=True)
    if cache is None:
        cache = usemenucaching
    elif usemenucaching is False:
        cache = False

    if content:
        # content is one of: files, songs, artists, albums, movies, tvshows, episodes, musicvideos
        xbmcplugin.setContent(plugin.handle, content=content)

    # Jump through hoops to get a stable breadcrumbs implementation
    category_label = ''
    if category:
        if not content:
            category_label = 'VRT NU / '
        if plugin.path.startswith(('/favorites/', '/resumepoints/')):
            category_label += localize(30428) + ' / '  # My
        if isinstance(category, int):
            category_label += localize(category)
        else:
            category_label += category
    elif not content:
        category_label = 'VRT NU'
    xbmcplugin.setPluginCategory(handle=plugin.handle, category=category_label)

    # FIXME: Since there is no way to influence descending order, we force it here
    if not ascending:
        sort = 'unsorted'

    # NOTE: When showing tvshow listings and 'showoneoff' was set, force 'unsorted'
    if get_setting_bool(
            'showoneoff',
            default=True) and sort == 'label' and content == 'tvshows':
        sort = 'unsorted'

    # Add all sort methods to GUI (start with preferred)
    xbmcplugin.addSortMethod(handle=plugin.handle,
                             sortMethod=SORT_METHODS[sort])
    for key in sorted(SORT_METHODS):
        if key != sort:
            xbmcplugin.addSortMethod(handle=plugin.handle,
                                     sortMethod=SORT_METHODS[key])

    # FIXME: This does not appear to be working, we have to order it ourselves
#    xbmcplugin.setProperty(handle=plugin.handle, key='sort.ascending', value='true' if ascending else 'false')
#    if ascending:
#        xbmcplugin.setProperty(handle=plugin.handle, key='sort.order', value=str(SORT_METHODS[sort]))
#    else:
#        # NOTE: When descending, use unsorted
#        xbmcplugin.setProperty(handle=plugin.handle, key='sort.order', value=str(SORT_METHODS['unsorted']))

    listing = []
    showfanart = get_setting_bool('showfanart', default=True)
    for title_item in list_items:
        # Three options:
        #  - item is a virtual directory/folder (not playable, path)
        #  - item is a playable file (playable, path)
        #  - item is non-actionable item (not playable, no path)
        is_folder = bool(not title_item.is_playable and title_item.path)
        is_playable = bool(title_item.is_playable and title_item.path)

        list_item = ListItem(label=title_item.label)

        prop_dict = dict(
            IsInternetStream='true' if is_playable else 'false',
            IsPlayable='true' if is_playable else 'false',
            IsFolder='false' if is_folder else 'true',
        )
        if title_item.prop_dict:
            title_item.prop_dict.update(prop_dict)
        else:
            title_item.prop_dict = prop_dict
        # NOTE: The setProperties method is new in Kodi18
        try:
            list_item.setProperties(title_item.prop_dict)
        except AttributeError:
            for key, value in list(title_item.prop_dict.items()):
                list_item.setProperty(key=key, value=str(value))

        # FIXME: The setIsFolder method is new in Kodi18, so we cannot use it just yet
        # list_item.setIsFolder(is_folder)

        if showfanart:
            # Add add-on fanart when fanart is missing
            if not title_item.art_dict:
                title_item.art_dict = dict(fanart=addon_fanart())
            elif not title_item.art_dict.get('fanart'):
                title_item.art_dict.update(fanart=addon_fanart())
            list_item.setArt(title_item.art_dict)

        if title_item.info_dict:
            # type is one of: video, music, pictures, game
            list_item.setInfo(type='video', infoLabels=title_item.info_dict)

        if title_item.stream_dict:
            # type is one of: video, audio, subtitle
            list_item.addStreamInfo('video', title_item.stream_dict)

        if title_item.context_menu:
            list_item.addContextMenuItems(title_item.context_menu)

        url = None
        if title_item.path:
            url = title_item.path

        listing.append((url, list_item, is_folder))

    # Jump to specific item
    if selected is not None:
        pass


#        from xbmcgui import getCurrentWindowId, Window
#        wnd = Window(getCurrentWindowId())
#        wnd.getControl(wnd.getFocusId()).selectItem(selected)

    succeeded = xbmcplugin.addDirectoryItems(plugin.handle, listing,
                                             len(listing))
    xbmcplugin.endOfDirectory(plugin.handle,
                              succeeded,
                              updateListing=False,
                              cacheToDisc=cache)
Ejemplo n.º 12
0
def translate_path(path):
    """Converts a Kodi special:// path to the corresponding OS-specific path"""
    return to_unicode(translatePath(from_unicode(path)))
Ejemplo n.º 13
0
def watchlater(episode_id, title):
    """The API interface to watch an episode used by the context menu"""
    from resumepoints import ResumePoints
    ResumePoints().watchlater(episode_id=episode_id,
                              title=to_unicode(
                                  unquote_plus(from_unicode(title))))
Ejemplo n.º 14
0
def follow(program_name, title, program_id=None):
    """The API interface to follow a program used by the context menu"""
    from favorites import Favorites
    Favorites().follow(program_name=program_name,
                       title=to_unicode(unquote_plus(from_unicode(title))),
                       program_id=program_id)
Ejemplo n.º 15
0
    def get_episodes(self,
                     program=None,
                     season=None,
                     episodes=None,
                     category=None,
                     feature=None,
                     programtype=None,
                     keywords=None,
                     whatson_id=None,
                     video_id=None,
                     video_url=None,
                     page=None,
                     use_favorites=False,
                     variety=None,
                     cache_file=None):
        """Get episodes or season data from VRT NU Search API"""

        # Contruct params
        if page:
            page = realpage(page)
            all_items = False
            items_per_page = get_setting_int('itemsperpage', default=50)
            params = {
                'from': ((page - 1) * items_per_page) + 1,
                'i': 'video',
                'size': items_per_page,
            }
        elif variety == 'single':
            all_items = False
            params = {
                'i': 'video',
                'size': '1',
            }
        else:
            all_items = True
            params = {
                'i': 'video',
                'size': '300',
            }

        if variety:
            season = 'allseasons'

            if variety == 'offline':
                from datetime import datetime, timedelta
                import dateutil.tz
                now = datetime.now(dateutil.tz.gettz('Europe/Brussels'))
                off_dates = [(now + timedelta(days=day)).strftime('%Y-%m-%d')
                             for day in range(0, 7)]
                params['facets[assetOffTime]'] = '[%s]' % (','.join(off_dates))

            if variety == 'oneoff':
                params[
                    'facets[episodeNumber]'] = '[0,1]'  # This to avoid VRT NU metadata errors (see #670)
                params['facets[programType]'] = 'oneoff'

            if variety == 'watchlater':
                self._resumepoints.refresh(ttl=ttl('direct'))
                episode_urls = self._resumepoints.watchlater_urls()
                params['facets[url]'] = '[%s]' % (','.join(episode_urls))

            if variety == 'continue':
                self._resumepoints.refresh(ttl=ttl('direct'))
                episode_urls = self._resumepoints.resumepoints_urls()
                params['facets[url]'] = '[%s]' % (','.join(episode_urls))

            if use_favorites:
                program_urls = [
                    program_to_url(p, 'medium')
                    for p in self._favorites.programs()
                ]
                params['facets[programUrl]'] = '[%s]' % (
                    ','.join(program_urls))
            elif variety in ('offline', 'recent'):
                channel_filter = []
                for channel in CHANNELS:
                    if channel.get('vod') is True and get_setting_bool(
                            channel.get('name'), default=True):
                        channel_filter.append(channel.get('name'))
                params['facets[programBrands]'] = '[%s]' % (
                    ','.join(channel_filter))

        if program:
            params['facets[programUrl]'] = program_to_url(program, 'medium')

        if season and season != 'allseasons':
            params['facets[seasonTitle]'] = season

        if episodes:
            params['facets[episodeNumber]'] = '[%s]' % (','.join(
                str(episode) for episode in episodes))

        if category:
            params['facets[categories]'] = category

        if feature:
            params['facets[programTags.title]'] = feature

        if programtype:
            params['facets[programType]'] = programtype

        if keywords:
            if not season:
                season = 'allseasons'
            params['q'] = quote_plus(from_unicode(keywords))
            params['highlight'] = 'true'

        if whatson_id:
            params['facets[whatsonId]'] = whatson_id

        if video_id:
            params['facets[videoId]'] = video_id

        if video_url:
            params['facets[url]'] = video_url

        # Construct VRT NU Search API Url and get api data
        querystring = '&'.join('{}={}'.format(key, value)
                               for key, value in list(params.items()))
        search_url = self._VRTNU_SEARCH_URL + '?' + querystring.replace(
            ' ', '%20')  # Only encode spaces to minimize url length
        if cache_file:
            search_json = get_cached_url_json(url=search_url,
                                              cache=cache_file,
                                              ttl=ttl('indirect'),
                                              fail={})
        else:
            search_json = get_url_json(url=search_url, fail={})

        # Check for multiple seasons
        seasons = []
        if 'facets[seasonTitle]' not in unquote(search_url):
            facets = search_json.get('facets', {}).get('facets')
            if facets:
                seasons = next((f.get('buckets', [])
                                for f in facets if f.get('name') == 'seasons'
                                and len(f.get('buckets', [])) > 1), None)
            # Experimental: VRT Search API only returns a maximum of 10 seasons, to get all seasons we need to use the "model.json" API
            if seasons and program and len(seasons) == 10:
                season_json = get_url_json(
                    'https://www.vrt.be/vrtnu/a-z/%s.model.json' % program)
                season_items = None
                try:
                    season_items = season_json.get(':items').get('parsys').get(':items').get('container') \
                                              .get(':items').get('banner').get(':items').get('navigation').get(':items')
                except AttributeError:
                    pass
                if season_items:
                    seasons = []
                    for item in season_items:
                        seasons.append(dict(key=item.lstrip('0')))

        episodes = search_json.get('results', [{}])
        show_seasons = bool(season != 'allseasons')

        # Return seasons
        if show_seasons and seasons:
            return (seasons, episodes)

        api_pages = search_json.get('meta').get('pages').get('total')
        api_page_size = search_json.get('meta').get('pages').get('size')
        total_results = search_json.get('meta').get('total_results')

        if all_items and total_results > api_page_size:
            for api_page in range(1, api_pages):
                api_page_url = search_url + '&from=' + str(api_page *
                                                           api_page_size + 1)
                api_page_json = get_url_json(api_page_url)
                if api_page_json is not None:
                    episodes += api_page_json.get('results', [{}])

        # Return episodes
        return episodes
Ejemplo n.º 16
0
    def get_context_menu(self, api_data, program, cache_file):
        """Get context menu"""
        from addon import plugin
        favorite_marker = ''
        watchlater_marker = ''
        context_menu = []

        # WATCH LATER
        if self._resumepoints.is_activated():
            asset_id = self.get_asset_id(api_data)

            # VRT NU Search API
            if api_data.get('type') == 'episode':
                program_title = api_data.get('program')

            # VRT NU Schedule API (some are missing vrt.whatson-id)
            elif api_data.get('vrt.whatson-id') or api_data.get('startTime'):
                program_title = api_data.get('title')

            if asset_id is not None:
                # We need to ensure forward slashes are quoted
                program_title = to_unicode(
                    quote_plus(from_unicode(program_title)))
                url = url_to_episode(api_data.get('url', ''))
                if self._resumepoints.is_watchlater(asset_id):
                    extras = {}
                    # If we are in a watchlater menu, move cursor down before removing a favorite
                    if plugin.path.startswith('/resumepoints/watchlater'):
                        extras = dict(move_down=True)
                    # Unwatch context menu
                    context_menu.append(
                        (capitalize(localize(30402)),
                         'RunPlugin(%s)' % url_for('unwatchlater',
                                                   asset_id=asset_id,
                                                   title=program_title,
                                                   url=url,
                                                   **extras)))
                    watchlater_marker = '[COLOR={highlighted}]ᶫ[/COLOR]'
                else:
                    # Watch context menu
                    context_menu.append(
                        (capitalize(localize(30401)),
                         'RunPlugin(%s)' % url_for('watchlater',
                                                   asset_id=asset_id,
                                                   title=program_title,
                                                   url=url)))

        # FOLLOW PROGRAM
        if self._favorites.is_activated():

            # VRT NU Search API
            if api_data.get('type') == 'episode':
                program_title = api_data.get('program')
                program_type = api_data.get('programType')
                follow_suffix = localize(
                    30410) if program_type != 'oneoff' else ''  # program
                follow_enabled = True

            # VRT NU Suggest API
            elif api_data.get('type') == 'program':
                program_title = api_data.get('title')
                follow_suffix = ''
                follow_enabled = True

            # VRT NU Schedule API (some are missing vrt.whatson-id)
            elif api_data.get('vrt.whatson-id') or api_data.get('startTime'):
                program_title = api_data.get('title')
                follow_suffix = localize(30410)  # program
                follow_enabled = bool(api_data.get('url'))

            if follow_enabled and program:
                program_title = to_unicode(
                    quote_plus(from_unicode(program_title))
                )  # We need to ensure forward slashes are quoted
                if self._favorites.is_favorite(program):
                    extras = {}
                    # If we are in a favorites menu, move cursor down before removing a favorite
                    if plugin.path.startswith('/favorites'):
                        extras = dict(move_down=True)
                    context_menu.append((
                        localize(30412, title=follow_suffix),  # Unfollow
                        'RunPlugin(%s)' % url_for('unfollow',
                                                  program=program,
                                                  title=program_title,
                                                  **extras)))
                    favorite_marker = '[COLOR={highlighted}]ᵛ[/COLOR]'
                else:
                    context_menu.append((
                        localize(30411, title=follow_suffix),  # Follow
                        'RunPlugin(%s)' % url_for(
                            'follow', program=program, title=program_title)))

        # GO TO PROGRAM
        if api_data.get('programType') != 'oneoff' and program:
            if plugin.path.startswith(
                ('/favorites/offline', '/favorites/recent', '/offline',
                 '/recent', '/resumepoints/continue',
                 '/resumepoints/watchlater', '/tvguide')):
                context_menu.append((
                    localize(30417),  # Go to program
                    'Container.Update(%s)' %
                    url_for('programs', program=program, season='allseasons')))

        # REFRESH MENU
        context_menu.append((
            localize(30413),  # Refresh menu
            'RunPlugin(%s)' % url_for('delete_cache', cache_file=cache_file)))

        return context_menu, colour(favorite_marker), colour(watchlater_marker)