Merge branch 'develop'

This commit is contained in:
tomkat83 2016-03-09 18:38:38 +01:00
commit a76b5397f6
13 changed files with 370 additions and 191 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect"
name="PlexKodiConnect"
version="1.0.4"
version="1.0.5"
provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>

View file

@ -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
- Sleep for a while in loops - drastically reduces CPU load
- Connect to remote PMS!

View file

@ -298,6 +298,7 @@
<string id="30533">Duration of the music library pop up (in seconds)</string>
<string id="30534">Server messages</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 -->
<string id="33000">Welcome</string>

View file

@ -24,6 +24,8 @@
<string id="30521">Bei Wiederaufnahme zurückspulen (in Sekunden)</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="30015">Netzwerk</string>

View file

@ -92,9 +92,11 @@ class PlexAPI():
'plexToken': utils.settings('plexToken'),
'plexhome': utils.settings('plexhome'),
'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.
@ -102,11 +104,13 @@ class PlexAPI():
plexhome is 'true' if plex home is used (the default)
"""
return {
'plexLogin': utils.settings('plexLogin').decode('utf-8'),
'plexLogin': utils.settings('plexLogin'),
'plexToken': utils.settings('plexToken'),
'plexhome': utils.settings('plexhome'),
'plexid': utils.settings('plexid'),
'myplexlogin': utils.settings('myplexlogin')
'myplexlogin': utils.settings('myplexlogin'),
'plexAvatar': utils.settings('plexAvatar'),
'plexHomeSize': utils.settings('plexHomeSize')
}
def GetPlexLoginAndPassword(self):
@ -158,12 +162,14 @@ class PlexAPI():
"""
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
'username':
'avatar': URL to user avator
'token':
'plexid': Plex user ID
'homesize': Number of Plex home users (defaults to '1')
}
Returns False if authentication did not work.
"""
@ -202,19 +208,23 @@ class PlexAPI():
else:
home = 'false'
username = xml.get('username', '')
avatar = xml.get('thumb')
avatar = xml.get('thumb', '')
token = xml.findtext('authentication-token')
homeSize = xml.get('homeSize', '1')
result = {
'plexhome': home,
'username': username,
'avatar': avatar,
'token': token,
'plexid': userid
'plexid': userid,
'homesize': homeSize
}
utils.settings('plexLogin', username)
utils.settings('plexToken', token)
utils.settings('plexhome', home)
utils.settings('plexid', userid)
utils.settings('plexAvatar', avatar)
utils.settings('plexHomeSize', homeSize)
# Let Kodi log into plex.tv on startup from now on
utils.settings('myplexlogin', 'true')
return result
@ -334,7 +344,7 @@ class PlexAPI():
# Add '/clients' to URL because then an authentication is necessary
# If a plex.tv URL was passed, this does not work.
header = self.getXArgsDeviceInfo()
if token is not None:
if token:
header['X-Plex-Token'] = token
sslverify = utils.settings('sslverify')
if sslverify == "true":
@ -780,7 +790,7 @@ class PlexAPI():
XML = etree.parse(response)
# Log received XML if debugging enabled.
self.logMsg("====== received PMS-XML ======", 1)
self.logMsg(XML.getroot(), 1)
self.logMsg(XML, 1)
self.logMsg("====== PMS-XML finished ======", 1)
return XML
@ -1050,42 +1060,41 @@ class PlexAPI():
self.logMsg("Avatar url for user %s is: %s" % (username, url), 1)
return url
def ChoosePlexHomeUser(self):
def ChoosePlexHomeUser(self, plexToken):
"""
Let's user choose from a list of Plex home users. Will switch to that
user accordingly.
Output:
username
userid
authtoken
Returns a dict:
{
'username': Unicode
'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__
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
users = self.MyPlexListHomeUsers(plexToken)
# Download users failed. Set username to Plex login
if not users:
utils.settings('username', value=plexLogin)
self.logMsg("User download failed. Set username = plexlogin", 0)
return ('', '', '')
self.logMsg("User download failed.", -1)
return False
userlist = []
userlistCoded = []
for user in users:
username = user['title']
userlist.append(username)
# To take care of non-ASCII usernames
userlistCoded.append(username.encode('utf-8'))
usernumber = len(userlist)
username = ''
usertoken = ''
# Plex home not in use: only 1 user returned
trials = 0
while trials < 3:
if usernumber > 1:
@ -1094,58 +1103,62 @@ class PlexAPI():
self.addonName + string(39306),
userlistCoded)
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)'
% self.addonId)
return ('', '', '')
# No Plex home in use - only 1 user
return False
# Only 1 user received, choose that one
else:
user_select = 0
selected_user = userlist[user_select]
self.logMsg("Selected user: %s" % selected_user, 1)
utils.settings('username', value=selected_user)
self.logMsg("Selected user: %s" % selected_user, 0)
user = users[user_select]
# Ask for PIN, if protected:
pin = None
if user['protected'] == '1':
# Please enter pin for user
self.logMsg('Asking for users PIN', 1)
pin = dialog.input(
string(39307) + selected_user,
type=xbmcgui.INPUT_NUMERIC,
option=xbmcgui.ALPHANUM_HIDE_INPUT)
# User chose to cancel
if pin is None:
break
else:
pin = None
# 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
if not pin:
trials += 1
continue
# Switch to this Plex Home user, if applicable
username, usertoken = self.PlexSwitchHomeUser(
user['id'],
pin,
plexToken,
machineIdentifier
)
utils.settings('plex_machineIdentifier'))
# Couldn't get user auth
if not username:
trials += 1
# Could not login user, please try again
if not dialog.yesno(self.addonName,
string(39308) + selected_user,
string(39309)):
# User chose to cancel
break
# Successfully retrieved: break out of while loop
else:
# Successfully retrieved username: break out of while loop
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.
@ -1153,7 +1166,6 @@ class PlexAPI():
userId id of the Plex home user
pin PIN of the Plex home user, if protected
token token for plex.tv
machineId Plex PMS machineIdentifier
Output:
(username, token)
@ -1165,7 +1177,9 @@ class PlexAPI():
url += '?pin=' + pin
self.logMsg('Switching to user %s' % userId, 0)
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)
return ('', '')
@ -1173,28 +1187,25 @@ class PlexAPI():
token = answer.attrib.get('authenticationToken', '')
# Get final token
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)
url = 'https://plex.tv/api/resources?includeHttps=1'
xml = self.TalkToPlexServer(url, talkType="GET", token=token)
try:
xml.attrib
except:
self.logMsg('switch HomeUser failed - plex.tv answer wrong', -1)
return ('', '')
found = 0
for child in answer:
if child.attrib['machineIdentifier'] == machineId:
token = child.attrib['accessToken']
self.logMsg('Found a plex home user token', 1)
for device in xml:
if device.attrib.get('clientIdentifier') == machineIdentifier:
found += 1
token = device.attrib.get('accessToken')
if found == 0:
self.logMsg('Error: plex.tv switch HomeUser change failed', -1)
self.logMsg('No tokens found for your server!', -1)
return ('', '')
self.logMsg('Plex.tv switch HomeUser change successfull', 0)
self.logMsg("username: %s, token: xxxx. "
"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)
self.logMsg("username: %s, token: xxxx. " % username, 0)
return (username, token)
def MyPlexListHomeUsers(self, authtoken):
@ -1218,9 +1229,11 @@ class PlexAPI():
If any value is missing, None is returned instead (or "" from plex.tv)
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:
# 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
# analyse response
root = XML.getroot()

View file

@ -37,7 +37,7 @@ class DownloadUtils():
# Requests session
s = None
timeout = 3
timeout = 10
def __init__(self):
@ -224,7 +224,8 @@ class DownloadUtils():
# Get user information
self.userId = utils.window('emby_currUser')
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)
verifyssl = False
cert = None

