jellyfin-kodi/resources/lib/userclient.py
angelblue05 6a2ea9a4dd Emby Connect (#58)
* Update with latest connect module

* Update string

* Change error behavior

* Add connectmanager

Handle dialogs for emby connect in one place

* Add user select dialog

* Add manual server dialog

* Add onAuthenticated

* Filter virtual episodes

* Update userclient with new methods
2016-09-04 05:18:31 -05:00

295 lines
9.2 KiB
Python

# -*- coding: utf-8 -*-
##################################################################################################
import logging
import threading
import xbmc
import xbmcgui
import artwork
import connectmanager
import downloadutils
import read_embyserver as embyserver
from utils import window, settings, language as lang
##################################################################################################
log = logging.getLogger("EMBY."+__name__)
##################################################################################################
class UserClient(threading.Thread):
_shared_state = {} # Borg
_stop_thread = False
_user = None
_server = None
_auth = True
_has_access = True
def __init__(self):
self.__dict__ = self._shared_state
self.doutils = downloadutils.DownloadUtils()
self.download = self.doutils.downloadUrl
self.emby = embyserver.Read_EmbyServer()
threading.Thread.__init__(self)
@classmethod
def get_username(cls):
return settings('username') or settings('connectUsername') or None
def get_user(self, data=None):
if data is not None:
self._user = data
self._set_user_server()
return self._user
def get_server_details(self):
return self._server
@classmethod
def get_server(cls):
###$ Begin transition phase $###
if settings('server') == "":
http = "https" if settings('https') == "true" else "http"
host = settings('ipaddress')
port = settings('port')
if host and port:
settings('server', value="%s://%s:%s" % (http, host, port))
###$ End transition phase $###
return settings('server') or None
def verify_server(self):
url = "%s/emby/Users/Public?format=json" % self.get_server()
result = self.download(url, authenticate=False)
if result != "": # Specific verification, due to possibility of returning empty dict
return True
else:
# Server connection failed
return False
@classmethod
def get_ssl(cls):
"""
Returns boolean value or path to certificate
True: Verify ssl
False: Don't verify connection
"""
certificate = settings('sslcert')
if certificate != "None":
return certificate
return True if settings('sslverify') == "true" else False
def get_access(self):
if not self._has_access:
self._set_access()
return self._has_access
def _set_access(self):
try:
self.download("{server}/emby/Users?format=json")
except Warning as error:
if self._has_access and "restricted" in error:
self._has_access = False
log.info("Access is restricted")
else:
if not self._has_access:
self._has_access = True
window('emby_serverStatus', clear=True)
log.info("Access is granted")
xbmcgui.Dialog().notification(lang(29999), lang(33007))
@classmethod
def get_userid(cls):
###$ Begin transition phase $###
if settings('userId') == "":
settings('userId', value=settings('userId%s' % settings('username')))
###$ End transition phase $###
return settings('userId') or None
@classmethod
def get_token(cls):
###$ Begin transition phase $###
if settings('token') == "":
settings('token', value=settings('accessToken'))
###$ End transition phase $###
return settings('token') or None
def _set_user_server(self):
self._server = self.download("{server}/emby/System/Configuration?format=json")
settings('markPlayed', value=str(self._server['MaxResumePct']))
self._user = self.download("{server}/emby/Users/{UserId}?format=json")
if "PrimaryImageTag" in self._user:
window('EmbyUserImage',
value=artwork.Artwork().getUserArtwork(self._user['Id'], 'Primary'))
def _authenticate(self):
if not self.get_server() or not self.get_username():
log.info('missing server or user information')
self._auth = False
elif self.get_token():
try:
self._load_user()
except Warning:
log.info("token is invalid")
else:
log.info("current user: %s", self.get_username())
log.info("current userid: %s", self.get_userid())
log.debug("current token: %s", self.get_token())
return
##### AUTHENTICATE USER #####
server = self.get_server()
username = self.get_username().decode('utf-8')
users = self.emby.getUsers(server)
user_found = None
for user in users:
if username == user['Name']:
user_found = user
break
try:
user = self.connectmanager.login_manual(server, user_found)
except RuntimeError:
window('emby_serverStatus', value="stop")
self._auth = False
return
else:
log.info("user: %s", user)
settings('username', value=user['User']['Name'])
settings('token', value=user['AccessToken'])
settings('userId', value=user['User']['Id'])
xbmcgui.Dialog().notification(lang(29999),
"%s %s!" % (lang(33000), username))
self._load_user(authenticated=True)
window('emby_serverStatus', clear=True)
def _load_user(self, authenticated=False):
doutils = self.doutils
userid = self.get_userid()
server = self.get_server()
token = self.get_token()
# Set properties
window('emby_currUser', value=userid)
window('emby_server%s' % userid, value=server)
window('emby_accessToken%s' % userid, value=token)
# Test the validity of the current token
if not authenticated:
try:
self.download("{server}/emby/Users/{UserId}?format=json")
except Warning as error:
if "401" in error:
# Token is not longer valid
raise
# Set downloadutils.py values
doutils.setUserId(userid)
doutils.setServer(server)
doutils.setToken(token)
doutils.setSSL(self.get_ssl())
# Start downloadutils.py session
doutils.startSession()
# Set _user and _server
# verify user access
try:
self._set_access()
self._set_user_server()
except Warning: # We don't need to raise any exceptions
pass
def _reset_client(self):
log.info("reset UserClient authentication")
settings('accessToken', value="")
window('emby_accessToken', clear=True)
log.info("user token revoked.")
self._user = None
self.auth = None
def run(self):
monitor = xbmc.Monitor()
self.connectmanager = connectmanager.ConnectManager()
log.warn("----===## Starting UserClient ##===----")
while not self._stop_thread:
status = window('emby_serverStatus')
if status:
# Verify the connection status to server
if status == "restricted":
# Parental control is restricting access
self._has_access = False
elif status == "401":
# Unauthorized access, revoke token
window('emby_serverStatus', value="auth")
self._reset_client()
if self._auth and self._user is None:
# Try to authenticate user
status = window('emby_serverStatus')
if not status or status == "auth":
# Set auth flag because we no longer need
# to authenticate the user
self._auth = False
self._authenticate()
if not self._auth and self._user is None:
# If authenticate failed.
server = self.get_server()
username = self.get_username()
status = window('emby_serverStatus')
# The status Stop is for when user cancelled password dialog.
if server and username and status != "stop":
# Only if there's information found to login
log.info("Server found: %s", server)
log.info("Username found: %s", username)
self._auth = True
if monitor.waitForAbort(1):
# Abort was requested while waiting. We should exit
break
self.doutils.stopSession()
log.warn("##===---- UserClient Stopped ----===##")
def stop_client(self):
self._stop_thread = True