Plex home user switch finally working
This commit is contained in:
parent
6e8bd3e7da
commit
eddfa23a71
5 changed files with 205 additions and 200 deletions
|
@ -94,7 +94,6 @@ class PlexAPI():
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
self.plexLogin = utils.settings('plexLogin')
|
self.plexLogin = utils.settings('plexLogin')
|
||||||
self.plexToken = utils.settings('plexToken')
|
self.plexToken = utils.settings('plexToken')
|
||||||
self.machineIdentifier = utils.window('plex_machineIdentifier')
|
|
||||||
|
|
||||||
self.doUtils = downloadutils.DownloadUtils()
|
self.doUtils = downloadutils.DownloadUtils()
|
||||||
|
|
||||||
|
@ -102,24 +101,18 @@ class PlexAPI():
|
||||||
className = self.__class__.__name__
|
className = self.__class__.__name__
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||||
|
|
||||||
def SetPlexLoginToSettings(self, plexLogin, plexToken):
|
|
||||||
"""
|
|
||||||
Saves retrieved Plex username and Plex token to Kodi settings file.
|
|
||||||
"""
|
|
||||||
utils.settings('plexLogin', value=plexLogin)
|
|
||||||
utils.settings('plexToken', value=plexToken)
|
|
||||||
|
|
||||||
def GetPlexLoginFromSettings(self):
|
def GetPlexLoginFromSettings(self):
|
||||||
"""
|
"""
|
||||||
Returns (myplexlogin, plexLogin, plexToken) from the Kodi file
|
Returns empty strings if not found.
|
||||||
settings. Returns empty strings if not found.
|
|
||||||
|
|
||||||
myplexlogin is 'true' if user opted to log into plex.tv (the default)
|
myplexlogin is 'true' if user opted to log into plex.tv (the default)
|
||||||
|
plexhome is 'true' if plex home is used (the default)
|
||||||
"""
|
"""
|
||||||
plexLogin = utils.settings('plexLogin')
|
plexLogin = utils.settings('plexLogin')
|
||||||
plexToken = utils.settings('plexToken')
|
plexToken = utils.settings('plexToken')
|
||||||
myplexlogin = utils.settings('myplexlogin')
|
myplexlogin = utils.settings('myplexlogin')
|
||||||
return (myplexlogin, plexLogin, plexToken)
|
plexhome = utils.settings('plexhome')
|
||||||
|
return (myplexlogin, plexhome, plexLogin, plexToken)
|
||||||
|
|
||||||
def GetPlexLoginAndPassword(self):
|
def GetPlexLoginAndPassword(self):
|
||||||
"""
|
"""
|
||||||
|
@ -157,12 +150,15 @@ class PlexAPI():
|
||||||
plexPassword,
|
plexPassword,
|
||||||
{'X-Plex-Client-Identifier': self.clientId}
|
{'X-Plex-Client-Identifier': self.clientId}
|
||||||
)
|
)
|
||||||
self.logMsg("plex.tv username and token: %s, %s" % (plexLogin, authtoken), 1)
|
self.logMsg("plex.tv username and token: %s, %s"
|
||||||
|
% (plexLogin, authtoken), 1)
|
||||||
if plexLogin == '':
|
if plexLogin == '':
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
dialog.ok(self.addonName, 'Could not sign in user %s' % plexLogin)
|
dialog.ok(self.addonName, 'Could not sign in user %s'
|
||||||
|
% plexLogin)
|
||||||
# Write to Kodi settings file
|
# Write to Kodi settings file
|
||||||
self.SetPlexLoginToSettings(retrievedPlexLogin, authtoken)
|
utils.settings('plexLogin', value=retrievedPlexLogin)
|
||||||
|
utils.settings('plexToken', value=authtoken)
|
||||||
return (retrievedPlexLogin, authtoken)
|
return (retrievedPlexLogin, authtoken)
|
||||||
|
|
||||||
def PlexTvSignInWithPin(self):
|
def PlexTvSignInWithPin(self):
|
||||||
|
@ -171,7 +167,7 @@ class PlexAPI():
|
||||||
|
|
||||||
Writes username and token to Kodi settings file. Returns:
|
Writes username and token to Kodi settings file. Returns:
|
||||||
{
|
{
|
||||||
'home': '1' if Plex Home, '0' otherwise
|
'plexhome': 'true' if Plex Home, 'false' otherwise
|
||||||
'username':
|
'username':
|
||||||
'avatar': URL to user avator
|
'avatar': URL to user avator
|
||||||
'token':
|
'token':
|
||||||
|
@ -207,16 +203,22 @@ class PlexAPI():
|
||||||
return False
|
return False
|
||||||
# Parse xml
|
# Parse xml
|
||||||
home = xml.get('home', '0')
|
home = xml.get('home', '0')
|
||||||
|
if home == '1':
|
||||||
|
home = 'true'
|
||||||
|
else:
|
||||||
|
home = 'false'
|
||||||
username = xml.get('username', '')
|
username = xml.get('username', '')
|
||||||
avatar = xml.get('thumb')
|
avatar = xml.get('thumb')
|
||||||
token = xml.findtext('authentication-token')
|
token = xml.findtext('authentication-token')
|
||||||
result = {
|
result = {
|
||||||
'home': home,
|
'plexhome': home,
|
||||||
'username': username,
|
'username': username,
|
||||||
'avatar': avatar,
|
'avatar': avatar,
|
||||||
'token': token
|
'token': token
|
||||||
}
|
}
|
||||||
self.SetPlexLoginToSettings(username, token)
|
utils.settings('plexLogin', value=username)
|
||||||
|
utils.settings('plexToken', value=token)
|
||||||
|
utils.settings('plexhome', value=home)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def CheckPlexTvSignin(self, identifier):
|
def CheckPlexTvSignin(self, identifier):
|
||||||
|
@ -261,13 +263,17 @@ class PlexAPI():
|
||||||
self.logMsg("plex.tv/pin: Identifier is: %s" % identifier, 2)
|
self.logMsg("plex.tv/pin: Identifier is: %s" % identifier, 2)
|
||||||
return code, identifier
|
return code, identifier
|
||||||
|
|
||||||
def TalkToPlexServer(self, url, talkType="GET", verify=True):
|
def TalkToPlexServer(self, url, talkType="GET", verify=True, token=None):
|
||||||
"""
|
"""
|
||||||
Start request with PMS with url.
|
Start request with PMS with url.
|
||||||
|
|
||||||
Returns the parsed XML answer as an etree object. Or False.
|
Returns the parsed XML answer as an etree object.
|
||||||
|
False if the server could not be reached/timeout occured.
|
||||||
|
False if HTTP error code of >=400 was returned.
|
||||||
"""
|
"""
|
||||||
header = self.getXArgsDeviceInfo()
|
header = self.getXArgsDeviceInfo()
|
||||||
|
if token:
|
||||||
|
header['X-Plex-Token'] = token
|
||||||
timeout = (3, 10)
|
timeout = (3, 10)
|
||||||
try:
|
try:
|
||||||
if talkType == "GET":
|
if talkType == "GET":
|
||||||
|
@ -276,6 +282,7 @@ class PlexAPI():
|
||||||
params=header,
|
params=header,
|
||||||
verify=verify,
|
verify=verify,
|
||||||
timeout=timeout)
|
timeout=timeout)
|
||||||
|
# Only seems to be used for initial plex.tv sign in
|
||||||
if talkType == "GET2":
|
if talkType == "GET2":
|
||||||
answer = requests.get(url,
|
answer = requests.get(url,
|
||||||
headers=header,
|
headers=header,
|
||||||
|
@ -315,46 +322,52 @@ class PlexAPI():
|
||||||
def CheckConnection(self, url, token):
|
def CheckConnection(self, url, token):
|
||||||
"""
|
"""
|
||||||
Checks connection to a Plex server, available at url. Can also be used
|
Checks connection to a Plex server, available at url. Can also be used
|
||||||
to check for connection with plex.tv!
|
to check for connection with plex.tv.
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
url URL to Plex server (e.g. https://192.168.1.1:32400)
|
url URL to Plex server (e.g. https://192.168.1.1:32400)
|
||||||
token appropriate token to access server
|
token appropriate token to access server. If None is passed,
|
||||||
|
the current token is used
|
||||||
Output:
|
Output:
|
||||||
200 if the connection was successfull
|
False if server could not be reached or timeout occured
|
||||||
'' empty string if connection failed for whatever reason
|
e.g. 200 if connection was successfull
|
||||||
401 integer if token has been revoked
|
int or other HTML status codes as received from the server
|
||||||
"""
|
"""
|
||||||
# Add '/clients' to URL because then an authentication is necessary
|
# Add '/clients' to URL because then an authentication is necessary
|
||||||
# If a plex.tv URL was passed, this does not work.
|
# If a plex.tv URL was passed, this does not work.
|
||||||
|
header = self.getXArgsDeviceInfo()
|
||||||
|
if token is not None:
|
||||||
|
header['X-Plex-Token'] = token
|
||||||
|
sslverify = utils.settings('sslverify')
|
||||||
|
if sslverify == "true":
|
||||||
|
sslverify = True
|
||||||
|
else:
|
||||||
|
sslverify = False
|
||||||
|
self.logMsg("Checking connection to server %s with header %s and "
|
||||||
|
"sslverify=%s" % (url, header, sslverify), 1)
|
||||||
|
timeout = (3, 10)
|
||||||
if 'plex.tv' in url:
|
if 'plex.tv' in url:
|
||||||
url = 'https://plex.tv/api/home/users'
|
url = 'https://plex.tv/api/home/users'
|
||||||
else:
|
else:
|
||||||
url = url + '/library/onDeck'
|
url = url + '/library/onDeck'
|
||||||
|
try:
|
||||||
if token:
|
answer = requests.get(url,
|
||||||
self.logMsg("CheckConnection for %s with a token" % url, 0)
|
headers={},
|
||||||
r = self.doUtils.downloadUrl(
|
params=header,
|
||||||
url,
|
verify=sslverify,
|
||||||
authenticate=False,
|
timeout=timeout)
|
||||||
headerOptions={'X-Plex-Token': token,
|
except requests.exceptions.ConnectionError as e:
|
||||||
'Accept': 'application/json'})
|
self.logMsg("Server is offline or cannot be reached. Url: %s."
|
||||||
else:
|
"Header: %s. Error message: %s"
|
||||||
self.logMsg("CheckConnection for %s without a token" % url, 0)
|
% (url, header, e), -1)
|
||||||
r = self.doUtils.downloadUrl(
|
return False
|
||||||
url,
|
except requests.exceptions.ReadTimeout:
|
||||||
authenticate=False,
|
self.logMsg("Server timeout reached for Url %s with header %s"
|
||||||
headerOptions={'Accept': 'application/json'})
|
% (url, header), -1)
|
||||||
self.logMsg("Response was: %s" % r, 2)
|
return False
|
||||||
# List of exception returns, when connection failed
|
result = answer.status_code
|
||||||
exceptionlist = [
|
self.logMsg("Result was: %s" % result, 1)
|
||||||
'',
|
return result
|
||||||
401
|
|
||||||
]
|
|
||||||
# To get rid of the stuff that was downloaded :-)
|
|
||||||
if r not in exceptionlist:
|
|
||||||
r = 200
|
|
||||||
return r
|
|
||||||
|
|
||||||
def GetgPMSKeylist(self):
|
def GetgPMSKeylist(self):
|
||||||
"""
|
"""
|
||||||
|
@ -593,7 +606,6 @@ class PlexAPI():
|
||||||
result:
|
result:
|
||||||
self.g_PMS dictionary for ATV_udid
|
self.g_PMS dictionary for ATV_udid
|
||||||
"""
|
"""
|
||||||
# Plex: changed CSettings to new function getServerFromSettings()
|
|
||||||
self.g_PMS[ATV_udid] = {}
|
self.g_PMS[ATV_udid] = {}
|
||||||
|
|
||||||
# install plex.tv "virtual" PMS - for myPlex, PlexHome
|
# install plex.tv "virtual" PMS - for myPlex, PlexHome
|
||||||
|
@ -612,25 +624,27 @@ class PlexAPI():
|
||||||
# local PMS
|
# local PMS
|
||||||
# PlexGDM
|
# PlexGDM
|
||||||
PMS_list = self.PlexGDM()
|
PMS_list = self.PlexGDM()
|
||||||
for uuid in PMS_list:
|
for uuid_id in PMS_list:
|
||||||
PMS = PMS_list[uuid]
|
PMS = PMS_list[uuid_id]
|
||||||
self.declarePMS(ATV_udid, PMS['uuid'], PMS['serverName'], 'http', PMS['ip'], PMS['port']) # dflt: token='', local, owned
|
self.declarePMS(ATV_udid, PMS['uuid'], PMS['serverName'], 'http', PMS['ip'], PMS['port']) # dflt: token='', local, owned
|
||||||
else:
|
else:
|
||||||
# MyPlex servers
|
# MyPlex servers
|
||||||
self.getPMSListFromMyPlex(ATV_udid, authtoken)
|
self.getPMSListFromMyPlex(ATV_udid, authtoken)
|
||||||
# all servers - update enableGzip
|
# all servers - update enableGzip
|
||||||
for uuid in self.g_PMS.get(ATV_udid, {}):
|
for uuid_id in self.g_PMS.get(ATV_udid, {}):
|
||||||
# enable Gzip if not on same host, local&remote PMS depending
|
# enable Gzip if not on same host, local&remote PMS depending
|
||||||
# on setting
|
# on setting
|
||||||
enableGzip = (not self.getPMSProperty(ATV_udid, uuid, 'ip') == IP_self) \
|
enableGzip = (not self.getPMSProperty(ATV_udid, uuid_id, 'ip') == IP_self) \
|
||||||
and (
|
and (
|
||||||
(self.getPMSProperty(ATV_udid, uuid, 'local') == '1'
|
(self.getPMSProperty(ATV_udid, uuid_id, 'local') == '1'
|
||||||
and False)
|
and False)
|
||||||
or
|
or
|
||||||
(self.getPMSProperty(ATV_udid, uuid, 'local') == '0'
|
(self.getPMSProperty(ATV_udid, uuid_id, 'local') == '0'
|
||||||
and True) == 'True'
|
and True) == 'True'
|
||||||
)
|
)
|
||||||
self.updatePMSProperty(ATV_udid, uuid, 'enableGzip', enableGzip)
|
self.updatePMSProperty(ATV_udid, uuid_id, 'enableGzip', enableGzip)
|
||||||
|
# Delete plex.tv again
|
||||||
|
del self.g_PMS[ATV_udid]['plex.tv']
|
||||||
|
|
||||||
def getPMSListFromMyPlex(self, ATV_udid, authtoken):
|
def getPMSListFromMyPlex(self, ATV_udid, authtoken):
|
||||||
"""
|
"""
|
||||||
|
@ -668,11 +682,12 @@ class PlexAPI():
|
||||||
uri = Con.get('uri')
|
uri = Con.get('uri')
|
||||||
# todo: handle unforeseen - like we get multiple suitable connections. how to choose one?
|
# todo: handle unforeseen - like we get multiple suitable connections. how to choose one?
|
||||||
|
|
||||||
# check MyPlex data age - skip if >2 days
|
# check MyPlex data age - skip if >1 days
|
||||||
infoAge = time.time() - int(Dir.get('lastSeenAt'))
|
infoAge = time.time() - int(Dir.get('lastSeenAt'))
|
||||||
oneDayInSec = 60*60*24
|
oneDayInSec = 60*60*24
|
||||||
if infoAge > 2*oneDayInSec: # two days in seconds -> expiration in setting?
|
if infoAge > 1*oneDayInSec:
|
||||||
dprint(__name__, 1, "Server {0} not updated for {1} days - skipping.", name, infoAge/oneDayInSec)
|
self.logMsg("Server %s not updated for 1 day - "
|
||||||
|
"skipping." % name, 0)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# poke PMS, own thread for each poke
|
# poke PMS, own thread for each poke
|
||||||
|
@ -1041,13 +1056,14 @@ class PlexAPI():
|
||||||
"""
|
"""
|
||||||
plexLogin = self.plexLogin
|
plexLogin = self.plexLogin
|
||||||
plexToken = self.plexToken
|
plexToken = self.plexToken
|
||||||
|
machineIdentifier = utils.settings('plex_machineIdentifier')
|
||||||
self.logMsg("Getting user list.", 1)
|
self.logMsg("Getting user list.", 1)
|
||||||
# Get list of Plex home users
|
# Get list of Plex home users
|
||||||
users = self.MyPlexListHomeUsers(plexToken)
|
users = self.MyPlexListHomeUsers(plexToken)
|
||||||
# Download users failed. Set username to Plex login
|
# Download users failed. Set username to Plex login
|
||||||
if not users:
|
if not users:
|
||||||
utils.settings('username', value=plexLogin)
|
utils.settings('username', value=plexLogin)
|
||||||
self.logMsg("User download failed. Set username = plexlogin", 1)
|
self.logMsg("User download failed. Set username = plexlogin", 0)
|
||||||
return ('', '', '')
|
return ('', '', '')
|
||||||
|
|
||||||
userlist = []
|
userlist = []
|
||||||
|
@ -1061,10 +1077,12 @@ class PlexAPI():
|
||||||
while trials < 3:
|
while trials < 3:
|
||||||
if usernumber > 1:
|
if usernumber > 1:
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
user_select = dialog.select(self.addonName + ": Select User", userlist)
|
user_select = dialog.select(self.addonName + ": Select User",
|
||||||
|
userlist)
|
||||||
if user_select == -1:
|
if user_select == -1:
|
||||||
self.logMsg("No user selected.", 1)
|
self.logMsg("No user selected.", 1)
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
|
xbmc.executebuiltin('Addon.OpenSettings(%s)'
|
||||||
|
% self.addonId)
|
||||||
return ('', '', '')
|
return ('', '', '')
|
||||||
# No Plex home in use - only 1 user
|
# No Plex home in use - only 1 user
|
||||||
else:
|
else:
|
||||||
|
@ -1084,10 +1102,11 @@ class PlexAPI():
|
||||||
else:
|
else:
|
||||||
pin = None
|
pin = None
|
||||||
# Switch to this Plex Home user, if applicable
|
# Switch to this Plex Home user, if applicable
|
||||||
username, usertoken = self.MyPlexSwitchHomeUser(
|
username, usertoken = self.PlexSwitchHomeUser(
|
||||||
user['id'],
|
user['id'],
|
||||||
pin,
|
pin,
|
||||||
plexToken
|
plexToken,
|
||||||
|
machineIdentifier
|
||||||
)
|
)
|
||||||
# Couldn't get user auth
|
# Couldn't get user auth
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -1106,67 +1125,70 @@ class PlexAPI():
|
||||||
return ('', '', '', '')
|
return ('', '', '', '')
|
||||||
return (username, user['id'], usertoken)
|
return (username, user['id'], usertoken)
|
||||||
|
|
||||||
def MyPlexSwitchHomeUser(self, id, pin, authtoken, options={}):
|
def PlexSwitchHomeUser(self, userId, pin, token, machineId):
|
||||||
"""
|
"""
|
||||||
Retrieves Plex home token for a Plex home user.
|
Retrieves Plex home token for a Plex home user.
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
id id of the Plex home user
|
userId id of the Plex home user
|
||||||
pin PIN of the Plex home user, if protected
|
pin PIN of the Plex home user, if protected
|
||||||
authtoken token for plex.tv
|
token token for plex.tv
|
||||||
options={} optional additional header options
|
machineId Plex PMS machineIdentifier
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
username Plex home username
|
(username, token)
|
||||||
authtoken token for Plex home user
|
|
||||||
|
|
||||||
Returns empty strings if unsuccessful
|
Returns 2 empty strings if unsuccessful
|
||||||
"""
|
"""
|
||||||
MyPlexHost = 'https://plex.tv'
|
url = 'https://plex.tv/api/home/users/' + userId + '/switch'
|
||||||
MyPlexURL = MyPlexHost + '/api/home/users/' + id + '/switch'
|
|
||||||
|
|
||||||
if pin:
|
if pin:
|
||||||
MyPlexURL += '?pin=' + pin
|
url += '?pin=' + pin
|
||||||
|
self.logMsg('Switching to user %s with url %s and machineId %s'
|
||||||
|
% (userId, url, machineId), 0)
|
||||||
|
answer = self.TalkToPlexServer(url, talkType="POST", token=token)
|
||||||
|
if not answer:
|
||||||
|
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
||||||
|
return ('', '')
|
||||||
|
|
||||||
xargs = {}
|
username = answer.attrib.get('title', '')
|
||||||
xargs = self.getXArgsDeviceInfo(options)
|
token = answer.attrib.get('authenticationToken', '')
|
||||||
xargs['X-Plex-Token'] = authtoken
|
|
||||||
|
|
||||||
request = urllib2.Request(MyPlexURL, None, xargs)
|
# Get final token
|
||||||
request.get_method = lambda: 'POST'
|
url = 'https://plex.tv/pms/servers.xml'
|
||||||
|
answer = self.TalkToPlexServer(url, talkType="GET", token=token)
|
||||||
|
if not answer:
|
||||||
|
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
||||||
|
return ('', '')
|
||||||
|
|
||||||
response = urllib2.urlopen(request).read()
|
found = 0
|
||||||
|
for child in answer:
|
||||||
self.logMsg("====== MyPlexHomeUser XML ======", 1)
|
if child.attrib['machineIdentifier'] == machineId:
|
||||||
self.logMsg(response, 1)
|
token = child.attrib['accessToken']
|
||||||
self.logMsg("====== MyPlexHomeUser XML finished ======", 1)
|
self.logMsg('Found a plex home user token', 1)
|
||||||
|
found += 1
|
||||||
# analyse response
|
if found == 0:
|
||||||
XMLTree = etree.ElementTree(etree.fromstring(response))
|
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
||||||
|
return ('', '')
|
||||||
el_user = XMLTree.getroot() # root=<user>. double check?
|
self.logMsg('Plex.tv switch HomeUser change successfull', 0)
|
||||||
username = el_user.attrib.get('title', '')
|
self.logMsg('username: %s, token: xxxx' % username, 0)
|
||||||
authtoken = el_user.attrib.get('authenticationToken', '')
|
return (username, token)
|
||||||
|
|
||||||
if username and authtoken:
|
|
||||||
self.logMsg('MyPlex switch HomeUser change successfull', 0)
|
|
||||||
else:
|
|
||||||
self.logMsg('MyPlex switch HomeUser change failed', 0)
|
|
||||||
return (username, authtoken)
|
|
||||||
|
|
||||||
def MyPlexListHomeUsers(self, authtoken):
|
def MyPlexListHomeUsers(self, authtoken):
|
||||||
"""
|
"""
|
||||||
Returns all myPlex home users for the currently signed in account.
|
Returns a list for myPlex home users for the current plex.tv account.
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
authtoken for plex.tv
|
authtoken for plex.tv
|
||||||
options, optional
|
|
||||||
Output:
|
Output:
|
||||||
List of users, where one entry is of the form:
|
List of users, where one entry is of the form:
|
||||||
{
|
"id": userId,
|
||||||
"id": userId, "admin": '1'/'0', "guest": '1'/'0',
|
"admin": '1'/'0',
|
||||||
"restricted": '1'/'0', "protected": '1'/'0',
|
"guest": '1'/'0',
|
||||||
"email": email, "title": title, "username": username,
|
"restricted": '1'/'0',
|
||||||
|
"protected": '1'/'0',
|
||||||
|
"email": email,
|
||||||
|
"title": title,
|
||||||
|
"username": username,
|
||||||
"thumb": thumb_url
|
"thumb": thumb_url
|
||||||
}
|
}
|
||||||
If any value is missing, None is returned instead (or "" from plex.tv)
|
If any value is missing, None is returned instead (or "" from plex.tv)
|
||||||
|
@ -1572,13 +1594,8 @@ class API():
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
# Include a letter to prohibit saving as an int!
|
# Include a letter to prohibit saving as an int!
|
||||||
checksum = "K%s%s%s%s%s" % (
|
checksum = "K%s%s" % (self.getKey(),
|
||||||
self.getKey(),
|
item.get('updatedAt', ''))
|
||||||
item['updatedAt'],
|
|
||||||
item.get('viewCount', ""),
|
|
||||||
item.get('lastViewedAt', ""),
|
|
||||||
item.get('viewOffset', "")
|
|
||||||
)
|
|
||||||
return checksum
|
return checksum
|
||||||
|
|
||||||
def getKey(self):
|
def getKey(self):
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
import json
|
|
||||||
import socket
|
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
|
@ -21,7 +18,6 @@ import PlexAPI
|
||||||
|
|
||||||
class InitialSetup():
|
class InitialSetup():
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.addon = xbmcaddon.Addon()
|
self.addon = xbmcaddon.Addon()
|
||||||
|
@ -40,19 +36,20 @@ class InitialSetup():
|
||||||
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
utils.logMsg("%s %s" % (self.addonName, className), msg, lvl)
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
# Check server, user, direct paths, music, direct stream if not direct path.
|
"""
|
||||||
string = self.__language__
|
Initial setup. Run once upon startup.
|
||||||
addonId = self.addonId
|
Check server, user, direct paths, music, direct stream if not direct
|
||||||
|
path.
|
||||||
|
"""
|
||||||
##### SERVER INFO #####
|
##### SERVER INFO #####
|
||||||
|
|
||||||
self.logMsg("Initial setup called.", 0)
|
self.logMsg("Initial setup called.", 0)
|
||||||
server = self.userClient.getServer()
|
server = self.userClient.getServer()
|
||||||
clientId = self.clientInfo.getDeviceId()
|
clientId = self.clientInfo.getDeviceId()
|
||||||
serverid = self.userClient.getServerId()
|
serverid = self.userClient.getServerId()
|
||||||
myplexlogin, plexLogin, plexToken = self.plx.GetPlexLoginFromSettings()
|
myplexlogin, plexhome, plexLogin, plexToken = self.plx.GetPlexLoginFromSettings()
|
||||||
|
|
||||||
# Optionally sign into plex.tv. Will not be called on very first run
|
# Optionally sign into plex.tv. Will not be called on very first run
|
||||||
|
# as plexToken will be ''
|
||||||
if plexToken and myplexlogin == 'true':
|
if plexToken and myplexlogin == 'true':
|
||||||
chk = self.plx.CheckConnection('plex.tv', plexToken)
|
chk = self.plx.CheckConnection('plex.tv', plexToken)
|
||||||
# HTTP Error: unauthorized
|
# HTTP Error: unauthorized
|
||||||
|
@ -67,7 +64,7 @@ class InitialSetup():
|
||||||
if result:
|
if result:
|
||||||
plexLogin = result['username']
|
plexLogin = result['username']
|
||||||
plexToken = result['token']
|
plexToken = result['token']
|
||||||
elif chk == "":
|
elif chk is False or chk >= 400:
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
dialog.ok(
|
dialog.ok(
|
||||||
self.addonName,
|
self.addonName,
|
||||||
|
@ -77,10 +74,8 @@ class InitialSetup():
|
||||||
# If a Plex server IP has already been set, return.
|
# If a Plex server IP has already been set, return.
|
||||||
if server:
|
if server:
|
||||||
self.logMsg("Server is already set.", 0)
|
self.logMsg("Server is already set.", 0)
|
||||||
self.logMsg(
|
self.logMsg("url: %s, Plex machineIdentifier: %s"
|
||||||
"url: %s, Plex machineIdentifier: %s"
|
% (server, serverid), 0)
|
||||||
% (server, serverid),
|
|
||||||
0)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# If not already retrieved myplex info, optionally let user sign in
|
# If not already retrieved myplex info, optionally let user sign in
|
||||||
|
@ -98,13 +93,12 @@ class InitialSetup():
|
||||||
else:
|
else:
|
||||||
tokenDict = {}
|
tokenDict = {}
|
||||||
# Populate g_PMS variable with the found Plex servers
|
# Populate g_PMS variable with the found Plex servers
|
||||||
self.plx.discoverPMS(
|
self.plx.discoverPMS(clientId,
|
||||||
clientId,
|
|
||||||
None,
|
None,
|
||||||
xbmc.getIPAddress(),
|
xbmc.getIPAddress(),
|
||||||
tokenDict=tokenDict
|
tokenDict=tokenDict)
|
||||||
)
|
self.logMsg("Result of setting g_PMS variable: %s"
|
||||||
self.logMsg("Result of setting g_PMS variable: %s" % self.plx.g_PMS, 2)
|
% self.plx.g_PMS, 2)
|
||||||
isconnected = False
|
isconnected = False
|
||||||
serverlist = self.plx.returnServerList(clientId, self.plx.g_PMS)
|
serverlist = self.plx.returnServerList(clientId, self.plx.g_PMS)
|
||||||
# Let user pick server from a list
|
# Let user pick server from a list
|
||||||
|
@ -122,7 +116,7 @@ class InitialSetup():
|
||||||
dialoglist.append(str(server['name']))
|
dialoglist.append(str(server['name']))
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
resp = dialog.select(
|
resp = dialog.select(
|
||||||
'Plex server to connect to?',
|
'Choose your Plex server',
|
||||||
dialoglist)
|
dialoglist)
|
||||||
server = serverlist[resp]
|
server = serverlist[resp]
|
||||||
activeServer = server['machineIdentifier']
|
activeServer = server['machineIdentifier']
|
||||||
|
@ -131,18 +125,19 @@ class InitialSetup():
|
||||||
# Deactive SSL verification if the server is local!
|
# Deactive SSL verification if the server is local!
|
||||||
if server['local'] == '1':
|
if server['local'] == '1':
|
||||||
self.addon.setSetting('sslverify', 'false')
|
self.addon.setSetting('sslverify', 'false')
|
||||||
self.logMsg("Setting SSL verify to false, because server is local", 1)
|
self.logMsg("Setting SSL verify to false, because server is "
|
||||||
|
"local", 1)
|
||||||
else:
|
else:
|
||||||
self.addon.setSetting('sslverify', 'true')
|
self.addon.setSetting('sslverify', 'true')
|
||||||
self.logMsg("Setting SSL verify to true, because server is not local", 1)
|
self.logMsg("Setting SSL verify to true, because server is "
|
||||||
|
"not local", 1)
|
||||||
chk = self.plx.CheckConnection(url, server['accesstoken'])
|
chk = self.plx.CheckConnection(url, server['accesstoken'])
|
||||||
# Unauthorized
|
# Unauthorized
|
||||||
if chk == 401:
|
if chk == 401:
|
||||||
dialog.ok(
|
dialog.ok(self.addonName,
|
||||||
self.addonName,
|
'Not yet authorized for Plex server %s'
|
||||||
'Not yet authorized for Plex server %s' % str(server['name']),
|
% str(server['name']),
|
||||||
'Please sign in to plex.tv.'
|
'Please sign in to plex.tv.')
|
||||||
)
|
|
||||||
result = self.plx.PlexTvSignInWithPin()
|
result = self.plx.PlexTvSignInWithPin()
|
||||||
if result:
|
if result:
|
||||||
plexLogin = result['username']
|
plexLogin = result['username']
|
||||||
|
@ -151,13 +146,11 @@ class InitialSetup():
|
||||||
# Exit while loop if user cancels
|
# Exit while loop if user cancels
|
||||||
break
|
break
|
||||||
# Problems connecting
|
# Problems connecting
|
||||||
elif chk == '':
|
elif chk >= 400 or chk is False:
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
resp = dialog.yesno(
|
resp = dialog.yesno(self.addonName,
|
||||||
self.addonName,
|
|
||||||
'Problems connecting to server.',
|
'Problems connecting to server.',
|
||||||
'Pick another server?'
|
'Pick another server?')
|
||||||
)
|
|
||||||
# Exit while loop if user chooses No
|
# Exit while loop if user chooses No
|
||||||
if not resp:
|
if not resp:
|
||||||
break
|
break
|
||||||
|
@ -167,16 +160,20 @@ class InitialSetup():
|
||||||
break
|
break
|
||||||
if not isconnected:
|
if not isconnected:
|
||||||
# Enter Kodi settings instead
|
# Enter Kodi settings instead
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId)
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
|
||||||
return
|
return
|
||||||
# Write to Kodi settings file
|
# Write to Kodi settings file
|
||||||
self.addon.setSetting('serverid', activeServer)
|
self.addon.setSetting('plex_machineIdentifier', activeServer)
|
||||||
self.addon.setSetting('ipaddress', server['ip'])
|
self.addon.setSetting('ipaddress', server['ip'])
|
||||||
self.addon.setSetting('port', server['port'])
|
self.addon.setSetting('port', server['port'])
|
||||||
if server['scheme'] == 'https':
|
if server['scheme'] == 'https':
|
||||||
self.addon.setSetting('https', 'true')
|
self.addon.setSetting('https', 'true')
|
||||||
else:
|
else:
|
||||||
self.addon.setSetting('https', 'false')
|
self.addon.setSetting('https', 'false')
|
||||||
|
self.logMsg("Wrote to Kodi user settings file:", 0)
|
||||||
|
self.logMsg("PMS machineIdentifier: %s, ip: %s, port: %s, https: %s "
|
||||||
|
% (activeServer, server['ip'], server['port'],
|
||||||
|
server['scheme']), 0)
|
||||||
|
|
||||||
##### ADDITIONAL PROMPTS #####
|
##### ADDITIONAL PROMPTS #####
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
|
@ -302,24 +302,11 @@ class LibrarySync(threading.Thread):
|
||||||
# Save last sync time
|
# Save last sync time
|
||||||
overlap = 2
|
overlap = 2
|
||||||
|
|
||||||
url = "{server}/Emby.Kodi.SyncQueue/GetServerDateTime?format=json"
|
|
||||||
result = self.doUtils.downloadUrl(url)
|
|
||||||
try: # datetime fails when used more than once, TypeError
|
|
||||||
server_time = result['ServerDateTime']
|
|
||||||
server_time = datetime.strptime(server_time, "%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
# If the server plugin is not installed or an error happened.
|
|
||||||
self.logMsg("An exception occurred: %s" % e, 1)
|
self.logMsg("An exception occurred: %s" % e, 1)
|
||||||
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
time_now = datetime.utcnow()-timedelta(minutes=overlap)
|
||||||
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1)
|
self.logMsg("New sync time: client time -%s min: %s"
|
||||||
|
% (overlap, lastSync), 1)
|
||||||
else:
|
|
||||||
lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
||||||
self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
utils.settings('LastIncrementalSync', value=lastSync)
|
utils.settings('LastIncrementalSync', value=lastSync)
|
||||||
|
|
||||||
def shouldStop(self):
|
def shouldStop(self):
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
|
@ -10,7 +9,6 @@ import xbmcgui
|
||||||
import xbmcaddon
|
import xbmcaddon
|
||||||
import xbmcvfs
|
import xbmcvfs
|
||||||
|
|
||||||
import artwork
|
|
||||||
import utils
|
import utils
|
||||||
import clientinfo
|
import clientinfo
|
||||||
import downloadutils
|
import downloadutils
|
||||||
|
@ -139,12 +137,7 @@ class UserClient(threading.Thread):
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def getServerId(self):
|
def getServerId(self):
|
||||||
alternate = utils.settings('altip') == "true"
|
serverId = utils.settings('plex_machineIdentifier')
|
||||||
if alternate:
|
|
||||||
# Alternate host
|
|
||||||
serverId = utils.settings('secondserverid')
|
|
||||||
else:
|
|
||||||
serverId = utils.settings('serverid')
|
|
||||||
return serverId
|
return serverId
|
||||||
|
|
||||||
def getToken(self):
|
def getToken(self):
|
||||||
|
@ -280,17 +273,17 @@ class UserClient(threading.Thread):
|
||||||
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
||||||
|
|
||||||
username = self.getUsername()
|
username = self.getUsername()
|
||||||
|
userId = utils.settings('userId%s' % username)
|
||||||
server = self.getServer()
|
server = self.getServer()
|
||||||
machineIdentifier = self.getServerId()
|
|
||||||
|
|
||||||
# If there's no settings.xml
|
# If there's no settings.xml
|
||||||
if not hasSettings:
|
if not hasSettings:
|
||||||
self.logMsg("No settings.xml found.", 1)
|
self.logMsg("No settings.xml found.", 0)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
# If no user information
|
# If no user information
|
||||||
elif not server:
|
elif not server:
|
||||||
self.logMsg("Missing server information.", 1)
|
self.logMsg("Missing server information.", 0)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return
|
||||||
# If there's a token, load the user
|
# If there's a token, load the user
|
||||||
|
@ -302,44 +295,50 @@ class UserClient(threading.Thread):
|
||||||
else:
|
else:
|
||||||
self.logMsg("Current user: %s" % self.currUser, 1)
|
self.logMsg("Current user: %s" % self.currUser, 1)
|
||||||
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
||||||
self.logMsg("Current accessToken: %s" % self.currToken, 2)
|
self.logMsg("Current accessToken: xxxx", 1)
|
||||||
return
|
return
|
||||||
|
|
||||||
##### AUTHENTICATE USER #####
|
##### AUTHENTICATE USER #####
|
||||||
|
|
||||||
# Choose Plex user login
|
# Choose Plex user login
|
||||||
accessToken = ""
|
myplexlogin, plexhome, plexLogin, dont_use_accessToken = \
|
||||||
myplexlogin = utils.settings('myplexlogin')
|
plx.GetPlexLoginFromSettings()
|
||||||
if myplexlogin == "true":
|
self.logMsg("myplexlogin: %s, plexhome: %s, plexLogin: %s"
|
||||||
|
% (myplexlogin, plexhome, plexLogin), 2)
|
||||||
|
if myplexlogin == "true" and plexhome == 'true':
|
||||||
username, userId, accessToken = plx.ChoosePlexHomeUser()
|
username, userId, accessToken = plx.ChoosePlexHomeUser()
|
||||||
else:
|
else:
|
||||||
# Try connecting without credentials
|
self.logMsg("Trying to connect to PMS without a token", 0)
|
||||||
pass
|
accessToken = ''
|
||||||
# Check connection
|
# Check connection
|
||||||
if plx.CheckConnection(server, accessToken) == 200:
|
if plx.CheckConnection(server, accessToken) == 200:
|
||||||
self.currUser = username
|
self.currUser = username
|
||||||
xbmcgui.Dialog().notification("Emby server", "Welcome %s!" % username)
|
if username:
|
||||||
|
xbmcgui.Dialog().notification(self.addonName, "Welcome %s"
|
||||||
|
% username)
|
||||||
|
else:
|
||||||
|
xbmcgui.Dialog().notification(self.addonName, "Welcome")
|
||||||
utils.settings('accessToken', value=accessToken)
|
utils.settings('accessToken', value=accessToken)
|
||||||
utils.settings('userId%s' % username, value=userId)
|
utils.settings('userId%s' % username, value=userId)
|
||||||
self.logMsg("User authenticated with an access token", 1)
|
self.logMsg("User authenticated with an access token", 1)
|
||||||
self.loadCurrUser(authenticated=True)
|
self.loadCurrUser(authenticated=True)
|
||||||
utils.window('emby_serverStatus', clear=True)
|
utils.window('emby_serverStatus', clear=True)
|
||||||
|
# Write plex_machineIdentifier to window
|
||||||
|
plex_machineIdentifier = utils.settings('plex_machineIdentifier')
|
||||||
|
utils.windows('plex_machineIdentifier', plex_machineIdentifier)
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
else:
|
else:
|
||||||
self.logMsg("User authentication failed.", 1)
|
self.logMsg("Error: user authentication failed.", -1)
|
||||||
utils.settings('accessToken', value="")
|
utils.settings('accessToken', value="")
|
||||||
utils.settings('userId%s' % username, value="")
|
utils.settings('userId%s' % username, value="")
|
||||||
|
|
||||||
# Give 3 attempts at entering password / selecting user
|
# Give 3 attempts at entering password / selecting user
|
||||||
if self.retry == 3:
|
if self.retry == 3:
|
||||||
self.logMsg("""Too many retries. You can retry by resetting
|
|
||||||
attempts in the addon settings.""", 1)
|
|
||||||
utils.window('emby_serverStatus', value="Stop")
|
utils.window('emby_serverStatus', value="Stop")
|
||||||
xbmcgui.Dialog().ok(
|
xbmcgui.Dialog().ok(heading=self.addonName,
|
||||||
heading=self.addonName,
|
line1="Failed to authenticate too many"
|
||||||
line1="Failed to authenticate too many times.",
|
"times.",
|
||||||
line2="You can retry by resetting attempts in the addon "
|
line2="You can retry by resetting attempts"
|
||||||
"settings.")
|
" in the addon settings.")
|
||||||
self.retry += 1
|
self.retry += 1
|
||||||
self.auth = False
|
self.auth = False
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,21 @@
|
||||||
<setting id="secondsslverify" subsetting="true" label="Verify Host SSL Certificate" type="bool" default="true" visible="eq(-1,true)" />
|
<setting id="secondsslverify" subsetting="true" label="Verify Host SSL Certificate" type="bool" default="true" visible="eq(-1,true)" />
|
||||||
<setting id="secondsslcert" subsetting="true" label="Client SSL certificate" type="file" default="None" visible="eq(-2,true)" />
|
<setting id="secondsslcert" subsetting="true" label="Client SSL certificate" type="file" default="None" visible="eq(-2,true)" />
|
||||||
<!-- User settings -->
|
<!-- User settings -->
|
||||||
<setting id="myplexlogin" label="Log into plex.tv?" type="bool" default="true" />
|
|
||||||
<setting id="plexLogin" label="30024" type="text" default="" visible="eq(-1,true)" />
|
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="deviceNameOpt" label="Use altername Device Name" type="bool" default="false" />
|
<setting id="deviceNameOpt" label="Change device name (friendly name)" type="bool" default="false" />
|
||||||
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
|
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
|
||||||
<setting label="[COLOR yellow]Reset login attempts[/COLOR]" type="action" visible="eq(1,) + !eq(-15,)" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=resetauth)" option="close" />
|
<setting label="[COLOR yellow]Reset login attempts[/COLOR]" type="action" visible="eq(1,) + !eq(-15,)" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=resetauth)" option="close" />
|
||||||
<setting id="accessToken" type="text" visible="false" default="" />
|
<setting id="accessToken" type="text" visible="false" default="" />
|
||||||
<setting id="pathsub" type="bool" visible="false" default="false" />
|
<setting id="pathsub" type="bool" visible="false" default="false" />
|
||||||
</category>
|
</category>
|
||||||
|
|
||||||
|
<category label="plex.tv"><!-- plex.tv -->
|
||||||
|
<!-- Primary address -->
|
||||||
|
<setting id="myplexlogin" label="Log into plex.tv?" type="bool" default="true" />
|
||||||
|
<setting id="plexLogin" label="plex.tv username" type="text" default="" visible="eq(-1,true)" />
|
||||||
|
<setting id="plexhome" label="Plex home in use (don't change this)" type="bool" default="true" />
|
||||||
|
</category>
|
||||||
|
|
||||||
<category label="Sync Options">
|
<category label="Sync Options">
|
||||||
<setting id="dbSyncIndicator" label="Show sync progress" type="bool" default="false" />
|
<setting id="dbSyncIndicator" label="Show sync progress" type="bool" default="false" />
|
||||||
<setting id="syncEmptyShows" type="bool" label="Sync empty TV Shows" default="false" />
|
<setting id="syncEmptyShows" type="bool" label="Sync empty TV Shows" default="false" />
|
||||||
|
|
Loading…
Reference in a new issue