View file

@ -79,41 +79,61 @@ def reConnect():
string = xbmcaddon.Addon().getLocalizedString
utils.logMsg("entrypoint reConnect",
"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.notification(
heading=addonName,
message=string(39207),
icon="special://home/addons/plugin.video.plexkodiconnect/icon.png",
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
while utils.window('emby_dbScan') == 'true':
xbmc.sleep(1000)
counter += 1
if counter > 20:
if counter > 100:
dialog.ok(
heading=addonName,
message=string(39208),
)
# Resuming threads, just in case
utils.window('suspend_LibraryThread', clear=True)
utils.window('suspend_Userclient', clear=True)
# Abort reConnection
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
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)
utils.window('plex_runLibScan', value='full')
# Restart user client
utils.window('suspend_Userclient', clear=True)
def PassPlaylist(xml, resume=None):
@ -222,7 +242,7 @@ def doMainListing():
addDirectoryItem(label, path)
# Plex user switch, if Plex home is in use
if utils.settings('plexhome') == 'true':
if int(utils.settings('plexHomeSize')) > 1:
addDirectoryItem(string(39200),
"plugin://plugin.video.plexkodiconnect/"
"?mode=switchuser")
@ -401,8 +421,17 @@ def switchPlexUser():
# Pause library sync thread - user needs to be auth in order to sync
utils.window('suspend_LibraryThread', value='true')
# Wait to ensure that any sync already going on has finished
counter = 0
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:
utils.window('emby_serverStatus', value="401")
# Request lib sync to get user view data (e.g. watched/unwatched)

