diff --git a/addon.xml b/addon.xml index 42f7a0e..b1450b8 100644 --- a/addon.xml +++ b/addon.xml @@ -7,7 +7,7 @@ - + video diff --git a/addon.py b/addon_entry.py similarity index 100% rename from addon.py rename to addon_entry.py diff --git a/resources/lib/kodilogging.py b/resources/lib/kodilogging.py index bc842e3..83d1b67 100644 --- a/resources/lib/kodilogging.py +++ b/resources/lib/kodilogging.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Log handler for Kodi""" -from __future__ import unicode_literals +from __future__ import absolute_import, division, unicode_literals import logging diff --git a/resources/lib/kodiutils.py b/resources/lib/kodiutils.py index 5e6ffed..c85685a 100644 --- a/resources/lib/kodiutils.py +++ b/resources/lib/kodiutils.py @@ -113,17 +113,17 @@ def addon_profile(): def url_for(name, *args, **kwargs): """Wrapper for routing.url_for() to lookup by name""" - from resources.lib import addon + import addon # pylint: disable=import-error return addon.routing.url_for(getattr(addon, name), *args, **kwargs) def show_listing(title_items, category=None, sort=None, content=None, cache=True): """ Show a virtual directory in Kodi """ - from resources.lib import addon + from addon import routing # pylint: disable=import-error if content: # content is one of: files, songs, artists, albums, movies, tvshows, episodes, musicvideos, videos, images, games - xbmcplugin.setContent(addon.routing.handle, content=content) + xbmcplugin.setContent(routing.handle, content=content) # Jump through hoops to get a stable breadcrumbs implementation category_label = '' @@ -137,7 +137,7 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True elif not content: category_label = addon_name() - xbmcplugin.setPluginCategory(handle=addon.routing.handle, category=category_label) + xbmcplugin.setPluginCategory(handle=routing.handle, category=category_label) # Add all sort methods to GUI (start with preferred) if sort is None: @@ -146,7 +146,7 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True sort = [sort] + DEFAULT_SORT_METHODS for key in sort: - xbmcplugin.addSortMethod(handle=addon.routing.handle, sortMethod=SORT_METHODS[key]) + xbmcplugin.addSortMethod(handle=routing.handle, sortMethod=SORT_METHODS[key]) # Add the listings listing = [] @@ -184,13 +184,13 @@ def show_listing(title_items, category=None, sort=None, content=None, cache=True url = title_item.path if title_item.path else None listing.append((url, list_item, is_folder)) - succeeded = xbmcplugin.addDirectoryItems(addon.routing.handle, listing, len(listing)) - xbmcplugin.endOfDirectory(addon.routing.handle, succeeded, cacheToDisc=cache) + succeeded = xbmcplugin.addDirectoryItems(routing.handle, listing, len(listing)) + xbmcplugin.endOfDirectory(routing.handle, succeeded, cacheToDisc=cache) def play(stream, title=None, art_dict=None, info_dict=None, prop_dict=None): """Play the given stream""" - from resources.lib.addon import routing + from addon import routing # pylint: disable=import-error play_item = xbmcgui.ListItem(label=title, path=stream) if art_dict: @@ -476,7 +476,7 @@ def container_update(url): def end_of_directory(): """Close a virtual directory, required to avoid a waiting Kodi""" - from resources.lib.addon import routing + from addon import routing # pylint: disable=import-error xbmcplugin.endOfDirectory(handle=routing.handle, succeeded=False, updateListing=False, cacheToDisc=False) @@ -510,8 +510,8 @@ def get_cache(key, ttl=None): """ Get an item from the cache """ import time path = get_cache_path() - file = '.'.join(key) - fullpath = path + file + filename = '.'.join(key) + fullpath = path + filename if not exists(fullpath): return None @@ -521,7 +521,7 @@ def get_cache(key, ttl=None): with open_file(fullpath, 'r') as fdesc: try: - _LOGGER.info('Fetching {file} from cache', file=file) + _LOGGER.info('Fetching {file} from cache', file=filename) import json value = json.load(fdesc) return value @@ -532,13 +532,13 @@ def get_cache(key, ttl=None): def set_cache(key, data): """ Store an item in the cache """ path = get_cache_path() - file = '.'.join(key) - fullpath = path + file + filename = '.'.join(key) + fullpath = path + filename if not exists(path): mkdirs(path) with open_file(fullpath, 'w') as fdesc: - _LOGGER.info('Storing to cache as {file}', file=file) + _LOGGER.info('Storing to cache as {file}', file=filename) import json json.dump(data, fdesc) diff --git a/resources/lib/modules/catalog.py b/resources/lib/modules/catalog.py index 9647ab8..393ad37 100644 --- a/resources/lib/modules/catalog.py +++ b/resources/lib/modules/catalog.py @@ -70,7 +70,7 @@ class Catalog: kodiutils.end_of_directory() return - if len(program.episodes) == 0: + 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 @@ -87,37 +87,41 @@ class Catalog: # 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, - }) + 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, - }) + 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. diff --git a/resources/lib/modules/channels.py b/resources/lib/modules/channels.py index f0fa2bf..45a2c09 100644 --- a/resources/lib/modules/channels.py +++ b/resources/lib/modules/channels.py @@ -38,21 +38,23 @@ class Channels: ] listing.append( - TitleItem(title=channel.get('name'), - path=kodiutils.url_for('show_channel_menu', channel=key), - art_dict={ - 'icon': icon, - 'thumb': icon, - 'fanart': fanart, - }, - info_dict={ - 'plot': None, - 'playcount': 0, - 'mediatype': 'video', - 'studio': channel.get('studio_icon'), - }, - stream_dict=STREAM_DICT, - context_menu=context_menu), + TitleItem( + title=channel.get('name'), + path=kodiutils.url_for('show_channel_menu', channel=key), + art_dict={ + 'icon': icon, + 'thumb': icon, + 'fanart': fanart, + }, + info_dict={ + 'plot': None, + 'playcount': 0, + 'mediatype': 'video', + 'studio': channel.get('studio_icon'), + }, + stream_dict=STREAM_DICT, + context_menu=context_menu + ), ) kodiutils.show_listing(listing, 30007) @@ -68,35 +70,41 @@ class Channels: fanart = '{path}/resources/logos/{logo}'.format(path=kodiutils.addon_path(), logo=channel.get('background')) listing = [ - TitleItem(title=kodiutils.localize(30053, channel=channel.get('name')), # TV Guide for {channel} - path=kodiutils.url_for('show_tvguide_channel', channel=key), - art_dict={ - 'icon': 'DefaultAddonTvInfo.png', - 'fanart': fanart, - }, - info_dict={ - 'plot': kodiutils.localize(30054, channel=channel.get('name')), # Browse the TV Guide for {channel} - }), - TitleItem(title=kodiutils.localize(30055, channel=channel.get('name')), # Catalog for {channel} - path=kodiutils.url_for('show_catalog_channel', channel=key), - art_dict={ - 'icon': 'DefaultMovieTitle.png', - 'fanart': fanart, - }, - info_dict={ - 'plot': kodiutils.localize(30056, channel=channel.get('name')), # Browse the Catalog for {channel} - }) + TitleItem( + title=kodiutils.localize(30053, channel=channel.get('name')), # TV Guide for {channel} + path=kodiutils.url_for('show_tvguide_channel', channel=key), + art_dict={ + 'icon': 'DefaultAddonTvInfo.png', + 'fanart': fanart, + }, + info_dict={ + 'plot': kodiutils.localize(30054, channel=channel.get('name')), # Browse the TV Guide for {channel} + } + ), + TitleItem( + title=kodiutils.localize(30055, channel=channel.get('name')), # Catalog for {channel} + path=kodiutils.url_for('show_catalog_channel', channel=key), + art_dict={ + 'icon': 'DefaultMovieTitle.png', + 'fanart': fanart, + }, + info_dict={ + 'plot': kodiutils.localize(30056, channel=channel.get('name')), # Browse the Catalog for {channel} + } + ) ] # Add YouTube channels if kodiutils.get_cond_visibility('System.HasAddon(plugin.video.youtube)') != 0: for youtube in channel.get('youtube', []): listing.append( - TitleItem(title=kodiutils.localize(30206, label=youtube.get('label')), # Watch {label} on YouTube - path=youtube.get('path'), - info_dict={ - 'plot': kodiutils.localize(30206, label=youtube.get('label')), # Watch {label} on YouTube - }) + TitleItem( + title=kodiutils.localize(30206, label=youtube.get('label')), # Watch {label} on YouTube + path=youtube.get('path'), + info_dict={ + 'plot': kodiutils.localize(30206, label=youtube.get('label')), # Watch {label} on YouTube + } + ) ) kodiutils.show_listing(listing, 30007, sort=['unsorted']) diff --git a/resources/lib/modules/menu.py b/resources/lib/modules/menu.py index 1707faf..82eac4f 100644 --- a/resources/lib/modules/menu.py +++ b/resources/lib/modules/menu.py @@ -19,33 +19,39 @@ class Menu: def show_mainmenu(): """ Show the main menu """ listing = [ - TitleItem(title=kodiutils.localize(30001), # A-Z - path=kodiutils.url_for('show_catalog'), - art_dict=dict( - icon='DefaultMovieTitle.png', - fanart=kodiutils.get_addon_info('fanart'), - ), - info_dict=dict( - plot=kodiutils.localize(30002), - )), - TitleItem(title=kodiutils.localize(30007), # TV Channels - path=kodiutils.url_for('show_channels'), - art_dict=dict( - icon='DefaultAddonPVRClient.png', - fanart=kodiutils.get_addon_info('fanart'), - ), - info_dict=dict( - plot=kodiutils.localize(30008), - )), - TitleItem(title=kodiutils.localize(30009), # Search - path=kodiutils.url_for('show_search'), - art_dict=dict( - icon='DefaultAddonsSearch.png', - fanart=kodiutils.get_addon_info('fanart'), - ), - info_dict=dict( - plot=kodiutils.localize(30010), - )) + TitleItem( + title=kodiutils.localize(30001), # A-Z + path=kodiutils.url_for('show_catalog'), + art_dict=dict( + icon='DefaultMovieTitle.png', + fanart=kodiutils.get_addon_info('fanart'), + ), + info_dict=dict( + plot=kodiutils.localize(30002), + ) + ), + TitleItem( + title=kodiutils.localize(30007), # TV Channels + path=kodiutils.url_for('show_channels'), + art_dict=dict( + icon='DefaultAddonPVRClient.png', + fanart=kodiutils.get_addon_info('fanart'), + ), + info_dict=dict( + plot=kodiutils.localize(30008), + ) + ), + TitleItem( + title=kodiutils.localize(30009), # Search + path=kodiutils.url_for('show_search'), + art_dict=dict( + icon='DefaultAddonsSearch.png', + fanart=kodiutils.get_addon_info('fanart'), + ), + info_dict=dict( + plot=kodiutils.localize(30010), + ) + ) ] kodiutils.show_listing(listing, sort=['unsorted']) diff --git a/resources/lib/viervijfzes/auth.py b/resources/lib/viervijfzes/auth.py index 134f30a..fba5cfe 100644 --- a/resources/lib/viervijfzes/auth.py +++ b/resources/lib/viervijfzes/auth.py @@ -72,11 +72,12 @@ class AuthApi: # Store new tokens in cache with open(self._cache + self.TOKEN_FILE, 'wb') as f: - f.write(json.dumps(dict( + data = json.dumps(dict( id_token=self._id_token, refresh_token=self._refresh_token, expiry=self._expiry, - ))) + )) + f.write(data.encode('utf8')) return self._id_token diff --git a/resources/lib/viervijfzes/auth_awsidp.py b/resources/lib/viervijfzes/auth_awsidp.py index 73b1fe0..6e587bd 100644 --- a/resources/lib/viervijfzes/auth_awsidp.py +++ b/resources/lib/viervijfzes/auth_awsidp.py @@ -185,10 +185,11 @@ class AwsIdp: secret_block_bytes = base64.standard_b64decode(secret_block) # the message is a combo of the pool_id, provided SRP userId, the Secret and Timestamp - msg = bytearray(self.pool_id.split('_')[1], 'utf-8') + \ - bytearray(user_id_for_srp, 'utf-8') + \ - bytearray(secret_block_bytes) + \ - bytearray(timestamp, 'utf-8') + msg = \ + bytearray(self.pool_id.split('_')[1], 'utf-8') + \ + bytearray(user_id_for_srp, 'utf-8') + \ + bytearray(secret_block_bytes) + \ + bytearray(timestamp, 'utf-8') hmac_obj = hmac.new(hkdf, msg, digestmod=hashlib.sha256) signature_string = base64.standard_b64encode(hmac_obj.digest()).decode('utf-8') challenge_request = { diff --git a/resources/lib/viervijfzes/content.py b/resources/lib/viervijfzes/content.py index 1eae505..398caaa 100644 --- a/resources/lib/viervijfzes/content.py +++ b/resources/lib/viervijfzes/content.py @@ -8,8 +8,8 @@ import logging import re from datetime import datetime -import requests from six.moves.html_parser import HTMLParser +import requests from resources.lib.viervijfzes import CHANNELS diff --git a/resources/lib/viervijfzes/search.py b/resources/lib/viervijfzes/search.py index e8066fa..40ef731 100644 --- a/resources/lib/viervijfzes/search.py +++ b/resources/lib/viervijfzes/search.py @@ -26,6 +26,9 @@ class SearchApi: :type query: str :rtype list[Program] """ + if not query: + return [] + response = self._session.post( self.API_ENDPOINT, json={ @@ -35,10 +38,11 @@ class SearchApi: "mode": "byDate" } ) - data = json.loads(response.content) - if data['timed_out']: - raise TimeoutError() + if response.status_code != 200: + raise Exception('Could not search') + + data = json.loads(response.content) results = [] for hit in data['hits']['hits']: diff --git a/test/__init__.py b/test/__init__.py index 0fb3477..31cfeb1 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """ Tests """ +from __future__ import absolute_import, division, unicode_literals + import logging logging.basicConfig(level=logging.DEBUG) diff --git a/test/xbmc.py b/test/xbmc.py index 8dd1c46..1f1efdb 100644 --- a/test/xbmc.py +++ b/test/xbmc.py @@ -132,6 +132,7 @@ class Player: ''' A stub implementation for the xbmc Player class getPlayingFile() method ''' return '' + class VideoInfoTag: ''' A stub implementation of the xbmc VideoInfoTag class '''