mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-12-25 18:26:15 +00:00
6a2ea9a4dd
* 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
295 lines
9.2 KiB
Python
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
|