Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,26 @@ msgctxt "#30967"
msgid "Could not retrieve this program list. VRT Search API url is too long. Go to [B]VRT NU settings » Interface » Manage My favorites…[/B] and unfollow older favorite programs to fix this problem."
msgstr ""

msgctxt "#30968"
msgid "No connection"
msgstr ""

msgctxt "#30969"
msgid "There is a problem connecting to the Internet. This could be related to Kodi, your network, your ISP or VRT NU. Check out the Kodi log for more details."
msgstr ""

msgctxt "#30970"
msgid "VRT NU authentication failed"
msgstr ""

msgctxt "#30971"
msgid "There is an unknown problem authenticating your user credentials at VRT NU. Please check your VRT NU account at https://www.vrt.be/vrtnu/"
msgstr ""

msgctxt "#30972"
msgid "There is a problem authenticating at VRT NU to enable favorites. Please check your VRT NU account at https://www.vrt.be/vrtnu/ or disable favorites in the add-on settings."
msgstr ""

msgctxt "#30975"
msgid "Failed to get user token from VRT NU"
msgstr ""
Expand Down
20 changes: 20 additions & 0 deletions resources/language/resource.language.nl_nl/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,26 @@ msgctxt "#30967"
msgid "Could not retrieve this program list. VRT Search API url is too long. Go to [B]VRT NU settings » Interface » Manage My favorites…[/B] and unfollow older favorite programs to fix this problem."
msgstr "Deze programmalijst kan niet opgehaald worden. VRT Search API url is te lang. Ga naar [B]VRT NU instellingen » Interface » Beheer Mijn favorieten[/B] en verwijder een aantal favoriete programma's om dit probleem op te lossen. "

msgctxt "#30968"
msgid "No connection"
msgstr "Geen verbinding"

msgctxt "#30969"
msgid "There is a problem connecting to the Internet. This could be related to Kodi, your network, your ISP or VRT NU. Check out the Kodi log for more details."
msgstr "Er is een probleem met het verbinden met internet. Dit kan liggen aan Kodi, aan jouw netwerk, aan jouw ISP of aan VRT NU. Bekijk de Kodi-log voor meer details."

msgctxt "#30970"
msgid "VRT NU authentication failed"
msgstr "VRT NU-verificatie is mislukt"

msgctxt "#30971"
msgid "There is an unknown problem authenticating your user credentials at VRT NU. Please check your VRT NU account at https://www.vrt.be/vrtnu/"
msgstr "Er is een onbekend probleem bij het verifiëren van je gebruikersgegevens bij VRT NU. Controleer je VRT NU-account op https://www.vrt.be/vrtnu/"

msgctxt "#30972"
msgid "There is a problem authenticating at VRT NU to enable favorites. Please check your VRT NU account at https://www.vrt.be/vrtnu/ or disable favorites in the add-on settings."
msgstr "Er is een probleem met de authenticatie bij VRT NU om favorieten in te schakelen. Controleer je VRT NU-account op https://www.vrt.be/vrtnu/ of schakel favorieten uit in de add-on-instellingen."

msgctxt "#30975"
msgid "Failed to get user token from VRT NU"
msgstr "Ophalen van het gebruikerstoken van VRT NU is mislukt"
Expand Down
6 changes: 2 additions & 4 deletions resources/lib/apihelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@

try: # Python 3
from urllib.parse import quote_plus, unquote
from urllib.request import build_opener, install_opener, ProxyHandler
except ImportError: # Python 2
from urllib import quote_plus
from urllib2 import build_opener, install_opener, ProxyHandler, unquote
from urllib2 import unquote

