mirror of
https://github.com/anxdpanic/plugin.video.youtube.git
synced 2025-12-15 15:10:34 -08:00
add developer login
This commit is contained in:
parent
a7ad573eba
commit
ee9527dcad
7 changed files with 467 additions and 92 deletions
155
resources/lib/youtube_authentication.py
Normal file
155
resources/lib/youtube_authentication.py
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
from youtube_plugin.youtube.provider import Provider
|
||||
from youtube_plugin.kodion.impl import Context
|
||||
from youtube_plugin.youtube.helper import yt_login
|
||||
|
||||
from youtube_plugin.youtube.youtube_exceptions import LoginException # NOQA
|
||||
|
||||
|
||||
SIGN_IN = 'in'
|
||||
SIGN_OUT = 'out'
|
||||
|
||||
|
||||
def __add_new_developer(addon_id):
|
||||
"""
|
||||
|
||||
:param addon_id: id of the add-on being added
|
||||
:return:
|
||||
"""
|
||||
params = {'addon_id': addon_id}
|
||||
context = Context(params=params, plugin_id='plugin.video.youtube')
|
||||
|
||||
access_manager = context.get_access_manager()
|
||||
developers = access_manager.get_developers()
|
||||
if not developers.get(addon_id, None):
|
||||
developers[addon_id] = access_manager.get_new_developer()
|
||||
access_manager.set_developers(developers)
|
||||
context.log_debug('Creating developer user: |%s|' % addon_id)
|
||||
|
||||
|
||||
def __auth(addon_id, mode=SIGN_IN):
|
||||
"""
|
||||
|
||||
:param addon_id: id of the add-on being signed in
|
||||
:param mode: SIGN_IN or SIGN_OUT
|
||||
:return: addon provider, context and client
|
||||
"""
|
||||
if not addon_id or addon_id == 'plugin.video.youtube':
|
||||
context = Context(plugin_id='plugin.video.youtube')
|
||||
context.log_error('Developer authentication: |%s| Invalid addon_id' % addon_id)
|
||||
return
|
||||
__add_new_developer(addon_id)
|
||||
params = {'addon_id': addon_id}
|
||||
provider = Provider()
|
||||
context = Context(params=params, plugin_id='plugin.video.youtube')
|
||||
|
||||
client = provider.get_client(context=context) # NOQA
|
||||
logged_in = provider.is_logged_in()
|
||||
if mode == SIGN_IN:
|
||||
if logged_in:
|
||||
return True
|
||||
else:
|
||||
provider.reset_client()
|
||||
yt_login.process(mode, provider, context, re_match=None, sign_out_refresh=False)
|
||||
elif mode == SIGN_OUT:
|
||||
if not logged_in:
|
||||
return True
|
||||
else:
|
||||
provider.reset_client()
|
||||
try:
|
||||
yt_login.process(mode, provider, context, re_match=None, sign_out_refresh=False)
|
||||
except:
|
||||
reset_access_tokens(addon_id)
|
||||
else:
|
||||
raise Exception('Unknown mode: |%s|' % mode)
|
||||
|
||||
client = provider.get_client(context=context) # NOQA
|
||||
if mode == SIGN_IN:
|
||||
return provider.is_logged_in()
|
||||
else:
|
||||
return not provider.is_logged_in()
|
||||
|
||||
|
||||
def sign_in(addon_id):
|
||||
"""
|
||||
To use the signed in context, see youtube_registration.py and youtube_requests.py
|
||||
Usage:
|
||||
|
||||
addon.xml
|
||||
---
|
||||
<import addon="plugin.video.youtube" version="6.1.0"/>
|
||||
---
|
||||
|
||||
.py
|
||||
---
|
||||
import youtube_registration
|
||||
import youtube_authentication
|
||||
|
||||
youtube_registration.register_api_keys(addon_id='plugin.video.example',
|
||||
api_key='A1zaSyA0b5sTjgxzTzYLmVtradlFVBfSHNOJKS0',
|
||||
client_id='825419953561-ert5tccq1r0upsuqdf5nm3le39czk23a.apps.googleusercontent.com',
|
||||
client_secret='Y5cE1IKzJQe1NZ0OsOoEqpu3')
|
||||
|
||||
try:
|
||||
signed_in = youtube_authentication.sign_in(addon_id='plugin.video.example') # refreshes access tokens if already signed in
|
||||
except youtube_authentication.LoginException as e:
|
||||
error_message = e.get_message()
|
||||
# handle error
|
||||
signed_in = False
|
||||
|
||||
if signed_in:
|
||||
pass # see youtube_registration.py and youtube_requests.py to use the signed in context
|
||||
---
|
||||
|
||||
:param addon_id: id of the add-on being signed in
|
||||
:return: boolean, True when signed in
|
||||
"""
|
||||
|
||||
return __auth(addon_id, mode=SIGN_IN)
|
||||
|
||||
|
||||
def sign_out(addon_id):
|
||||
"""
|
||||
Usage:
|
||||
|
||||
addon.xml
|
||||
---
|
||||
<import addon="plugin.video.youtube" version="6.1.0"/>
|
||||
---
|
||||
|
||||
.py
|
||||
---
|
||||
import youtube_registration
|
||||
import youtube_authentication
|
||||
|
||||
youtube_registration.register_api_keys(addon_id='plugin.video.example',
|
||||
api_key='A1zaSyA0b5sTjgxzTzYLmVtradlFVBfSHNOJKS0',
|
||||
client_id='825419953561-ert5tccq1r0upsuqdf5nm3le39czk23a.apps.googleusercontent.com',
|
||||
client_secret='Y5cE1IKzJQe1NZ0OsOoEqpu3')
|
||||
|
||||
signed_out = youtube_authentication.sign_out(addon_id='plugin.video.example')
|
||||
if signed_out:
|
||||
pass
|
||||
---
|
||||
|
||||
:param addon_id: id of the add-on being signed out
|
||||
:return: boolean, True when signed out
|
||||
"""
|
||||
|
||||
return __auth(addon_id, mode=SIGN_OUT)
|
||||
|
||||
|
||||
def reset_access_tokens(addon_id):
|
||||
"""
|
||||
|
||||
:param addon_id: id of the add-on having it's access tokens reset
|
||||
:return:
|
||||
"""
|
||||
if not addon_id or addon_id == 'plugin.video.youtube':
|
||||
context = Context(plugin_id='plugin.video.youtube')
|
||||
context.log_error('Developer reset access tokens: |%s| Invalid addon_id' % addon_id)
|
||||
return
|
||||
params = {'addon_id': addon_id}
|
||||
context = Context(params=params, plugin_id='plugin.video.youtube')
|
||||
|
||||
access_manager = context.get_access_manager()
|
||||
access_manager.update_dev_access_token(addon_id, access_token='', refresh_token='')
|
||||
|
|
@ -37,6 +37,8 @@ class LoginTokenStore(JSONStore):
|
|||
data['access_manager']['current_user'] = '0'
|
||||
if 'last_origin' not in data['access_manager']:
|
||||
data['access_manager']['last_origin'] = 'plugin.video.youtube'
|
||||
if 'developers' not in data['access_manager']:
|
||||
data['access_manager']['developers'] = dict()
|
||||
|
||||
# clean up
|
||||
if data['access_manager']['current_user'] == 'default':
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import uuid
|
||||
import time
|
||||
|
||||
from hashlib import md5
|
||||
|
||||
from ..json_store import LoginTokenStore
|
||||
|
||||
__author__ = 'bromix'
|
||||
|
|
@ -22,9 +24,10 @@ class AccessManager(object):
|
|||
self._json = self._jstore.get_data()
|
||||
return self._json['access_manager']['users'][self.get_user()]['id']
|
||||
|
||||
def get_new_user(self, user_name):
|
||||
def get_new_user(self, user_name='', addon_id=''):
|
||||
"""
|
||||
:param user_name: string, users name
|
||||
:param addon_id: string, addon id
|
||||
:return: a new user dict
|
||||
"""
|
||||
uuids = list()
|
||||
|
|
@ -196,6 +199,8 @@ class AccessManager(object):
|
|||
"""
|
||||
Updates the old access token with the new one.
|
||||
:param access_token:
|
||||
:param unix_timestamp:
|
||||
:param refresh_token:
|
||||
:return:
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
|
|
@ -208,3 +213,127 @@ class AccessManager(object):
|
|||
self._json['access_manager']['users'][self._user]['refresh_token'] = refresh_token
|
||||
|
||||
self._jstore.save(self._json)
|
||||
|
||||
def get_new_developer(self, addon_id):
|
||||
"""
|
||||
:param addon_id: string, addon id
|
||||
:return: a new developer dict
|
||||
"""
|
||||
|
||||
return {'access_token': '', 'refresh_token': '', 'token_expires': -1, 'last_key_hash': ''}
|
||||
|
||||
def get_developers(self):
|
||||
"""
|
||||
Returns developers
|
||||
:return: dict, developers
|
||||
"""
|
||||
return self._json['access_manager'].get('developers', {})
|
||||
|
||||
def set_developers(self, developers):
|
||||
"""
|
||||
Updates the users
|
||||
:param developers: dict, developers
|
||||
:return:
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
self._json['access_manager']['developers'] = developers
|
||||
self._jstore.save(self._json)
|
||||
|
||||
def get_dev_access_token(self, addon_id):
|
||||
"""
|
||||
Returns the access token for some API
|
||||
:param addon_id: addon id
|
||||
:return: access_token
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
return self._json['access_manager']['developers'].get(addon_id, {}).get('access_token', '')
|
||||
|
||||
def get_dev_refresh_token(self, addon_id):
|
||||
"""
|
||||
Returns the refresh token
|
||||
:return: refresh token
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
return self._json['access_manager']['developers'].get(addon_id, {}).get('refresh_token', '')
|
||||
|
||||
def developer_has_refresh_token(self, addon_id):
|
||||
return self.get_dev_refresh_token(addon_id) != ''
|
||||
|
||||
def is_dev_access_token_expired(self, addon_id):
|
||||
"""
|
||||
Returns True if the access_token is expired otherwise False.
|
||||
If no expiration date was provided and an access_token exists
|
||||
this method will always return True
|
||||
:return:
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
access_token = self._json['access_manager']['developers'].get(addon_id, {}).get('access_token', '')
|
||||
expires = int(self._json['access_manager']['developers'].get(addon_id, {}).get('token_expires', -1))
|
||||
|
||||
# with no access_token it must be expired
|
||||
if not access_token:
|
||||
return True
|
||||
|
||||
# in this case no expiration date was set
|
||||
if expires == -1:
|
||||
return False
|
||||
|
||||
now = int(time.time())
|
||||
return expires <= now
|
||||
|
||||
def update_dev_access_token(self, addon_id, access_token, unix_timestamp=None, refresh_token=None):
|
||||
"""
|
||||
Updates the old access token with the new one.
|
||||
:param addon_id:
|
||||
:param access_token:
|
||||
:param unix_timestamp:
|
||||
:param refresh_token:
|
||||
:return:
|
||||
"""
|
||||
self._json = self._jstore.get_data()
|
||||
self._json['access_manager']['developers'][addon_id]['access_token'] = access_token
|
||||
|
||||
if unix_timestamp is not None:
|
||||
self._json['access_manager']['developers'][addon_id]['token_expires'] = int(unix_timestamp)
|
||||
|
||||
if refresh_token is not None:
|
||||
self._json['access_manager']['developers'][addon_id]['refresh_token'] = refresh_token
|
||||
|
||||
self._jstore.save(self._json)
|
||||
|
||||
def get_dev_last_key_hash(self, addon_id):
|
||||
self._json = self._jstore.get_data()
|
||||
return self._json['access_manager']['developers'][addon_id]['last_key_hash']
|
||||
|
||||
def set_dev_last_key_hash(self, addon_id, key_hash):
|
||||
self._json = self._jstore.get_data()
|
||||
self._json['access_manager']['developers'][addon_id]['last_key_hash'] = key_hash
|
||||
self._jstore.save(self._json)
|
||||
|
||||
def dev_keys_changed(self, addon_id, api_key, client_id, client_secret):
|
||||
self._json = self._jstore.get_data()
|
||||
last_hash = self._json['access_manager']['developers'][addon_id]['last_key_hash']
|
||||
current_hash = self.__calc_key_hash(api_key, client_id, client_secret)
|
||||
if not last_hash and current_hash:
|
||||
self.set_dev_last_key_hash(addon_id, current_hash)
|
||||
return False
|
||||
if last_hash != current_hash:
|
||||
self.set_dev_last_key_hash(addon_id, current_hash)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def __calc_key_hash(api_key, client_id, client_secret):
|
||||
|
||||
m = md5()
|
||||
try:
|
||||
m.update(api_key.encode('utf-8'))
|
||||
m.update(client_id.encode('utf-8'))
|
||||
m.update(client_secret.encode('utf-8'))
|
||||
except:
|
||||
m.update(api_key)
|
||||
m.update(client_id)
|
||||
m.update(client_secret)
|
||||
|
||||
return m.hexdigest()
|
||||
|
|
|
|||
|
|
@ -9,21 +9,36 @@ from ...youtube.youtube_exceptions import LoginException
|
|||
|
||||
|
||||
def process(mode, provider, context, re_match, sign_out_refresh=True):
|
||||
addon_id = context.get_param('addon_id', None)
|
||||
|
||||
def _do_logout():
|
||||
# we clear the cache, so none cached data of an old account will be displayed.
|
||||
provider.get_resource_manager(context).clear()
|
||||
|
||||
signout_access_manager = context.get_access_manager()
|
||||
if signout_access_manager.has_refresh_token():
|
||||
refresh_tokens = signout_access_manager.get_refresh_token().split('|')
|
||||
refresh_tokens = list(set(refresh_tokens))
|
||||
for _refresh_token in refresh_tokens:
|
||||
provider.get_client(context).revoke(_refresh_token)
|
||||
if addon_id:
|
||||
if signout_access_manager.developer_has_refresh_token(addon_id):
|
||||
refresh_tokens = signout_access_manager.get_dev_refresh_token(addon_id).split('|')
|
||||
refresh_tokens = list(set(refresh_tokens))
|
||||
for _refresh_token in refresh_tokens:
|
||||
provider.get_client(context).revoke(_refresh_token)
|
||||
else:
|
||||
if signout_access_manager.has_refresh_token():
|
||||
refresh_tokens = signout_access_manager.get_refresh_token().split('|')
|
||||
refresh_tokens = list(set(refresh_tokens))
|
||||
for _refresh_token in refresh_tokens:
|
||||
provider.get_client(context).revoke(_refresh_token)
|
||||
|
||||
provider.reset_client()
|
||||
signout_access_manager.update_access_token(access_token='', refresh_token='')
|
||||
|
||||
if addon_id:
|
||||
signout_access_manager.update_dev_access_token(addon_id, access_token='', refresh_token='')
|
||||
else:
|
||||
signout_access_manager.update_access_token(access_token='', refresh_token='')
|
||||
|
||||
def _do_login(_for_tv=False):
|
||||
_client = provider.get_client(context)
|
||||
|
||||
try:
|
||||
if _for_tv:
|
||||
json_data = _client.request_device_and_user_code_tv()
|
||||
|
|
@ -32,6 +47,7 @@ def process(mode, provider, context, re_match, sign_out_refresh=True):
|
|||
except LoginException:
|
||||
_do_logout()
|
||||
raise
|
||||
|
||||
interval = int(json_data.get('interval', 5)) * 1000
|
||||
if interval > 60000:
|
||||
interval = 5000
|
||||
|
|
@ -89,6 +105,7 @@ def process(mode, provider, context, re_match, sign_out_refresh=True):
|
|||
_do_logout()
|
||||
if sign_out_refresh:
|
||||
context.get_ui().refresh_container()
|
||||
|
||||
elif mode == 'in':
|
||||
context.get_ui().on_ok(context.localize(provider.LOCAL_MAP['youtube.sign.twice.title']),
|
||||
context.localize(provider.LOCAL_MAP['youtube.sign.twice.text']))
|
||||
|
|
@ -98,7 +115,10 @@ def process(mode, provider, context, re_match, sign_out_refresh=True):
|
|||
context.log_debug('YouTube-TV Login: Access Token |%s| Refresh Token |%s| Expires |%s|' % (access_token_tv != '', refresh_token_tv != '', expires_in_tv))
|
||||
if not access_token_tv and not refresh_token_tv:
|
||||
provider.reset_client()
|
||||
context.get_access_manager().update_access_token('')
|
||||
if addon_id:
|
||||
context.get_access_manager().update_dev_access_token(addon_id, '')
|
||||
else:
|
||||
context.get_access_manager().update_access_token('')
|
||||
context.get_ui().refresh_container()
|
||||
return
|
||||
|
||||
|
|
@ -107,7 +127,10 @@ def process(mode, provider, context, re_match, sign_out_refresh=True):
|
|||
context.log_debug('YouTube-Kodi Login: Access Token |%s| Refresh Token |%s| Expires |%s|' % (access_token_kodi != '', refresh_token_kodi != '', expires_in_kodi))
|
||||
if not access_token_kodi and not refresh_token_kodi:
|
||||
provider.reset_client()
|
||||
context.get_access_manager().update_access_token('')
|
||||
if addon_id:
|
||||
context.get_access_manager().update_dev_access_token(addon_id, '')
|
||||
else:
|
||||
context.get_access_manager().update_access_token('')
|
||||
context.get_ui().refresh_container()
|
||||
return
|
||||
|
||||
|
|
@ -119,5 +142,10 @@ def process(mode, provider, context, re_match, sign_out_refresh=True):
|
|||
provider.get_resource_manager(context).clear()
|
||||
|
||||
provider.reset_client()
|
||||
context.get_access_manager().update_access_token(access_token, expires_in, refresh_token)
|
||||
|
||||
if addon_id:
|
||||
context.get_access_manager().update_dev_access_token(addon_id, access_token, expires_in, refresh_token)
|
||||
else:
|
||||
context.get_access_manager().update_access_token(access_token, expires_in, refresh_token)
|
||||
|
||||
context.get_ui().refresh_container()
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class Provider(kodion.AbstractProvider):
|
|||
_dev_config = context.get_ui().get_home_window_property('configs')
|
||||
context.get_ui().clear_home_window_property('configs')
|
||||
|
||||
dev_config = None
|
||||
dev_config = dict()
|
||||
if _dev_config is not None:
|
||||
context.log_debug('Using window property for developer keys is deprecated, instead use the youtube_registration module.')
|
||||
try:
|
||||
|
|
@ -172,7 +172,7 @@ class Provider(kodion.AbstractProvider):
|
|||
if not dev_config and addon_id and dev_configs:
|
||||
dev_config = dev_configs.get(addon_id)
|
||||
|
||||
if dev_config is not None and not context.get_settings().allow_dev_keys():
|
||||
if dev_config and not context.get_settings().allow_dev_keys():
|
||||
context.log_debug('Developer config ignored')
|
||||
return None
|
||||
elif dev_config:
|
||||
|
|
@ -181,7 +181,7 @@ class Provider(kodion.AbstractProvider):
|
|||
or not dev_config['main'].get('id') or not dev_config['main'].get('secret'):
|
||||
context.log_error('Error loading developer config: |invalid structure| '
|
||||
'expected: |{"origin": ADDON_ID, "main": {"system": SYSTEM_NAME, "key": API_KEY, "id": CLIENT_ID, "secret": CLIENT_SECRET}}|')
|
||||
return None
|
||||
return dict()
|
||||
else:
|
||||
dev_origin = dev_config['origin']
|
||||
dev_main = dev_config['main']
|
||||
|
|
@ -197,7 +197,7 @@ class Provider(kodion.AbstractProvider):
|
|||
context.log_debug('Using developer config: origin: |{0}| system |{1}|'.format(dev_origin, dev_system))
|
||||
return {'origin': dev_origin, 'main': {'id': dev_id, 'secret': dev_secret, 'key': dev_key, 'system': dev_system}}
|
||||
else:
|
||||
return None
|
||||
return dict()
|
||||
|
||||
def reset_client(self):
|
||||
self._client = None
|
||||
|
|
@ -221,88 +221,136 @@ class Provider(kodion.AbstractProvider):
|
|||
dev_id = context.get_param('addon_id', None)
|
||||
dev_configs = YouTube.CONFIGS.get('developer')
|
||||
dev_config = self.get_dev_config(context, dev_id, dev_configs)
|
||||
dev_keys = dict()
|
||||
if dev_config:
|
||||
dev_keys = dev_config.get('main')
|
||||
if api_last_origin != dev_config.get('origin'):
|
||||
context.log_debug('API key origin changed, clearing cache. |%s|' % dev_config.get('origin'))
|
||||
|
||||
client = None
|
||||
refresh_tokens = list()
|
||||
|
||||
if dev_id:
|
||||
dev_origin = dev_config.get('origin') if dev_config.get('origin') else dev_id
|
||||
if api_last_origin != dev_origin:
|
||||
context.log_debug('API key origin changed, clearing cache. |%s|' % dev_origin)
|
||||
access_manager.set_last_origin(dev_origin)
|
||||
self.get_resource_manager(context).clear()
|
||||
access_manager.set_last_origin(dev_config.get('origin'))
|
||||
self._client = YouTube(items_per_page=items_per_page, language=language, region=region, config=dev_keys)
|
||||
self._client.set_log_error(context.log_error)
|
||||
else:
|
||||
if api_last_origin != 'plugin.video.youtube':
|
||||
context.log_debug('API key origin changed, clearing cache. |plugin.video.youtube|')
|
||||
self.get_resource_manager(context).clear()
|
||||
access_manager.set_last_origin('plugin.video.youtube')
|
||||
self.get_resource_manager(context).clear()
|
||||
|
||||
if dev_id:
|
||||
access_tokens = access_manager.get_dev_access_token(dev_id).split('|')
|
||||
if len(access_tokens) != 2 or access_manager.is_dev_access_token_expired(dev_id):
|
||||
# reset access_token
|
||||
access_manager.update_dev_access_token(dev_id, '')
|
||||
access_tokens = list()
|
||||
else:
|
||||
access_tokens = access_manager.get_access_token().split('|')
|
||||
if len(access_tokens) != 2 or access_manager.is_access_token_expired():
|
||||
# reset access_token
|
||||
access_manager.update_access_token('')
|
||||
# we clear the cache, so none cached data of an old account will be displayed.
|
||||
# context.get_function_cache().clear()
|
||||
# reset the client
|
||||
self._client = None
|
||||
access_tokens = list()
|
||||
|
||||
if not self._client:
|
||||
context.log_debug('Selecting YouTube config "%s"' % youtube_config['system'])
|
||||
if dev_id:
|
||||
if dev_keys:
|
||||
context.log_debug('Selecting YouTube developer config "%s"' % dev_id)
|
||||
else:
|
||||
context.log_debug('Selecting YouTube config "%s" w/ developer access tokens' % youtube_config['system'])
|
||||
|
||||
if access_manager.has_refresh_token():
|
||||
if YouTube.api_keys_changed:
|
||||
context.log_warning('API key set changed: Resetting client and updating access token')
|
||||
self.reset_client()
|
||||
access_manager.update_access_token(access_token='', refresh_token='')
|
||||
|
||||
access_tokens = access_manager.get_access_token()
|
||||
if access_tokens:
|
||||
access_tokens = access_tokens.split('|')
|
||||
|
||||
refresh_tokens = access_manager.get_refresh_token()
|
||||
if refresh_tokens:
|
||||
refresh_tokens = refresh_tokens.split('|')
|
||||
context.log_debug('Access token count: |%d| Refresh token count: |%d|' % (len(access_tokens), len(refresh_tokens)))
|
||||
# create a new access_token
|
||||
client = YouTube(language=language, region=region, items_per_page=items_per_page, config=youtube_config)
|
||||
if len(access_tokens) != 2 and len(refresh_tokens) == 2:
|
||||
try:
|
||||
|
||||
access_token_kodi, expires_in_kodi = client.refresh_token(refresh_tokens[1])
|
||||
|
||||
access_token_tv, expires_in_tv = client.refresh_token_tv(refresh_tokens[0])
|
||||
|
||||
access_tokens = [access_token_tv, access_token_kodi]
|
||||
|
||||
access_token = '%s|%s' % (access_token_tv, access_token_kodi)
|
||||
expires_in = min(expires_in_tv, expires_in_kodi)
|
||||
|
||||
access_manager.update_access_token(access_token, expires_in)
|
||||
except LoginException as ex:
|
||||
self.handle_exception(context, ex)
|
||||
access_tokens = ['', '']
|
||||
# reset access_token
|
||||
access_manager.update_access_token('')
|
||||
# we clear the cache, so none cached data of an old account will be displayed.
|
||||
self.get_resource_manager(context).clear()
|
||||
|
||||
# in debug log the login status
|
||||
self._is_logged_in = len(access_tokens) == 2
|
||||
if self._is_logged_in:
|
||||
context.log_debug('User is logged in')
|
||||
else:
|
||||
context.log_debug('User is not logged in')
|
||||
|
||||
if len(access_tokens) == 0:
|
||||
access_tokens = ['', '']
|
||||
client.set_access_token(access_token=access_tokens[1])
|
||||
client.set_access_token_tv(access_token_tv=access_tokens[0])
|
||||
self._client = client
|
||||
self._client.set_log_error(context.log_error)
|
||||
if access_manager.developer_has_refresh_token(dev_id):
|
||||
if dev_keys:
|
||||
keys_changed = access_manager.dev_keys_changed(dev_id, dev_keys['key'], dev_keys['id'], dev_keys['secret'])
|
||||
else:
|
||||
self._client = YouTube(items_per_page=items_per_page, language=language, region=region, config=youtube_config)
|
||||
self._client.set_log_error(context.log_error)
|
||||
keys_changed = access_manager.dev_keys_changed(dev_id, youtube_config['key'], youtube_config['id'], youtube_config['secret'])
|
||||
|
||||
# in debug log the login status
|
||||
context.log_debug('User is not logged in')
|
||||
if keys_changed:
|
||||
context.log_warning('API key set changed: Resetting client and updating access token')
|
||||
self.reset_client()
|
||||
access_manager.update_dev_access_token(dev_id, access_token='', refresh_token='')
|
||||
|
||||
access_tokens = access_manager.get_dev_access_token(dev_id)
|
||||
if access_tokens:
|
||||
access_tokens = access_tokens.split('|')
|
||||
|
||||
refresh_tokens = access_manager.get_dev_refresh_token(dev_id)
|
||||
if refresh_tokens:
|
||||
refresh_tokens = refresh_tokens.split('|')
|
||||
context.log_debug('Access token count: |%d| Refresh token count: |%d|' % (len(access_tokens), len(refresh_tokens)))
|
||||
# create a new access_token
|
||||
|
||||
if dev_keys:
|
||||
client = YouTube(language=language, region=region, items_per_page=items_per_page, config=dev_keys)
|
||||
else:
|
||||
client = YouTube(language=language, region=region, items_per_page=items_per_page, config=youtube_config)
|
||||
|
||||
else:
|
||||
context.log_debug('Selecting YouTube config "%s"' % youtube_config['system'])
|
||||
|
||||
if access_manager.has_refresh_token():
|
||||
if YouTube.api_keys_changed:
|
||||
context.log_warning('API key set changed: Resetting client and updating access token')
|
||||
self.reset_client()
|
||||
access_manager.update_access_token(access_token='', refresh_token='')
|
||||
|
||||
access_tokens = access_manager.get_access_token()
|
||||
if access_tokens:
|
||||
access_tokens = access_tokens.split('|')
|
||||
|
||||
refresh_tokens = access_manager.get_refresh_token()
|
||||
if refresh_tokens:
|
||||
refresh_tokens = refresh_tokens.split('|')
|
||||
context.log_debug('Access token count: |%d| Refresh token count: |%d|' % (len(access_tokens), len(refresh_tokens)))
|
||||
# create a new access_token
|
||||
client = YouTube(language=language, region=region, items_per_page=items_per_page, config=youtube_config)
|
||||
|
||||
if client:
|
||||
if len(access_tokens) != 2 and len(refresh_tokens) == 2:
|
||||
try:
|
||||
|
||||
access_token_kodi, expires_in_kodi = client.refresh_token(refresh_tokens[1])
|
||||
|
||||
access_token_tv, expires_in_tv = client.refresh_token_tv(refresh_tokens[0])
|
||||
|
||||
access_tokens = [access_token_tv, access_token_kodi]
|
||||
|
||||
access_token = '%s|%s' % (access_token_tv, access_token_kodi)
|
||||
expires_in = min(expires_in_tv, expires_in_kodi)
|
||||
if dev_id:
|
||||
access_manager.update_dev_access_token(dev_id, access_token, expires_in)
|
||||
else:
|
||||
access_manager.update_access_token(access_token, expires_in)
|
||||
except LoginException as ex:
|
||||
self.handle_exception(context, ex)
|
||||
access_tokens = ['', '']
|
||||
# reset access_token
|
||||
if dev_id:
|
||||
access_manager.update_dev_access_token(dev_id, '')
|
||||
else:
|
||||
access_manager.update_access_token('')
|
||||
# we clear the cache, so none cached data of an old account will be displayed.
|
||||
self.get_resource_manager(context).clear()
|
||||
|
||||
# in debug log the login status
|
||||
self._is_logged_in = len(access_tokens) == 2
|
||||
if self._is_logged_in:
|
||||
context.log_debug('User is logged in')
|
||||
else:
|
||||
context.log_debug('User is not logged in')
|
||||
|
||||
if len(access_tokens) == 0:
|
||||
access_tokens = ['', '']
|
||||
client.set_access_token(access_token=access_tokens[1])
|
||||
client.set_access_token_tv(access_token_tv=access_tokens[0])
|
||||
self._client = client
|
||||
self._client.set_log_error(context.log_error)
|
||||
else:
|
||||
self._client = YouTube(items_per_page=items_per_page, language=language, region=region, config=youtube_config)
|
||||
self._client.set_log_error(context.log_error)
|
||||
|
||||
# in debug log the login status
|
||||
context.log_debug('User is not logged in')
|
||||
|
||||
return self._client
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ def register_api_keys(addon_id, api_key, client_id, client_secret):
|
|||
# then use your keys by appending an addon_id param to the plugin url
|
||||
xbmc.executebuiltin('RunPlugin(plugin://plugin.video.youtube/channel/UCaBf1a-dpIsw8OxqH4ki2Kg/?addon_id=plugin.video.example)')
|
||||
# addon_id will be passed to all following calls
|
||||
# also see youtube_authentication.py and youtube_requests.py
|
||||
---
|
||||
|
||||
:param addon_id: id of the add-on being registered
|
||||
|
|
@ -39,6 +40,8 @@ def register_api_keys(addon_id, api_key, client_id, client_secret):
|
|||
api_jstore = APIKeyStore()
|
||||
json_api = api_jstore.get_data()
|
||||
|
||||
access_manager = context.get_access_manager()
|
||||
|
||||
jkeys = json_api['keys']['developer'].get(addon_id, {})
|
||||
|
||||
api_keys = {'origin': addon_id, 'main': {'system': 'JSONStore', 'key': b64encode(api_key), 'id': b64encode(client_id), 'secret': b64encode(client_secret)}}
|
||||
|
|
@ -48,3 +51,9 @@ def register_api_keys(addon_id, api_key, client_id, client_secret):
|
|||
json_api['keys']['developer'][addon_id] = api_keys
|
||||
api_jstore.save(json_api)
|
||||
context.log_debug('Register API Keys: |%s| Keys registered' % addon_id)
|
||||
|
||||
developers = access_manager.get_developers()
|
||||
if not developers.get(addon_id, None):
|
||||
developers[addon_id] = access_manager.get_new_developer()
|
||||
access_manager.set_developers(developers)
|
||||
context.log_debug('Creating developer user: |%s|' % addon_id)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ from youtube_plugin.youtube.provider import Provider
|
|||
from youtube_plugin.kodion.impl import Context
|
||||
|
||||
|
||||
def get_core_components(addon_id=None):
|
||||
def __get_core_components(addon_id=None):
|
||||
"""
|
||||
:param addon_id: addon id associated with developer keys to use for requests
|
||||
:return: addon provider, context and client
|
||||
"""
|
||||
provider = Provider()
|
||||
if addon_id is not None:
|
||||
context = Context(params={'addon_id': addon_id}, plugin_id='plugin.video.youtube')
|
||||
|
|
@ -30,7 +34,7 @@ def v3_request(method='GET', headers=None, path=None, post_data=None, params=Non
|
|||
:param addon_id: addon id associated with developer keys to use for requests
|
||||
:type addon_id: str
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
return client._perform_v3_request(method=method, headers=headers, path=path, post_data=post_data, params=params, allow_redirects=allow_redirects)
|
||||
|
||||
|
||||
|
|
@ -52,7 +56,7 @@ def get_videos(video_id, addon_id=None):
|
|||
see also https://developers.google.com/youtube/v3/docs/videos#resource
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
json_data = client.get_videos(video_id)
|
||||
if not handle_error(context, json_data):
|
||||
|
|
@ -76,7 +80,7 @@ def get_activities(channel_id, page_token='', all_pages=False, addon_id=None):
|
|||
last item contains nextPageToken
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
items = []
|
||||
|
||||
|
|
@ -117,7 +121,7 @@ def get_playlist_items(playlist_id, page_token='', all_pages=False, addon_id=Non
|
|||
last item contains nextPageToken
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
items = []
|
||||
|
||||
|
|
@ -153,7 +157,7 @@ def get_channel_id(channel_name, addon_id=None):
|
|||
see also https://developers.google.com/youtube/v3/docs/channels#resource
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
json_data = client.get_channel_by_username(channel_name)
|
||||
if not handle_error(context, json_data):
|
||||
|
|
@ -173,7 +177,7 @@ def get_channels(channel_id, addon_id=None):
|
|||
see also https://developers.google.com/youtube/v3/docs/channels#resource
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
json_data = client.get_channels(channel_id)
|
||||
if not handle_error(context, json_data):
|
||||
|
|
@ -193,7 +197,7 @@ def get_channel_sections(channel_id, addon_id=None):
|
|||
see also https://developers.google.com/youtube/v3/docs/channelSections#resource
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
json_data = client.get_channel_sections(channel_id)
|
||||
if not handle_error(context, json_data):
|
||||
|
|
@ -218,7 +222,7 @@ def get_playlists_of_channel(channel_id, page_token='', all_pages=False, addon_i
|
|||
last item contains nextPageToken
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
items = []
|
||||
|
||||
|
|
@ -254,7 +258,7 @@ def get_playlists(playlist_id, addon_id=None):
|
|||
see also https://developers.google.com/youtube/v3/docs/playlists#resource
|
||||
:rtype: list of dict
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
json_data = client.get_playlists(playlist_id)
|
||||
if not handle_error(context, json_data):
|
||||
|
|
@ -278,7 +282,7 @@ def get_related_videos(video_id, page_token='', addon_id=None):
|
|||
:rtype: list of dict
|
||||
:note: this is a search api request with high cost
|
||||
"""
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
items = []
|
||||
|
||||
|
|
@ -328,7 +332,7 @@ def get_search(q, search_type='', event_type='', channel_id='', order='relevance
|
|||
:note: this is a search api request with high cost
|
||||
"""
|
||||
search_type = search_type or ['video', 'channel', 'playlist']
|
||||
provider, context, client = get_core_components(addon_id)
|
||||
provider, context, client = __get_core_components(addon_id)
|
||||
|
||||
items = []
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue