Plex home user switch working
This commit is contained in:
parent
0d4c8ae53e
commit
42bd570187
7 changed files with 66 additions and 39 deletions
|
@ -39,9 +39,9 @@ def plexCompanion(fullurl, resume=None):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# No matches found, url not like:
|
# No matches found, url not like:
|
||||||
# http://192.168.0.2:32400/library/metadata/243480
|
# http://192.168.0.2:32400/library/metadata/243480
|
||||||
utils.logMsg("plexCompanion", "Could not parse url: %s" % fullurl, -1)
|
utils.logMsg("entrypoint - plexCompanion",
|
||||||
|
"Could not parse url: %s" % fullurl, -1)
|
||||||
return False
|
return False
|
||||||
# TODO: direct play an URL
|
|
||||||
# Initialize embydb
|
# Initialize embydb
|
||||||
embyconn = utils.kodiSQL('emby')
|
embyconn = utils.kodiSQL('emby')
|
||||||
embycursor = embyconn.cursor()
|
embycursor = embyconn.cursor()
|
||||||
|
@ -272,14 +272,16 @@ def switchPlexUser():
|
||||||
# Delete any userimages. Since there's always only 1 user: position = 0
|
# Delete any userimages. Since there's always only 1 user: position = 0
|
||||||
# position = 0
|
# position = 0
|
||||||
# utils.window('EmbyAdditionalUserImage.%s' % position, clear=True)
|
# utils.window('EmbyAdditionalUserImage.%s' % position, clear=True)
|
||||||
utils.logMsg("PLEX", "Plex home user switch requested", 0)
|
utils.logMsg("entrypoint switchPlexUser",
|
||||||
|
"Plex home user switch requested", 0)
|
||||||
# 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 1 second to ensure that library thread is indeed suspended
|
||||||
|
xbmc.sleep(1000)
|
||||||
# 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)
|
||||||
utils.window('plex_runLibScan', value='true')
|
utils.window('plex_runLibScan', value='true')
|
||||||
# Reset Plex userdata: resume points, watched/unwatched status
|
|
||||||
|
|
||||||
|
|
||||||
##### THEME MUSIC/VIDEOS #####
|
##### THEME MUSIC/VIDEOS #####
|
||||||
|
|
|
@ -178,6 +178,7 @@ class ThreadedShowSyncInfo(threading.Thread):
|
||||||
|
|
||||||
|
|
||||||
@utils.logging
|
@utils.logging
|
||||||
|
@utils.ThreadMethodsAdditionalSuspend('suspend_LibraryThread')
|
||||||
@utils.ThreadMethodsStopsync
|
@utils.ThreadMethodsStopsync
|
||||||
@utils.ThreadMethods
|
@utils.ThreadMethods
|
||||||
class LibrarySync(threading.Thread):
|
class LibrarySync(threading.Thread):
|
||||||
|
@ -225,7 +226,7 @@ class LibrarySync(threading.Thread):
|
||||||
self.maintainViews()
|
self.maintainViews()
|
||||||
completed = False
|
completed = False
|
||||||
|
|
||||||
completed = self.fastSync()
|
# completed = self.fastSync()
|
||||||
|
|
||||||
if not completed:
|
if not completed:
|
||||||
# Fast sync failed or server plugin is not found
|
# Fast sync failed or server plugin is not found
|
||||||
|
@ -1158,9 +1159,14 @@ class LibrarySync(threading.Thread):
|
||||||
# This will prevent an infinite loop in case something goes wrong.
|
# This will prevent an infinite loop in case something goes wrong.
|
||||||
startupComplete = True
|
startupComplete = True
|
||||||
|
|
||||||
# Process updates
|
|
||||||
if utils.window('emby_dbScan') != "true":
|
if utils.window('emby_dbScan') != "true":
|
||||||
self.incrementalSync()
|
# Currently no db scan, so we can start a new scan
|
||||||
|
if utils.window('plex_runLibScan') == "true":
|
||||||
|
self.logMsg('Full library scan requested, starting', 1)
|
||||||
|
utils.window('plex_runLibScan', value='false')
|
||||||
|
self.fullSync(manualrun=True)
|
||||||
|
else:
|
||||||
|
self.incrementalSync()
|
||||||
|
|
||||||
if (utils.window('emby_onWake') == "true" and
|
if (utils.window('emby_onWake') == "true" and
|
||||||
utils.window('emby_online') == "true"):
|
utils.window('emby_online') == "true"):
|
||||||
|
@ -1172,10 +1178,7 @@ class LibrarySync(threading.Thread):
|
||||||
librarySync = self.startSync()
|
librarySync = self.startSync()
|
||||||
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0)
|
||||||
|
|
||||||
if self.threadStopped():
|
xbmc.sleep(1000)
|
||||||
# Set in service.py
|
|
||||||
self.logMsg("Service terminated thread.", 2)
|
|
||||||
break
|
|
||||||
|
|
||||||
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
|
self.logMsg("###===--- LibrarySync Stopped ---===###", 0)
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,8 @@ class PlaybackUtils():
|
||||||
|
|
||||||
def play(self, itemid, dbid=None, seektime=None):
|
def play(self, itemid, dbid=None, seektime=None):
|
||||||
|
|
||||||
self.logMsg("Play called.", 1)
|
self.logMsg("Play called with itemid: %s, dbid: %s, seektime: %s."
|
||||||
|
% (itemid, dbid, seektime), 1)
|
||||||
|
|
||||||
doUtils = self.doUtils
|
doUtils = self.doUtils
|
||||||
item = self.item
|
item = self.item
|
||||||
|
|
|
@ -82,6 +82,7 @@ class Player(xbmc.Player):
|
||||||
else: tryCount += 1
|
else: tryCount += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
utils.window('Plex_currently_playing_itemid', value=itemId)
|
||||||
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0)
|
||||||
|
|
||||||
# Only proceed if an itemId was found.
|
# Only proceed if an itemId was found.
|
||||||
|
@ -113,7 +114,7 @@ class Player(xbmc.Player):
|
||||||
muted = result.get('muted')
|
muted = result.get('muted')
|
||||||
|
|
||||||
# Postdata structure to send to Emby server
|
# Postdata structure to send to Emby server
|
||||||
url = "{server}/emby/Sessions/Playing"
|
url = "{server}/:/timeline?"
|
||||||
postdata = {
|
postdata = {
|
||||||
|
|
||||||
'QueueableMediaTypes': "Video",
|
'QueueableMediaTypes': "Video",
|
||||||
|
@ -194,8 +195,8 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
|
|
||||||
# Post playback to server
|
# Post playback to server
|
||||||
self.logMsg("Sending POST play started: %s." % postdata, 2)
|
# self.logMsg("Sending POST play started: %s." % postdata, 2)
|
||||||
self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
# self.doUtils.downloadUrl(url, postBody=postdata, type="POST")
|
||||||
|
|
||||||
# Ensure we do have a runtime
|
# Ensure we do have a runtime
|
||||||
try:
|
try:
|
||||||
|
@ -370,10 +371,9 @@ class Player(xbmc.Player):
|
||||||
|
|
||||||
# Report progress via websocketclient
|
# Report progress via websocketclient
|
||||||
# postdata = json.dumps(postdata)
|
# postdata = json.dumps(postdata)
|
||||||
self.logMsg("Report: %s" % postdata, 2)
|
|
||||||
# self.ws.sendProgressUpdate(postdata)
|
# self.ws.sendProgressUpdate(postdata)
|
||||||
url = "{server}/:/timeline?" + urlencode(postdata)
|
self.doUtils.downloadUrl(
|
||||||
self.doUtils.downloadUrl(url, type="GET")
|
"{server}/:/timeline?" + urlencode(postdata), type="GET")
|
||||||
|
|
||||||
def onPlayBackPaused( self ):
|
def onPlayBackPaused( self ):
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ class SubscriptionManager:
|
||||||
self.protocol = "http"
|
self.protocol = "http"
|
||||||
self.port = ""
|
self.port = ""
|
||||||
self.playerprops = {}
|
self.playerprops = {}
|
||||||
self.sentstopped = True
|
|
||||||
self.download = downloadutils.DownloadUtils()
|
self.download = downloadutils.DownloadUtils()
|
||||||
|
|
||||||
def getVolume(self):
|
def getVolume(self):
|
||||||
|
@ -60,11 +59,25 @@ class SubscriptionManager:
|
||||||
ret = "\r\n"+'<Timeline location="%s" state="%s" time="%s" type="%s"' % (self.mainlocation, state, time, ptype)
|
ret = "\r\n"+'<Timeline location="%s" state="%s" time="%s" type="%s"' % (self.mainlocation, state, time, ptype)
|
||||||
if playerid is not None:
|
if playerid is not None:
|
||||||
WINDOW = xbmcgui.Window(10000)
|
WINDOW = xbmcgui.Window(10000)
|
||||||
pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server'))
|
|
||||||
keyid = str(WINDOW.getProperty('plexbmc.nowplaying.id'))
|
# pbmc_server = str(WINDOW.getProperty('plexbmc.nowplaying.server'))
|
||||||
|
# userId = str(WINDOW.getProperty('emby_currUser'))
|
||||||
|
# pbmc_server = str(WINDOW.getProperty('emby_server%s' % userId))
|
||||||
|
pbmc_server = None
|
||||||
|
keyid = None
|
||||||
|
count = 0
|
||||||
|
while not keyid:
|
||||||
|
if count > 10:
|
||||||
|
break
|
||||||
|
keyid = str(WINDOW.getProperty('Plex_currently_playing_itemid'))
|
||||||
|
xbmc.sleep(1000)
|
||||||
|
count += 1
|
||||||
if keyid:
|
if keyid:
|
||||||
self.lastkey = "/library/metadata/%s"%keyid
|
self.lastkey = "/library/metadata/%s"%keyid
|
||||||
self.lastratingkey = keyid
|
self.lastratingkey = keyid
|
||||||
|
ret += ' containerKey="%s"' % (self.lastkey)
|
||||||
|
ret += ' key="%s"' % (self.lastkey)
|
||||||
|
ret += ' ratingKey="%s"' % (self.lastratingkey)
|
||||||
if pbmc_server:
|
if pbmc_server:
|
||||||
(self.server, self.port) = pbmc_server.split(':')
|
(self.server, self.port) = pbmc_server.split(':')
|
||||||
serv = getServerByHost(self.server)
|
serv = getServerByHost(self.server)
|
||||||
|
@ -76,9 +89,6 @@ class SubscriptionManager:
|
||||||
ret += ' address="%s"' % serv.get('server', self.server)
|
ret += ' address="%s"' % serv.get('server', self.server)
|
||||||
ret += ' port="%s"' % serv.get('port', self.port)
|
ret += ' port="%s"' % serv.get('port', self.port)
|
||||||
ret += ' guid="%s"' % info['guid']
|
ret += ' guid="%s"' % info['guid']
|
||||||
ret += ' containerKey="%s"' % (self.lastkey or "/library/metadata/900000")
|
|
||||||
ret += ' key="%s"' % (self.lastkey or "/library/metadata/900000")
|
|
||||||
ret += ' ratingKey="%s"' % (self.lastratingkey or "900000")
|
|
||||||
ret += ' volume="%s"' % info['volume']
|
ret += ' volume="%s"' % info['volume']
|
||||||
ret += ' shuffle="%s"' % info['shuffle']
|
ret += ' shuffle="%s"' % info['shuffle']
|
||||||
|
|
||||||
|
@ -103,7 +113,8 @@ class SubscriptionManager:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def notifyServer(self, players):
|
def notifyServer(self, players):
|
||||||
if not players and self.sentstopped: return True
|
if not players:
|
||||||
|
return True
|
||||||
params = {'state': 'stopped'}
|
params = {'state': 'stopped'}
|
||||||
for p in players.values():
|
for p in players.values():
|
||||||
info = self.playerprops[p.get('playerid')]
|
info = self.playerprops[p.get('playerid')]
|
||||||
|
@ -120,13 +131,11 @@ class SubscriptionManager:
|
||||||
+ serv.get('port', 32400) + "/:/timeline"
|
+ serv.get('port', 32400) + "/:/timeline"
|
||||||
self.download.downloadUrl(url, type="GET", parameters=params)
|
self.download.downloadUrl(url, type="GET", parameters=params)
|
||||||
# requests.getwithparams(serv.get('server', 'localhost'), serv.get('port', 32400), "/:/timeline", params, getPlexHeaders(), serv.get('protocol', 'http'))
|
# requests.getwithparams(serv.get('server', 'localhost'), serv.get('port', 32400), "/:/timeline", params, getPlexHeaders(), serv.get('protocol', 'http'))
|
||||||
|
printDebug("params: %s" % params)
|
||||||
|
printDebug("players: %s" % players)
|
||||||
printDebug("sent server notification with state = %s" % params['state'])
|
printDebug("sent server notification with state = %s" % params['state'])
|
||||||
WINDOW = xbmcgui.Window(10000)
|
WINDOW = xbmcgui.Window(10000)
|
||||||
WINDOW.setProperty('plexbmc.nowplaying.sent', '1')
|
WINDOW.setProperty('plexbmc.nowplaying.sent', '1')
|
||||||
if players:
|
|
||||||
self.sentstopped = False
|
|
||||||
else:
|
|
||||||
self.sentstopped = True
|
|
||||||
|
|
||||||
def controllable(self):
|
def controllable(self):
|
||||||
return "playPause,play,stop,skipPrevious,skipNext,volume,stepBack,stepForward,seekTo"
|
return "playPause,play,stop,skipPrevious,skipNext,volume,stepBack,stepForward,seekTo"
|
||||||
|
|
|
@ -260,7 +260,6 @@ class UserClient(threading.Thread):
|
||||||
def authenticate(self):
|
def authenticate(self):
|
||||||
# Get /profile/addon_data
|
# Get /profile/addon_data
|
||||||
plx = PlexAPI.PlexAPI()
|
plx = PlexAPI.PlexAPI()
|
||||||
lib = librarysync.LibrarySync()
|
|
||||||
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
|
addondir = xbmc.translatePath(self.addon.getAddonInfo('profile')).decode('utf-8')
|
||||||
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
hasSettings = xbmcvfs.exists("%ssettings.xml" % addondir)
|
||||||
|
|
||||||
|
@ -288,7 +287,8 @@ class UserClient(threading.Thread):
|
||||||
self.logMsg("Current user: %s" % self.currUser, 1)
|
self.logMsg("Current user: %s" % self.currUser, 1)
|
||||||
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
self.logMsg("Current userId: %s" % self.currUserId, 1)
|
||||||
self.logMsg("Current accessToken: xxxx", 1)
|
self.logMsg("Current accessToken: xxxx", 1)
|
||||||
lib.resumeThread()
|
|
||||||
|
utils.window('suspend_LibraryThread', value='false')
|
||||||
return
|
return
|
||||||
|
|
||||||
##### AUTHENTICATE USER #####
|
##### AUTHENTICATE USER #####
|
||||||
|
@ -326,7 +326,7 @@ class UserClient(threading.Thread):
|
||||||
utils.window('plex_machineIdentifier', plex_machineIdentifier)
|
utils.window('plex_machineIdentifier', plex_machineIdentifier)
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
# Make sure that lib sync thread is not paused
|
# Make sure that lib sync thread is not paused
|
||||||
lib.resumeThread()
|
utils.window('suspend_LibraryThread', value='false')
|
||||||
else:
|
else:
|
||||||
self.logMsg("Error: user authentication failed.", -1)
|
self.logMsg("Error: user authentication failed.", -1)
|
||||||
utils.settings('accessToken', value="")
|
utils.settings('accessToken', value="")
|
||||||
|
|
|
@ -37,6 +37,22 @@ def ThreadMethodsStopsync(cls):
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
def ThreadMethodsAdditionalSuspend(windowAttribute):
|
||||||
|
"""
|
||||||
|
Decorator to replace threadSuspended(): thread now also suspends if a
|
||||||
|
Kodi windowAttribute is set to 'true', e.g. 'suspend_LibraryThread'
|
||||||
|
|
||||||
|
Use with any library sync threads. @ThreadMethods still required FIRST
|
||||||
|
"""
|
||||||
|
def wrapper(cls):
|
||||||
|
def threadSuspended(self):
|
||||||
|
return (self._threadSuspended or True if
|
||||||
|
window(windowAttribute) == 'true' else False)
|
||||||
|
cls.threadSuspended = threadSuspended
|
||||||
|
return cls
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def ThreadMethods(cls):
|
def ThreadMethods(cls):
|
||||||
"""
|
"""
|
||||||
Decorator to add the following methods to a threading class:
|
Decorator to add the following methods to a threading class:
|
||||||
|
@ -86,8 +102,8 @@ def ThreadMethods(cls):
|
||||||
|
|
||||||
def logging(cls):
|
def logging(cls):
|
||||||
"""
|
"""
|
||||||
A decorator adding logging capabilities to classes. Also adds
|
A decorator adding logging capabilities to classes.
|
||||||
self.addonName to the class
|
Also adds self.addonName to the class
|
||||||
|
|
||||||
Syntax: self.logMsg(message, loglevel)
|
Syntax: self.logMsg(message, loglevel)
|
||||||
|
|
||||||
|
@ -102,9 +118,6 @@ def logging(cls):
|
||||||
logMsg(title, msg, lvl)
|
logMsg(title, msg, lvl)
|
||||||
cls.logMsg = newFunction
|
cls.logMsg = newFunction
|
||||||
|
|
||||||
# Override the name, we don't want the decorators name showing up
|
|
||||||
__name__ = cls.__name__
|
|
||||||
|
|
||||||
# Return class to render this a decorator
|
# Return class to render this a decorator
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
@ -143,7 +156,6 @@ def window(property, value=None, clear=False, windowid=10000):
|
||||||
property = property.encode("utf-8")
|
property = property.encode("utf-8")
|
||||||
if isinstance(value, unicode):
|
if isinstance(value, unicode):
|
||||||
value = value.encode("utf-8")'''
|
value = value.encode("utf-8")'''
|
||||||
|
|
||||||
if clear:
|
if clear:
|
||||||
WINDOW.clearProperty(property)
|
WINDOW.clearProperty(property)
|
||||||
elif value is not None:
|
elif value is not None:
|
||||||
|
|
Loading…
Reference in a new issue