Greatly simplify handling of PKC playqueues
This commit is contained in:
parent
e0f1225c21
commit
e17824609a
16 changed files with 259 additions and 381 deletions
|
@ -3,7 +3,7 @@ The Plex Companion master python file
|
||||||
"""
|
"""
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Queue, Empty
|
from Queue import Empty
|
||||||
from socket import SHUT_RDWR
|
from socket import SHUT_RDWR
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import json_rpc as js
|
||||||
import player
|
import player
|
||||||
import variables as v
|
import variables as v
|
||||||
import state
|
import state
|
||||||
|
import playqueue as PQ
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -32,9 +33,9 @@ class PlexCompanion(Thread):
|
||||||
"""
|
"""
|
||||||
Plex Companion monitoring class. Invoke only once
|
Plex Companion monitoring class. Invoke only once
|
||||||
"""
|
"""
|
||||||
def __init__(self, callback=None):
|
def __init__(self):
|
||||||
LOG.info("----===## Starting PlexCompanion ##===----")
|
LOG.info("----===## Starting PlexCompanion ##===----")
|
||||||
self.mgr = callback
|
# Init Plex Companion queue
|
||||||
# Start GDM for server/client discovery
|
# Start GDM for server/client discovery
|
||||||
self.client = plexgdm.plexgdm()
|
self.client = plexgdm.plexgdm()
|
||||||
self.client.clientDetails()
|
self.client.clientDetails()
|
||||||
|
@ -42,7 +43,6 @@ class PlexCompanion(Thread):
|
||||||
# kodi player instance
|
# kodi player instance
|
||||||
self.player = player.PKC_Player()
|
self.player = player.PKC_Player()
|
||||||
self.httpd = False
|
self.httpd = False
|
||||||
self.queue = None
|
|
||||||
self.subscription_manager = None
|
self.subscription_manager = None
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
|
@ -57,8 +57,9 @@ class PlexCompanion(Thread):
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
if api.getType() == v.PLEX_TYPE_ALBUM:
|
if api.getType() == v.PLEX_TYPE_ALBUM:
|
||||||
LOG.debug('Plex music album detected')
|
LOG.debug('Plex music album detected')
|
||||||
self.mgr.playqueue.init_playqueue_from_plex_children(
|
PQ.init_playqueue_from_plex_children(
|
||||||
api.getRatingKey(), transient_token=data.get('token'))
|
api.getRatingKey(),
|
||||||
|
transient_token=data.get('token'))
|
||||||
else:
|
else:
|
||||||
state.PLEX_TRANSIENT_TOKEN = data.get('token')
|
state.PLEX_TRANSIENT_TOKEN = data.get('token')
|
||||||
params = {
|
params = {
|
||||||
|
@ -91,7 +92,7 @@ class PlexCompanion(Thread):
|
||||||
# Get the playqueue ID
|
# Get the playqueue ID
|
||||||
_, container_key, query = ParseContainerKey(data['containerKey'])
|
_, container_key, query = ParseContainerKey(data['containerKey'])
|
||||||
try:
|
try:
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# E.g. Plex web does not supply the media type
|
# E.g. Plex web does not supply the media type
|
||||||
|
@ -103,13 +104,13 @@ class PlexCompanion(Thread):
|
||||||
LOG.error('Could not download Plex metadata')
|
LOG.error('Could not download Plex metadata')
|
||||||
return
|
return
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
||||||
if container_key == playqueue.id:
|
if container_key == playqueue.id:
|
||||||
LOG.info('Already know this playqueue - ignoring')
|
LOG.info('Already know this playqueue - ignoring')
|
||||||
playqueue.transient_token = data.get('token')
|
playqueue.transient_token = data.get('token')
|
||||||
else:
|
else:
|
||||||
self.mgr.playqueue.update_playqueue_from_PMS(
|
PQ.update_playqueue_from_PMS(
|
||||||
playqueue,
|
playqueue,
|
||||||
playqueue_id=container_key,
|
playqueue_id=container_key,
|
||||||
repeat=query.get('repeat'),
|
repeat=query.get('repeat'),
|
||||||
|
@ -121,7 +122,7 @@ class PlexCompanion(Thread):
|
||||||
"""
|
"""
|
||||||
Plex Companion client adjusted audio or subtitle stream
|
Plex Companion client adjusted audio or subtitle stream
|
||||||
"""
|
"""
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
|
||||||
pos = js.get_position(playqueue.playlistid)
|
pos = js.get_position(playqueue.playlistid)
|
||||||
if 'audioStreamID' in data:
|
if 'audioStreamID' in data:
|
||||||
|
@ -151,15 +152,13 @@ class PlexCompanion(Thread):
|
||||||
plex_type = get_plextype_from_xml(xml)
|
plex_type = get_plextype_from_xml(xml)
|
||||||
if plex_type is None:
|
if plex_type is None:
|
||||||
return
|
return
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type])
|
||||||
playqueue.clear()
|
playqueue.clear()
|
||||||
return
|
return
|
||||||
playqueue = self.mgr.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']])
|
||||||
self.mgr.playqueue.update_playqueue_from_PMS(
|
PQ.update_playqueue_from_PMS(playqueue, data['playQueueID'])
|
||||||
playqueue,
|
|
||||||
data['playQueueID'])
|
|
||||||
|
|
||||||
def _process_tasks(self, task):
|
def _process_tasks(self, task):
|
||||||
"""
|
"""
|
||||||
|
@ -217,11 +216,9 @@ class PlexCompanion(Thread):
|
||||||
|
|
||||||
# Start up instances
|
# Start up instances
|
||||||
request_mgr = httppersist.RequestMgr()
|
request_mgr = httppersist.RequestMgr()
|
||||||
subscription_manager = subscribers.SubscriptionMgr(
|
subscription_manager = subscribers.SubscriptionMgr(request_mgr,
|
||||||
request_mgr, self.player, self.mgr)
|
self.player)
|
||||||
self.subscription_manager = subscription_manager
|
self.subscription_manager = subscription_manager
|
||||||
queue = Queue(maxsize=100)
|
|
||||||
self.queue = queue
|
|
||||||
|
|
||||||
if settings('plexCompanion') == 'true':
|
if settings('plexCompanion') == 'true':
|
||||||
# Start up httpd
|
# Start up httpd
|
||||||
|
@ -231,7 +228,6 @@ class PlexCompanion(Thread):
|
||||||
httpd = listener.ThreadedHTTPServer(
|
httpd = listener.ThreadedHTTPServer(
|
||||||
client,
|
client,
|
||||||
subscription_manager,
|
subscription_manager,
|
||||||
queue,
|
|
||||||
('', v.COMPANION_PORT),
|
('', v.COMPANION_PORT),
|
||||||
listener.MyHandler)
|
listener.MyHandler)
|
||||||
httpd.timeout = 0.95
|
httpd.timeout = 0.95
|
||||||
|
@ -290,13 +286,13 @@ class PlexCompanion(Thread):
|
||||||
LOG.warn(traceback.format_exc())
|
LOG.warn(traceback.format_exc())
|
||||||
# See if there's anything we need to process
|
# See if there's anything we need to process
|
||||||
try:
|
try:
|
||||||
task = queue.get(block=False)
|
task = state.COMPANION_QUEUE.get(block=False)
|
||||||
except Empty:
|
except Empty:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# Got instructions, process them
|
# Got instructions, process them
|
||||||
self._process_tasks(task)
|
self._process_tasks(task)
|
||||||
queue.task_done()
|
state.COMPANION_QUEUE.task_done()
|
||||||
# Don't sleep
|
# Don't sleep
|
||||||
continue
|
continue
|
||||||
sleep(50)
|
sleep(50)
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
import logging
|
import logging
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Queue
|
|
||||||
|
|
||||||
from xbmc import sleep
|
from xbmc import sleep
|
||||||
|
|
||||||
|
@ -10,8 +9,7 @@ from utils import window, thread_methods
|
||||||
import state
|
import state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
log = logging.getLogger("PLEX."+__name__)
|
LOG = logging.getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,16 +21,10 @@ class Monitor_Window(Thread):
|
||||||
|
|
||||||
Adjusts state.py accordingly
|
Adjusts state.py accordingly
|
||||||
"""
|
"""
|
||||||
# Borg - multiple instances, shared state
|
|
||||||
def __init__(self, callback=None):
|
|
||||||
self.mgr = callback
|
|
||||||
self.playback_queue = Queue()
|
|
||||||
Thread.__init__(self)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
thread_stopped = self.thread_stopped
|
thread_stopped = self.thread_stopped
|
||||||
queue = self.playback_queue
|
queue = state.COMMAND_PIPELINE_QUEUE
|
||||||
log.info("----===## Starting Kodi_Play_Client ##===----")
|
LOG.info("----===## Starting Kodi_Play_Client ##===----")
|
||||||
while not thread_stopped():
|
while not thread_stopped():
|
||||||
if window('plex_command'):
|
if window('plex_command'):
|
||||||
value = window('plex_command')
|
value = window('plex_command')
|
||||||
|
@ -70,4 +62,4 @@ class Monitor_Window(Thread):
|
||||||
sleep(50)
|
sleep(50)
|
||||||
# Put one last item into the queue to let playback_starter end
|
# Put one last item into the queue to let playback_starter end
|
||||||
queue.put(None)
|
queue.put(None)
|
||||||
log.info("----===## Kodi_Play_Client stopped ##===----")
|
LOG.info("----===## Kodi_Play_Client stopped ##===----")
|
||||||
|
|
|
@ -6,7 +6,7 @@ from logging import getLogger
|
||||||
from xbmc import Player
|
from xbmc import Player
|
||||||
|
|
||||||
from variables import ALEXA_TO_COMPANION
|
from variables import ALEXA_TO_COMPANION
|
||||||
from playqueue import Playqueue
|
import playqueue as PQ
|
||||||
from PlexFunctions import GetPlexKeyNumber
|
from PlexFunctions import GetPlexKeyNumber
|
||||||
import json_rpc as js
|
import json_rpc as js
|
||||||
import state
|
import state
|
||||||
|
@ -29,9 +29,8 @@ def skip_to(params):
|
||||||
LOG.debug('Skipping to playQueueItemID %s, plex_id %s',
|
LOG.debug('Skipping to playQueueItemID %s, plex_id %s',
|
||||||
playqueue_item_id, plex_id)
|
playqueue_item_id, plex_id)
|
||||||
found = True
|
found = True
|
||||||
playqueues = Playqueue()
|
|
||||||
for player in js.get_players().values():
|
for player in js.get_players().values():
|
||||||
playqueue = playqueues.playqueues[player['playerid']]
|
playqueue = PQ.PLAYQUEUES[player['playerid']]
|
||||||
for i, item in enumerate(playqueue.items):
|
for i, item in enumerate(playqueue.items):
|
||||||
if item.id == playqueue_item_id:
|
if item.id == playqueue_item_id:
|
||||||
found = True
|
found = True
|
||||||
|
@ -57,7 +56,7 @@ def convert_alexa_to_companion(dictionary):
|
||||||
del dictionary[key]
|
del dictionary[key]
|
||||||
|
|
||||||
|
|
||||||
def process_command(request_path, params, queue=None):
|
def process_command(request_path, params):
|
||||||
"""
|
"""
|
||||||
queue: Queue() of PlexCompanion.py
|
queue: Queue() of PlexCompanion.py
|
||||||
"""
|
"""
|
||||||
|
@ -67,12 +66,12 @@ def process_command(request_path, params, queue=None):
|
||||||
if request_path == 'player/playback/playMedia':
|
if request_path == 'player/playback/playMedia':
|
||||||
# We need to tell service.py
|
# We need to tell service.py
|
||||||
action = 'alexa' if params.get('deviceName') == 'Alexa' else 'playlist'
|
action = 'alexa' if params.get('deviceName') == 'Alexa' else 'playlist'
|
||||||
queue.put({
|
state.COMPANION_QUEUE.put({
|
||||||
'action': action,
|
'action': action,
|
||||||
'data': params
|
'data': params
|
||||||
})
|
})
|
||||||
elif request_path == 'player/playback/refreshPlayQueue':
|
elif request_path == 'player/playback/refreshPlayQueue':
|
||||||
queue.put({
|
state.COMPANION_QUEUE.put({
|
||||||
'action': 'refreshPlayQueue',
|
'action': 'refreshPlayQueue',
|
||||||
'data': params
|
'data': params
|
||||||
})
|
})
|
||||||
|
@ -114,7 +113,7 @@ def process_command(request_path, params, queue=None):
|
||||||
elif request_path == "player/navigation/back":
|
elif request_path == "player/navigation/back":
|
||||||
js.input_back()
|
js.input_back()
|
||||||
elif request_path == "player/playback/setStreams":
|
elif request_path == "player/playback/setStreams":
|
||||||
queue.put({
|
state.COMPANION_QUEUE.put({
|
||||||
'action': 'setStreams',
|
'action': 'setStreams',
|
||||||
'data': params
|
'data': params
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from Queue import Queue
|
||||||
|
|
||||||
import xbmc
|
import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
@ -15,10 +15,11 @@ from PlexAPI import PlexAPI
|
||||||
from PlexFunctions import GetMachineIdentifier, get_PMS_settings
|
from PlexFunctions import GetMachineIdentifier, get_PMS_settings
|
||||||
import state
|
import state
|
||||||
from migration import check_migration
|
from migration import check_migration
|
||||||
|
import playqueue as PQ
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ log = getLogger("PLEX."+__name__)
|
||||||
class InitialSetup():
|
class InitialSetup():
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
log.debug('Entering initialsetup class')
|
LOG.debug('Entering initialsetup class')
|
||||||
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
self.doUtils = downloadutils.DownloadUtils().downloadUrl
|
||||||
self.plx = PlexAPI()
|
self.plx = PlexAPI()
|
||||||
self.dialog = xbmcgui.Dialog()
|
self.dialog = xbmcgui.Dialog()
|
||||||
|
@ -42,7 +43,7 @@ class InitialSetup():
|
||||||
# Token for the PMS, not plex.tv
|
# Token for the PMS, not plex.tv
|
||||||
self.pms_token = settings('accessToken')
|
self.pms_token = settings('accessToken')
|
||||||
if self.plexToken:
|
if self.plexToken:
|
||||||
log.debug('Found a plex.tv token in the settings')
|
LOG.debug('Found a plex.tv token in the settings')
|
||||||
|
|
||||||
def PlexTVSignIn(self):
|
def PlexTVSignIn(self):
|
||||||
"""
|
"""
|
||||||
|
@ -68,7 +69,7 @@ class InitialSetup():
|
||||||
chk = self.plx.CheckConnection('plex.tv', token=self.plexToken)
|
chk = self.plx.CheckConnection('plex.tv', token=self.plexToken)
|
||||||
if chk in (401, 403):
|
if chk in (401, 403):
|
||||||
# HTTP Error: unauthorized. Token is no longer valid
|
# HTTP Error: unauthorized. Token is no longer valid
|
||||||
log.info('plex.tv connection returned HTTP %s' % str(chk))
|
LOG.info('plex.tv connection returned HTTP %s', str(chk))
|
||||||
# Delete token in the settings
|
# Delete token in the settings
|
||||||
settings('plexToken', value='')
|
settings('plexToken', value='')
|
||||||
settings('plexLogin', value='')
|
settings('plexLogin', value='')
|
||||||
|
@ -77,12 +78,12 @@ class InitialSetup():
|
||||||
answer = self.PlexTVSignIn()
|
answer = self.PlexTVSignIn()
|
||||||
elif chk is False or chk >= 400:
|
elif chk is False or chk >= 400:
|
||||||
# Problems connecting to plex.tv. Network or internet issue?
|
# Problems connecting to plex.tv. Network or internet issue?
|
||||||
log.info('Problems connecting to plex.tv; connection returned '
|
LOG.info('Problems connecting to plex.tv; connection returned '
|
||||||
'HTTP %s' % str(chk))
|
'HTTP %s', str(chk))
|
||||||
self.dialog.ok(lang(29999), lang(39010))
|
self.dialog.ok(lang(29999), lang(39010))
|
||||||
answer = False
|
answer = False
|
||||||
else:
|
else:
|
||||||
log.info('plex.tv connection with token successful')
|
LOG.info('plex.tv connection with token successful')
|
||||||
settings('plex_status', value=lang(39227))
|
settings('plex_status', value=lang(39227))
|
||||||
# Refresh the info from Plex.tv
|
# Refresh the info from Plex.tv
|
||||||
xml = self.doUtils('https://plex.tv/users/account',
|
xml = self.doUtils('https://plex.tv/users/account',
|
||||||
|
@ -91,14 +92,14 @@ class InitialSetup():
|
||||||
try:
|
try:
|
||||||
self.plexLogin = xml.attrib['title']
|
self.plexLogin = xml.attrib['title']
|
||||||
except (AttributeError, KeyError):
|
except (AttributeError, KeyError):
|
||||||
log.error('Failed to update Plex info from plex.tv')
|
LOG.error('Failed to update Plex info from plex.tv')
|
||||||
else:
|
else:
|
||||||
settings('plexLogin', value=self.plexLogin)
|
settings('plexLogin', value=self.plexLogin)
|
||||||
home = 'true' if xml.attrib.get('home') == '1' else 'false'
|
home = 'true' if xml.attrib.get('home') == '1' else 'false'
|
||||||
settings('plexhome', value=home)
|
settings('plexhome', value=home)
|
||||||
settings('plexAvatar', value=xml.attrib.get('thumb'))
|
settings('plexAvatar', value=xml.attrib.get('thumb'))
|
||||||
settings('plexHomeSize', value=xml.attrib.get('homeSize', '1'))
|
settings('plexHomeSize', value=xml.attrib.get('homeSize', '1'))
|
||||||
log.info('Updated Plex info from plex.tv')
|
LOG.info('Updated Plex info from plex.tv')
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def CheckPMS(self):
|
def CheckPMS(self):
|
||||||
|
@ -114,24 +115,24 @@ class InitialSetup():
|
||||||
answer = True
|
answer = True
|
||||||
chk = self.plx.CheckConnection(self.server, verifySSL=False)
|
chk = self.plx.CheckConnection(self.server, verifySSL=False)
|
||||||
if chk is False:
|
if chk is False:
|
||||||
log.warn('Could not reach PMS %s' % self.server)
|
LOG.warn('Could not reach PMS %s', self.server)
|
||||||
answer = False
|
answer = False
|
||||||
if answer is True and not self.serverid:
|
if answer is True and not self.serverid:
|
||||||
log.info('No PMS machineIdentifier found for %s. Trying to '
|
LOG.info('No PMS machineIdentifier found for %s. Trying to '
|
||||||
'get the PMS unique ID' % self.server)
|
'get the PMS unique ID', self.server)
|
||||||
self.serverid = GetMachineIdentifier(self.server)
|
self.serverid = GetMachineIdentifier(self.server)
|
||||||
if self.serverid is None:
|
if self.serverid is None:
|
||||||
log.warn('Could not retrieve machineIdentifier')
|
LOG.warn('Could not retrieve machineIdentifier')
|
||||||
answer = False
|
answer = False
|
||||||
else:
|
else:
|
||||||
settings('plex_machineIdentifier', value=self.serverid)
|
settings('plex_machineIdentifier', value=self.serverid)
|
||||||
elif answer is True:
|
elif answer is True:
|
||||||
tempServerid = GetMachineIdentifier(self.server)
|
tempServerid = GetMachineIdentifier(self.server)
|
||||||
if tempServerid != self.serverid:
|
if tempServerid != self.serverid:
|
||||||
log.warn('The current PMS %s was expected to have a '
|
LOG.warn('The current PMS %s was expected to have a '
|
||||||
'unique machineIdentifier of %s. But we got '
|
'unique machineIdentifier of %s. But we got '
|
||||||
'%s. Pick a new server to be sure'
|
'%s. Pick a new server to be sure',
|
||||||
% (self.server, self.serverid, tempServerid))
|
self.server, self.serverid, tempServerid)
|
||||||
answer = False
|
answer = False
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ class InitialSetup():
|
||||||
self.plx.discoverPMS(xbmc.getIPAddress(),
|
self.plx.discoverPMS(xbmc.getIPAddress(),
|
||||||
plexToken=self.plexToken)
|
plexToken=self.plexToken)
|
||||||
serverlist = self.plx.returnServerList(self.plx.g_PMS)
|
serverlist = self.plx.returnServerList(self.plx.g_PMS)
|
||||||
log.debug('PMS serverlist: %s' % serverlist)
|
LOG.debug('PMS serverlist: %s', serverlist)
|
||||||
return serverlist
|
return serverlist
|
||||||
|
|
||||||
def _checkServerCon(self, server):
|
def _checkServerCon(self, server):
|
||||||
|
@ -209,7 +210,7 @@ class InitialSetup():
|
||||||
try:
|
try:
|
||||||
xml.attrib
|
xml.attrib
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log.error('Could not get PMS settings for %s' % url)
|
LOG.error('Could not get PMS settings for %s', url)
|
||||||
return
|
return
|
||||||
for entry in xml:
|
for entry in xml:
|
||||||
if entry.attrib.get('id', '') == 'allowMediaDeletion':
|
if entry.attrib.get('id', '') == 'allowMediaDeletion':
|
||||||
|
@ -236,9 +237,9 @@ class InitialSetup():
|
||||||
server = item
|
server = item
|
||||||
if server is None:
|
if server is None:
|
||||||
name = settings('plex_servername')
|
name = settings('plex_servername')
|
||||||
log.warn('The PMS you have used before with a unique '
|
LOG.warn('The PMS you have used before with a unique '
|
||||||
'machineIdentifier of %s and name %s is '
|
'machineIdentifier of %s and name %s is '
|
||||||
'offline' % (self.serverid, name))
|
'offline', self.serverid, name)
|
||||||
return
|
return
|
||||||
chk = self._checkServerCon(server)
|
chk = self._checkServerCon(server)
|
||||||
if chk == 504 and httpsUpdated is False:
|
if chk == 504 and httpsUpdated is False:
|
||||||
|
@ -247,8 +248,8 @@ class InitialSetup():
|
||||||
httpsUpdated = True
|
httpsUpdated = True
|
||||||
continue
|
continue
|
||||||
if chk == 401:
|
if chk == 401:
|
||||||
log.warn('Not yet authorized for Plex server %s'
|
LOG.warn('Not yet authorized for Plex server %s',
|
||||||
% server['name'])
|
server['name'])
|
||||||
if self.CheckPlexTVSignIn() is True:
|
if self.CheckPlexTVSignIn() is True:
|
||||||
if checkedPlexTV is False:
|
if checkedPlexTV is False:
|
||||||
# Try again
|
# Try again
|
||||||
|
@ -256,7 +257,7 @@ class InitialSetup():
|
||||||
httpsUpdated = False
|
httpsUpdated = False
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
log.warn('Not authorized even though we are signed '
|
LOG.warn('Not authorized even though we are signed '
|
||||||
' in to plex.tv correctly')
|
' in to plex.tv correctly')
|
||||||
self.dialog.ok(lang(29999), '%s %s'
|
self.dialog.ok(lang(29999), '%s %s'
|
||||||
% (lang(39214),
|
% (lang(39214),
|
||||||
|
@ -266,11 +267,11 @@ class InitialSetup():
|
||||||
return
|
return
|
||||||
# Problems connecting
|
# Problems connecting
|
||||||
elif chk >= 400 or chk is False:
|
elif chk >= 400 or chk is False:
|
||||||
log.warn('Problems connecting to server %s. chk is %s'
|
LOG.warn('Problems connecting to server %s. chk is %s',
|
||||||
% (server['name'], chk))
|
server['name'], chk)
|
||||||
return
|
return
|
||||||
log.info('We found a server to automatically connect to: %s'
|
LOG.info('We found a server to automatically connect to: %s',
|
||||||
% server['name'])
|
server['name'])
|
||||||
return server
|
return server
|
||||||
|
|
||||||
def _UserPickPMS(self):
|
def _UserPickPMS(self):
|
||||||
|
@ -285,7 +286,7 @@ class InitialSetup():
|
||||||
serverlist = self._getServerList()
|
serverlist = self._getServerList()
|
||||||
# Exit if no servers found
|
# Exit if no servers found
|
||||||
if len(serverlist) == 0:
|
if len(serverlist) == 0:
|
||||||
log.warn('No plex media servers found!')
|
LOG.warn('No plex media servers found!')
|
||||||
self.dialog.ok(lang(29999), lang(39011))
|
self.dialog.ok(lang(29999), lang(39011))
|
||||||
return
|
return
|
||||||
# Get a nicer list
|
# Get a nicer list
|
||||||
|
@ -322,8 +323,8 @@ class InitialSetup():
|
||||||
continue
|
continue
|
||||||
httpsUpdated = False
|
httpsUpdated = False
|
||||||
if chk == 401:
|
if chk == 401:
|
||||||
log.warn('Not yet authorized for Plex server %s'
|
LOG.warn('Not yet authorized for Plex server %s',
|
||||||
% server['name'])
|
server['name'])
|
||||||
# Please sign in to plex.tv
|
# Please sign in to plex.tv
|
||||||
self.dialog.ok(lang(29999),
|
self.dialog.ok(lang(29999),
|
||||||
lang(39013) + server['name'],
|
lang(39013) + server['name'],
|
||||||
|
@ -370,7 +371,7 @@ class InitialSetup():
|
||||||
scheme = server['scheme']
|
scheme = server['scheme']
|
||||||
settings('ipaddress', server['ip'])
|
settings('ipaddress', server['ip'])
|
||||||
settings('port', server['port'])
|
settings('port', server['port'])
|
||||||
log.debug("Setting SSL verify to false, because server is "
|
LOG.debug("Setting SSL verify to false, because server is "
|
||||||
"local")
|
"local")
|
||||||
settings('sslverify', 'false')
|
settings('sslverify', 'false')
|
||||||
else:
|
else:
|
||||||
|
@ -378,7 +379,7 @@ class InitialSetup():
|
||||||
scheme = baseURL[0]
|
scheme = baseURL[0]
|
||||||
settings('ipaddress', baseURL[1].replace('//', ''))
|
settings('ipaddress', baseURL[1].replace('//', ''))
|
||||||
settings('port', baseURL[2])
|
settings('port', baseURL[2])
|
||||||
log.debug("Setting SSL verify to true, because server is not "
|
LOG.debug("Setting SSL verify to true, because server is not "
|
||||||
"local")
|
"local")
|
||||||
settings('sslverify', 'true')
|
settings('sslverify', 'true')
|
||||||
|
|
||||||
|
@ -387,10 +388,10 @@ class InitialSetup():
|
||||||
else:
|
else:
|
||||||
settings('https', 'false')
|
settings('https', 'false')
|
||||||
# And finally do some logging
|
# And finally do some logging
|
||||||
log.debug("Writing to Kodi user settings file")
|
LOG.debug("Writing to Kodi user settings file")
|
||||||
log.debug("PMS machineIdentifier: %s, ip: %s, port: %s, https: %s "
|
LOG.debug("PMS machineIdentifier: %s, ip: %s, port: %s, https: %s ",
|
||||||
% (server['machineIdentifier'], server['ip'],
|
server['machineIdentifier'], server['ip'], server['port'],
|
||||||
server['port'], server['scheme']))
|
server['scheme'])
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""
|
"""
|
||||||
|
@ -399,14 +400,14 @@ class InitialSetup():
|
||||||
Check server, user, direct paths, music, direct stream if not direct
|
Check server, user, direct paths, music, direct stream if not direct
|
||||||
path.
|
path.
|
||||||
"""
|
"""
|
||||||
log.info("Initial setup called.")
|
LOG.info("Initial setup called.")
|
||||||
dialog = self.dialog
|
dialog = self.dialog
|
||||||
|
|
||||||
# Get current Kodi video cache setting
|
# Get current Kodi video cache setting
|
||||||
cache, _ = advancedsettings_xml(['cache', 'memorysize'])
|
cache, _ = advancedsettings_xml(['cache', 'memorysize'])
|
||||||
# Kodi default cache if no setting is set
|
# Kodi default cache if no setting is set
|
||||||
cache = str(cache.text) if cache is not None else '20971520'
|
cache = str(cache.text) if cache is not None else '20971520'
|
||||||
log.info('Current Kodi video memory cache in bytes: %s', cache)
|
LOG.info('Current Kodi video memory cache in bytes: %s', cache)
|
||||||
settings('kodi_video_cache', value=cache)
|
settings('kodi_video_cache', value=cache)
|
||||||
# Disable foreground "Loading media information from files"
|
# Disable foreground "Loading media information from files"
|
||||||
# (still used by Kodi, even though the Wiki says otherwise)
|
# (still used by Kodi, even though the Wiki says otherwise)
|
||||||
|
@ -420,13 +421,20 @@ class InitialSetup():
|
||||||
if self.plexToken and self.myplexlogin:
|
if self.plexToken and self.myplexlogin:
|
||||||
self.CheckPlexTVSignIn()
|
self.CheckPlexTVSignIn()
|
||||||
|
|
||||||
|
# Initialize the PKC playqueues
|
||||||
|
PQ.init_playqueues()
|
||||||
|
# Init some Queues()
|
||||||
|
state.COMMAND_PIPELINE_QUEUE = Queue()
|
||||||
|
state.COMPANION_QUEUE = Queue(maxsize=100)
|
||||||
|
state.WEBSOCKET_QUEUE = Queue()
|
||||||
|
|
||||||
# If a Plex server IP has already been set
|
# If a Plex server IP has already been set
|
||||||
# return only if the right machine identifier is found
|
# return only if the right machine identifier is found
|
||||||
if self.server:
|
if self.server:
|
||||||
log.info("PMS is already set: %s. Checking now..." % self.server)
|
LOG.info("PMS is already set: %s. Checking now...", self.server)
|
||||||
if self.CheckPMS():
|
if self.CheckPMS():
|
||||||
log.info("Using PMS %s with machineIdentifier %s"
|
LOG.info("Using PMS %s with machineIdentifier %s",
|
||||||
% (self.server, self.serverid))
|
self.server, self.serverid)
|
||||||
self._write_PMS_settings(self.server, self.pms_token)
|
self._write_PMS_settings(self.server, self.pms_token)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -452,19 +460,19 @@ class InitialSetup():
|
||||||
lang(39028),
|
lang(39028),
|
||||||
nolabel="Addon (Default)",
|
nolabel="Addon (Default)",
|
||||||
yeslabel="Native (Direct Paths)"):
|
yeslabel="Native (Direct Paths)"):
|
||||||
log.debug("User opted to use direct paths.")
|
LOG.debug("User opted to use direct paths.")
|
||||||
settings('useDirectPaths', value="1")
|
settings('useDirectPaths', value="1")
|
||||||
state.DIRECT_PATHS = True
|
state.DIRECT_PATHS = True
|
||||||
# Are you on a system where you would like to replace paths
|
# Are you on a system where you would like to replace paths
|
||||||
# \\NAS\mymovie.mkv with smb://NAS/mymovie.mkv? (e.g. Windows)
|
# \\NAS\mymovie.mkv with smb://NAS/mymovie.mkv? (e.g. Windows)
|
||||||
if dialog.yesno(heading=lang(29999), line1=lang(39033)):
|
if dialog.yesno(heading=lang(29999), line1=lang(39033)):
|
||||||
log.debug("User chose to replace paths with smb")
|
LOG.debug("User chose to replace paths with smb")
|
||||||
else:
|
else:
|
||||||
settings('replaceSMB', value="false")
|
settings('replaceSMB', value="false")
|
||||||
|
|
||||||
# complete replace all original Plex library paths with custom SMB
|
# complete replace all original Plex library paths with custom SMB
|
||||||
if dialog.yesno(heading=lang(29999), line1=lang(39043)):
|
if dialog.yesno(heading=lang(29999), line1=lang(39043)):
|
||||||
log.debug("User chose custom smb paths")
|
LOG.debug("User chose custom smb paths")
|
||||||
settings('remapSMB', value="true")
|
settings('remapSMB', value="true")
|
||||||
# Please enter your custom smb paths in the settings under
|
# Please enter your custom smb paths in the settings under
|
||||||
# "Sync Options" and then restart Kodi
|
# "Sync Options" and then restart Kodi
|
||||||
|
@ -475,22 +483,22 @@ class InitialSetup():
|
||||||
if dialog.yesno(heading=lang(29999),
|
if dialog.yesno(heading=lang(29999),
|
||||||
line1=lang(39029),
|
line1=lang(39029),
|
||||||
line2=lang(39030)):
|
line2=lang(39030)):
|
||||||
log.debug("Presenting network credentials dialog.")
|
LOG.debug("Presenting network credentials dialog.")
|
||||||
from utils import passwordsXML
|
from utils import passwordsXML
|
||||||
passwordsXML()
|
passwordsXML()
|
||||||
# Disable Plex music?
|
# Disable Plex music?
|
||||||
if dialog.yesno(heading=lang(29999), line1=lang(39016)):
|
if dialog.yesno(heading=lang(29999), line1=lang(39016)):
|
||||||
log.debug("User opted to disable Plex music library.")
|
LOG.debug("User opted to disable Plex music library.")
|
||||||
settings('enableMusic', value="false")
|
settings('enableMusic', value="false")
|
||||||
|
|
||||||
# Download additional art from FanArtTV
|
# Download additional art from FanArtTV
|
||||||
if dialog.yesno(heading=lang(29999), line1=lang(39061)):
|
if dialog.yesno(heading=lang(29999), line1=lang(39061)):
|
||||||
log.debug("User opted to use FanArtTV")
|
LOG.debug("User opted to use FanArtTV")
|
||||||
settings('FanartTV', value="true")
|
settings('FanartTV', value="true")
|
||||||
# Do you want to replace your custom user ratings with an indicator of
|
# Do you want to replace your custom user ratings with an indicator of
|
||||||
# how many versions of a media item you posses?
|
# how many versions of a media item you posses?
|
||||||
if dialog.yesno(heading=lang(29999), line1=lang(39718)):
|
if dialog.yesno(heading=lang(29999), line1=lang(39718)):
|
||||||
log.debug("User opted to replace user ratings with version number")
|
LOG.debug("User opted to replace user ratings with version number")
|
||||||
settings('indicate_media_versions', value="true")
|
settings('indicate_media_versions', value="true")
|
||||||
|
|
||||||
# If you use several Plex libraries of one kind, e.g. "Kids Movies" and
|
# If you use several Plex libraries of one kind, e.g. "Kids Movies" and
|
||||||
|
|
|
@ -16,6 +16,7 @@ import json_rpc as js
|
||||||
import playlist_func as PL
|
import playlist_func as PL
|
||||||
import state
|
import state
|
||||||
import variables as v
|
import variables as v
|
||||||
|
import playqueue as PQ
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -51,10 +52,8 @@ class KodiMonitor(Monitor):
|
||||||
"""
|
"""
|
||||||
PKC implementation of the Kodi Monitor class. Invoke only once.
|
PKC implementation of the Kodi Monitor class. Invoke only once.
|
||||||
"""
|
"""
|
||||||
def __init__(self, callback):
|
def __init__(self):
|
||||||
self.mgr = callback
|
|
||||||
self.xbmcplayer = Player()
|
self.xbmcplayer = Player()
|
||||||
self.playqueue = self.mgr.playqueue
|
|
||||||
Monitor.__init__(self)
|
Monitor.__init__(self)
|
||||||
LOG.info("Kodi monitor started.")
|
LOG.info("Kodi monitor started.")
|
||||||
|
|
||||||
|
@ -198,7 +197,7 @@ class KodiMonitor(Monitor):
|
||||||
}
|
}
|
||||||
Will NOT be called if playback initiated by Kodi widgets
|
Will NOT be called if playback initiated by Kodi widgets
|
||||||
"""
|
"""
|
||||||
playqueue = self.playqueue.playqueues[data['playlistid']]
|
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||||
# Did PKC cause this add? Then lets not do anything
|
# Did PKC cause this add? Then lets not do anything
|
||||||
if playqueue.is_kodi_onadd() is False:
|
if playqueue.is_kodi_onadd() is False:
|
||||||
LOG.debug('PKC added this item to the playqueue - ignoring')
|
LOG.debug('PKC added this item to the playqueue - ignoring')
|
||||||
|
@ -228,7 +227,7 @@ class KodiMonitor(Monitor):
|
||||||
u'position': 0
|
u'position': 0
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
playqueue = self.playqueue.playqueues[data['playlistid']]
|
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||||
# Did PKC cause this add? Then lets not do anything
|
# Did PKC cause this add? Then lets not do anything
|
||||||
if playqueue.is_kodi_onremove() is False:
|
if playqueue.is_kodi_onremove() is False:
|
||||||
LOG.debug('PKC removed this item already from playqueue - ignoring')
|
LOG.debug('PKC removed this item already from playqueue - ignoring')
|
||||||
|
@ -249,7 +248,7 @@ class KodiMonitor(Monitor):
|
||||||
u'playlistid': 1,
|
u'playlistid': 1,
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
playqueue = self.playqueue.playqueues[data['playlistid']]
|
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||||
if playqueue.is_kodi_onclear() is False:
|
if playqueue.is_kodi_onclear() is False:
|
||||||
LOG.debug('PKC already cleared the playqueue - ignoring')
|
LOG.debug('PKC already cleared the playqueue - ignoring')
|
||||||
return
|
return
|
||||||
|
@ -317,7 +316,7 @@ class KodiMonitor(Monitor):
|
||||||
LOG.debug('Set the player state: %s', state.PLAYER_STATES[playerid])
|
LOG.debug('Set the player state: %s', state.PLAYER_STATES[playerid])
|
||||||
# Check whether we need to init our playqueues (e.g. direct play)
|
# Check whether we need to init our playqueues (e.g. direct play)
|
||||||
init = False
|
init = False
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = PQ.PLAYQUEUES[playerid]
|
||||||
try:
|
try:
|
||||||
playqueue.items[info['position']]
|
playqueue.items[info['position']]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -340,7 +339,7 @@ class KodiMonitor(Monitor):
|
||||||
container_key = None
|
container_key = None
|
||||||
if info['playlistid'] != -1:
|
if info['playlistid'] != -1:
|
||||||
# -1 is Kodi's answer if there is no playlist
|
# -1 is Kodi's answer if there is no playlist
|
||||||
container_key = self.playqueue.playqueues[playerid].id
|
container_key = PQ.PLAYQUEUES[playerid].id
|
||||||
if container_key is not None:
|
if container_key is not None:
|
||||||
container_key = '/playQueues/%s' % container_key
|
container_key = '/playQueues/%s' % container_key
|
||||||
elif plex_id is not None:
|
elif plex_id is not None:
|
||||||
|
|
|
@ -43,9 +43,7 @@ log = getLogger("PLEX."+__name__)
|
||||||
class LibrarySync(Thread):
|
class LibrarySync(Thread):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
def __init__(self, callback=None):
|
def __init__(self):
|
||||||
self.mgr = callback
|
|
||||||
|
|
||||||
self.itemsToProcess = []
|
self.itemsToProcess = []
|
||||||
self.sessionKeys = []
|
self.sessionKeys = []
|
||||||
self.fanartqueue = Queue.Queue()
|
self.fanartqueue = Queue.Queue()
|
||||||
|
@ -1527,7 +1525,7 @@ class LibrarySync(Thread):
|
||||||
oneDay = 60*60*24
|
oneDay = 60*60*24
|
||||||
|
|
||||||
# Link to Websocket queue
|
# Link to Websocket queue
|
||||||
queue = self.mgr.ws.queue
|
queue = state.WEBSOCKET_QUEUE
|
||||||
|
|
||||||
startupComplete = False
|
startupComplete = False
|
||||||
self.views = []
|
self.views = []
|
||||||
|
|
|
@ -12,7 +12,7 @@ from playbackutils import PlaybackUtils
|
||||||
from utils import window
|
from utils import window
|
||||||
from PlexFunctions import GetPlexMetadata
|
from PlexFunctions import GetPlexMetadata
|
||||||
from PlexAPI import API
|
from PlexAPI import API
|
||||||
from playqueue import LOCK
|
import playqueue as PQ
|
||||||
import variables as v
|
import variables as v
|
||||||
from downloadutils import DownloadUtils
|
from downloadutils import DownloadUtils
|
||||||
from PKC_listitem import convert_PKC_to_listitem
|
from PKC_listitem import convert_PKC_to_listitem
|
||||||
|
@ -21,7 +21,8 @@ from context_entry import ContextMenu
|
||||||
import state
|
import state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
log = getLogger("PLEX."+__name__)
|
|
||||||
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -30,26 +31,21 @@ class Playback_Starter(Thread):
|
||||||
"""
|
"""
|
||||||
Processes new plays
|
Processes new plays
|
||||||
"""
|
"""
|
||||||
def __init__(self, callback=None):
|
|
||||||
self.mgr = callback
|
|
||||||
self.playqueue = self.mgr.playqueue
|
|
||||||
Thread.__init__(self)
|
|
||||||
|
|
||||||
def process_play(self, plex_id, kodi_id=None):
|
def process_play(self, plex_id, kodi_id=None):
|
||||||
"""
|
"""
|
||||||
Processes Kodi playback init for ONE item
|
Processes Kodi playback init for ONE item
|
||||||
"""
|
"""
|
||||||
log.info("Process_play called with plex_id %s, kodi_id %s"
|
LOG.info("Process_play called with plex_id %s, kodi_id %s",
|
||||||
% (plex_id, kodi_id))
|
plex_id, kodi_id)
|
||||||
if not state.AUTHENTICATED:
|
if not state.AUTHENTICATED:
|
||||||
log.error('Not yet authenticated for PMS, abort starting playback')
|
LOG.error('Not yet authenticated for PMS, abort starting playback')
|
||||||
# Todo: Warn user with dialog
|
# Todo: Warn user with dialog
|
||||||
return
|
return
|
||||||
xml = GetPlexMetadata(plex_id)
|
xml = GetPlexMetadata(plex_id)
|
||||||
try:
|
try:
|
||||||
xml[0].attrib
|
xml[0].attrib
|
||||||
except (IndexError, TypeError, AttributeError):
|
except (IndexError, TypeError, AttributeError):
|
||||||
log.error('Could not get a PMS xml for plex id %s' % plex_id)
|
LOG.error('Could not get a PMS xml for plex id %s', plex_id)
|
||||||
return
|
return
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
if api.getType() == v.PLEX_TYPE_PHOTO:
|
if api.getType() == v.PLEX_TYPE_PHOTO:
|
||||||
|
@ -60,15 +56,14 @@ class Playback_Starter(Thread):
|
||||||
result.listitem = listitem
|
result.listitem = listitem
|
||||||
else:
|
else:
|
||||||
# Video and Music
|
# Video and Music
|
||||||
playqueue = self.playqueue.get_playqueue_from_type(
|
playqueue = PQ.get_playqueue_from_type(
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()])
|
||||||
with LOCK:
|
with PQ.LOCK:
|
||||||
result = PlaybackUtils(xml, playqueue).play(
|
result = PlaybackUtils(xml, playqueue).play(
|
||||||
plex_id,
|
plex_id,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
xml.attrib.get('librarySectionUUID'))
|
xml.attrib.get('librarySectionUUID'))
|
||||||
log.info('Done process_play, playqueues: %s'
|
LOG.info('Done process_play, playqueues: %s', PQ.PLAYQUEUES)
|
||||||
% self.playqueue.playqueues)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def process_plex_node(self, url, viewOffset, directplay=False,
|
def process_plex_node(self, url, viewOffset, directplay=False,
|
||||||
|
@ -77,8 +72,8 @@ class Playback_Starter(Thread):
|
||||||
Called for Plex directories or redirect for playback (e.g. trailers,
|
Called for Plex directories or redirect for playback (e.g. trailers,
|
||||||
clips, watchlater)
|
clips, watchlater)
|
||||||
"""
|
"""
|
||||||
log.info('process_plex_node called with url: %s, viewOffset: %s'
|
LOG.info('process_plex_node called with url: %s, viewOffset: %s',
|
||||||
% (url, viewOffset))
|
url, viewOffset)
|
||||||
# Plex redirect, e.g. watch later. Need to get actual URLs
|
# Plex redirect, e.g. watch later. Need to get actual URLs
|
||||||
if url.startswith('http') or url.startswith('{server}'):
|
if url.startswith('http') or url.startswith('{server}'):
|
||||||
xml = DownloadUtils().downloadUrl(url)
|
xml = DownloadUtils().downloadUrl(url)
|
||||||
|
@ -87,7 +82,7 @@ class Playback_Starter(Thread):
|
||||||
try:
|
try:
|
||||||
xml[0].attrib
|
xml[0].attrib
|
||||||
except:
|
except:
|
||||||
log.error('Could not download PMS metadata')
|
LOG.error('Could not download PMS metadata')
|
||||||
return
|
return
|
||||||
if viewOffset != '0':
|
if viewOffset != '0':
|
||||||
try:
|
try:
|
||||||
|
@ -96,7 +91,7 @@ class Playback_Starter(Thread):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
window('plex_customplaylist.seektime', value=str(viewOffset))
|
window('plex_customplaylist.seektime', value=str(viewOffset))
|
||||||
log.info('Set resume point to %s' % str(viewOffset))
|
LOG.info('Set resume point to %s', viewOffset)
|
||||||
api = API(xml[0])
|
api = API(xml[0])
|
||||||
typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]
|
typus = v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.getType()]
|
||||||
if node is True:
|
if node is True:
|
||||||
|
@ -110,10 +105,10 @@ class Playback_Starter(Thread):
|
||||||
try:
|
try:
|
||||||
kodi_id = plexdb_item[0]
|
kodi_id = plexdb_item[0]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
log.info('Couldnt find item %s in Kodi db'
|
LOG.info('Couldnt find item %s in Kodi db',
|
||||||
% api.getRatingKey())
|
api.getRatingKey())
|
||||||
playqueue = self.playqueue.get_playqueue_from_type(typus)
|
playqueue = PQ.get_playqueue_from_type(typus)
|
||||||
with LOCK:
|
with PQ.LOCK:
|
||||||
result = PlaybackUtils(xml, playqueue).play(
|
result = PlaybackUtils(xml, playqueue).play(
|
||||||
plex_id,
|
plex_id,
|
||||||
kodi_id=kodi_id,
|
kodi_id=kodi_id,
|
||||||
|
@ -130,7 +125,7 @@ class Playback_Starter(Thread):
|
||||||
_, params = item.split('?', 1)
|
_, params = item.split('?', 1)
|
||||||
params = dict(parse_qsl(params))
|
params = dict(parse_qsl(params))
|
||||||
mode = params.get('mode')
|
mode = params.get('mode')
|
||||||
log.debug('Received mode: %s, params: %s' % (mode, params))
|
LOG.debug('Received mode: %s, params: %s', mode, params)
|
||||||
try:
|
try:
|
||||||
if mode == 'play':
|
if mode == 'play':
|
||||||
result = self.process_play(params.get('id'),
|
result = self.process_play(params.get('id'),
|
||||||
|
@ -147,18 +142,18 @@ class Playback_Starter(Thread):
|
||||||
ContextMenu()
|
ContextMenu()
|
||||||
result = Playback_Successful()
|
result = Playback_Successful()
|
||||||
except:
|
except:
|
||||||
log.error('Error encountered for mode %s, params %s'
|
LOG.error('Error encountered for mode %s, params %s',
|
||||||
% (mode, params))
|
mode, params)
|
||||||
import traceback
|
import traceback
|
||||||
log.error(traceback.format_exc())
|
LOG.error(traceback.format_exc())
|
||||||
# Let default.py know!
|
# Let default.py know!
|
||||||
pickle_me(None)
|
pickle_me(None)
|
||||||
else:
|
else:
|
||||||
pickle_me(result)
|
pickle_me(result)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
queue = self.mgr.command_pipeline.playback_queue
|
queue = state.COMMAND_PIPELINE_QUEUE
|
||||||
log.info("----===## Starting Playback_Starter ##===----")
|
LOG.info("----===## Starting Playback_Starter ##===----")
|
||||||
while True:
|
while True:
|
||||||
item = queue.get()
|
item = queue.get()
|
||||||
if item is None:
|
if item is None:
|
||||||
|
@ -167,4 +162,4 @@ class Playback_Starter(Thread):
|
||||||
else:
|
else:
|
||||||
self.triage(item)
|
self.triage(item)
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
log.info("----===## Playback_Starter stopped ##===----")
|
LOG.info("----===## Playback_Starter stopped ##===----")
|
||||||
|
|
|
@ -97,6 +97,7 @@ class PlaybackUtils():
|
||||||
startPos = max(playqueue.kodi_pl.getposition(), 0)
|
startPos = max(playqueue.kodi_pl.getposition(), 0)
|
||||||
self.currentPosition = startPos
|
self.currentPosition = startPos
|
||||||
|
|
||||||
|
propertiesPlayback = window('plex_playbackProps') == "true"
|
||||||
introsPlaylist = False
|
introsPlaylist = False
|
||||||
dummyPlaylist = False
|
dummyPlaylist = False
|
||||||
|
|
||||||
|
@ -113,8 +114,8 @@ class PlaybackUtils():
|
||||||
|
|
||||||
# We need to ensure we add the intro and additional parts only once.
|
# We need to ensure we add the intro and additional parts only once.
|
||||||
# Otherwise we get a loop.
|
# Otherwise we get a loop.
|
||||||
if not state.PLAYBACK_SETUP_DONE:
|
if not propertiesPlayback:
|
||||||
state.PLAYBACK_SETUP_DONE = True
|
window('plex_playbackProps', value="true")
|
||||||
LOG.info("Setting up properties in playlist.")
|
LOG.info("Setting up properties in playlist.")
|
||||||
# Where will the player need to start?
|
# Where will the player need to start?
|
||||||
# Do we need to get trailers?
|
# Do we need to get trailers?
|
||||||
|
@ -139,7 +140,6 @@ class PlaybackUtils():
|
||||||
get_playlist_details_from_xml(playqueue, xml=xml)
|
get_playlist_details_from_xml(playqueue, xml=xml)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return
|
return
|
||||||
|
|
||||||
if (not homeScreen and not seektime and sizePlaylist < 2 and
|
if (not homeScreen and not seektime and sizePlaylist < 2 and
|
||||||
window('plex_customplaylist') != "true" and
|
window('plex_customplaylist') != "true" and
|
||||||
not contextmenu_play):
|
not contextmenu_play):
|
||||||
|
@ -198,7 +198,7 @@ class PlaybackUtils():
|
||||||
api.set_listitem_artwork(listitem)
|
api.set_listitem_artwork(listitem)
|
||||||
add_listitem_to_Kodi_playlist(
|
add_listitem_to_Kodi_playlist(
|
||||||
playqueue,
|
playqueue,
|
||||||
self.currentPosition,
|
self.currentPosition+1,
|
||||||
convert_PKC_to_listitem(listitem),
|
convert_PKC_to_listitem(listitem),
|
||||||
file=playurl,
|
file=playurl,
|
||||||
kodi_item={'id': kodi_id, 'type': kodi_type})
|
kodi_item={'id': kodi_id, 'type': kodi_type})
|
||||||
|
@ -206,7 +206,7 @@ class PlaybackUtils():
|
||||||
# Full metadata$
|
# Full metadata$
|
||||||
add_item_to_kodi_playlist(
|
add_item_to_kodi_playlist(
|
||||||
playqueue,
|
playqueue,
|
||||||
self.currentPosition,
|
self.currentPosition+1,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
kodi_type)
|
kodi_type)
|
||||||
self.currentPosition += 1
|
self.currentPosition += 1
|
||||||
|
@ -230,9 +230,9 @@ class PlaybackUtils():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# We just skipped adding properties. Reset flag for next time.
|
# We just skipped adding properties. Reset flag for next time.
|
||||||
elif state.PLAYBACK_SETUP_DONE:
|
elif propertiesPlayback:
|
||||||
LOG.debug("Resetting properties playback flag.")
|
LOG.debug("Resetting properties playback flag.")
|
||||||
state.PLAYBACK_SETUP_DONE = False
|
window('plex_playbackProps', clear=True)
|
||||||
|
|
||||||
# SETUP MAIN ITEM ##########
|
# SETUP MAIN ITEM ##########
|
||||||
# For transcoding only, ask for audio/subs pref
|
# For transcoding only, ask for audio/subs pref
|
||||||
|
@ -275,7 +275,7 @@ class PlaybackUtils():
|
||||||
Play all items contained in the xml passed in. Called by Plex Companion
|
Play all items contained in the xml passed in. Called by Plex Companion
|
||||||
"""
|
"""
|
||||||
LOG.info("Playbackutils play_all called")
|
LOG.info("Playbackutils play_all called")
|
||||||
state.PLAYBACK_SETUP_DONE = True
|
window('plex_playbackProps', value="true")
|
||||||
self.currentPosition = 0
|
self.currentPosition = 0
|
||||||
for item in self.xml:
|
for item in self.xml:
|
||||||
api = API(item)
|
api = API(item)
|
||||||
|
|
|
@ -268,7 +268,6 @@ class PKC_Player(Player):
|
||||||
# We might have saved a transient token from a user flinging media via
|
# We might have saved a transient token from a user flinging media via
|
||||||
# Companion (if we could not use the playqueue to store the token)
|
# Companion (if we could not use the playqueue to store the token)
|
||||||
state.PLEX_TRANSIENT_TOKEN = None
|
state.PLEX_TRANSIENT_TOKEN = None
|
||||||
state.PLAYBACK_SETUP_DONE = False
|
|
||||||
LOG.debug("Cleared playlist properties.")
|
LOG.debug("Cleared playlist properties.")
|
||||||
|
|
||||||
def onPlayBackEnded(self):
|
def onPlayBackEnded(self):
|
||||||
|
|
|
@ -4,9 +4,9 @@ Monitors the Kodi playqueue and adjusts the Plex playqueue accordingly
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from threading import RLock, Thread
|
from threading import RLock, Thread
|
||||||
|
|
||||||
from xbmc import sleep, Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO
|
from xbmc import Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO
|
||||||
|
|
||||||
from utils import window, thread_methods
|
from utils import window
|
||||||
import playlist_func as PL
|
import playlist_func as PL
|
||||||
from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren
|
from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren
|
||||||
from PlexAPI import API
|
from PlexAPI import API
|
||||||
|
@ -20,218 +20,126 @@ LOG = getLogger("PLEX." + __name__)
|
||||||
# lock used for playqueue manipulations
|
# lock used for playqueue manipulations
|
||||||
LOCK = RLock()
|
LOCK = RLock()
|
||||||
PLUGIN = 'plugin://%s' % v.ADDON_ID
|
PLUGIN = 'plugin://%s' % v.ADDON_ID
|
||||||
|
|
||||||
|
# Our PKC playqueues (3 instances of Playqueue_Object())
|
||||||
|
PLAYQUEUES = []
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_suspends=['PMS_STATUS'])
|
def init_playqueues():
|
||||||
class Playqueue(Thread):
|
|
||||||
"""
|
"""
|
||||||
Monitors Kodi's playqueues for changes on the Kodi side
|
Call this once on startup to initialize the PKC playqueue objects in
|
||||||
|
the list PLAYQUEUES
|
||||||
"""
|
"""
|
||||||
# Borg - multiple instances, shared state
|
if PLAYQUEUES:
|
||||||
__shared_state = {}
|
LOG.debug('Playqueues have already been initialized')
|
||||||
playqueues = None
|
return
|
||||||
|
# Initialize Kodi playqueues
|
||||||
def __init__(self, callback=None):
|
with LOCK:
|
||||||
self.__dict__ = self.__shared_state
|
for i in (0, 1, 2):
|
||||||
if self.playqueues is not None:
|
# Just in case the Kodi response is not sorted correctly
|
||||||
LOG.debug('Playqueue thread has already been initialized')
|
|
||||||
Thread.__init__(self)
|
|
||||||
return
|
|
||||||
self.mgr = callback
|
|
||||||
|
|
||||||
# Initialize Kodi playqueues
|
|
||||||
with LOCK:
|
|
||||||
self.playqueues = []
|
|
||||||
for queue in js.get_playlists():
|
for queue in js.get_playlists():
|
||||||
|
if queue['playlistid'] != i:
|
||||||
|
continue
|
||||||
playqueue = PL.Playqueue_Object()
|
playqueue = PL.Playqueue_Object()
|
||||||
playqueue.playlistid = queue['playlistid']
|
playqueue.playlistid = i
|
||||||
playqueue.type = queue['type']
|
playqueue.type = queue['type']
|
||||||
# Initialize each Kodi playlist
|
# Initialize each Kodi playlist
|
||||||
if playqueue.type == 'audio':
|
if playqueue.type == v.KODI_TYPE_AUDIO:
|
||||||
playqueue.kodi_pl = PlayList(PLAYLIST_MUSIC)
|
playqueue.kodi_pl = PlayList(PLAYLIST_MUSIC)
|
||||||
elif playqueue.type == 'video':
|
elif playqueue.type == v.KODI_TYPE_VIDEO:
|
||||||
playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO)
|
playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO)
|
||||||
else:
|
else:
|
||||||
# Currently, only video or audio playqueues available
|
# Currently, only video or audio playqueues available
|
||||||
playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO)
|
playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO)
|
||||||
# Overwrite 'picture' with 'photo'
|
# Overwrite 'picture' with 'photo'
|
||||||
playqueue.type = v.KODI_TYPE_PHOTO
|
playqueue.type = v.KODI_TYPE_PHOTO
|
||||||
self.playqueues.append(playqueue)
|
PLAYQUEUES.append(playqueue)
|
||||||
# sort the list by their playlistid, just in case
|
LOG.debug('Initialized the Kodi playqueues: %s', PLAYQUEUES)
|
||||||
self.playqueues = sorted(
|
|
||||||
self.playqueues, key=lambda i: i.playlistid)
|
|
||||||
LOG.debug('Initialized the Kodi play queues: %s', self.playqueues)
|
|
||||||
Thread.__init__(self)
|
|
||||||
|
|
||||||
def get_playqueue_from_type(self, typus):
|
|
||||||
"""
|
|
||||||
Returns the playqueue according to the typus ('video', 'audio',
|
|
||||||
'picture') passed in
|
|
||||||
"""
|
|
||||||
with LOCK:
|
|
||||||
for playqueue in self.playqueues:
|
|
||||||
if playqueue.type == typus:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError('Wrong playlist type passed in: %s' % typus)
|
|
||||||
return playqueue
|
|
||||||
|
|
||||||
def init_playqueue_from_plex_children(self, plex_id, transient_token=None):
|
def get_playqueue_from_type(typus):
|
||||||
"""
|
"""
|
||||||
Init a new playqueue e.g. from an album. Alexa does this
|
Returns the playqueue according to the typus ('video', 'audio',
|
||||||
|
'picture') passed in
|
||||||
Returns the Playlist_Object
|
"""
|
||||||
"""
|
with LOCK:
|
||||||
xml = GetAllPlexChildren(plex_id)
|
for playqueue in PLAYQUEUES:
|
||||||
try:
|
if playqueue.type == typus:
|
||||||
xml[0].attrib
|
break
|
||||||
except (TypeError, IndexError, AttributeError):
|
else:
|
||||||
LOG.error('Could not download the PMS xml for %s', plex_id)
|
raise ValueError('Wrong playlist type passed in: %s' % typus)
|
||||||
return
|
|
||||||
playqueue = self.get_playqueue_from_type(
|
|
||||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']])
|
|
||||||
playqueue.clear()
|
|
||||||
for i, child in enumerate(xml):
|
|
||||||
api = API(child)
|
|
||||||
PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey())
|
|
||||||
playqueue.plex_transient_token = transient_token
|
|
||||||
LOG.debug('Firing up Kodi player')
|
|
||||||
Player().play(playqueue.kodi_pl, None, False, 0)
|
|
||||||
return playqueue
|
return playqueue
|
||||||
|
|
||||||
def update_playqueue_from_PMS(self,
|
|
||||||
playqueue,
|
|
||||||
playqueue_id=None,
|
|
||||||
repeat=None,
|
|
||||||
offset=None,
|
|
||||||
transient_token=None):
|
|
||||||
"""
|
|
||||||
Completely updates the Kodi playqueue with the new Plex playqueue. Pass
|
|
||||||
in playqueue_id if we need to fetch a new playqueue
|
|
||||||
|
|
||||||
repeat = 0, 1, 2
|
def init_playqueue_from_plex_children(plex_id, transient_token=None):
|
||||||
offset = time offset in Plextime (milliseconds)
|
"""
|
||||||
"""
|
Init a new playqueue e.g. from an album. Alexa does this
|
||||||
LOG.info('New playqueue %s received from Plex companion with offset '
|
|
||||||
'%s, repeat %s', playqueue_id, offset, repeat)
|
|
||||||
# Safe transient token from being deleted
|
|
||||||
if transient_token is None:
|
|
||||||
transient_token = playqueue.plex_transient_token
|
|
||||||
with LOCK:
|
|
||||||
xml = PL.get_PMS_playlist(playqueue, playqueue_id)
|
|
||||||
playqueue.clear()
|
|
||||||
try:
|
|
||||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
|
||||||
except KeyError:
|
|
||||||
LOG.error('Could not get playqueue ID %s', playqueue_id)
|
|
||||||
return
|
|
||||||
playqueue.repeat = 0 if not repeat else int(repeat)
|
|
||||||
playqueue.plex_transient_token = transient_token
|
|
||||||
PlaybackUtils(xml, playqueue).play_all()
|
|
||||||
window('plex_customplaylist', value="true")
|
|
||||||
if offset not in (None, "0"):
|
|
||||||
window('plex_customplaylist.seektime',
|
|
||||||
str(ConvertPlexToKodiTime(offset)))
|
|
||||||
for startpos, item in enumerate(playqueue.items):
|
|
||||||
if item.id == playqueue.selectedItemID:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
startpos = 0
|
|
||||||
# Start playback. Player does not return in time
|
|
||||||
LOG.debug('Playqueues after Plex Companion update are now: %s',
|
|
||||||
self.playqueues)
|
|
||||||
thread = Thread(target=Player().play,
|
|
||||||
args=(playqueue.kodi_pl,
|
|
||||||
None,
|
|
||||||
False,
|
|
||||||
startpos))
|
|
||||||
thread.setDaemon(True)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
def _compare_playqueues(self, playqueue, new):
|
Returns the Playlist_Object
|
||||||
"""
|
"""
|
||||||
Used to poll the Kodi playqueue and update the Plex playqueue if needed
|
xml = GetAllPlexChildren(plex_id)
|
||||||
"""
|
try:
|
||||||
old = list(playqueue.items)
|
xml[0].attrib
|
||||||
index = list(range(0, len(old)))
|
except (TypeError, IndexError, AttributeError):
|
||||||
LOG.debug('Comparing new Kodi playqueue %s with our play queue %s',
|
LOG.error('Could not download the PMS xml for %s', plex_id)
|
||||||
new, old)
|
return
|
||||||
if self.thread_stopped():
|
playqueue = get_playqueue_from_type(
|
||||||
# Chances are that we got an empty Kodi playlist due to
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[xml[0].attrib['type']])
|
||||||
# Kodi exit
|
playqueue.clear()
|
||||||
|
for i, child in enumerate(xml):
|
||||||
|
api = API(child)
|
||||||
|
PL.add_item_to_playlist(playqueue, i, plex_id=api.getRatingKey())
|
||||||
|
playqueue.plex_transient_token = transient_token
|
||||||
|
LOG.debug('Firing up Kodi player')
|
||||||
|
Player().play(playqueue.kodi_pl, None, False, 0)
|
||||||
|
return playqueue
|
||||||
|
|
||||||
|
|
||||||
|
def update_playqueue_from_PMS(playqueue,
|
||||||
|
playqueue_id=None,
|
||||||
|
repeat=None,
|
||||||
|
offset=None,
|
||||||
|
transient_token=None):
|
||||||
|
"""
|
||||||
|
Completely updates the Kodi playqueue with the new Plex playqueue. Pass
|
||||||
|
in playqueue_id if we need to fetch a new playqueue
|
||||||
|
|
||||||
|
repeat = 0, 1, 2
|
||||||
|
offset = time offset in Plextime (milliseconds)
|
||||||
|
"""
|
||||||
|
LOG.info('New playqueue %s received from Plex companion with offset '
|
||||||
|
'%s, repeat %s', playqueue_id, offset, repeat)
|
||||||
|
# Safe transient token from being deleted
|
||||||
|
if transient_token is None:
|
||||||
|
transient_token = playqueue.plex_transient_token
|
||||||
|
with LOCK:
|
||||||
|
xml = PL.get_PMS_playlist(playqueue, playqueue_id)
|
||||||
|
playqueue.clear()
|
||||||
|
try:
|
||||||
|
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||||
|
except KeyError:
|
||||||
|
LOG.error('Could not get playqueue ID %s', playqueue_id)
|
||||||
return
|
return
|
||||||
for i, new_item in enumerate(new):
|
playqueue.repeat = 0 if not repeat else int(repeat)
|
||||||
if (new_item['file'].startswith('plugin://') and
|
playqueue.plex_transient_token = transient_token
|
||||||
not new_item['file'].startswith(PLUGIN)):
|
PlaybackUtils(xml, playqueue).play_all()
|
||||||
# Ignore new media added by other addons
|
window('plex_customplaylist', value="true")
|
||||||
continue
|
if offset not in (None, "0"):
|
||||||
for j, old_item in enumerate(old):
|
window('plex_customplaylist.seektime',
|
||||||
try:
|
str(ConvertPlexToKodiTime(offset)))
|
||||||
if (old_item.file.startswith('plugin://') and
|
for startpos, item in enumerate(playqueue.items):
|
||||||
not old_item['file'].startswith(PLUGIN)):
|
if item.id == playqueue.selectedItemID:
|
||||||
# Ignore media by other addons
|
break
|
||||||
continue
|
else:
|
||||||
except (TypeError, AttributeError):
|
startpos = 0
|
||||||
# were not passed a filename; ignore
|
# Start playback. Player does not return in time
|
||||||
pass
|
LOG.debug('Playqueues after Plex Companion update are now: %s',
|
||||||
if new_item.get('id') is None:
|
PLAYQUEUES)
|
||||||
identical = old_item.file == new_item['file']
|
thread = Thread(target=Player().play,
|
||||||
else:
|
args=(playqueue.kodi_pl,
|
||||||
identical = (old_item.kodi_id == new_item['id'] and
|
None,
|
||||||
old_item.kodi_type == new_item['type'])
|
False,
|
||||||
if j == 0 and identical:
|
startpos))
|
||||||
del old[j], index[j]
|
thread.setDaemon(True)
|
||||||
break
|
thread.start()
|
||||||
elif identical:
|
|
||||||
LOG.debug('Detected playqueue item %s moved to position %s',
|
|
||||||
i+j, i)
|
|
||||||
PL.move_playlist_item(playqueue, i + j, i)
|
|
||||||
del old[j], index[j]
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
LOG.debug('Detected new Kodi element at position %s: %s ',
|
|
||||||
i, new_item)
|
|
||||||
if playqueue.id is None:
|
|
||||||
PL.init_Plex_playlist(playqueue,
|
|
||||||
kodi_item=new_item)
|
|
||||||
else:
|
|
||||||
PL.add_item_to_PMS_playlist(playqueue,
|
|
||||||
i,
|
|
||||||
kodi_item=new_item)
|
|
||||||
for j in range(i, len(index)):
|
|
||||||
index[j] += 1
|
|
||||||
for i in reversed(index):
|
|
||||||
LOG.debug('Detected deletion of playqueue element at pos %s', i)
|
|
||||||
PL.delete_playlist_item_from_PMS(playqueue, i)
|
|
||||||
LOG.debug('Done comparing playqueues')
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
thread_stopped = self.thread_stopped
|
|
||||||
thread_suspended = self.thread_suspended
|
|
||||||
LOG.info("----===## Starting PlayQueue client ##===----")
|
|
||||||
# Initialize the playqueues, if Kodi already got items in them
|
|
||||||
for playqueue in self.playqueues:
|
|
||||||
for i, item in enumerate(js.playlist_get_items(playqueue.id)):
|
|
||||||
if i == 0:
|
|
||||||
PL.init_Plex_playlist(playqueue, kodi_item=item)
|
|
||||||
else:
|
|
||||||
PL.add_item_to_PMS_playlist(playqueue, i, kodi_item=item)
|
|
||||||
while not thread_stopped():
|
|
||||||
while thread_suspended():
|
|
||||||
if thread_stopped():
|
|
||||||
break
|
|
||||||
sleep(1000)
|
|
||||||
# with LOCK:
|
|
||||||
# for playqueue in self.playqueues:
|
|
||||||
# kodi_playqueue = js.playlist_get_items(playqueue.id)
|
|
||||||
# if playqueue.old_kodi_pl != kodi_playqueue:
|
|
||||||
# # compare old and new playqueue
|
|
||||||
# self._compare_playqueues(playqueue, kodi_playqueue)
|
|
||||||
# playqueue.old_kodi_pl = list(kodi_playqueue)
|
|
||||||
# # Still sleep a bit so Kodi does not become
|
|
||||||
# # unresponsive
|
|
||||||
# sleep(10)
|
|
||||||
# continue
|
|
||||||
sleep(200)
|
|
||||||
LOG.info("----===## PlayQueue client stopped ##===----")
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ from urlparse import urlparse, parse_qs
|
||||||
|
|
||||||
from xbmc import sleep
|
from xbmc import sleep
|
||||||
from companion import process_command
|
from companion import process_command
|
||||||
from utils import window
|
|
||||||
import json_rpc as js
|
import json_rpc as js
|
||||||
from clientinfo import getXArgsDeviceInfo
|
from clientinfo import getXArgsDeviceInfo
|
||||||
import variables as v
|
import variables as v
|
||||||
|
@ -154,7 +153,7 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||||
sub_mgr.remove_subscriber(uuid)
|
sub_mgr.remove_subscriber(uuid)
|
||||||
else:
|
else:
|
||||||
# Throw it to companion.py
|
# Throw it to companion.py
|
||||||
process_command(request_path, params, self.server.queue)
|
process_command(request_path, params)
|
||||||
self.response('', getXArgsDeviceInfo(include_token=False))
|
self.response('', getXArgsDeviceInfo(include_token=False))
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,7 +163,7 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
"""
|
"""
|
||||||
daemon_threads = True
|
daemon_threads = True
|
||||||
|
|
||||||
def __init__(self, client, subscription_manager, queue, *args, **kwargs):
|
def __init__(self, client, subscription_manager, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
client: Class handle to plexgdm.plexgdm. We can thus ask for an up-to-
|
client: Class handle to plexgdm.plexgdm. We can thus ask for an up-to-
|
||||||
date serverlist without instantiating anything
|
date serverlist without instantiating anything
|
||||||
|
@ -173,5 +172,4 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
"""
|
"""
|
||||||
self.client = client
|
self.client = client
|
||||||
self.subscription_manager = subscription_manager
|
self.subscription_manager = subscription_manager
|
||||||
self.queue = queue
|
|
||||||
HTTPServer.__init__(self, *args, **kwargs)
|
HTTPServer.__init__(self, *args, **kwargs)
|
||||||
|
|
|
@ -10,6 +10,7 @@ from utils import window, kodi_time_to_millis, Lock_Function
|
||||||
import state
|
import state
|
||||||
import variables as v
|
import variables as v
|
||||||
import json_rpc as js
|
import json_rpc as js
|
||||||
|
import playqueue as PQ
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ class SubscriptionMgr(object):
|
||||||
"""
|
"""
|
||||||
Manages Plex companion subscriptions
|
Manages Plex companion subscriptions
|
||||||
"""
|
"""
|
||||||
def __init__(self, request_mgr, player, mgr):
|
def __init__(self, request_mgr, player):
|
||||||
self.serverlist = []
|
self.serverlist = []
|
||||||
self.subscribers = {}
|
self.subscribers = {}
|
||||||
self.info = {}
|
self.info = {}
|
||||||
|
@ -124,11 +125,8 @@ class SubscriptionMgr(object):
|
||||||
self.lastplayers = {}
|
self.lastplayers = {}
|
||||||
|
|
||||||
self.xbmcplayer = player
|
self.xbmcplayer = player
|
||||||
self.playqueue = mgr.playqueue
|
|
||||||
self.request_mgr = request_mgr
|
self.request_mgr = request_mgr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _server_by_host(self, host):
|
def _server_by_host(self, host):
|
||||||
if len(self.serverlist) == 1:
|
if len(self.serverlist) == 1:
|
||||||
return self.serverlist[0]
|
return self.serverlist[0]
|
||||||
|
@ -180,7 +178,7 @@ class SubscriptionMgr(object):
|
||||||
def _timeline_dict(self, player, ptype):
|
def _timeline_dict(self, player, ptype):
|
||||||
playerid = player['playerid']
|
playerid = player['playerid']
|
||||||
info = state.PLAYER_STATES[playerid]
|
info = state.PLAYER_STATES[playerid]
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = PQ.PLAYQUEUES[playerid]
|
||||||
pos = info['position']
|
pos = info['position']
|
||||||
try:
|
try:
|
||||||
item = playqueue.items[pos]
|
item = playqueue.items[pos]
|
||||||
|
@ -284,7 +282,7 @@ class SubscriptionMgr(object):
|
||||||
|
|
||||||
stream_type: 'video', 'audio', 'subtitle'
|
stream_type: 'video', 'audio', 'subtitle'
|
||||||
"""
|
"""
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = PQ.PLAYQUEUES[playerid]
|
||||||
info = state.PLAYER_STATES[playerid]
|
info = state.PLAYER_STATES[playerid]
|
||||||
return playqueue.items[info['position']].plex_stream_index(
|
return playqueue.items[info['position']].plex_stream_index(
|
||||||
info[STREAM_DETAILS[stream_type]]['index'], stream_type)
|
info[STREAM_DETAILS[stream_type]]['index'], stream_type)
|
||||||
|
@ -306,7 +304,7 @@ class SubscriptionMgr(object):
|
||||||
"""
|
"""
|
||||||
for player in players.values():
|
for player in players.values():
|
||||||
info = state.PLAYER_STATES[player['playerid']]
|
info = state.PLAYER_STATES[player['playerid']]
|
||||||
playqueue = self.playqueue.playqueues[player['playerid']]
|
playqueue = PQ.PLAYQUEUES[player['playerid']]
|
||||||
try:
|
try:
|
||||||
item = playqueue.items[info['position']]
|
item = playqueue.items[info['position']]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -362,7 +360,7 @@ class SubscriptionMgr(object):
|
||||||
|
|
||||||
def _get_pms_params(self, playerid):
|
def _get_pms_params(self, playerid):
|
||||||
info = state.PLAYER_STATES[playerid]
|
info = state.PLAYER_STATES[playerid]
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = PQ.PLAYQUEUES[playerid]
|
||||||
try:
|
try:
|
||||||
item = playqueue.items[info['position']]
|
item = playqueue.items[info['position']]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
@ -386,7 +384,7 @@ class SubscriptionMgr(object):
|
||||||
|
|
||||||
def _send_pms_notification(self, playerid, params):
|
def _send_pms_notification(self, playerid, params):
|
||||||
serv = self._server_by_host(self.server)
|
serv = self._server_by_host(self.server)
|
||||||
playqueue = self.playqueue.playqueues[playerid]
|
playqueue = PQ.PLAYQUEUES[playerid]
|
||||||
xargs = params_pms()
|
xargs = params_pms()
|
||||||
xargs.update(params)
|
xargs.update(params)
|
||||||
if state.PLEX_TRANSIENT_TOKEN:
|
if state.PLEX_TRANSIENT_TOKEN:
|
||||||
|
|
|
@ -75,6 +75,13 @@ PLEX_USER_ID = None
|
||||||
# another user playing something! Token identifies user
|
# another user playing something! Token identifies user
|
||||||
PLEX_TRANSIENT_TOKEN = None
|
PLEX_TRANSIENT_TOKEN = None
|
||||||
|
|
||||||
|
# Plex Companion Queue()
|
||||||
|
COMPANION_QUEUE = None
|
||||||
|
# Command Pipeline Queue()
|
||||||
|
COMMAND_PIPELINE_QUEUE = None
|
||||||
|
# Websocket_client queue to communicate with librarysync
|
||||||
|
WEBSOCKET_QUEUE = None
|
||||||
|
|
||||||
# Kodi player states - here, initial values are set
|
# Kodi player states - here, initial values are set
|
||||||
PLAYER_STATES = {
|
PLAYER_STATES = {
|
||||||
1: {
|
1: {
|
||||||
|
@ -117,10 +124,6 @@ PLAYER_STATES = {
|
||||||
# paths for playback (since we're not receiving a Kodi id)
|
# paths for playback (since we're not receiving a Kodi id)
|
||||||
PLEX_IDS = {}
|
PLEX_IDS = {}
|
||||||
PLAYED_INFO = {}
|
PLAYED_INFO = {}
|
||||||
# Former playbackProps; used by playbackutils.py and set to True if initial
|
|
||||||
# playback setup has been done (and playbackutils will be called again
|
|
||||||
# subsequently)
|
|
||||||
PLAYBACK_SETUP_DONE = False
|
|
||||||
|
|
||||||
# Kodi webserver details
|
# Kodi webserver details
|
||||||
WEBSERVER_PORT = 8080
|
WEBSERVER_PORT = 8080
|
||||||
|
|
|
@ -30,10 +30,8 @@ class UserClient(Thread):
|
||||||
# Borg - multiple instances, shared state
|
# Borg - multiple instances, shared state
|
||||||
__shared_state = {}
|
__shared_state = {}
|
||||||
|
|
||||||
def __init__(self, callback=None):
|
def __init__(self):
|
||||||
self.__dict__ = self.__shared_state
|
self.__dict__ = self.__shared_state
|
||||||
if callback is not None:
|
|
||||||
self.mgr = callback
|
|
||||||
|
|
||||||
self.auth = True
|
self.auth = True
|
||||||
self.retry = 0
|
self.retry = 0
|
||||||
|
|
|
@ -6,7 +6,6 @@ import websocket
|
||||||
from json import loads
|
from json import loads
|
||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Queue
|
|
||||||
from ssl import CERT_NONE
|
from ssl import CERT_NONE
|
||||||
|
|
||||||
from xbmc import sleep
|
from xbmc import sleep
|
||||||
|
@ -165,9 +164,6 @@ class PMS_Websocket(WebSocket):
|
||||||
"""
|
"""
|
||||||
Websocket connection with the PMS for Plex Companion
|
Websocket connection with the PMS for Plex Companion
|
||||||
"""
|
"""
|
||||||
# Communication with librarysync
|
|
||||||
queue = Queue()
|
|
||||||
|
|
||||||
def getUri(self):
|
def getUri(self):
|
||||||
server = window('pms_server')
|
server = window('pms_server')
|
||||||
# Get the appropriate prefix for the websocket
|
# Get the appropriate prefix for the websocket
|
||||||
|
@ -221,7 +217,7 @@ class PMS_Websocket(WebSocket):
|
||||||
% self.__class__.__name__)
|
% self.__class__.__name__)
|
||||||
else:
|
else:
|
||||||
# Put PMS message on queue and let libsync take care of it
|
# Put PMS message on queue and let libsync take care of it
|
||||||
self.queue.put(message)
|
state.WEBSOCKET_QUEUE.put(message)
|
||||||
|
|
||||||
def IOError_response(self):
|
def IOError_response(self):
|
||||||
log.warn("Repeatedly could not connect to PMS, "
|
log.warn("Repeatedly could not connect to PMS, "
|
||||||
|
@ -271,8 +267,7 @@ class Alexa_Websocket(WebSocket):
|
||||||
% self.__class__.__name__)
|
% self.__class__.__name__)
|
||||||
return
|
return
|
||||||
process_command(message.attrib['path'][1:],
|
process_command(message.attrib['path'][1:],
|
||||||
message.attrib,
|
message.attrib)
|
||||||
queue=self.mgr.plexCompanion.queue)
|
|
||||||
|
|
||||||
def IOError_response(self):
|
def IOError_response(self):
|
||||||
pass
|
pass
|
||||||
|
|
30
service.py
30
service.py
|
@ -36,7 +36,6 @@ from librarysync import LibrarySync
|
||||||
import videonodes
|
import videonodes
|
||||||
from websocket_client import PMS_Websocket, Alexa_Websocket
|
from websocket_client import PMS_Websocket, Alexa_Websocket
|
||||||
import downloadutils
|
import downloadutils
|
||||||
from playqueue import Playqueue
|
|
||||||
import clientinfo
|
import clientinfo
|
||||||
|
|
||||||
import PlexAPI
|
import PlexAPI
|
||||||
|
@ -80,14 +79,12 @@ class Service():
|
||||||
ws = None
|
ws = None
|
||||||
library = None
|
library = None
|
||||||
plexCompanion = None
|
plexCompanion = None
|
||||||
playqueue = None
|
|
||||||
|
|
||||||
user_running = False
|
user_running = False
|
||||||
ws_running = False
|
ws_running = False
|
||||||
alexa_running = False
|
alexa_running = False
|
||||||
library_running = False
|
library_running = False
|
||||||
plexCompanion_running = False
|
plexCompanion_running = False
|
||||||
playqueue_running = False
|
|
||||||
kodimonitor_running = False
|
kodimonitor_running = False
|
||||||
playback_starter_running = False
|
playback_starter_running = False
|
||||||
image_cache_thread_running = False
|
image_cache_thread_running = False
|
||||||
|
@ -145,21 +142,20 @@ class Service():
|
||||||
monitor = self.monitor
|
monitor = self.monitor
|
||||||
kodiProfile = v.KODI_PROFILE
|
kodiProfile = v.KODI_PROFILE
|
||||||
|
|
||||||
# Detect playback start early on
|
|
||||||
self.command_pipeline = Monitor_Window(self)
|
|
||||||
self.command_pipeline.start()
|
|
||||||
|
|
||||||
# Server auto-detect
|
# Server auto-detect
|
||||||
initialsetup.InitialSetup().setup()
|
initialsetup.InitialSetup().setup()
|
||||||
|
|
||||||
|
# Detect playback start early on
|
||||||
|
self.command_pipeline = Monitor_Window()
|
||||||
|
self.command_pipeline.start()
|
||||||
|
|
||||||
# Initialize important threads, handing over self for callback purposes
|
# Initialize important threads, handing over self for callback purposes
|
||||||
self.user = UserClient(self)
|
self.user = UserClient()
|
||||||
self.ws = PMS_Websocket(self)
|
self.ws = PMS_Websocket()
|
||||||
self.alexa = Alexa_Websocket(self)
|
self.alexa = Alexa_Websocket()
|
||||||
self.library = LibrarySync(self)
|
self.library = LibrarySync()
|
||||||
self.plexCompanion = PlexCompanion(self)
|
self.plexCompanion = PlexCompanion()
|
||||||
self.playqueue = Playqueue(self)
|
self.playback_starter = Playback_Starter()
|
||||||
self.playback_starter = Playback_Starter(self)
|
|
||||||
if settings('enableTextureCache') == "true":
|
if settings('enableTextureCache') == "true":
|
||||||
self.image_cache_thread = Image_Cache_Thread()
|
self.image_cache_thread = Image_Cache_Thread()
|
||||||
|
|
||||||
|
@ -200,11 +196,7 @@ class Service():
|
||||||
time=2000,
|
time=2000,
|
||||||
sound=False)
|
sound=False)
|
||||||
# Start monitoring kodi events
|
# Start monitoring kodi events
|
||||||
self.kodimonitor_running = KodiMonitor(self)
|
self.kodimonitor_running = KodiMonitor()
|
||||||
# Start playqueue client
|
|
||||||
if not self.playqueue_running:
|
|
||||||
self.playqueue_running = True
|
|
||||||
self.playqueue.start()
|
|
||||||
# Start the Websocket Client
|
# Start the Websocket Client
|
||||||
if not self.ws_running:
|
if not self.ws_running:
|
||||||
self.ws_running = True
|
self.ws_running = True
|
||||||
|
|
Loading…
Reference in a new issue