From 55a901d629198bfbf75bd9dad6efb95635ab039a Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Wed, 8 May 2019 16:30:50 +0200 Subject: [PATCH] Add new logging infrastructure This PR includes: - A predefined set of log_levels: Quiet, Info, Verbose, Debug - Default log_level is 'Info' --- addon.py | 2 + resources/lib/kodiwrappers/kodiwrapper.py | 41 +++++++++------ resources/lib/vrtplayer/streamservice.py | 14 +++-- resources/lib/vrtplayer/tokenresolver.py | 11 ++-- resources/lib/vrtplayer/tvguide.py | 1 + resources/lib/vrtplayer/vrtapihelper.py | 6 +-- resources/lib/vrtplayer/vrtplayer.py | 64 +++++++++++------------ resources/settings.xml | 2 +- test/vrtplayertests.py | 6 ++- 9 files changed, 85 insertions(+), 62 deletions(-) diff --git a/addon.py b/addon.py index c96ba54e..3a12c5b0 100644 --- a/addon.py +++ b/addon.py @@ -28,6 +28,8 @@ def router(params_string): kodi_wrapper = kodiwrapper.KodiWrapper(_ADDON_HANDLE, _ADDON_URL, addon) + kodi_wrapper.log_notice('Path: ' + _ADDON_URL, 'Verbose') + if action == actions.CLEAR_COOKIES: from resources.lib.vrtplayer import tokenresolver token_resolver = tokenresolver.TokenResolver(kodi_wrapper) diff --git a/resources/lib/kodiwrappers/kodiwrapper.py b/resources/lib/kodiwrappers/kodiwrapper.py index cf485f40..0132a7f9 100644 --- a/resources/lib/kodiwrappers/kodiwrapper.py +++ b/resources/lib/kodiwrappers/kodiwrapper.py @@ -11,18 +11,25 @@ except ImportError: from urllib import urlencode -sort_methods = { - # 'date': xbmcplugin.SORT_METHOD_DATE, - 'dateadded': xbmcplugin.SORT_METHOD_DATEADDED, - 'duration': xbmcplugin.SORT_METHOD_DURATION, - 'episode': xbmcplugin.SORT_METHOD_EPISODE, - # 'genre': xbmcplugin.SORT_METHOD_GENRE, - 'label': xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE, - # 'none': xbmcplugin.SORT_METHOD_UNSORTED, +sort_methods = dict( + # date=xbmcplugin.SORT_METHOD_DATE, + dateadded=xbmcplugin.SORT_METHOD_DATEADDED, + duration=xbmcplugin.SORT_METHOD_DURATION, + episode=xbmcplugin.SORT_METHOD_EPISODE, + # genre=xbmcplugin.SORT_METHOD_GENRE, + label=xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE, + # none=xbmcplugin.SORT_METHOD_UNSORTED, # FIXME: We would like to be able to sort by unprefixed title (ignore date/episode prefix) - # 'title': xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE, - 'unsorted': xbmcplugin.SORT_METHOD_UNSORTED, -} + # title=xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE, + unsorted=xbmcplugin.SORT_METHOD_UNSORTED, +) + +log_levels = dict( + Quiet=0, + Info=1, + Verbose=2, + Debug=3, +) def has_socks(): @@ -44,6 +51,7 @@ def __init__(self, handle, url, addon): self._url = url self._addon = addon self._addon_id = addon.getAddonInfo('id') + self._max_log_level = log_levels.get(self.get_setting('max_log_level'), 3) def show_listing(self, list_items, sort='unsorted', ascending=True, content_type=None, cache=True): import xbmcgui @@ -114,7 +122,7 @@ def play(self, video): subtitles_visible = self.get_setting('showsubtitles') == 'true' # Separate subtitle url for hls-streams if subtitles_visible and video.subtitle_url is not None: - self.log_notice('subtitle ' + video.subtitle_url) + self.log_notice('Subtitle URL: ' + video.subtitle_url) play_item.setSubtitles([video.subtitle_url]) xbmcplugin.setResolvedUrl(self._handle, True, listitem=play_item) @@ -133,7 +141,7 @@ def set_locale(self): # NOTE: This only works if the platform supports the Kodi configured locale locale.setlocale(locale.LC_ALL, locale_lang) except Exception as e: - self.log_notice(e) + self.log_notice(e, 'Verbose') def get_localized_string(self, string_id): return self._addon.getLocalizedString(string_id) @@ -241,10 +249,11 @@ def delete_path(self, path): import xbmcvfs return xbmcvfs.delete(path) - def log_notice(self, message): + def log_notice(self, message, log_level='Info'): ''' Log info messages to Kodi ''' - xbmc.log(msg='[%s] %s' % (self._addon_id, message), level=xbmc.LOGNOTICE) + if log_levels.get(log_level, 0) <= self._max_log_level: + xbmc.log(msg='[%s] %s' % (self._addon_id, message), level=xbmc.LOGNOTICE) - def log_error(self, message): + def log_error(self, message, log_level='Info'): ''' Log error messages to Kodi ''' xbmc.log(msg='[%s] %s' % (self._addon_id, message), level=xbmc.LOGERROR) diff --git a/resources/lib/vrtplayer/streamservice.py b/resources/lib/vrtplayer/streamservice.py index ffbe5178..7790e466 100644 --- a/resources/lib/vrtplayer/streamservice.py +++ b/resources/lib/vrtplayer/streamservice.py @@ -32,6 +32,7 @@ def __init__(self, kodi_wrapper, token_resolver): self._license_url = None def _get_license_url(self): + self._kodi_wrapper.log_notice('URL get: ' + self._VUPLAY_API_URL, 'Verbose') self._license_url = json.loads(urlopen(self._VUPLAY_API_URL).read()).get('drm_providers', dict()).get('widevine', dict()).get('la_url') def _create_settings_dir(self): @@ -98,6 +99,7 @@ def _get_api_data(self, video): def _webscrape_api_data(self, video_url): '''Scrape api data from VRT NU html page''' from bs4 import BeautifulSoup, SoupStrainer + self._kodi_wrapper.log_notice('URL get: ' + video_url, 'Verbose') html_page = urlopen(video_url).read() strainer = SoupStrainer('div', {'class': 'cq-dd-vrtvideo'}) soup = BeautifulSoup(html_page, 'html.parser', parse_only=strainer) @@ -147,6 +149,7 @@ def _get_video_json(self, api_data): if playertoken: api_url = api_data.media_api_url + '/videos/' + api_data.publication_id + \ api_data.video_id + '?vrtPlayerToken=' + playertoken + '&client=' + api_data.client + self._kodi_wrapper.log_notice('URL get: ' + api_url, 'Verbose') try: video_json = json.loads(urlopen(api_url).read()) except HTTPError as e: @@ -200,7 +203,7 @@ def get_stream(self, video, retry=False, api_data=None): return self._select_stream(stream_dict, vudrm_token) if video_json.get('code') in ('INCOMPLETE_ROAMING_CONFIG', 'INVALID_LOCATION'): - self._kodi_wrapper.log_notice(video_json.get('message')) + self._kodi_wrapper.log_error(video_json.get('message')) roaming_xvrttoken = self.token_resolver.get_xvrttoken(True) if not retry and roaming_xvrttoken is not None: # Delete cached playertokens @@ -234,22 +237,22 @@ def _select_stream(self, stream_dict, vudrm_token): protocol = None if vudrm_token and self._can_play_drm and self._kodi_wrapper.get_setting('usedrm') == 'true': protocol = 'mpeg_dash drm' - self._kodi_wrapper.log_notice('protocol: ' + protocol) + self._kodi_wrapper.log_notice('Protocol: ' + protocol) stream_url = self._try_get_drm_stream(stream_dict, vudrm_token) if vudrm_token and stream_url is None: protocol = 'hls_aes' - self._kodi_wrapper.log_notice('protocol: ' + protocol) + self._kodi_wrapper.log_notice('Protocol: ' + protocol) stream_url = streamurls.StreamURLS(*self._select_hls_substreams(stream_dict[protocol])) if protocol in stream_dict else None if self._kodi_wrapper.has_inputstream_adaptive_installed() and stream_url is None: protocol = 'mpeg_dash' - self._kodi_wrapper.log_notice('protocol: ' + protocol) + self._kodi_wrapper.log_notice('Protocol: ' + protocol) stream_url = streamurls.StreamURLS(stream_dict[protocol], use_inputstream_adaptive=True) if protocol in stream_dict else None if stream_url is None: protocol = 'hls' - self._kodi_wrapper.log_notice('protocol: ' + protocol) + self._kodi_wrapper.log_notice('Protocol: ' + protocol) # No if-else statement because this is the last resort stream selection stream_url = streamurls.StreamURLS(*self._select_hls_substreams(stream_dict[protocol])) @@ -267,6 +270,7 @@ def _select_hls_substreams(self, master_hls_url): if any(x in master_hls_url for x in ('.ism/', '.isml/')) and bandwidth_limit == 0: import re base_url = master_hls_url.split('.m3u8')[0] + self._kodi_wrapper.log_notice('URL get: ' + master_hls_url, 'Verbose') m3u8 = urlopen(master_hls_url).read() # Get audio uri diff --git a/resources/lib/vrtplayer/tokenresolver.py b/resources/lib/vrtplayer/tokenresolver.py index 8d8bba5f..851c930c 100644 --- a/resources/lib/vrtplayer/tokenresolver.py +++ b/resources/lib/vrtplayer/tokenresolver.py @@ -75,6 +75,7 @@ def get_cookie_from_cookiejar(cookiename, cookiejar): def _get_new_playertoken(self, path, token_url, headers): import json + self._kodi_wrapper.log_notice('URL post: ' + token_url, 'Verbose') req = Request(token_url, data='', headers=headers) playertoken = json.loads(urlopen(req).read()) json.dump(playertoken, open(path, 'w')) @@ -92,10 +93,10 @@ def _get_cached_token(self, path, token_name): now = datetime.now(dateutil.tz.tzlocal()) exp = dateutil.parser.parse(token.get('expirationDate')) if exp > now: - self._kodi_wrapper.log_notice('Got cached token') + self._kodi_wrapper.log_notice('Got cached token', 'Verbose') cached_token = token.get(token_name) else: - self._kodi_wrapper.log_notice('Cached token deleted') + self._kodi_wrapper.log_notice('Cached token deleted', 'Info') self._kodi_wrapper.delete_path(path) return cached_token @@ -112,6 +113,7 @@ def _get_new_xvrttoken(self, path, get_roaming_token): APIKey=self._API_KEY, targetEnv='jssdk', ) + self._kodi_wrapper.log_notice('URL post: ' + self._LOGIN_URL, 'Verbose') req = Request(self._LOGIN_URL, data=urlencode(data)) logon_json = json.loads(urlopen(req).read()) token = None @@ -125,7 +127,8 @@ def _get_new_xvrttoken(self, path, get_roaming_token): email=cred.username, ) headers = {'Content-Type': 'application/json', 'Cookie': login_cookie} - req = Request(self._TOKEN_GATEWAY_URL, json.dumps(payload), headers) + self._kodi_wrapper.log_notice('URL post: ' + self._TOKEN_GATEWAY_URL, 'Verbose') + req = Request(self._TOKEN_GATEWAY_URL, data=json.dumps(payload), headers=headers) cookie_data = urlopen(req).info().getheader('Set-Cookie').split('X-VRT-Token=')[1].split('; ') xvrttoken = TokenResolver._create_token_dictionary_from_urllib(cookie_data) if get_roaming_token: @@ -157,6 +160,7 @@ def _get_roaming_xvrttoken(self, xvrttoken): cookie_value = 'X-VRT-Token=' + xvrttoken.get('X-VRT-Token') headers = {'Cookie': cookie_value} opener = build_opener(NoRedirection, ProxyHandler(self._proxies)) + self._kodi_wrapper.log_notice('URL post: ' + url, 'Verbose') req = Request(url, headers=headers) req_info = opener.open(req).info() cookie_value += '; state=' + req_info.getheader('Set-Cookie').split('state=')[1].split('; ')[0] @@ -164,6 +168,7 @@ def _get_roaming_xvrttoken(self, xvrttoken): url = opener.open(url).info().getheader('Location') headers = {'Cookie': cookie_value} if url is not None: + self._kodi_wrapper.log_notice('URL post: ' + url, 'Verbose') req = Request(url, headers=headers) cookie_data = opener.open(req).info().getheader('Set-Cookie').split('X-VRT-Token=')[1].split('; ') roaming_xvrttoken = TokenResolver._create_token_dictionary_from_urllib(cookie_data) diff --git a/resources/lib/vrtplayer/tvguide.py b/resources/lib/vrtplayer/tvguide.py index 8db1c3cc..9792ce2f 100644 --- a/resources/lib/vrtplayer/tvguide.py +++ b/resources/lib/vrtplayer/tvguide.py @@ -92,6 +92,7 @@ def show_tvguide(self, params): dateobj = dateutil.parser.parse(date) datelong = dateobj.strftime(self._kodi_wrapper.get_localized_datelong()) api_url = dateobj.strftime(self.VRT_TVGUIDE) + self._kodi_wrapper.log_notice('URL get: ' + api_url, 'Verbose') schedule = json.loads(urlopen(api_url).read()) name = channel try: diff --git a/resources/lib/vrtplayer/vrtapihelper.py b/resources/lib/vrtplayer/vrtapihelper.py index 9033001f..756f4e05 100644 --- a/resources/lib/vrtplayer/vrtapihelper.py +++ b/resources/lib/vrtplayer/vrtapihelper.py @@ -41,7 +41,7 @@ def get_tvshow_items(self, category=None, channel=None): params['facets[programBrands]'] = channel api_url = self._VRTNU_SUGGEST_URL + '?' + urlencode(params) - # tvshows = requests.get(api_url, proxies=self._proxies).json() + self._kodi_wrapper.log_notice('URL get: ' + api_url, 'Verbose') tvshows = json.loads(urlopen(api_url).read()) tvshow_items = [] for tvshow in tvshows: @@ -98,7 +98,7 @@ def get_episode_items(self, path=None, page=None): 'facets[programBrands]': '[een,canvas,sporza,vrtnws,vrtnxt,radio1,radio2,klara,stubru,mnm]', } api_url = self._VRTNU_SEARCH_URL + '?' + urlencode(params) - # api_json = requests.get(api_url, proxies=self._proxies).json() + self._kodi_wrapper.log_notice('URL get: ' + api_url, 'Verbose') api_json = json.loads(urlopen(api_url).read()) episode_items, sort, ascending = self._map_to_episode_items(api_json.get('results', []), titletype='recent') @@ -112,7 +112,7 @@ def get_episode_items(self, path=None, page=None): api_url = self._VRTNU_SEARCH_URL + '?' + urlencode(params) else: api_url = path - # api_json = requests.get(api_url, proxies=self._proxies).json() + self._kodi_wrapper.log_notice('URL get: ' + api_url, 'Verbose') api_json = json.loads(urlopen(api_url).read()) episodes = api_json.get('results', [{}]) diff --git a/resources/lib/vrtplayer/vrtplayer.py b/resources/lib/vrtplayer/vrtplayer.py index 9afcad44..459adb09 100644 --- a/resources/lib/vrtplayer/vrtplayer.py +++ b/resources/lib/vrtplayer/vrtplayer.py @@ -12,37 +12,6 @@ from urllib2 import build_opener, install_opener, ProxyHandler, urlopen -def get_categories(proxies=None): - from bs4 import BeautifulSoup, SoupStrainer - response = urlopen('https://www.vrt.be/vrtnu/categorieen/') - tiles = SoupStrainer('a', {'class': 'nui-tile'}) - soup = BeautifulSoup(response.read(), 'html.parser', parse_only=tiles) - - categories = [] - for tile in soup.find_all(class_='nui-tile'): - categories.append(dict( - id=tile.get('href').split('/')[-2], - thumbnail=get_category_thumbnail(tile), - name=get_category_title(tile), - )) - - return categories - - -def get_category_thumbnail(element): - from resources.lib.vrtplayer import statichelper - raw_thumbnail = element.find(class_='media').get('data-responsive-image', 'DefaultGenre.png') - return statichelper.add_https_method(raw_thumbnail) - - -def get_category_title(element): - from resources.lib.vrtplayer import statichelper - found_element = element.find('h3') - if found_element is not None: - return statichelper.strip_newlines(found_element.contents[0]) - return '' - - class VRTPlayer: def __init__(self, kodi_wrapper, api_helper): @@ -187,7 +156,7 @@ def play(self, params): def __get_category_menu_items(self): try: - categories = get_categories(self._proxies) + categories = self.get_categories(self._proxies) except Exception: categories = [] @@ -205,3 +174,34 @@ def __get_category_menu_items(self): art_dict=dict(thumb=thumbnail, icon='DefaultGenre.png', fanart=thumbnail), video_dict=dict(plot='[B]%s[/B]' % category.get('name'), studio='VRT'))) return category_items + + def get_categories(self, proxies=None): + from bs4 import BeautifulSoup, SoupStrainer + self._kodi_wrapper.log_notice('URL get: https://www.vrt.be/vrtnu/categorieen/', 'Verbose') + response = urlopen('https://www.vrt.be/vrtnu/categorieen/') + tiles = SoupStrainer('a', {'class': 'nui-tile'}) + soup = BeautifulSoup(response.read(), 'html.parser', parse_only=tiles) + + categories = [] + for tile in soup.find_all(class_='nui-tile'): + categories.append(dict( + id=tile.get('href').split('/')[-2], + thumbnail=self.get_category_thumbnail(tile), + name=self.get_category_title(tile), + )) + + return categories + + @staticmethod + def get_category_thumbnail(element): + from resources.lib.vrtplayer import statichelper + raw_thumbnail = element.find(class_='media').get('data-responsive-image', 'DefaultGenre.png') + return statichelper.add_https_method(raw_thumbnail) + + @staticmethod + def get_category_title(element): + from resources.lib.vrtplayer import statichelper + found_element = element.find('h3') + if found_element is not None: + return statichelper.strip_newlines(found_element.contents[0]) + return '' diff --git a/resources/settings.xml b/resources/settings.xml index e16ee574..626b5973 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -18,6 +18,6 @@ - + diff --git a/test/vrtplayertests.py b/test/vrtplayertests.py index 0174abde..f1649cbd 100644 --- a/test/vrtplayertests.py +++ b/test/vrtplayertests.py @@ -98,12 +98,14 @@ def test_get_tvshows(self): def test_categories_scraping(self): ''' Test to ensure our hardcoded categories conforms to scraped categories ''' # Remove thumbnails from scraped categories first - categories = [dict(id=c['id'], name=c['name']) for c in vrtplayer.get_categories()] + vrt_player = vrtplayer.VRTPlayer(self._kodi_wrapper, self._api_helper) + categories = [dict(id=c['id'], name=c['name']) for c in vrt_player.get_categories()] self.assertEqual(categories, CATEGORIES) def test_random_tvshow_episodes(self): ''' Rest episode from a random tvshow in a random category ''' - categories = vrtplayer.get_categories() + vrt_player = vrtplayer.VRTPlayer(self._kodi_wrapper, self._api_helper) + categories = vrt_player.get_categories() self.assertTrue(categories) category = random.choice(categories)