View file

@ -40,7 +40,7 @@ class image_cache_thread(threading.Thread):
"http://%s:%s/image/image://%s"
% (self.xbmc_host, self.xbmc_port, self.urlToProcess)),
auth=(self.xbmc_username, self.xbmc_password),
timeout=(0.1, 0.1))
timeout=(2, 2))
# We don't need the result
except: pass

View file

@ -68,6 +68,24 @@ class InitialSetup():
# Problems connecting to plex.tv. Network or internet issue?
dialog.ok(self.addonName,
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 server and forcePlexTV is False:
self.logMsg("Server is already set.", 0)
@ -175,6 +193,7 @@ class InitialSetup():
return
# Write to Kodi settings file
utils.settings('plex_machineIdentifier', activeServer)
utils.settings('plex_servername', server['name'])
if server['local'] == '1':
scheme = server['scheme']
utils.settings('ipaddress', server['ip'])

View file

@ -284,6 +284,17 @@ class Movies(Items):
self.add_updateBoxset(boxset)
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
kodicursor = self.kodicursor
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
update_item = True
itemid = API.getRatingKey()
self.logMsg("Processing item %s" % itemid, 1)
# Cannot parse XML, abort
if not itemid:
self.logMsg("Cannot parse XML data for movie", -1)
@ -360,6 +372,7 @@ class Movies(Items):
studio = studios[0]
except IndexError:
studio = None
self.logMsg('Read all attributes', 1)
# Find one trailer
trailer = None
@ -408,6 +421,7 @@ class Movies(Items):
'mode': "play"
}
filename = "%s?%s" % (path, urllib.urlencode(params))
self.logMsg('Path set for item', 1)
##### UPDATE THE MOVIE #####
if update_item:
self.logMsg("UPDATE movie itemid: %s - Title: %s" % (itemid, title), 1)
@ -454,6 +468,7 @@ class Movies(Items):
# Create the reference in emby table
emby_db.addReference(itemid, movieid, "Movie", "movie", fileid, pathid, None, checksum, viewid)
self.logMsg('Done add or update for item', 1)
# Update the path
query = ' '.join((
@ -902,6 +917,17 @@ class TVShows(Items):
self.contentPop(title, self.newvideo_time)
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
kodicursor = self.kodicursor
emby_db = self.emby_db
@ -911,6 +937,8 @@ class TVShows(Items):
update_item = True
itemid = API.getRatingKey()
self.logMsg("Processing item %s" % itemid, 1)
if not itemid:
self.logMsg("Cannot parse XML data for TV show", -1)
return
@ -964,6 +992,8 @@ class TVShows(Items):
except IndexError:
studio = None
self.logMsg('Read all attributes', 1)
# GET THE FILE AND PATH #####
playurl = API.getKey()
@ -997,7 +1027,7 @@ class TVShows(Items):
# Set plugin path
toplevelpath = "plugin://plugin.video.plexkodiconnect.tvshows/"
path = "%s%s/" % (toplevelpath, itemid)
self.logMsg('Path set for item', 1)
# UPDATE THE TVSHOW #####
if update_item:
self.logMsg("UPDATE tvshow itemid: %s - Title: %s" % (itemid, title), 1)
@ -1052,7 +1082,7 @@ class TVShows(Items):
# Create the reference in emby table
emby_db.addReference(itemid, showid, "Series", "tvshow", pathid=pathid,
checksum=checksum, mediafolderid=viewid)
self.logMsg('Done add or update for item', 1)
# Update the path
query = ' '.join((
@ -1083,13 +1113,25 @@ class TVShows(Items):
all_episodes = embyserver.getEpisodesbyShow(itemid)
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)
showid = viewid
itemid = API.getRatingKey()
if not itemid:
self.logMsg('Error getting itemid for season, skipping', -1)
return
self.logMsg("Processing item %s" % itemid, 1)
kodicursor = self.kodicursor
emby_db = self.emby_db
kodi_db = self.kodi_db
@ -1117,6 +1159,17 @@ class TVShows(Items):
emby_db.addReference(itemid, seasonid, "Season", "season", parentid=showid, checksum=checksum)
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!
"""
@ -1136,6 +1189,7 @@ class TVShows(Items):
if not itemid:
self.logMsg('Error getting itemid for episode, skipping', -1)
return
self.logMsg("Processing item %s" % itemid, 1)
emby_dbitem = emby_db.getItem_byId(itemid)
try:
episodeid = emby_dbitem[0]
@ -1584,7 +1638,19 @@ class Music(Items):
if not pdialog and self.contentmsg:
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"):
kodicursor = self.kodicursor
emby_db = self.emby_db
@ -1594,6 +1660,7 @@ class Music(Items):
update_item = True
itemid = API.getRatingKey()
self.logMsg("Processing item %s" % itemid, 1)
emby_dbitem = emby_db.getItem_byId(itemid)
try:
artistid = emby_dbitem[0]
@ -1668,6 +1735,18 @@ class Music(Items):
artwork.addArtwork(artworks, artistid, "artist", kodicursor)
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
kodicursor = self.kodicursor
emby_db = self.emby_db
@ -1680,6 +1759,7 @@ class Music(Items):
if not itemid:
self.logMsg('Error processing Album, skipping', -1)
return
self.logMsg("Processing item %s" % itemid, 1)
emby_dbitem = emby_db.getItem_byId(itemid)
try:
albumid = emby_dbitem[0]
@ -1853,6 +1933,18 @@ class Music(Items):
artwork.addArtwork(artworks, albumid, "album", kodicursor)
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
kodiversion = self.kodiversion
kodicursor = self.kodicursor
@ -1866,6 +1958,7 @@ class Music(Items):
if not itemid:
self.logMsg('Error processing Song; skipping', -1)
return
self.logMsg("Processing item %s" % itemid, 1)
emby_dbitem = emby_db.getItem_byId(itemid)
try:
songid = emby_dbitem[0]

View file

@ -111,6 +111,8 @@ class UserClient(threading.Thread):
settings = utils.settings
# Original host
self.machineIdentifier = utils.settings('plex_machineIdentifier')
self.servername = utils.settings('plex_servername')
HTTPS = settings('https') == "true"
host = settings('ipaddress')
port = settings('port')
@ -192,6 +194,8 @@ class UserClient(threading.Thread):
def setUserPref(self):
self.logMsg('Setting user preferences', 0)
# Only try to get user avatar if there is a token
if self.currToken:
url = PlexAPI.PlexAPI().GetUserArtworkURL(self.currUser)
if url:
utils.window('EmbyUserImage', value=url)
@ -201,20 +205,6 @@ class UserClient(threading.Thread):
# 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):
# Plex: always return True for now
return True
@ -241,18 +231,15 @@ class UserClient(threading.Thread):
xbmcgui.Dialog().notification(self.addonName,
utils.language(33007))
def loadCurrUser(self, authenticated=False):
def loadCurrUser(self, username, userId, usertoken, authenticated=False):
self.logMsg('Loading current user', 0)
window = utils.window
settings = utils.settings
doUtils = self.doUtils
username = self.getUsername()
userId = self.getUserId()
self.currUserId = userId
self.currToken = usertoken
self.currServer = self.getServer()
self.currToken = self.getToken()
self.machineIdentifier = utils.settings('plex_machineIdentifier')
self.ssl = self.getSSLverify()
self.sslcert = self.getSSL()
@ -268,7 +255,6 @@ class UserClient(threading.Thread):
return False
elif res == 401:
self.logMsg('Token is no longer valid', -1)
self.resetClient()
return False
elif res >= 400:
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_server%s' % userId, value=self.currServer)
window('plex_machineIdentifier', value=self.machineIdentifier)
window('emby_serverStatus', clear=True)
window('suspend_LibraryThread', clear=True)
window('plex_servername', value=self.servername)
# Set DownloadUtils values
doUtils.setUsername(username)
@ -290,8 +274,6 @@ class UserClient(threading.Thread):
doUtils.setServer(self.currServer)
doUtils.setToken(self.currToken)
doUtils.setSSL(self.ssl, self.sslcert)
# parental control - let's verify if access is restricted
# self.hasAccess()
# Start DownloadUtils session
doUtils.startSession()
@ -299,70 +281,13 @@ class UserClient(threading.Thread):
# Set user preferences in settings
self.currUser = username
self.setUserPref()
return True
def authenticate(self):
log = self.logMsg
log('Authenticating user', 1)
lang = utils.language
window = utils.window
settings = utils.settings
# Writing values to settings file
settings('username', value=username)
settings('userid', value=userId)
settings('accessToken', value=usertoken)
dialog = xbmcgui.Dialog()
# Get /profile/addon_data
addondir = xbmc.translatePath(
self.addon.getAddonInfo('profile')).decode('utf-8')
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
# If there's no settings.xml
if not hasSettings:
log("Error, no settings.xml found.", -1)
self.auth = False
return
server = self.getServer()
# If no user information
if not server:
log("Missing server information.", 0)
self.auth = False
return
username = self.getUsername()
userId = self.getUserId(username)
# If there's a token, load the user
if self.getToken(username=username, userId=userId):
if self.loadCurrUser() is False:
pass
else:
# We successfully loaded a user
log("Current user: %s" % self.currUser, 1)
log("Current userId: %s" % self.currUserId, 1)
log("Current accessToken: xxxx", 1)
return
# AUTHENTICATE USER #####
plx = PlexAPI.PlexAPI()
# Choose Plex user login
plexdict = plx.GetPlexLoginFromSettings()
myplexlogin = plexdict['myplexlogin']
plexhome = plexdict['plexhome']
if myplexlogin == "true" and plexhome == 'true':
username, userId, accessToken = plx.ChoosePlexHomeUser()
else:
log("Trying to connect to PMS without a token", 0)
accessToken = ''
# Check connection
if plx.CheckConnection(server, accessToken) == 200:
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.currUser = None
return
# Success!
if username:
dialog.notification(
heading=self.addonName,
@ -373,12 +298,15 @@ class UserClient(threading.Thread):
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:
self.logMsg("Error: user authentication failed.", -1)
settings('accessToken', value="")
settings('userId', value="")
return True
def authenticate(self):
log = self.logMsg
log('Authenticating user', 1)
lang = utils.language
window = utils.window
settings = utils.settings
dialog = xbmcgui.Dialog()
# Give attempts at entering password / selecting user
if self.retry >= 2:
@ -389,27 +317,103 @@ class UserClient(threading.Thread):
xbmc.executebuiltin(
'Addon.OpenSettings(plugin.video.plexkodiconnect)')
self.retry += 1
# Get /profile/addon_data
addondir = xbmc.translatePath(
self.addon.getAddonInfo('profile')).decode('utf-8')
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
# If there's no settings.xml
if not hasSettings:
log("Error, no settings.xml found.", -1)
self.auth = False
return False
server = self.getServer()
# If there is no server we can connect to
if not server:
log("Missing server information.", 0)
self.auth = False
return False
# If there is a username in the settings, try authenticating
username = settings('username')
userId = settings('userid')
usertoken = settings('accessToken')
enforceLogin = settings('enforceUserLogin')
# Found a user in the settings, try to authenticate
if username and enforceLogin == 'false':
log('Trying to authenticate with old settings', 0)
if self.loadCurrUser(username,
userId,
usertoken,
authenticated=False):
# SUCCESS: loaded a user from the settings
return True
else:
# Failed to use the settings - delete them!
log("Failed to use the settings credentials. Deleting them", 1)
settings('username', value='')
settings('userid', value='')
settings('accessToken', value='')
plx = PlexAPI.PlexAPI()
# Could not use settings - try to get Plex user list from plex.tv
plextoken = settings('plexToken')
if plextoken:
log("Trying to connect to plex.tv to get a user list", 0)
userInfo = plx.ChoosePlexHomeUser(plextoken)
if userInfo is False:
# FAILURE: Something went wrong, try again
self.auth = True
self.retry += 1
return False
username = userInfo['username']
userId = userInfo['userid']
usertoken = userInfo['token']
else:
log("Trying to authenticate without a token", 0)
username = ''
userId = ''
usertoken = ''
if self.loadCurrUser(username, userId, usertoken, authenticated=False):
# SUCCESS: loaded a user from the settings
return True
else:
# FAILUR: Something went wrong, try again
self.auth = True
self.retry += 1
return False
def resetClient(self):
self.logMsg("Reset UserClient authentication.", 1)
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.logMsg("User token has been removed. Pausing Lib sync thread", 1)
utils.window('suspend_LibraryThread', value="true")
self.auth = True
self.currUser = None
self.currUserId = None
self.retry = 0
def run(self):
log = self.logMsg
window = utils.window
# Start library sync thread in a suspended mode, until signed in
utils.window('suspend_LibraryThread', value="true")
log("----===## Starting UserClient ##===----", 0)
while not self.threadStopped():
@ -429,6 +433,7 @@ class UserClient(threading.Thread):
# Unauthorized access, revoke token
window('emby_serverStatus', value="Auth")
self.resetClient()
xbmc.sleep(2000)
if self.auth and (self.currUser is None):
# Try to authenticate user
@ -436,7 +441,14 @@ class UserClient(threading.Thread):
# Set auth flag because we no longer need
# to authenticate the user
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):
# Loop if no server found

View file

@ -16,7 +16,9 @@
<setting id="secondsslcert" subsetting="true" label="30501" type="file" default="None" visible="eq(-2,true)" />
<!-- User settings -->
<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="30517" type="action" action="RunPlugin(plugin://plugin.video.plexkodiconnect?mode=passwords)" option="close" /><!-- Network credentials -->
<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="plexhome" label="Plex home in use" type="bool" default="" visible="false" />
<setting id="plexToken" label="plexToken" type="text" default="" visible="false" />
<setting id="plexHomeSize" type="number" default="1" visible="false" />
</category>
<category label="Plex Companion">
<setting type="lsep" label="39008" />
<setting id="plexCompanion" label="39004" type="bool" default="true" />
<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="companionDebugging" label="39006" type="bool" default="false" visible="eq(-3,true)"/>
<setting id="companionGDMDebugging" label="39007" type="bool" default="false" visible="eq(-4,true)"/>
<setting id="deviceName" label="30016" type="text" visible="eq(-1,true)" default="Kodi" />
<setting id="companionPort" label="39005" type="number" default="3005" option="int" visible="eq(-3,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 label="30506"><!-- Sync Options -->
<setting id="serverSync" type="bool" label="30514" default="true" visible="false"/><!-- Enable fast startup (requires server plugin) -->