PlexKodiConnect/resources/lib/userclient.py

351 lines
12 KiB
Python

# -*- coding: utf-8 -*-
###############################################################################
import logging
import threading
import xbmc
import xbmcgui
import xbmcaddon
from xbmcvfs import exists
from utils import window, settings, language as lang, ThreadMethods
import downloadutils
import PlexAPI
from PlexFunctions import GetMachineIdentifier
import state
###############################################################################
log = logging.getLogger("PLEX."+__name__)
###############################################################################
@ThreadMethods(add_suspends=[state.SUSPEND_USER_CLIENT])
class UserClient(threading.Thread):
# Borg - multiple instances, shared state
__shared_state = {}
def __init__(self, callback=None):
self.__dict__ = self.__shared_state
if callback is not None:
self.mgr = callback
self.auth = True
self.retry = 0
self.currUser = None
self.currUserId = None
self.currServer = None
self.currToken = None
self.HasAccess = True
self.AdditionalUser = []
self.userSettings = None
self.addon = xbmcaddon.Addon()
self.doUtils = downloadutils.DownloadUtils()
threading.Thread.__init__(self)
def getUsername(self):
"""
Returns username as unicode
"""
username = settings('username')
if not username:
log.debug("No username saved, trying to get Plex username")
username = settings('plexLogin')
if not username:
log.debug("Also no Plex username found")
return ""
return username
def getServer(self, prefix=True):
# Original host
self.servername = settings('plex_servername')
HTTPS = settings('https') == "true"
host = settings('ipaddress')
port = settings('port')
self.machineIdentifier = settings('plex_machineIdentifier')
server = host + ":" + port
if not host:
log.debug("No server information saved.")
return False
# If https is true
if prefix and HTTPS:
server = "https://%s" % server
# If https is false
elif prefix and not HTTPS:
server = "http://%s" % server
# User entered IP; we need to get the machineIdentifier
if self.machineIdentifier == '' and prefix is True:
self.machineIdentifier = GetMachineIdentifier(server)
if self.machineIdentifier is None:
self.machineIdentifier = ''
settings('plex_machineIdentifier', value=self.machineIdentifier)
log.debug('Returning active server: %s' % server)
return server
def getSSLverify(self):
# Verify host certificate
return None if settings('sslverify') == 'true' else False
def getSSL(self):
# Client side certificate
return None if settings('sslcert') == 'None' \
else settings('sslcert')
def setUserPref(self):
log.debug('Setting user preferences')
# Only try to get user avatar if there is a token
if self.currToken:
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
if url:
window('PlexUserImage', value=url)
# Set resume point max
# url = "{server}/emby/System/Configuration?format=json"
# result = doUtils.downloadUrl(url)
def hasAccess(self):
# Plex: always return True for now
return True
def loadCurrUser(self, username, userId, usertoken, authenticated=False):
log.debug('Loading current user')
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:
log.debug('Testing validity of current token')
res = PlexAPI.PlexAPI().CheckConnection(self.currServer,
token=self.currToken,
verifySSL=self.ssl)
if res is False:
# PMS probably offline
return False
elif res == 401:
log.error('Token is no longer valid')
return 401
elif res >= 400:
log.error('Answer from PMS is not as expected. Retrying')
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('plexToken'))
state.PLEX_TOKEN = settings('plexToken') or None
window('plex_restricteduser', value=settings('plex_restricteduser'))
state.RESTRICTED_USER = True \
if settings('plex_restricteduser') == 'true' else False
window('pms_server', value=self.currServer)
window('plex_machineIdentifier', value=self.machineIdentifier)
window('plex_servername', value=self.servername)
window('plex_authenticated', value='true')
window('useDirectPaths', value='true'
if settings('useDirectPaths') == "1" else 'false')
window('plex_force_transcode_pix', value='true'
if settings('force_transcode_pix') == "1" else 'false')
# Start DownloadUtils session
doUtils.startSession(reset=True)
# 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)
return True
def authenticate(self):
log.debug('Authenticating user')
dialog = xbmcgui.Dialog()
# Give attempts at entering password / selecting user
if self.retry >= 2:
log.error("Too many retries to login.")
state.PMS_STATUS = 'Stop'
dialog.ok(lang(33001),
lang(39023))
xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)')
return False
# Get /profile/addon_data
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile'))
# If there's no settings.xml
if not exists("%ssettings.xml" % addondir):
log.error("Error, no settings.xml found.")
self.auth = False
return False
server = self.getServer()
# If there is no server we can connect to
if not server:
log.info("Missing server information.")
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.debug('Trying to authenticate with old settings')
answ = self.loadCurrUser(username,
userId,
usertoken,
authenticated=False)
if answ is True:
# SUCCESS: loaded a user from the settings
return True
elif answ == 401:
log.error("User token no longer valid. Sign user out")
settings('username', value='')
settings('userid', value='')
settings('accessToken', value='')
else:
log.debug("Could not yet authenticate user")
return False
plx = PlexAPI.PlexAPI()
# Could not use settings - try to get Plex user list from plex.tv
plextoken = settings('plexToken')
if plextoken:
log.info("Trying to connect to plex.tv to get a user list")
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.info("Trying to authenticate without a token")
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):
log.debug("Reset UserClient authentication.")
self.doUtils.stopSession()
window('plex_authenticated', clear=True)
window('pms_token', clear=True)
state.PLEX_TOKEN = None
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)
window('plex_restricteduser', clear=True)
state.RESTRICTED_USER = False
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.info("----===## Starting UserClient ##===----")
thread_stopped = self.thread_stopped
thread_suspended = self.thread_suspended
while not thread_stopped():
while thread_suspended():
if thread_stopped():
break
xbmc.sleep(1000)
if state.PMS_STATUS == "Stop":
xbmc.sleep(500)
continue
# Verify the connection status to server
elif state.PMS_STATUS == "restricted":
# Parental control is restricting access
self.HasAccess = False
elif state.PMS_STATUS == "401":
# Unauthorized access, revoke token
state.PMS_STATUS = 'Auth'
self.resetClient()
xbmc.sleep(2000)
if self.auth and (self.currUser is None):
# Try to authenticate user
if not state.PMS_STATUS or state.PMS_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.info("Successfully authenticated!")
log.info("Current user: %s" % self.currUser)
log.info("Current userId: %s" % self.currUserId)
self.retry = 0
state.SUSPEND_LIBRARY_THREAD = False
window('plex_serverStatus', clear=True)
state.PMS_STATUS = False
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 state.PMS_STATUS != "Stop":
# Only if there's information found to login
log.debug("Server found: %s" % server)
self.auth = True
# Minimize CPU load
xbmc.sleep(100)
log.info("##===---- UserClient Stopped ----===##")