415 lines
13 KiB
Python
415 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
###############################################################################
|
|
|
|
import threading
|
|
|
|
import xbmc
|
|
import xbmcgui
|
|
import xbmcaddon
|
|
import xbmcvfs
|
|
|
|
import utils
|
|
import downloadutils
|
|
|
|
import PlexAPI
|
|
|
|
###############################################################################
|
|
|
|
|
|
@utils.logging
|
|
@utils.ThreadMethodsAdditionalSuspend('suspend_Userclient')
|
|
@utils.ThreadMethods
|
|
class UserClient(threading.Thread):
|
|
|
|
# Borg - multiple instances, shared state
|
|
__shared_state = {}
|
|
|
|
auth = True
|
|
retry = 0
|
|
|
|
currUser = None
|
|
currUserId = None
|
|
currServer = None
|
|
currToken = None
|
|
HasAccess = True
|
|
AdditionalUser = []
|
|
|
|
userSettings = None
|
|
|
|
def __init__(self):
|
|
|
|
self.__dict__ = self.__shared_state
|
|
self.addon = xbmcaddon.Addon()
|
|
|
|
self.doUtils = downloadutils.DownloadUtils()
|
|
|
|
threading.Thread.__init__(self)
|
|
|
|
def getUsername(self):
|
|
"""
|
|
Returns username as unicode
|
|
"""
|
|
|
|
username = utils.settings('username')
|
|
|
|
if not username:
|
|
self.logMsg("No username saved, trying to get Plex username", 0)
|
|
username = utils.settings('plexLogin')
|
|
if not username:
|
|
self.logMsg("Also no Plex username found", 0)
|
|
return ""
|
|
|
|
return username
|
|
|
|
def getLogLevel(self):
|
|
|
|
try:
|
|
logLevel = int(utils.settings('logLevel'))
|
|
except ValueError:
|
|
logLevel = 0
|
|
|
|
return logLevel
|
|
|
|
def getServer(self, prefix=True):
|
|
|
|
settings = utils.settings
|
|
|
|
# Original host
|
|
self.machineIdentifier = utils.settings('plex_machineIdentifier')
|
|
self.servername = utils.settings('plex_servername')
|
|
HTTPS = settings('https') == "true"
|
|
host = settings('ipaddress')
|
|
port = settings('port')
|
|
|
|
server = host + ":" + port
|
|
|
|
if not host:
|
|
self.logMsg("No server information saved.", 2)
|
|
return False
|
|
|
|
# If https is true
|
|
if prefix and HTTPS:
|
|
server = "https://%s" % server
|
|
return server
|
|
# If https is false
|
|
elif prefix and not HTTPS:
|
|
server = "http://%s" % server
|
|
return server
|
|
# If only the host:port is required
|
|
elif not prefix:
|
|
return server
|
|
|
|
def getSSLverify(self):
|
|
# Verify host certificate
|
|
settings = utils.settings
|
|
|
|
s_sslverify = settings('sslverify')
|
|
if settings('altip') == "true":
|
|
s_sslverify = settings('secondsslverify')
|
|
|
|
if s_sslverify == "true":
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def getSSL(self):
|
|
# Client side certificate
|
|
settings = utils.settings
|
|
|
|
s_cert = settings('sslcert')
|
|
if settings('altip') == "true":
|
|
s_cert = settings('secondsslcert')
|
|
|
|
if s_cert == "None":
|
|
return None
|
|
else:
|
|
return s_cert
|
|
|
|
def setUserPref(self):
|
|
self.logMsg('Setting user preferences', 0)
|
|
# Only try to get user avatar if there is a token
|
|
if self.currToken:
|
|
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
|
|
if url:
|
|
utils.window('EmbyUserImage', value=url)
|
|
# Set resume point max
|
|
# url = "{server}/emby/System/Configuration?format=json"
|
|
# result = doUtils.downloadUrl(url)
|
|
|
|
# utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
|
|
|
def hasAccess(self):
|
|
# Plex: always return True for now
|
|
return True
|
|
# hasAccess is verified in service.py
|
|
log = self.logMsg
|
|
window = utils.window
|
|
|
|
url = "{server}/emby/Users?format=json"
|
|
result = self.doUtils.downloadUrl(url)
|
|
|
|
if result == False:
|
|
# Access is restricted, set in downloadutils.py via exception
|
|
log("Access is restricted.", 1)
|
|
self.HasAccess = False
|
|
|
|
elif window('emby_online') != "true":
|
|
# Server connection failed
|
|
pass
|
|
|
|
elif window('emby_serverStatus') == "restricted":
|
|
log("Access is granted.", 1)
|
|
self.HasAccess = True
|
|
window('emby_serverStatus', clear=True)
|
|
xbmcgui.Dialog().notification(self.addonName,
|
|
utils.language(33007))
|
|
|
|
def loadCurrUser(self, username, userId, usertoken, authenticated=False):
|
|
self.logMsg('Loading current user', 0)
|
|
window = utils.window
|
|
settings = utils.settings
|
|
doUtils = self.doUtils
|
|
|
|
self.currUserId = userId
|
|
self.currToken = usertoken
|
|
self.currServer = self.getServer()
|
|
self.ssl = self.getSSLverify()
|
|
self.sslcert = self.getSSL()
|
|
|
|
if authenticated is False:
|
|
self.logMsg('Testing validity of current token', 0)
|
|
res = PlexAPI.PlexAPI().CheckConnection(
|
|
self.currServer, self.currToken)
|
|
if res is False:
|
|
self.logMsg('Answer from PMS is not as expected. Retrying', -1)
|
|
return False
|
|
elif res == 401:
|
|
self.logMsg('Token is no longer valid', -1)
|
|
return False
|
|
elif res >= 400:
|
|
self.logMsg('Answer from PMS is not as expected. Retrying', -1)
|
|
return False
|
|
|
|
# Set to windows property
|
|
window('currUserId', value=userId)
|
|
window('plex_username', value=username)
|
|
# This is the token for the current PMS (might also be '')
|
|
window('pms_token', value=self.currToken)
|
|
# This is the token for plex.tv for the current user
|
|
# Is only '' if user is not signed in to plex.tv
|
|
window('plex_token', value=settings('accessToken'))
|
|
window('pms_server', value=self.currServer)
|
|
window('plex_machineIdentifier', value=self.machineIdentifier)
|
|
window('plex_servername', value=self.servername)
|
|
window('plex_authenticated', value='true')
|
|
|
|
# Set DownloadUtils values
|
|
doUtils.setUsername(username)
|
|
doUtils.setUserId(self.currUserId)
|
|
doUtils.setServer(self.currServer)
|
|
doUtils.setToken(self.currToken)
|
|
doUtils.setSSL(self.ssl, self.sslcert)
|
|
|
|
# Start DownloadUtils session
|
|
doUtils.startSession()
|
|
# self.getAdditionalUsers()
|
|
# Set user preferences in settings
|
|
self.currUser = username
|
|
self.setUserPref()
|
|
|
|
# Writing values to settings file
|
|
settings('username', value=username)
|
|
settings('userid', value=userId)
|
|
settings('accessToken', value=usertoken)
|
|
|
|
dialog = xbmcgui.Dialog()
|
|
if username:
|
|
dialog.notification(
|
|
heading=self.addonName,
|
|
message="Welcome " + username,
|
|
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
|
|
else:
|
|
dialog.notification(
|
|
heading=self.addonName,
|
|
message="Welcome",
|
|
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png")
|
|
return True
|
|
|
|
def authenticate(self):
|
|
log = self.logMsg
|
|
log('Authenticating user', 1)
|
|
lang = utils.language
|
|
window = utils.window
|
|
settings = utils.settings
|
|
dialog = xbmcgui.Dialog()
|
|
|
|
# Give attempts at entering password / selecting user
|
|
if self.retry >= 2:
|
|
log("Too many retries to login.", -1)
|
|
window('emby_serverStatus', value="Stop")
|
|
dialog.ok(lang(33001),
|
|
lang(39023))
|
|
xbmc.executebuiltin(
|
|
'Addon.OpenSettings(plugin.video.plexkodiconnect)')
|
|
|
|
# Get /profile/addon_data
|
|
addondir = xbmc.translatePath(
|
|
self.addon.getAddonInfo('profile')).decode('utf-8')
|
|
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
|
|
|
# If there's no settings.xml
|
|
if not hasSettings:
|
|
log("Error, no settings.xml found.", -1)
|
|
self.auth = False
|
|
return False
|
|
server = self.getServer()
|
|
# If there is no server we can connect to
|
|
if not server:
|
|
log("Missing server information.", 0)
|
|
self.auth = False
|
|
return False
|
|
|
|
# If there is a username in the settings, try authenticating
|
|
username = settings('username')
|
|
userId = settings('userid')
|
|
usertoken = settings('accessToken')
|
|
enforceLogin = settings('enforceUserLogin')
|
|
# Found a user in the settings, try to authenticate
|
|
if username and enforceLogin == 'false':
|
|
log('Trying to authenticate with old settings', 0)
|
|
if self.loadCurrUser(username,
|
|
userId,
|
|
usertoken,
|
|
authenticated=False):
|
|
# SUCCESS: loaded a user from the settings
|
|
return True
|
|
else:
|
|
# Failed to use the settings - delete them!
|
|
log("Failed to use the settings credentials. Deleting them", 1)
|
|
settings('username', value='')
|
|
settings('userid', value='')
|
|
settings('accessToken', value='')
|
|
|
|
plx = PlexAPI.PlexAPI()
|
|
|
|
# Could not use settings - try to get Plex user list from plex.tv
|
|
plextoken = settings('plexToken')
|
|
if plextoken:
|
|
log("Trying to connect to plex.tv to get a user list", 0)
|
|
userInfo = plx.ChoosePlexHomeUser(plextoken)
|
|
if userInfo is False:
|
|
# FAILURE: Something went wrong, try again
|
|
self.auth = True
|
|
self.retry += 1
|
|
return False
|
|
username = userInfo['username']
|
|
userId = userInfo['userid']
|
|
usertoken = userInfo['token']
|
|
else:
|
|
log("Trying to authenticate without a token", 0)
|
|
username = ''
|
|
userId = ''
|
|
usertoken = ''
|
|
|
|
if self.loadCurrUser(username, userId, usertoken, authenticated=False):
|
|
# SUCCESS: loaded a user from the settings
|
|
return True
|
|
else:
|
|
# FAILUR: Something went wrong, try again
|
|
self.auth = True
|
|
self.retry += 1
|
|
return False
|
|
|
|
def resetClient(self):
|
|
self.logMsg("Reset UserClient authentication.", 1)
|
|
|
|
self.doUtils.stopSession()
|
|
|
|
settings = utils.settings
|
|
window = utils.window
|
|
|
|
window('plex_authenticated', clear=True)
|
|
window('pms_token', clear=True)
|
|
window('plex_token', clear=True)
|
|
window('pms_server', clear=True)
|
|
window('plex_machineIdentifier', clear=True)
|
|
window('plex_servername', clear=True)
|
|
window('currUserId', clear=True)
|
|
window('plex_username', clear=True)
|
|
|
|
settings('username', value='')
|
|
settings('userid', value='')
|
|
settings('accessToken', value='')
|
|
|
|
# Reset token in downloads
|
|
self.doUtils.setToken('')
|
|
self.doUtils.setUserId('')
|
|
self.doUtils.setUsername('')
|
|
|
|
self.currToken = None
|
|
self.auth = True
|
|
self.currUser = None
|
|
self.currUserId = None
|
|
|
|
self.retry = 0
|
|
|
|
def run(self):
|
|
log = self.logMsg
|
|
window = utils.window
|
|
|
|
log("----===## Starting UserClient ##===----", 0)
|
|
while not self.threadStopped():
|
|
while self.threadSuspended():
|
|
if self.threadStopped():
|
|
break
|
|
xbmc.sleep(1000)
|
|
|
|
status = window('emby_serverStatus')
|
|
|
|
if status == "Stop":
|
|
xbmc.sleep(500)
|
|
continue
|
|
|
|
# Verify the connection status to server
|
|
elif status == "restricted":
|
|
# Parental control is restricting access
|
|
self.HasAccess = False
|
|
|
|
elif status == "401":
|
|
# Unauthorized access, revoke token
|
|
window('emby_serverStatus', value="Auth")
|
|
self.resetClient()
|
|
xbmc.sleep(2000)
|
|
|
|
if self.auth and (self.currUser is None):
|
|
# Try to authenticate user
|
|
if not status or status == "Auth":
|
|
# Set auth flag because we no longer need
|
|
# to authenticate the user
|
|
self.auth = False
|
|
if self.authenticate():
|
|
# Successfully authenticated and loaded a user
|
|
log("Current user: %s" % self.currUser, 1)
|
|
log("Current userId: %s" % self.currUserId, 1)
|
|
log("Current accessToken: xxxx", 1)
|
|
self.retry = 0
|
|
window('suspend_LibraryThread', clear=True)
|
|
window('emby_serverStatus', clear=True)
|
|
|
|
if not self.auth and (self.currUser is None):
|
|
# Loop if no server found
|
|
server = self.getServer()
|
|
|
|
# The status Stop is for when user cancelled password dialog.
|
|
# Or retried too many times
|
|
if server and status != "Stop":
|
|
# Only if there's information found to login
|
|
log("Server found: %s" % server, 2)
|
|
self.auth = True
|
|
|
|
# Minimize CPU load
|
|
xbmc.sleep(100)
|
|
|
|
self.doUtils.stopSession()
|
|
log("##===---- UserClient Stopped ----===##", 0)
|