Handle authentication errors better
This commit is contained in:
parent
57cf842ca7
commit
5a19cf02a9
@ -96,6 +96,10 @@ msgctxt "#30701"
|
||||
msgid "To watch a video, you need to enter your credentials. Do you want to enter them now?"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#30702"
|
||||
msgid "An error occurred while authenticating: {error}."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#30709"
|
||||
msgid "Geo-blocked video"
|
||||
msgstr ""
|
||||
|
@ -97,6 +97,10 @@ msgctxt "#30701"
|
||||
msgid "To watch a video, you need to enter your credentials. Do you want to enter them now?"
|
||||
msgstr "Om een video te bekijken moet je je inloggegevens ingeven. Wil je dit nu doen?"
|
||||
|
||||
msgctxt "#30702"
|
||||
msgid "An error occurred while authenticating: {error}."
|
||||
msgstr "Er is een fout opgetreden tijdens het aanmelden: {error}."
|
||||
|
||||
msgctxt "#30709"
|
||||
msgid "Geo-blocked video"
|
||||
msgstr "Video is geografisch geblokkeerd"
|
||||
|
@ -9,7 +9,6 @@ from resources.lib import kodiutils
|
||||
from resources.lib.kodiutils import TitleItem
|
||||
from resources.lib.modules.menu import Menu
|
||||
from resources.lib.viervijfzes import CHANNELS
|
||||
from resources.lib.viervijfzes.auth import AuthApi
|
||||
from resources.lib.viervijfzes.content import ContentApi, UnavailableException
|
||||
|
||||
_LOGGER = logging.getLogger('catalog')
|
||||
@ -20,8 +19,7 @@ class 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._api = ContentApi()
|
||||
self._menu = Menu()
|
||||
|
||||
def show_catalog(self):
|
||||
|
@ -7,6 +7,7 @@ import logging
|
||||
|
||||
from resources.lib import kodiutils
|
||||
from resources.lib.viervijfzes.auth import AuthApi
|
||||
from resources.lib.viervijfzes.auth_awsidp import InvalidLoginException, AuthenticationException
|
||||
from resources.lib.viervijfzes.content import ContentApi, UnavailableException, GeoblockedException
|
||||
|
||||
_LOGGER = logging.getLogger('player')
|
||||
@ -38,15 +39,22 @@ class Player:
|
||||
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?
|
||||
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
|
||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
token = auth.get_token()
|
||||
try:
|
||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
token = auth.get_token()
|
||||
except (InvalidLoginException, AuthenticationException) as ex:
|
||||
_LOGGER.error(ex)
|
||||
kodiutils.ok_dialog(message=kodiutils.localize(30702, error=ex.message))
|
||||
kodiutils.end_of_directory()
|
||||
return
|
||||
|
||||
# Get stream information
|
||||
resolved_stream = ContentApi(token).get_stream(channel, item)
|
||||
|
@ -8,7 +8,7 @@ import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from resources.lib.viervijfzes.auth_awsidp import AwsIdp
|
||||
from resources.lib.viervijfzes.auth_awsidp import AwsIdp, InvalidLoginException, AuthenticationException
|
||||
|
||||
_LOGGER = logging.getLogger('auth-api')
|
||||
|
||||
@ -25,15 +25,15 @@ class AuthApi:
|
||||
""" Initialise object """
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._cache = cache_dir
|
||||
self._cache_dir = cache_dir
|
||||
self._id_token = None
|
||||
self._expiry = 0
|
||||
self._refresh_token = None
|
||||
|
||||
if self._cache:
|
||||
if self._cache_dir:
|
||||
# Load tokens from cache
|
||||
try:
|
||||
with open(self._cache + self.TOKEN_FILE, 'rb') as f:
|
||||
with open(self._cache_dir + self.TOKEN_FILE, 'rb') as f:
|
||||
data_json = json.loads(f.read())
|
||||
self._id_token = data_json.get('id_token')
|
||||
self._refresh_token = data_json.get('refresh_token')
|
||||
@ -53,25 +53,33 @@ class AuthApi:
|
||||
if self._refresh_token:
|
||||
# We have a valid refresh token, use that to refresh our id token
|
||||
# The refresh token is valid for 30 days. If this refresh fails, we just continue by logging in again.
|
||||
self._id_token = self._refresh(self._refresh_token)
|
||||
if self._id_token:
|
||||
_LOGGER.debug('Getting an id token by refreshing')
|
||||
try:
|
||||
self._id_token = self._refresh(self._refresh_token)
|
||||
self._expiry = now + 3600
|
||||
_LOGGER.debug('Got an id token by refreshing: %s', self._id_token)
|
||||
except (InvalidLoginException, AuthenticationException) as e:
|
||||
_LOGGER.error('Error logging in: %s', e.message)
|
||||
self._id_token = None
|
||||
self._refresh_token = None
|
||||
self._expiry = 0
|
||||
# We continue by logging in with username and password
|
||||
|
||||
if not self._id_token:
|
||||
# We have no tokens, or they are all invalid, do a login
|
||||
_LOGGER.debug('Getting an id token by logging in')
|
||||
id_token, refresh_token = self._authenticate(self._username, self._password)
|
||||
self._id_token = id_token
|
||||
self._refresh_token = refresh_token
|
||||
self._expiry = now + 3600
|
||||
_LOGGER.debug('Got an id token by logging in: %s', self._id_token)
|
||||
|
||||
if self._cache:
|
||||
if not os.path.isdir(self._cache):
|
||||
os.mkdir(self._cache)
|
||||
if self._cache_dir:
|
||||
if not os.path.isdir(self._cache_dir):
|
||||
os.mkdir(self._cache_dir)
|
||||
|
||||
# Store new tokens in cache
|
||||
with open(self._cache + self.TOKEN_FILE, 'wb') as f:
|
||||
with open(self._cache_dir + self.TOKEN_FILE, 'wb') as f:
|
||||
data = json.dumps(dict(
|
||||
id_token=self._id_token,
|
||||
refresh_token=self._refresh_token,
|
||||
@ -83,11 +91,11 @@ class AuthApi:
|
||||
|
||||
def clear_cache(self):
|
||||
""" Remove the cached tokens. """
|
||||
if not self._cache:
|
||||
if not self._cache_dir:
|
||||
return
|
||||
|
||||
# Remove cache
|
||||
os.remove(self._cache + self.TOKEN_FILE)
|
||||
os.remove(self._cache_dir + self.TOKEN_FILE)
|
||||
|
||||
# Clear tokens in memory
|
||||
self._id_token = None
|
||||
|
@ -20,6 +20,16 @@ import six
|
||||
_LOGGER = logging.getLogger('auth-awsidp')
|
||||
|
||||
|
||||
class InvalidLoginException(Exception):
|
||||
""" The login credentials are invalid """
|
||||
pass
|
||||
|
||||
|
||||
class AuthenticationException(Exception):
|
||||
""" Something went wrong while logging in """
|
||||
pass
|
||||
|
||||
|
||||
class AwsIdp:
|
||||
""" AWS Identity Provider """
|
||||
|
||||
@ -86,9 +96,7 @@ class AwsIdp:
|
||||
|
||||
challenge_name = auth_response_json.get("ChallengeName")
|
||||
if not challenge_name == "PASSWORD_VERIFIER":
|
||||
message = auth_response_json.get("message")
|
||||
_LOGGER.error("Cannot start authentication challenge: %s", message or None)
|
||||
return None
|
||||
raise AuthenticationException(auth_response_json.get("message"))
|
||||
|
||||
# Step 2: Respond to the Challenge with a valid ChallengeResponse
|
||||
challenge_request = self.__get_challenge_response_request(challenge_parameters, password)
|
||||
@ -102,8 +110,7 @@ class AwsIdp:
|
||||
_LOGGER.debug("Got response: %s", auth_response_json)
|
||||
|
||||
if "message" in auth_response_json:
|
||||
_LOGGER.error("Error logging in: %s", auth_response_json.get("message"))
|
||||
return None, None
|
||||
raise InvalidLoginException(auth_response_json.get("message"))
|
||||
|
||||
id_token = auth_response_json.get("AuthenticationResult", {}).get("IdToken")
|
||||
refresh_token = auth_response_json.get("AuthenticationResult", {}).get("RefreshToken")
|
||||
@ -134,8 +141,7 @@ class AwsIdp:
|
||||
refresh_json = json.loads(refresh_response.content)
|
||||
|
||||
if "message" in refresh_json:
|
||||
_LOGGER.error("Error refreshing: %s", refresh_json.get("message"))
|
||||
return None
|
||||
raise AuthenticationException(refresh_json.get("message"))
|
||||
|
||||
id_token = refresh_json.get("AuthenticationResult", {}).get("IdToken")
|
||||
return id_token
|
||||
|
@ -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
|
||||
|
||||
|
@ -18,19 +18,16 @@ _LOGGER = logging.getLogger('test-api')
|
||||
class TestApi(unittest.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestApi, self).__init__(*args, **kwargs)
|
||||
self._api = ContentApi()
|
||||
|
||||
def test_programs(self):
|
||||
api = ContentApi()
|
||||
|
||||
for channel in ['vier', 'vijf', 'zes']:
|
||||
channels = api.get_programs(channel)
|
||||
channels = self._api.get_programs(channel)
|
||||
self.assertIsInstance(channels, list)
|
||||
|
||||
def test_episodes(self):
|
||||
api = ContentApi()
|
||||
|
||||
for channel, program in [('vier', 'auwch'), ('vijf', 'zo-man-zo-vrouw')]:
|
||||
program = api.get_program(channel, program)
|
||||
program = self._api.get_program(channel, program)
|
||||
self.assertIsInstance(program, Program)
|
||||
self.assertIsInstance(program.seasons, dict)
|
||||
# self.assertIsInstance(program.seasons[0], Season)
|
||||
@ -39,13 +36,12 @@ class TestApi(unittest.TestCase):
|
||||
_LOGGER.info('Got program: %s', program)
|
||||
|
||||
def test_get_stream(self):
|
||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
token = auth.get_token()
|
||||
|
||||
api = ContentApi(token)
|
||||
program = api.get_program('vier', 'auwch')
|
||||
program = self._api.get_program('vier', 'auwch')
|
||||
episode = program.episodes[0]
|
||||
video = api.get_stream(episode.channel, episode.uuid)
|
||||
|
||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
api_authed = ContentApi(auth.get_token())
|
||||
video = api_authed.get_stream(episode.channel, episode.uuid)
|
||||
self.assertTrue(video)
|
||||
|
||||
_LOGGER.info('Got video URL: %s', video)
|
||||
|
@ -20,47 +20,43 @@ _LOGGER = logging.getLogger('test-epg')
|
||||
class TestEpg(unittest.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestEpg, self).__init__(*args, **kwargs)
|
||||
self._auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
self._epg = EpgApi()
|
||||
|
||||
def test_vier_today(self):
|
||||
epg = EpgApi()
|
||||
programs = epg.get_epg('vier', date.today().strftime('%Y-%m-%d'))
|
||||
programs = self._epg.get_epg('vier', date.today().strftime('%Y-%m-%d'))
|
||||
self.assertIsInstance(programs, list)
|
||||
self.assertIsInstance(programs[0], EpgProgram)
|
||||
|
||||
def test_vijf_today(self):
|
||||
epg = EpgApi()
|
||||
programs = epg.get_epg('vijf', date.today().strftime('%Y-%m-%d'))
|
||||
programs = self._epg.get_epg('vijf', date.today().strftime('%Y-%m-%d'))
|
||||
self.assertIsInstance(programs, list)
|
||||
self.assertIsInstance(programs[0], EpgProgram)
|
||||
|
||||
def test_zes_today(self):
|
||||
epg = EpgApi()
|
||||
programs = epg.get_epg('zes', date.today().strftime('%Y-%m-%d'))
|
||||
programs = self._epg.get_epg('zes', date.today().strftime('%Y-%m-%d'))
|
||||
self.assertIsInstance(programs, list)
|
||||
self.assertIsInstance(programs[0], EpgProgram)
|
||||
|
||||
def test_unknown_today(self):
|
||||
epg = EpgApi()
|
||||
with self.assertRaises(Exception):
|
||||
epg.get_epg('vtm', date.today().strftime('%Y-%m-%d'))
|
||||
self._epg.get_epg('vtm', date.today().strftime('%Y-%m-%d'))
|
||||
|
||||
def test_vier_out_of_range(self):
|
||||
epg = EpgApi()
|
||||
programs = epg.get_epg('vier', '2020-01-01')
|
||||
programs = self._epg.get_epg('vier', '2020-01-01')
|
||||
self.assertEqual(programs, [])
|
||||
|
||||
def test_play_video_from_epg(self):
|
||||
epg = EpgApi()
|
||||
epg_programs = epg.get_epg('vier', date.today().strftime('%Y-%m-%d'))
|
||||
epg_programs = self._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())
|
||||
api = ContentApi()
|
||||
episode = api.get_episode(epg_program.channel, epg_program.video_url)
|
||||
self.assertIsInstance(episode, Episode)
|
||||
|
||||
# Get stream based on the Episode's UUID
|
||||
auth = AuthApi(kodiutils.get_setting('username'), kodiutils.get_setting('password'), kodiutils.get_tokens_path())
|
||||
api = ContentApi(auth.get_token())
|
||||
video = api.get_stream(episode.channel, episode.uuid)
|
||||
self.assertTrue(video)
|
||||
|
||||
|
@ -17,21 +17,19 @@ _LOGGER = logging.getLogger('test-search')
|
||||
class TestSearch(unittest.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestSearch, self).__init__(*args, **kwargs)
|
||||
self._search = SearchApi()
|
||||
|
||||
def test_search(self):
|
||||
search = SearchApi()
|
||||
programs = search.search('de mol')
|
||||
programs = self._search.search('de mol')
|
||||
self.assertIsInstance(programs, list)
|
||||
self.assertIsInstance(programs[0], Program)
|
||||
|
||||
def test_search_empty(self):
|
||||
search = SearchApi()
|
||||
programs = search.search('')
|
||||
programs = self._search.search('')
|
||||
self.assertIsInstance(programs, list)
|
||||
|
||||
def test_search_space(self):
|
||||
search = SearchApi()
|
||||
programs = search.search(' ')
|
||||
programs = self._search.search(' ')
|
||||
self.assertIsInstance(programs, list)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user