Merge branch 'develop'
This commit is contained in:
commit
a76b5397f6
13 changed files with 370 additions and 191 deletions
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.plexkodiconnect"
|
<addon id="plugin.video.plexkodiconnect"
|
||||||
name="PlexKodiConnect"
|
name="PlexKodiConnect"
|
||||||
version="1.0.4"
|
version="1.0.5"
|
||||||
provider-name="croneter">
|
provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
version 1.0.5
|
||||||
|
- Catch exceptions in itemtypes and log them
|
||||||
|
- Slightly increased download timeouts
|
||||||
|
- Overhaul userclient
|
||||||
|
|
||||||
version 1.0.4
|
version 1.0.4
|
||||||
- Sleep for a while in loops - drastically reduces CPU load
|
- Sleep for a while in loops - drastically reduces CPU load
|
||||||
- Connect to remote PMS!
|
- Connect to remote PMS!
|
||||||
|
|
|
@ -298,6 +298,7 @@
|
||||||
<string id="30533">Duration of the music library pop up (in seconds)</string>
|
<string id="30533">Duration of the music library pop up (in seconds)</string>
|
||||||
<string id="30534">Server messages</string>
|
<string id="30534">Server messages</string>
|
||||||
<string id="30535">Generate a new device Id</string>
|
<string id="30535">Generate a new device Id</string>
|
||||||
|
<string id="30536">Users must log in every time when Kodi restarts</string>
|
||||||
|
|
||||||
<!-- service add-on -->
|
<!-- service add-on -->
|
||||||
<string id="33000">Welcome</string>
|
<string id="33000">Welcome</string>
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
<string id="30521">Bei Wiederaufnahme zurückspulen (in Sekunden)</string>
|
<string id="30521">Bei Wiederaufnahme zurückspulen (in Sekunden)</string>
|
||||||
<string id="30505">[COLOR yellow]Anzahl Login-Versuche zurücksetzen[/COLOR]</string>
|
<string id="30505">[COLOR yellow]Anzahl Login-Versuche zurücksetzen[/COLOR]</string>
|
||||||
|
|
||||||
|
<string id="30536">Benutzer müssen sich bei jedem Neustart von Kodi neu anmelden</string>
|
||||||
|
|
||||||
|
|
||||||
<string id="30014">Verbindung</string>
|
<string id="30014">Verbindung</string>
|
||||||
<string id="30015">Netzwerk</string>
|
<string id="30015">Netzwerk</string>
|
||||||
|
|
|
@ -92,9 +92,11 @@ class PlexAPI():
|
||||||
'plexToken': utils.settings('plexToken'),
|
'plexToken': utils.settings('plexToken'),
|
||||||
'plexhome': utils.settings('plexhome'),
|
'plexhome': utils.settings('plexhome'),
|
||||||
'plexid': utils.settings('plexid'),
|
'plexid': utils.settings('plexid'),
|
||||||
'myplexlogin': utils.settings('myplexlogin')
|
'myplexlogin': utils.settings('myplexlogin'),
|
||||||
|
'plexAvatar': utils.settings('plexAvatar'),
|
||||||
|
'plexHomeSize': utils.settings('plexHomeSize')
|
||||||
|
|
||||||
plexLogin is unicode or empty unicode string u''
|
Returns strings or unicode
|
||||||
|
|
||||||
Returns empty strings '' for a setting if not found.
|
Returns empty strings '' for a setting if not found.
|
||||||
|
|
||||||
|
@ -102,11 +104,13 @@ class PlexAPI():
|
||||||
plexhome is 'true' if plex home is used (the default)
|
plexhome is 'true' if plex home is used (the default)
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'plexLogin': utils.settings('plexLogin').decode('utf-8'),
|
'plexLogin': utils.settings('plexLogin'),
|
||||||
'plexToken': utils.settings('plexToken'),
|
'plexToken': utils.settings('plexToken'),
|
||||||
'plexhome': utils.settings('plexhome'),
|
'plexhome': utils.settings('plexhome'),
|
||||||
'plexid': utils.settings('plexid'),
|
'plexid': utils.settings('plexid'),
|
||||||
'myplexlogin': utils.settings('myplexlogin')
|
'myplexlogin': utils.settings('myplexlogin'),
|
||||||
|
'plexAvatar': utils.settings('plexAvatar'),
|
||||||
|
'plexHomeSize': utils.settings('plexHomeSize')
|
||||||
}
|
}
|
||||||
|
|
||||||
def GetPlexLoginAndPassword(self):
|
def GetPlexLoginAndPassword(self):
|
||||||
|
@ -158,12 +162,14 @@ class PlexAPI():
|
||||||
"""
|
"""
|
||||||
Prompts user to sign in by visiting https://plex.tv/pin
|
Prompts user to sign in by visiting https://plex.tv/pin
|
||||||
|
|
||||||
Writes plexhome, username and token to Kodi settings file. Returns:
|
Writes to Kodi settings file. Also returns:
|
||||||
{
|
{
|
||||||
'plexhome': 'true' if Plex Home, 'false' otherwise
|
'plexhome': 'true' if Plex Home, 'false' otherwise
|
||||||
'username':
|
'username':
|
||||||
'avatar': URL to user avator
|
'avatar': URL to user avator
|
||||||
'token':
|
'token':
|
||||||
|
'plexid': Plex user ID
|
||||||
|
'homesize': Number of Plex home users (defaults to '1')
|
||||||
}
|
}
|
||||||
Returns False if authentication did not work.
|
Returns False if authentication did not work.
|
||||||
"""
|
"""
|
||||||
|
@ -202,19 +208,23 @@ class PlexAPI():
|
||||||
else:
|
else:
|
||||||
home = 'false'
|
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')
|
||||||
|
homeSize = xml.get('homeSize', '1')
|
||||||
result = {
|
result = {
|
||||||
'plexhome': home,
|
'plexhome': home,
|
||||||
'username': username,
|
'username': username,
|
||||||
'avatar': avatar,
|
'avatar': avatar,
|
||||||
'token': token,
|
'token': token,
|
||||||
'plexid': userid
|
'plexid': userid,
|
||||||
|
'homesize': homeSize
|
||||||
}
|
}
|
||||||
utils.settings('plexLogin', username)
|
utils.settings('plexLogin', username)
|
||||||
utils.settings('plexToken', token)
|
utils.settings('plexToken', token)
|
||||||
utils.settings('plexhome', home)
|
utils.settings('plexhome', home)
|
||||||
utils.settings('plexid', userid)
|
utils.settings('plexid', userid)
|
||||||
|
utils.settings('plexAvatar', avatar)
|
||||||
|
utils.settings('plexHomeSize', homeSize)
|
||||||
# Let Kodi log into plex.tv on startup from now on
|
# Let Kodi log into plex.tv on startup from now on
|
||||||
utils.settings('myplexlogin', 'true')
|
utils.settings('myplexlogin', 'true')
|
||||||
return result
|
return result
|
||||||
|
@ -334,7 +344,7 @@ class PlexAPI():
|
||||||
# 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()
|
header = self.getXArgsDeviceInfo()
|
||||||
if token is not None:
|
if token:
|
||||||
header['X-Plex-Token'] = token
|
header['X-Plex-Token'] = token
|
||||||
sslverify = utils.settings('sslverify')
|
sslverify = utils.settings('sslverify')
|
||||||
if sslverify == "true":
|
if sslverify == "true":
|
||||||
|
@ -780,7 +790,7 @@ class PlexAPI():
|
||||||
XML = etree.parse(response)
|
XML = etree.parse(response)
|
||||||
# Log received XML if debugging enabled.
|
# Log received XML if debugging enabled.
|
||||||
self.logMsg("====== received PMS-XML ======", 1)
|
self.logMsg("====== received PMS-XML ======", 1)
|
||||||
self.logMsg(XML.getroot(), 1)
|
self.logMsg(XML, 1)
|
||||||
self.logMsg("====== PMS-XML finished ======", 1)
|
self.logMsg("====== PMS-XML finished ======", 1)
|
||||||
return XML
|
return XML
|
||||||
|
|
||||||
|
@ -1050,42 +1060,41 @@ class PlexAPI():
|
||||||
self.logMsg("Avatar url for user %s is: %s" % (username, url), 1)
|
self.logMsg("Avatar url for user %s is: %s" % (username, url), 1)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def ChoosePlexHomeUser(self):
|
def ChoosePlexHomeUser(self, plexToken):
|
||||||
"""
|
"""
|
||||||
Let's user choose from a list of Plex home users. Will switch to that
|
Let's user choose from a list of Plex home users. Will switch to that
|
||||||
user accordingly.
|
user accordingly.
|
||||||
|
|
||||||
Output:
|
Returns a dict:
|
||||||
username
|
{
|
||||||
userid
|
'username': Unicode
|
||||||
authtoken
|
'userid': '' Plex ID of the user
|
||||||
|
'token': '' User's token
|
||||||
|
'protected': True if PIN is needed, else False
|
||||||
|
}
|
||||||
|
|
||||||
Will return empty strings if failed.
|
Will return False if something went wrong (wrong PIN, no connection)
|
||||||
"""
|
"""
|
||||||
string = self.__language__
|
string = self.__language__
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
|
|
||||||
plexLogin = utils.settings('plexLogin')
|
|
||||||
plexToken = utils.settings('plexToken')
|
|
||||||
machineIdentifier = utils.settings('plex_machineIdentifier')
|
|
||||||
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
|
|
||||||
if not users:
|
if not users:
|
||||||
utils.settings('username', value=plexLogin)
|
self.logMsg("User download failed.", -1)
|
||||||
self.logMsg("User download failed. Set username = plexlogin", 0)
|
return False
|
||||||
return ('', '', '')
|
|
||||||
|
|
||||||
userlist = []
|
userlist = []
|
||||||
userlistCoded = []
|
userlistCoded = []
|
||||||
for user in users:
|
for user in users:
|
||||||
username = user['title']
|
username = user['title']
|
||||||
userlist.append(username)
|
userlist.append(username)
|
||||||
|
# To take care of non-ASCII usernames
|
||||||
userlistCoded.append(username.encode('utf-8'))
|
userlistCoded.append(username.encode('utf-8'))
|
||||||
usernumber = len(userlist)
|
usernumber = len(userlist)
|
||||||
|
|
||||||
|
username = ''
|
||||||
usertoken = ''
|
usertoken = ''
|
||||||
# Plex home not in use: only 1 user returned
|
|
||||||
trials = 0
|
trials = 0
|
||||||
while trials < 3:
|
while trials < 3:
|
||||||
if usernumber > 1:
|
if usernumber > 1:
|
||||||
|
@ -1094,58 +1103,62 @@ class PlexAPI():
|
||||||
self.addonName + string(39306),
|
self.addonName + string(39306),
|
||||||
userlistCoded)
|
userlistCoded)
|
||||||
if user_select == -1:
|
if user_select == -1:
|
||||||
self.logMsg("No user selected.", 1)
|
self.logMsg("No user selected.", 0)
|
||||||
|
utils.settings('username', value='')
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)'
|
xbmc.executebuiltin('Addon.OpenSettings(%s)'
|
||||||
% self.addonId)
|
% self.addonId)
|
||||||
return ('', '', '')
|
return False
|
||||||
# No Plex home in use - only 1 user
|
# Only 1 user received, choose that one
|
||||||
else:
|
else:
|
||||||
user_select = 0
|
user_select = 0
|
||||||
selected_user = userlist[user_select]
|
selected_user = userlist[user_select]
|
||||||
self.logMsg("Selected user: %s" % selected_user, 1)
|
self.logMsg("Selected user: %s" % selected_user, 0)
|
||||||
utils.settings('username', value=selected_user)
|
|
||||||
user = users[user_select]
|
user = users[user_select]
|
||||||
# Ask for PIN, if protected:
|
# Ask for PIN, if protected:
|
||||||
|
pin = None
|
||||||
if user['protected'] == '1':
|
if user['protected'] == '1':
|
||||||
# Please enter pin for user
|
|
||||||
self.logMsg('Asking for users PIN', 1)
|
self.logMsg('Asking for users PIN', 1)
|
||||||
pin = dialog.input(
|
pin = dialog.input(
|
||||||
string(39307) + selected_user,
|
string(39307) + selected_user,
|
||||||
type=xbmcgui.INPUT_NUMERIC,
|
type=xbmcgui.INPUT_NUMERIC,
|
||||||
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
option=xbmcgui.ALPHANUM_HIDE_INPUT)
|
||||||
# User chose to cancel
|
# User chose to cancel
|
||||||
if pin is None:
|
# Plex bug: don't call url for protected user with empty PIN
|
||||||
break
|
if not pin:
|
||||||
else:
|
trials += 1
|
||||||
pin = None
|
continue
|
||||||
# Switch to this Plex Home user, if applicable
|
# Switch to this Plex Home user, if applicable
|
||||||
# Plex bug: don't call url for protected user with empty PIN
|
|
||||||
if user['protected'] == '1' and not pin:
|
|
||||||
break
|
|
||||||
username, usertoken = self.PlexSwitchHomeUser(
|
username, usertoken = self.PlexSwitchHomeUser(
|
||||||
user['id'],
|
user['id'],
|
||||||
pin,
|
pin,
|
||||||
plexToken,
|
plexToken,
|
||||||
machineIdentifier
|
utils.settings('plex_machineIdentifier'))
|
||||||
)
|
|
||||||
# Couldn't get user auth
|
# Couldn't get user auth
|
||||||
if not username:
|
if not username:
|
||||||
|
trials += 1
|
||||||
# Could not login user, please try again
|
# Could not login user, please try again
|
||||||
if not dialog.yesno(self.addonName,
|
if not dialog.yesno(self.addonName,
|
||||||
string(39308) + selected_user,
|
string(39308) + selected_user,
|
||||||
string(39309)):
|
string(39309)):
|
||||||
# User chose to cancel
|
# User chose to cancel
|
||||||
break
|
break
|
||||||
# Successfully retrieved: break out of while loop
|
|
||||||
else:
|
else:
|
||||||
|
# Successfully retrieved username: break out of while loop
|
||||||
break
|
break
|
||||||
trials += trials
|
|
||||||
if not username:
|
|
||||||
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
|
|
||||||
return ('', '', '')
|
|
||||||
return (username, user['id'], usertoken)
|
|
||||||
|
|
||||||
def PlexSwitchHomeUser(self, userId, pin, token, machineId):
|
if not username:
|
||||||
|
self.logMsg('Failed signing in a user to plex.tv', -1)
|
||||||
|
xbmc.executebuiltin('Addon.OpenSettings(%s)' % self.addonId)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return {
|
||||||
|
'username': username,
|
||||||
|
'userid': user['id'],
|
||||||
|
'protected': True if user['protected'] == '1' else False,
|
||||||
|
'token': usertoken
|
||||||
|
}
|
||||||
|
|
||||||
|
def PlexSwitchHomeUser(self, userId, pin, token, machineIdentifier):
|
||||||
"""
|
"""
|
||||||
Retrieves Plex home token for a Plex home user.
|
Retrieves Plex home token for a Plex home user.
|
||||||
|
|
||||||
|
@ -1153,7 +1166,6 @@ class PlexAPI():
|
||||||
userId 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
|
||||||
token token for plex.tv
|
token token for plex.tv
|
||||||
machineId Plex PMS machineIdentifier
|
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
(username, token)
|
(username, token)
|
||||||
|
@ -1165,7 +1177,9 @@ class PlexAPI():
|
||||||
url += '?pin=' + pin
|
url += '?pin=' + pin
|
||||||
self.logMsg('Switching to user %s' % userId, 0)
|
self.logMsg('Switching to user %s' % userId, 0)
|
||||||
answer = self.TalkToPlexServer(url, talkType="POST", token=token)
|
answer = self.TalkToPlexServer(url, talkType="POST", token=token)
|
||||||
if not answer:
|
try:
|
||||||
|
answer.attrib
|
||||||
|
except:
|
||||||
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
||||||
return ('', '')
|
return ('', '')
|
||||||
|
|
||||||
|
@ -1173,28 +1187,25 @@ class PlexAPI():
|
||||||
token = answer.attrib.get('authenticationToken', '')
|
token = answer.attrib.get('authenticationToken', '')
|
||||||
|
|
||||||
# Get final token
|
# Get final token
|
||||||
url = 'https://plex.tv/pms/servers.xml'
|
url = 'https://plex.tv/api/resources?includeHttps=1'
|
||||||
answer = self.TalkToPlexServer(url, talkType="GET", token=token)
|
xml = self.TalkToPlexServer(url, talkType="GET", token=token)
|
||||||
if not answer:
|
try:
|
||||||
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
xml.attrib
|
||||||
|
except:
|
||||||
|
self.logMsg('switch HomeUser failed - plex.tv answer wrong', -1)
|
||||||
return ('', '')
|
return ('', '')
|
||||||
|
|
||||||
found = 0
|
found = 0
|
||||||
for child in answer:
|
for device in xml:
|
||||||
if child.attrib['machineIdentifier'] == machineId:
|
if device.attrib.get('clientIdentifier') == machineIdentifier:
|
||||||
token = child.attrib['accessToken']
|
|
||||||
self.logMsg('Found a plex home user token', 1)
|
|
||||||
found += 1
|
found += 1
|
||||||
|
token = device.attrib.get('accessToken')
|
||||||
if found == 0:
|
if found == 0:
|
||||||
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
|
self.logMsg('No tokens found for your server!', -1)
|
||||||
return ('', '')
|
return ('', '')
|
||||||
|
|
||||||
self.logMsg('Plex.tv switch HomeUser change successfull', 0)
|
self.logMsg('Plex.tv switch HomeUser change successfull', 0)
|
||||||
self.logMsg("username: %s, token: xxxx. "
|
self.logMsg("username: %s, token: xxxx. " % username, 0)
|
||||||
"Saving to window and file settings" % username, 0)
|
|
||||||
utils.window('emby_currUser', value=userId)
|
|
||||||
utils.settings('userId', value=userId)
|
|
||||||
utils.settings('username', value=username)
|
|
||||||
utils.window('emby_accessToken%s' % userId, value=token)
|
|
||||||
return (username, token)
|
return (username, token)
|
||||||
|
|
||||||
def MyPlexListHomeUsers(self, authtoken):
|
def MyPlexListHomeUsers(self, authtoken):
|
||||||
|
@ -1218,9 +1229,11 @@ class PlexAPI():
|
||||||
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)
|
||||||
If an error is encountered, False is returned
|
If an error is encountered, False is returned
|
||||||
"""
|
"""
|
||||||
XML = self.getXMLFromPMS('https://plex.tv', '/api/home/users/', {}, authtoken)
|
XML = self.getXMLFromPMS(
|
||||||
|
'https://plex.tv', '/api/home/users/', {}, authtoken)
|
||||||
if not XML:
|
if not XML:
|
||||||
# Download failed; quitting with False
|
self.logMsg('Download of Plex home users failed.', -1)
|
||||||
|
self.logMsg('plex.tv xml received was: %s' % XML, -1)
|
||||||
return False
|
return False
|
||||||
# analyse response
|
# analyse response
|
||||||
root = XML.getroot()
|
root = XML.getroot()
|
||||||
|
|
|
@ -37,7 +37,7 @@ class DownloadUtils():
|
||||||
|
|
||||||
# Requests session
|
# Requests session
|
||||||
s = None
|
s = None
|
||||||
timeout = 3
|
timeout = 10
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
|
@ -224,7 +224,8 @@ class DownloadUtils():
|
||||||
# Get user information
|
# Get user information
|
||||||
self.userId = utils.window('emby_currUser')
|
self.userId = utils.window('emby_currUser')
|
||||||
self.server = utils.window('emby_server%s' % self.userId)
|
self.server = utils.window('emby_server%s' % self.userId)
|
||||||
self.token = utils.window('emby_accessToken%s' % self.userId)
|
self.token = utils.window(
|
||||||
|
'emby_accessToken%s' % self.userId)
|
||||||
header = self.getHeader(options=headerOptions)
|
header = self.getHeader(options=headerOptions)
|
||||||
verifyssl = False
|
verifyssl = False
|
||||||
cert = None
|
cert = None
|
||||||
|
|
|
@ -79,41 +79,61 @@ def reConnect():
|
||||||
string = xbmcaddon.Addon().getLocalizedString
|
string = xbmcaddon.Addon().getLocalizedString
|
||||||
utils.logMsg("entrypoint reConnect",
|
utils.logMsg("entrypoint reConnect",
|
||||||
"Connection resets requested", 0)
|
"Connection resets requested", 0)
|
||||||
# Pause library sync thread - user needs to be auth in order to sync
|
|
||||||
utils.window('suspend_LibraryThread', value='true')
|
|
||||||
# Suspend the user client during procedure
|
|
||||||
utils.window('suspend_Userclient', value='true')
|
|
||||||
dialog = xbmcgui.Dialog()
|
dialog = xbmcgui.Dialog()
|
||||||
dialog.notification(
|
dialog.notification(
|
||||||
heading=addonName,
|
heading=addonName,
|
||||||
message=string(39207),
|
message=string(39207),
|
||||||
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
|
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
|
||||||
sound=False)
|
sound=False)
|
||||||
|
# Pause library sync thread - user needs to be auth in order to sync
|
||||||
|
utils.window('suspend_LibraryThread', value='true')
|
||||||
|
|
||||||
# Wait max for 20 seconds for all lib scans to finish
|
# Delete plex credentials in settings
|
||||||
|
utils.settings('myplexlogin', value="true")
|
||||||
|
utils.settings('plexLogin', value="")
|
||||||
|
utils.settings('plexToken', value=""),
|
||||||
|
utils.settings('plexid', value="")
|
||||||
|
utils.settings('plexHomeSize', value="")
|
||||||
|
utils.settings('plexAvatar', value="")
|
||||||
|
|
||||||
|
# Wait max for 5 seconds for all lib scans to finish
|
||||||
counter = 0
|
counter = 0
|
||||||
while utils.window('emby_dbScan') == 'true':
|
while utils.window('emby_dbScan') == 'true':
|
||||||
xbmc.sleep(1000)
|
if counter > 100:
|
||||||
counter += 1
|
|
||||||
if counter > 20:
|
|
||||||
dialog.ok(
|
dialog.ok(
|
||||||
heading=addonName,
|
heading=addonName,
|
||||||
message=string(39208),
|
message=string(39208),
|
||||||
)
|
)
|
||||||
# Resuming threads, just in case
|
# Resuming threads, just in case
|
||||||
utils.window('suspend_LibraryThread', clear=True)
|
utils.window('suspend_LibraryThread', clear=True)
|
||||||
utils.window('suspend_Userclient', clear=True)
|
|
||||||
# Abort reConnection
|
# Abort reConnection
|
||||||
return
|
return
|
||||||
|
counter += 1
|
||||||
|
xbmc.sleep(50)
|
||||||
|
# Log out currently signed in user:
|
||||||
|
utils.window('emby_serverStatus', value="401")
|
||||||
|
|
||||||
|
# Above method needs to have run its course! Hence wait
|
||||||
|
counter = 0
|
||||||
|
while utils.window('emby_serverStatus') == "401":
|
||||||
|
if counter > 100:
|
||||||
|
dialog.ok(
|
||||||
|
heading=addonName,
|
||||||
|
message=string(39208),
|
||||||
|
)
|
||||||
|
# Abort reConnection
|
||||||
|
return
|
||||||
|
counter += 1
|
||||||
|
xbmc.sleep(50)
|
||||||
|
# Suspend the user client during procedure
|
||||||
|
utils.window('suspend_Userclient', value='true')
|
||||||
|
|
||||||
import initialsetup
|
import initialsetup
|
||||||
initialsetup.InitialSetup().setup(forcePlexTV=True)
|
initialsetup.InitialSetup().setup(forcePlexTV=True)
|
||||||
# Log out currently signed in user:
|
|
||||||
utils.window('emby_serverStatus', value="401")
|
|
||||||
# Restart user client
|
|
||||||
utils.window('suspend_Userclient', clear=True)
|
|
||||||
# Request lib sync to get user view data (e.g. watched/unwatched)
|
# Request lib sync to get user view data (e.g. watched/unwatched)
|
||||||
utils.window('plex_runLibScan', value='full')
|
utils.window('plex_runLibScan', value='full')
|
||||||
|
# Restart user client
|
||||||
|
utils.window('suspend_Userclient', clear=True)
|
||||||
|
|
||||||
|
|
||||||
def PassPlaylist(xml, resume=None):
|
def PassPlaylist(xml, resume=None):
|
||||||
|
@ -222,7 +242,7 @@ def doMainListing():
|
||||||
addDirectoryItem(label, path)
|
addDirectoryItem(label, path)
|
||||||
|
|
||||||
# Plex user switch, if Plex home is in use
|
# Plex user switch, if Plex home is in use
|
||||||
if utils.settings('plexhome') == 'true':
|
if int(utils.settings('plexHomeSize')) > 1:
|
||||||
addDirectoryItem(string(39200),
|
addDirectoryItem(string(39200),
|
||||||
"plugin://plugin.video.plexkodiconnect/"
|
"plugin://plugin.video.plexkodiconnect/"
|
||||||
"?mode=switchuser")
|
"?mode=switchuser")
|
||||||
|
@ -401,8 +421,17 @@ def switchPlexUser():
|
||||||
# Pause library sync thread - user needs to be auth in order to sync
|
# Pause library sync thread - user needs to be auth in order to sync
|
||||||
utils.window('suspend_LibraryThread', value='true')
|
utils.window('suspend_LibraryThread', value='true')
|
||||||
# Wait to ensure that any sync already going on has finished
|
# Wait to ensure that any sync already going on has finished
|
||||||
|
counter = 0
|
||||||
while utils.window('emby_dbScan') == 'true':
|
while utils.window('emby_dbScan') == 'true':
|
||||||
xbmc.sleep(1000)
|
if counter > 100:
|
||||||
|
# Something went wrong, aborting
|
||||||
|
# Resuming threads, just in case
|
||||||
|
utils.window('suspend_LibraryThread', clear=True)
|
||||||
|
# Abort reConnection
|
||||||
|
return
|
||||||
|
counter += 1
|
||||||
|
xbmc.sleep(50)
|
||||||
|
|
||||||
# Log out currently signed in user:
|
# Log out currently signed in user:
|
||||||
utils.window('emby_serverStatus', value="401")
|
utils.window('emby_serverStatus', value="401")
|
||||||
# Request lib sync to get user view data (e.g. watched/unwatched)
|
# Request lib sync to get user view data (e.g. watched/unwatched)
|
||||||
|
|
|
@ -40,7 +40,7 @@ class image_cache_thread(threading.Thread):
|
||||||
"http://%s:%s/image/image://%s"
|
"http://%s:%s/image/image://%s"
|
||||||
% (self.xbmc_host, self.xbmc_port, self.urlToProcess)),
|
% (self.xbmc_host, self.xbmc_port, self.urlToProcess)),
|
||||||
auth=(self.xbmc_username, self.xbmc_password),
|
auth=(self.xbmc_username, self.xbmc_password),
|
||||||
timeout=(0.1, 0.1))
|
timeout=(2, 2))
|
||||||
# We don't need the result
|
# We don't need the result
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,24 @@ class InitialSetup():
|
||||||
# Problems connecting to plex.tv. Network or internet issue?
|
# Problems connecting to plex.tv. Network or internet issue?
|
||||||
dialog.ok(self.addonName,
|
dialog.ok(self.addonName,
|
||||||
string(39010))
|
string(39010))
|
||||||
|
else:
|
||||||
|
# Successful connected to plex.tv
|
||||||
|
# Refresh the info from Plex.tv
|
||||||
|
url = 'https://plex.tv/'
|
||||||
|
path = 'users/account'
|
||||||
|
xml = self.plx.getXMLFromPMS(url, path, authtoken=plexToken)
|
||||||
|
if xml:
|
||||||
|
xml = xml.getroot()
|
||||||
|
plexLogin = xml.attrib.get('title')
|
||||||
|
utils.settings('plexLogin', value=plexLogin)
|
||||||
|
home = 'true' if xml.attrib.get('home') == '1' else 'false'
|
||||||
|
utils.settings('plexhome', value=home)
|
||||||
|
utils.settings('plexAvatar', value=xml.attrib.get('thumb'))
|
||||||
|
utils.settings(
|
||||||
|
'plexHomeSize', value=xml.attrib.get('homeSize', '1'))
|
||||||
|
self.logMsg('Updated Plex info from plex.tv', 0)
|
||||||
|
else:
|
||||||
|
self.logMsg('Failed to update Plex info from plex.tv', -1)
|
||||||
# If a Plex server IP has already been set, return.
|
# If a Plex server IP has already been set, return.
|
||||||
if server and forcePlexTV is False:
|
if server and forcePlexTV is False:
|
||||||
self.logMsg("Server is already set.", 0)
|
self.logMsg("Server is already set.", 0)
|
||||||
|
@ -175,6 +193,7 @@ class InitialSetup():
|
||||||
return
|
return
|
||||||
# Write to Kodi settings file
|
# Write to Kodi settings file
|
||||||
utils.settings('plex_machineIdentifier', activeServer)
|
utils.settings('plex_machineIdentifier', activeServer)
|
||||||
|
utils.settings('plex_servername', server['name'])
|
||||||
if server['local'] == '1':
|
if server['local'] == '1':
|
||||||
scheme = server['scheme']
|
scheme = server['scheme']
|
||||||
utils.settings('ipaddress', server['ip'])
|
utils.settings('ipaddress', server['ip'])
|
||||||
|
|
|
@ -284,6 +284,17 @@ class Movies(Items):
|
||||||
self.add_updateBoxset(boxset)
|
self.add_updateBoxset(boxset)
|
||||||
|
|
||||||
def add_update(self, item, viewtag=None, viewid=None):
|
def add_update(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_update(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for movies has crashed for item %s. '
|
||||||
|
'Error:' % item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_update(self, item, viewtag=None, viewid=None):
|
||||||
# Process single movie
|
# Process single movie
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
|
@ -296,6 +307,7 @@ class Movies(Items):
|
||||||
# If the item doesn't exist, we'll add it to the database
|
# If the item doesn't exist, we'll add it to the database
|
||||||
update_item = True
|
update_item = True
|
||||||
itemid = API.getRatingKey()
|
itemid = API.getRatingKey()
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
# Cannot parse XML, abort
|
# Cannot parse XML, abort
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg("Cannot parse XML data for movie", -1)
|
self.logMsg("Cannot parse XML data for movie", -1)
|
||||||
|
@ -360,6 +372,7 @@ class Movies(Items):
|
||||||
studio = studios[0]
|
studio = studios[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
studio = None
|
studio = None
|
||||||
|
self.logMsg('Read all attributes', 1)
|
||||||
|
|
||||||
# Find one trailer
|
# Find one trailer
|
||||||
trailer = None
|
trailer = None
|
||||||
|
@ -408,6 +421,7 @@ class Movies(Items):
|
||||||
'mode': "play"
|
'mode': "play"
|
||||||
}
|
}
|
||||||
filename = "%s?%s" % (path, urllib.urlencode(params))
|
filename = "%s?%s" % (path, urllib.urlencode(params))
|
||||||
|
self.logMsg('Path set for item', 1)
|
||||||
##### UPDATE THE MOVIE #####
|
##### UPDATE THE MOVIE #####
|
||||||
if update_item:
|
if update_item:
|
||||||
self.logMsg("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
|
self.logMsg("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
|
||||||
|
@ -454,6 +468,7 @@ class Movies(Items):
|
||||||
# Create the reference in emby table
|
# Create the reference in emby table
|
||||||
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None, checksum, viewid)
|
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None, checksum, viewid)
|
||||||
|
|
||||||
|
self.logMsg('Done add or update for item', 1)
|
||||||
# Update the path
|
# Update the path
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
|
@ -902,6 +917,17 @@ class TVShows(Items):
|
||||||
self.contentPop(title, self.newvideo_time)
|
self.contentPop(title, self.newvideo_time)
|
||||||
|
|
||||||
def add_update(self, item, viewtag=None, viewid=None):
|
def add_update(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_update(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for tv show has crashed for item %s. '
|
||||||
|
'Error:' % item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_update(self, item, viewtag=None, viewid=None):
|
||||||
# Process single tvshow
|
# Process single tvshow
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
|
@ -911,6 +937,8 @@ class TVShows(Items):
|
||||||
|
|
||||||
update_item = True
|
update_item = True
|
||||||
itemid = API.getRatingKey()
|
itemid = API.getRatingKey()
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
|
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg("Cannot parse XML data for TV show", -1)
|
self.logMsg("Cannot parse XML data for TV show", -1)
|
||||||
return
|
return
|
||||||
|
@ -964,6 +992,8 @@ class TVShows(Items):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
studio = None
|
studio = None
|
||||||
|
|
||||||
|
self.logMsg('Read all attributes', 1)
|
||||||
|
|
||||||
# GET THE FILE AND PATH #####
|
# GET THE FILE AND PATH #####
|
||||||
playurl = API.getKey()
|
playurl = API.getKey()
|
||||||
|
|
||||||
|
@ -997,7 +1027,7 @@ class TVShows(Items):
|
||||||
# Set plugin path
|
# Set plugin path
|
||||||
toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/"
|
toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/"
|
||||||
path = "%s%s/" % (toplevelpath, itemid)
|
path = "%s%s/" % (toplevelpath, itemid)
|
||||||
|
self.logMsg('Path set for item', 1)
|
||||||
# UPDATE THE TVSHOW #####
|
# UPDATE THE TVSHOW #####
|
||||||
if update_item:
|
if update_item:
|
||||||
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
|
||||||
|
@ -1052,7 +1082,7 @@ class TVShows(Items):
|
||||||
# Create the reference in emby table
|
# Create the reference in emby table
|
||||||
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
|
||||||
checksum=checksum, mediafolderid=viewid)
|
checksum=checksum, mediafolderid=viewid)
|
||||||
|
self.logMsg('Done add or update for item', 1)
|
||||||
# Update the path
|
# Update the path
|
||||||
query = ' '.join((
|
query = ' '.join((
|
||||||
|
|
||||||
|
@ -1083,13 +1113,25 @@ class TVShows(Items):
|
||||||
all_episodes = embyserver.getEpisodesbyShow(itemid)
|
all_episodes = embyserver.getEpisodesbyShow(itemid)
|
||||||
self.added_episode(all_episodes['Items'], None)
|
self.added_episode(all_episodes['Items'], None)
|
||||||
|
|
||||||
def add_updateSeason(self, item, viewid=None, viewtag=None):
|
def add_updateSeason(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_updateSeason(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for tv seasons has crashed for item %s. '
|
||||||
|
'Error:' % item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_updateSeason(self, item, viewid=None, viewtag=None):
|
||||||
API = PlexAPI.API(item)
|
API = PlexAPI.API(item)
|
||||||
showid = viewid
|
showid = viewid
|
||||||
itemid = API.getRatingKey()
|
itemid = API.getRatingKey()
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg('Error getting itemid for season, skipping', -1)
|
self.logMsg('Error getting itemid for season, skipping', -1)
|
||||||
return
|
return
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
kodi_db = self.kodi_db
|
kodi_db = self.kodi_db
|
||||||
|
@ -1117,6 +1159,17 @@ class TVShows(Items):
|
||||||
emby_db.addReference(itemid, seasonid, "Season", "season", parentid=showid, checksum=checksum)
|
emby_db.addReference(itemid, seasonid, "Season", "season", parentid=showid, checksum=checksum)
|
||||||
|
|
||||||
def add_updateEpisode(self, item, viewtag=None, viewid=None):
|
def add_updateEpisode(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_updateEpisode(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for tv episode has crashed for item %s. '
|
||||||
|
'Error:' % item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_updateEpisode(self, item, viewtag=None, viewid=None):
|
||||||
"""
|
"""
|
||||||
viewtag and viewid are irrelevant!
|
viewtag and viewid are irrelevant!
|
||||||
"""
|
"""
|
||||||
|
@ -1136,6 +1189,7 @@ class TVShows(Items):
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg('Error getting itemid for episode, skipping', -1)
|
self.logMsg('Error getting itemid for episode, skipping', -1)
|
||||||
return
|
return
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||||
try:
|
try:
|
||||||
episodeid = emby_dbitem[0]
|
episodeid = emby_dbitem[0]
|
||||||
|
@ -1584,7 +1638,19 @@ class Music(Items):
|
||||||
if not pdialog and self.contentmsg:
|
if not pdialog and self.contentmsg:
|
||||||
self.contentPop(title, self.newmusic_time)
|
self.contentPop(title, self.newmusic_time)
|
||||||
|
|
||||||
def add_updateArtist(self, item, viewtag=None, viewid=None,
|
def add_updateArtist(self, item, viewtag=None, viewid=None, artisttype="MusicArtist"):
|
||||||
|
try:
|
||||||
|
self.run_add_updateArtist(item, viewtag, viewid, artisttype)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for music artist has crashed for '
|
||||||
|
'item %s. Error:'
|
||||||
|
% item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_updateArtist(self, item, viewtag=None, viewid=None,
|
||||||
artisttype="MusicArtist"):
|
artisttype="MusicArtist"):
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
|
@ -1594,6 +1660,7 @@ class Music(Items):
|
||||||
|
|
||||||
update_item = True
|
update_item = True
|
||||||
itemid = API.getRatingKey()
|
itemid = API.getRatingKey()
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||||
try:
|
try:
|
||||||
artistid = emby_dbitem[0]
|
artistid = emby_dbitem[0]
|
||||||
|
@ -1668,6 +1735,18 @@ class Music(Items):
|
||||||
artwork.addArtwork(artworks, artistid, "artist", kodicursor)
|
artwork.addArtwork(artworks, artistid, "artist", kodicursor)
|
||||||
|
|
||||||
def add_updateAlbum(self, item, viewtag=None, viewid=None):
|
def add_updateAlbum(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_updateAlbum(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for music album has crashed for '
|
||||||
|
'item %s. Error:'
|
||||||
|
% item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_updateAlbum(self, item, viewtag=None, viewid=None):
|
||||||
kodiversion = self.kodiversion
|
kodiversion = self.kodiversion
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
emby_db = self.emby_db
|
emby_db = self.emby_db
|
||||||
|
@ -1680,6 +1759,7 @@ class Music(Items):
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg('Error processing Album, skipping', -1)
|
self.logMsg('Error processing Album, skipping', -1)
|
||||||
return
|
return
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||||
try:
|
try:
|
||||||
albumid = emby_dbitem[0]
|
albumid = emby_dbitem[0]
|
||||||
|
@ -1853,6 +1933,18 @@ class Music(Items):
|
||||||
artwork.addArtwork(artworks, albumid, "album", kodicursor)
|
artwork.addArtwork(artworks, albumid, "album", kodicursor)
|
||||||
|
|
||||||
def add_updateSong(self, item, viewtag=None, viewid=None):
|
def add_updateSong(self, item, viewtag=None, viewid=None):
|
||||||
|
try:
|
||||||
|
self.run_add_updateSong(item, viewtag, viewid)
|
||||||
|
except Exception as e:
|
||||||
|
utils.window('emby_dbScan', clear=True)
|
||||||
|
self.logMsg('itemtypes.py for music song has crashed for '
|
||||||
|
'item %s. Error:'
|
||||||
|
% item.attrib.get('ratingKey', None), -1)
|
||||||
|
self.logMsg(e, -1)
|
||||||
|
# skip this item for now
|
||||||
|
return
|
||||||
|
|
||||||
|
def run_add_updateSong(self, item, viewtag=None, viewid=None):
|
||||||
# Process single song
|
# Process single song
|
||||||
kodiversion = self.kodiversion
|
kodiversion = self.kodiversion
|
||||||
kodicursor = self.kodicursor
|
kodicursor = self.kodicursor
|
||||||
|
@ -1866,6 +1958,7 @@ class Music(Items):
|
||||||
if not itemid:
|
if not itemid:
|
||||||
self.logMsg('Error processing Song; skipping', -1)
|
self.logMsg('Error processing Song; skipping', -1)
|
||||||
return
|
return
|
||||||
|
self.logMsg("Processing item %s" % itemid, 1)
|
||||||
emby_dbitem = emby_db.getItem_byId(itemid)
|
emby_dbitem = emby_db.getItem_byId(itemid)
|
||||||
try:
|
try:
|
||||||
songid = emby_dbitem[0]
|
songid = emby_dbitem[0]
|
||||||
|
|
|
@ -111,6 +111,8 @@ class UserClient(threading.Thread):
|
||||||
settings = utils.settings
|
settings = utils.settings
|
||||||
|
|
||||||
# Original host
|
# Original host
|
||||||
|
self.machineIdentifier = utils.settings('plex_machineIdentifier')
|
||||||
|
self.servername = utils.settings('plex_servername')
|
||||||
HTTPS = settings('https') == "true"
|
HTTPS = settings('https') == "true"
|
||||||
host = settings('ipaddress')
|
host = settings('ipaddress')
|
||||||
port = settings('port')
|
port = settings('port')
|
||||||
|
@ -192,29 +194,17 @@ class UserClient(threading.Thread):
|
||||||
|
|
||||||
def setUserPref(self):
|
def setUserPref(self):
|
||||||
self.logMsg('Setting user preferences', 0)
|
self.logMsg('Setting user preferences', 0)
|
||||||
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
|
# Only try to get user avatar if there is a token
|
||||||
if url:
|
if self.currToken:
|
||||||
utils.window('EmbyUserImage', value=url)
|
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
|
||||||
|
if url:
|
||||||
|
utils.window('EmbyUserImage', value=url)
|
||||||
# Set resume point max
|
# Set resume point max
|
||||||
# url = "{server}/emby/System/Configuration?format=json"
|
# url = "{server}/emby/System/Configuration?format=json"
|
||||||
# result = doUtils.downloadUrl(url)
|
# result = doUtils.downloadUrl(url)
|
||||||
|
|
||||||
# utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
# utils.settings('markPlayed', value=str(result['MaxResumePct']))
|
||||||
|
|
||||||
def getPublicUsers(self):
|
|
||||||
|
|
||||||
server = self.getServer()
|
|
||||||
|
|
||||||
# Get public Users
|
|
||||||
url = "%s/emby/Users/Public?format=json" % server
|
|
||||||
result = self.doUtils.downloadUrl(url, authenticate=False)
|
|
||||||
|
|
||||||
if result != "":
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
# Server connection failed
|
|
||||||
return False
|
|
||||||
|
|
||||||
def hasAccess(self):
|
def hasAccess(self):
|
||||||
# Plex: always return True for now
|
# Plex: always return True for now
|
||||||
return True
|
return True
|
||||||
|
@ -241,18 +231,15 @@ class UserClient(threading.Thread):
|
||||||
xbmcgui.Dialog().notification(self.addonName,
|
xbmcgui.Dialog().notification(self.addonName,
|
||||||
utils.language(33007))
|
utils.language(33007))
|
||||||
|
|
||||||
def loadCurrUser(self, authenticated=False):
|
def loadCurrUser(self, username, userId, usertoken, authenticated=False):
|
||||||
self.logMsg('Loading current user', 0)
|
self.logMsg('Loading current user', 0)
|
||||||
window = utils.window
|
window = utils.window
|
||||||
|
settings = utils.settings
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
username = self.getUsername()
|
|
||||||
userId = self.getUserId()
|
|
||||||
|
|
||||||
self.currUserId = userId
|
self.currUserId = userId
|
||||||
|
self.currToken = usertoken
|
||||||
self.currServer = self.getServer()
|
self.currServer = self.getServer()
|
||||||
self.currToken = self.getToken()
|
|
||||||
self.machineIdentifier = utils.settings('plex_machineIdentifier')
|
|
||||||
self.ssl = self.getSSLverify()
|
self.ssl = self.getSSLverify()
|
||||||
self.sslcert = self.getSSL()
|
self.sslcert = self.getSSL()
|
||||||
|
|
||||||
|
@ -268,7 +255,6 @@ class UserClient(threading.Thread):
|
||||||
return False
|
return False
|
||||||
elif res == 401:
|
elif res == 401:
|
||||||
self.logMsg('Token is no longer valid', -1)
|
self.logMsg('Token is no longer valid', -1)
|
||||||
self.resetClient()
|
|
||||||
return False
|
return False
|
||||||
elif res >= 400:
|
elif res >= 400:
|
||||||
self.logMsg('Answer from PMS is not as expected. Retrying', -1)
|
self.logMsg('Answer from PMS is not as expected. Retrying', -1)
|
||||||
|
@ -280,9 +266,7 @@ class UserClient(threading.Thread):
|
||||||
window('emby_accessToken%s' % userId, value=self.currToken)
|
window('emby_accessToken%s' % userId, value=self.currToken)
|
||||||
window('emby_server%s' % userId, value=self.currServer)
|
window('emby_server%s' % userId, value=self.currServer)
|
||||||
window('plex_machineIdentifier', value=self.machineIdentifier)
|
window('plex_machineIdentifier', value=self.machineIdentifier)
|
||||||
|
window('plex_servername', value=self.servername)
|
||||||
window('emby_serverStatus', clear=True)
|
|
||||||
window('suspend_LibraryThread', clear=True)
|
|
||||||
|
|
||||||
# Set DownloadUtils values
|
# Set DownloadUtils values
|
||||||
doUtils.setUsername(username)
|
doUtils.setUsername(username)
|
||||||
|
@ -290,8 +274,6 @@ class UserClient(threading.Thread):
|
||||||
doUtils.setServer(self.currServer)
|
doUtils.setServer(self.currServer)
|
||||||
doUtils.setToken(self.currToken)
|
doUtils.setToken(self.currToken)
|
||||||
doUtils.setSSL(self.ssl, self.sslcert)
|
doUtils.setSSL(self.ssl, self.sslcert)
|
||||||
# parental control - let's verify if access is restricted
|
|
||||||
# self.hasAccess()
|
|
||||||
|
|
||||||
# Start DownloadUtils session
|
# Start DownloadUtils session
|
||||||
doUtils.startSession()
|
doUtils.startSession()
|
||||||
|
@ -299,6 +281,23 @@ class UserClient(threading.Thread):
|
||||||
# Set user preferences in settings
|
# Set user preferences in settings
|
||||||
self.currUser = username
|
self.currUser = username
|
||||||
self.setUserPref()
|
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
|
return True
|
||||||
|
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
|
@ -309,6 +308,15 @@ class UserClient(threading.Thread):
|
||||||
settings = utils.settings
|
settings = utils.settings
|
||||||
dialog = xbmcgui.Dialog()
|
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
|
# Get /profile/addon_data
|
||||||
addondir = xbmc.translatePath(
|
addondir = xbmc.translatePath(
|
||||||
self.addon.getAddonInfo('profile')).decode('utf-8')
|
self.addon.getAddonInfo('profile')).decode('utf-8')
|
||||||
|
@ -318,98 +326,94 @@ class UserClient(threading.Thread):
|
||||||
if not hasSettings:
|
if not hasSettings:
|
||||||
log("Error, no settings.xml found.", -1)
|
log("Error, no settings.xml found.", -1)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return False
|
||||||
server = self.getServer()
|
server = self.getServer()
|
||||||
# If no user information
|
# If there is no server we can connect to
|
||||||
if not server:
|
if not server:
|
||||||
log("Missing server information.", 0)
|
log("Missing server information.", 0)
|
||||||
self.auth = False
|
self.auth = False
|
||||||
return
|
return False
|
||||||
|
|
||||||
username = self.getUsername()
|
# If there is a username in the settings, try authenticating
|
||||||
userId = self.getUserId(username)
|
username = settings('username')
|
||||||
# If there's a token, load the user
|
userId = settings('userid')
|
||||||
if self.getToken(username=username, userId=userId):
|
usertoken = settings('accessToken')
|
||||||
if self.loadCurrUser() is False:
|
enforceLogin = settings('enforceUserLogin')
|
||||||
pass
|
# 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:
|
else:
|
||||||
# We successfully loaded a user
|
# Failed to use the settings - delete them!
|
||||||
log("Current user: %s" % self.currUser, 1)
|
log("Failed to use the settings credentials. Deleting them", 1)
|
||||||
log("Current userId: %s" % self.currUserId, 1)
|
settings('username', value='')
|
||||||
log("Current accessToken: xxxx", 1)
|
settings('userid', value='')
|
||||||
return
|
settings('accessToken', value='')
|
||||||
|
|
||||||
# AUTHENTICATE USER #####
|
|
||||||
plx = PlexAPI.PlexAPI()
|
plx = PlexAPI.PlexAPI()
|
||||||
# Choose Plex user login
|
|
||||||
plexdict = plx.GetPlexLoginFromSettings()
|
|
||||||
myplexlogin = plexdict['myplexlogin']
|
|
||||||
plexhome = plexdict['plexhome']
|
|
||||||
|
|
||||||
if myplexlogin == "true" and plexhome == 'true':
|
# Could not use settings - try to get Plex user list from plex.tv
|
||||||
username, userId, accessToken = plx.ChoosePlexHomeUser()
|
plextoken = settings('plexToken')
|
||||||
else:
|
if plextoken:
|
||||||
log("Trying to connect to PMS without a token", 0)
|
log("Trying to connect to plex.tv to get a user list", 0)
|
||||||
accessToken = ''
|
userInfo = plx.ChoosePlexHomeUser(plextoken)
|
||||||
# Check connection
|
if userInfo is False:
|
||||||
if plx.CheckConnection(server, accessToken) == 200:
|
# FAILURE: Something went wrong, try again
|
||||||
self.currUser = username
|
|
||||||
settings('accessToken', value=accessToken)
|
|
||||||
settings('userId', value=userId)
|
|
||||||
log("User authenticated with an access token", 1)
|
|
||||||
if self.loadCurrUser(authenticated=True) is False:
|
|
||||||
# Something went really wrong, return and try again
|
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.currUser = None
|
self.retry += 1
|
||||||
return
|
return False
|
||||||
# Success!
|
username = userInfo['username']
|
||||||
if username:
|
userId = userInfo['userid']
|
||||||
dialog.notification(
|
usertoken = userInfo['token']
|
||||||
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")
|
|
||||||
self.retry = 0
|
|
||||||
# Make sure that lib sync thread is not paused
|
|
||||||
else:
|
else:
|
||||||
self.logMsg("Error: user authentication failed.", -1)
|
log("Trying to authenticate without a token", 0)
|
||||||
settings('accessToken', value="")
|
username = ''
|
||||||
settings('userId', value="")
|
userId = ''
|
||||||
|
usertoken = ''
|
||||||
# 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)')
|
|
||||||
|
|
||||||
|
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
|
self.retry += 1
|
||||||
self.auth = False
|
return False
|
||||||
|
|
||||||
def resetClient(self):
|
def resetClient(self):
|
||||||
self.logMsg("Reset UserClient authentication.", 1)
|
self.logMsg("Reset UserClient authentication.", 1)
|
||||||
|
settings = utils.settings
|
||||||
|
window = utils.window
|
||||||
|
|
||||||
|
window('emby_accessToken%s' % self.currUserId, clear=True)
|
||||||
|
window('emby_server%s' % self.currUserId, clear=True)
|
||||||
|
window('emby_currUser', 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('')
|
||||||
|
|
||||||
utils.settings('accessToken', value="")
|
|
||||||
utils.window('emby_accessToken%s' % self.currUserId, clear=True)
|
|
||||||
self.currToken = None
|
self.currToken = None
|
||||||
self.logMsg("User token has been removed. Pausing Lib sync thread", 1)
|
|
||||||
utils.window('suspend_LibraryThread', value="true")
|
|
||||||
|
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.currUser = None
|
self.currUser = None
|
||||||
self.currUserId = None
|
self.currUserId = None
|
||||||
|
|
||||||
|
self.retry = 0
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
log = self.logMsg
|
log = self.logMsg
|
||||||
window = utils.window
|
window = utils.window
|
||||||
# Start library sync thread in a suspended mode, until signed in
|
|
||||||
utils.window('suspend_LibraryThread', value="true")
|
|
||||||
|
|
||||||
log("----===## Starting UserClient ##===----", 0)
|
log("----===## Starting UserClient ##===----", 0)
|
||||||
while not self.threadStopped():
|
while not self.threadStopped():
|
||||||
|
@ -429,6 +433,7 @@ class UserClient(threading.Thread):
|
||||||
# Unauthorized access, revoke token
|
# Unauthorized access, revoke token
|
||||||
window('emby_serverStatus', value="Auth")
|
window('emby_serverStatus', value="Auth")
|
||||||
self.resetClient()
|
self.resetClient()
|
||||||
|
xbmc.sleep(2000)
|
||||||
|
|
||||||
if self.auth and (self.currUser is None):
|
if self.auth and (self.currUser is None):
|
||||||
# Try to authenticate user
|
# Try to authenticate user
|
||||||
|
@ -436,7 +441,14 @@ class UserClient(threading.Thread):
|
||||||
# Set auth flag because we no longer need
|
# Set auth flag because we no longer need
|
||||||
# to authenticate the user
|
# to authenticate the user
|
||||||
self.auth = False
|
self.auth = False
|
||||||
self.authenticate()
|
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):
|
if not self.auth and (self.currUser is None):
|
||||||
# Loop if no server found
|
# Loop if no server found
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
<setting id="secondsslcert" subsetting="true" label="30501" type="file" default="None" visible="eq(-2,true)" />
|
<setting id="secondsslcert" subsetting="true" label="30501" type="file" default="None" visible="eq(-2,true)" />
|
||||||
<!-- User settings -->
|
<!-- User settings -->
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
|
|
||||||
|
<setting id="enforceUserLogin" label="30536" type="bool" default="false" />
|
||||||
|
|
||||||
<setting label="30505" type="action" visible="eq(1,) + !eq(-15,)" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=resetauth)" option="close" />
|
<setting label="30505" type="action" visible="eq(1,) + !eq(-15,)" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=resetauth)" option="close" />
|
||||||
<setting label="30517" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials -->
|
<setting label="30517" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials -->
|
||||||
<setting id="accessToken" type="text" visible="false" default="" />
|
<setting id="accessToken" type="text" visible="false" default="" />
|
||||||
|
@ -32,14 +34,16 @@
|
||||||
<setting id="plexLogin" label="plex.tv username" type="text" default="" visible="false" />
|
<setting id="plexLogin" label="plex.tv username" type="text" default="" visible="false" />
|
||||||
<setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" />
|
<setting id="plexhome" label="Plex home in use" type="bool" default="" visible="false" />
|
||||||
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
|
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
|
||||||
|
<setting id="plexHomeSize" type="number" default="1" visible="false" />
|
||||||
</category>
|
</category>
|
||||||
<category label="Plex Companion">
|
<category label="Plex Companion">
|
||||||
<setting type="lsep" label="39008" />
|
<setting type="lsep" label="39008" />
|
||||||
<setting id="plexCompanion" label="39004" type="bool" default="true" />
|
<setting id="plexCompanion" label="39004" type="bool" default="true" />
|
||||||
<setting id="deviceNameOpt" label="30504" type="bool" default="false" />
|
<setting id="deviceNameOpt" label="30504" type="bool" default="false" />
|
||||||
<setting id="companionPort" label="39005" type="number" default="3005" option="int" visible="eq(-1,true)"/>
|
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
|
||||||
<setting id="companionDebugging" label="39006" type="bool" default="false" visible="eq(-3,true)"/>
|
<setting id="companionPort" label="39005" type="number" default="3005" option="int" visible="eq(-3,true)"/>
|
||||||
<setting id="companionGDMDebugging" label="39007" type="bool" default="false" visible="eq(-4,true)"/>
|
<setting id="companionDebugging" label="39006" type="bool" default="false" visible="eq(-4,true)"/>
|
||||||
|
<setting id="companionGDMDebugging" label="39007" type="bool" default="false" visible="eq(-5,true)"/>
|
||||||
</category>
|
</category>
|
||||||
<category label="30506"><!-- Sync Options -->
|
<category label="30506"><!-- Sync Options -->
|
||||||
<setting id="serverSync" type="bool" label="30514" default="true" visible="false"/><!-- Enable fast startup (requires server plugin) -->
|
<setting id="serverSync" type="bool" label="30514" default="true" visible="false"/><!-- Enable fast startup (requires server plugin) -->
|
||||||
|
|
Loading…
Reference in a new issue