from data import CHANNELS
from helperobjects import TitleItem
from kodiutils import (delete_cached_thumbnail, get_cache, get_cached_url_json, get_global_setting,
get_proxies, get_setting_bool, get_setting_int, get_url_json, has_addon, localize,
get_setting_bool, get_setting_int, get_url_json, has_addon, localize,
localize_from_data, log, ttl, url_for)
from metadata import Metadata
from utils import (html_to_kodi, find_entry, from_unicode, play_url_to_id,
Expand All @@ -34,7 +33,6 @@ def __init__(self, _favorites, _resumepoints):
self._favorites = _favorites
self._resumepoints = _resumepoints
self._metadata = Metadata(_favorites, _resumepoints)
install_opener(build_opener(ProxyHandler(get_proxies())))

def get_tvshows(self, category=None, channel=None, feature=None):
"""Get all TV shows for a given category, channel or feature, optionally filtered by favorites"""
Expand Down
8 changes: 3 additions & 5 deletions resources/lib/favorites.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
try: # Python 3
from urllib.error import HTTPError
from urllib.parse import unquote
from urllib.request import build_opener, install_opener, ProxyHandler
except ImportError: # Python 2
from urllib2 import build_opener, HTTPError, install_opener, ProxyHandler, unquote
from urllib2 import unquote

from kodiutils import (container_refresh, get_cache, get_proxies, get_setting_bool, get_url_json,
from kodiutils import (container_refresh, get_cache, get_setting_bool, get_url_json,
has_credentials, input_down, invalidate_caches, localize, log_error,
multiselect, notification, ok_dialog, update_cache)
from utils import program_to_id
Expand All @@ -24,7 +23,6 @@ class Favorites:
def __init__(self):
"""Initialize favorites, relies on XBMC vfs and a special VRT token"""
self._data = dict() # Our internal representation
install_opener(build_opener(ProxyHandler(get_proxies())))

@staticmethod
def is_activated():
Expand Down Expand Up @@ -79,7 +77,7 @@ def update(self, program, title, value=True):
data = dumps(payload).encode('utf-8')
program_id = program_to_id(program)
try:
get_url_json('https://video-user-data.vrt.be/favorites/{program_id}'.format(program_id=program_id), headers=headers, data=data)
get_url_json('https://video-user-data.vrt.be/favorites/{program_id}'.format(program_id=program_id), headers=headers, data=data, raise_errors='all')
except HTTPError as exc:
log_error("Failed to (un)follow program '{program}' at VRT NU ({error})", program=program, error=exc)
notification(message=localize(30976, program=program))
Expand Down
133 changes: 95 additions & 38 deletions resources/lib/kodiutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
from __future__ import absolute_import, division, unicode_literals
from contextlib import contextmanager
from sys import version_info
from socket import timeout
from ssl import SSLError

import xbmc
import xbmcaddon
import xbmcplugin
from utils import from_unicode, to_unicode

try: # Python 3
from urllib.request import HTTPErrorProcessor
except ImportError: # Python 2
from urllib2 import HTTPErrorProcessor

ADDON = xbmcaddon.Addon()
DEFAULT_CACHE_DIR = 'cache'

Expand Down Expand Up @@ -80,6 +87,15 @@
}


class NoRedirection(HTTPErrorProcessor):
"""Prevent urllib from following http redirects"""

def http_response(self, request, response):
return response

https_response = http_response


class SafeDict(dict):
"""A safe dictionary implementation that does not break down on missing keys"""
def __missing__(self, key):
Expand Down Expand Up @@ -861,10 +877,10 @@ def wait_for_resumepoints():
update = get_property('vrtnu_resumepoints')
if update == 'busy':
import time
timeout = time.time() + 5 # 5 seconds timeout
time_out = time.time() + 5 # 5 seconds timeout
log(3, 'Resumepoint update is busy, wait')
while update != 'ready':
if time.time() > timeout: # Exit loop in case something goes wrong
if time.time() > time_out: # Exit loop in case something goes wrong
break
xbmc.sleep(50)
update = get_property('vrtnu_resumepoints')
Expand Down Expand Up @@ -1034,43 +1050,45 @@ def ttl(kind='direct'):
return 5 * 60


def get_json_data(response, fail=None):
"""Return json object from HTTP response"""
from json import load, loads
try:
if (3, 0, 0) <= version_info < (3, 6, 0): # the JSON object must be str, not 'bytes'
return loads(to_unicode(response.read()))
return load(response)
except TypeError as exc: # 'NoneType' object is not callable
log_error('JSON TypeError: {exc}', exc=exc)
return fail
except ValueError as exc: # No JSON object could be decoded
log_error('JSON ValueError: {exc}', exc=exc)
return fail


def get_url_json(url, cache=None, headers=None, data=None, fail=None):
"""Return HTTP data"""
def open_url(url, data=None, headers=None, method=None, cookiejar=None, follow_redirects=True, raise_errors=None):
"""Return a urllib http response"""
try: # Python 3
from urllib.error import HTTPError
from urllib.error import HTTPError, URLError
from urllib.parse import unquote
from urllib.request import urlopen, Request
from urllib.request import build_opener, HTTPCookieProcessor, ProxyHandler, Request
except ImportError: # Python 2
from urllib2 import HTTPError, unquote, urlopen, Request

if headers is None:
from urllib2 import build_opener, HTTPError, HTTPCookieProcessor, ProxyHandler, Request, URLError, unquote

opener_args = []
if not follow_redirects:
opener_args.append(NoRedirection)
if cookiejar is not None:
opener_args.append(HTTPCookieProcessor(cookiejar))
proxies = get_proxies()
if proxies:
opener_args.append(ProxyHandler(proxies))
opener = build_opener(*opener_args)

if not headers:
headers = dict()
req = Request(url, headers=headers)

if data is not None:
req.data = data
log(2, 'URL post: {url}', url=unquote(url))
log(2, 'URL post data: {data}', data=data)
req.data = data
else:
log(2, 'URL get: {url}', url=unquote(url))

if method is not None:
req.get_method = lambda: method

if raise_errors is None:
raise_errors = list()
try:
json_data = get_json_data(urlopen(req), fail=fail)
return opener.open(req)
except HTTPError as exc:
if isinstance(raise_errors, list) and 401 in raise_errors or raise_errors == 'all':
raise
if hasattr(req, 'selector'): # Python 3.4+
url_length = len(req.selector)
else: # Python 2.7
Expand All @@ -1079,25 +1097,64 @@ def get_url_json(url, cache=None, headers=None, data=None, fail=None):
ok_dialog(heading='HTTP Error 400', message=localize(30967))
log_error('HTTP Error 400: Probably exceeded maximum url length: '
'VRT Search API url has a length of {length} characters.', length=url_length)
return fail
return None
if exc.code == 413 and url_length > 8192:
ok_dialog(heading='HTTP Error 413', message=localize(30967))
log_error('HTTP Error 413: Exceeded maximum url length: '
'VRT Search API url has a length of {length} characters.', length=url_length)
return fail
return None
if exc.code == 431:
ok_dialog(heading='HTTP Error 431', message=localize(30967))
log_error('HTTP Error 431: Request header fields too large: '
'VRT Search API url has a length of {length} characters.', length=url_length)
return fail
json_data = get_json_data(exc, fail=fail)
if json_data is None:
raise
else:
if cache:
from json import dumps
update_cache(cache, dumps(json_data))
return json_data
return None
if exc.code == 401:
ok_dialog(heading='HTTP Error {code}'.format(code=exc.code), message='{}\n{}'.format(url, exc.reason))
log_error('HTTP Error {code}: {reason}', code=exc.code, reason=exc.reason)
return None
if exc.code in (400, 403) and exc.headers.get('Content-Type') and 'application/json' in exc.headers.get('Content-Type'):
return exc
reason = exc.reason
code = exc.code
ok_dialog(heading='HTTP Error {code}'.format(code=code), message='{}\n{}'.format(url, reason))
log_error('HTTP Error {code}: {reason}', code=code, reason=reason)
return None
except URLError as exc:
ok_dialog(heading=localize(30968), message=localize(30969))
log_error('URLError: {error}\nurl: {url}', error=exc.reason, url=url)
return None
except (timeout, SSLError) as exc:
ok_dialog(heading=localize(30968), message=localize(30969))
log_error('{error}\nurl: {url}', error=exc.reason, url=url)
return None


def get_json_data(response, fail=None):
"""Return json object from HTTP response"""
from json import load, loads
try:
if (3, 0, 0) <= version_info < (3, 6, 0): # the JSON object must be str, not 'bytes'
return loads(to_unicode(response.read()))
return load(response)
except TypeError as exc: # 'NoneType' object is not callable
log_error('JSON TypeError: {exc}', exc=exc)
return fail
except ValueError as exc: # No JSON object could be decoded
log_error('JSON ValueError: {exc}', exc=exc)
return fail


def get_url_json(url, cache=None, headers=None, data=None, fail=None, raise_errors=None):
"""Return HTTP data"""
response = open_url(url, headers=headers, data=data, raise_errors=raise_errors)
if response:
json_data = get_json_data(response, fail=fail)
if json_data:
if cache:
from json import dumps
update_cache(cache, dumps(json_data))
return json_data
return fail


def delete_cache(cache_file, cache_dir=DEFAULT_CACHE_DIR):
Expand Down
15 changes: 6 additions & 9 deletions resources/lib/resumepoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@

try: # Python 3
from urllib.error import HTTPError
from urllib.request import build_opener, install_opener, ProxyHandler, Request, urlopen
except ImportError: # Python 2
from urllib2 import build_opener, HTTPError, install_opener, ProxyHandler, Request, urlopen
from urllib2 import HTTPError

from data import SECONDS_MARGIN
from kodiutils import (container_refresh, get_cache, get_proxies, get_setting_bool, get_url_json, has_credentials,
input_down, invalidate_caches, localize, log, log_error, notification, update_cache)
from kodiutils import (container_refresh, get_cache, get_setting_bool, get_url_json, has_credentials, input_down,
invalidate_caches, localize, log, log_error, notification, open_url, update_cache)


class ResumePoints:
Expand All @@ -22,12 +21,11 @@ class ResumePoints:
def __init__(self):
"""Initialize resumepoints, relies on XBMC vfs and a special VRT token"""
self._data = dict() # Our internal representation
install_opener(build_opener(ProxyHandler(get_proxies())))

@staticmethod
def is_activated():
"""Is resumepoints activated in the menu and do we have credentials ?"""
return get_setting_bool('useresumepoints', default=True) and has_credentials()
return get_setting_bool('usefavorites', default=True) and get_setting_bool('useresumepoints', default=True) and has_credentials()

@staticmethod
def resumepoint_headers(url=None):
Expand Down Expand Up @@ -179,10 +177,9 @@ def delete_local(self, asset_id, menu_caches=None):

def delete_online(self, asset_id):
"""Delete resumepoint online"""
req = Request('https://video-user-data.vrt.be/resume_points/{asset_id}'.format(asset_id=asset_id), headers=self.resumepoint_headers())
req.get_method = lambda: 'DELETE'
try:
result = urlopen(req)
result = open_url('https://video-user-data.vrt.be/resume_points/{asset_id}'.format(asset_id=asset_id),
headers=self.resumepoint_headers(), method='DELETE', raise_errors='all')
log(3, "[Resumepoints] '{asset_id}' online deleted: {code}", asset_id=asset_id, code=result.getcode())
except HTTPError as exc:
log_error("Failed to remove '{asset_id}' from resumepoints: {error}", asset_id=asset_id, error=exc)
Expand Down
Loading