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