210 lines
8.2 KiB
Python
210 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
###############################################################################
|
|
import logging
|
|
from threading import Lock, Thread
|
|
|
|
import xbmc
|
|
|
|
from utils import window, ThreadMethods, ThreadMethodsAdditionalSuspend, \
|
|
Lock_Function
|
|
import playlist_func as PL
|
|
from PlexFunctions import ConvertPlexToKodiTime
|
|
|
|
###############################################################################
|
|
log = logging.getLogger("PLEX."+__name__)
|
|
|
|
# Lock used to lock methods
|
|
lock = Lock()
|
|
lockmethod = Lock_Function(lock)
|
|
###############################################################################
|
|
|
|
|
|
@ThreadMethodsAdditionalSuspend('plex_serverStatus')
|
|
@ThreadMethods
|
|
class Playqueue(Thread):
|
|
"""
|
|
Monitors Kodi's playqueues for changes on the Kodi side
|
|
"""
|
|
# Borg - multiple instances, shared state
|
|
__shared_state = {}
|
|
playqueues = None
|
|
|
|
@lockmethod.lockthis
|
|
def __init__(self, callback=None):
|
|
self.__dict__ = self.__shared_state
|
|
Thread.__init__(self)
|
|
if self.playqueues is not None:
|
|
return
|
|
self.mgr = callback
|
|
self.player = xbmc.Player()
|
|
|
|
# Initialize Kodi playqueues
|
|
self.playqueues = []
|
|
for queue in PL.get_kodi_playqueues():
|
|
playqueue = PL.Playqueue_Object()
|
|
playqueue.playlistid = queue['playlistid']
|
|
playqueue.type = queue['type']
|
|
# Initialize each Kodi playlist
|
|
if playqueue.type == 'audio':
|
|
playqueue.kodi_pl = xbmc.PlayList(xbmc.PLAYLIST_MUSIC)
|
|
elif playqueue.type == 'video':
|
|
playqueue.kodi_pl = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
|
else:
|
|
# Currently, only video or audio playqueues available
|
|
playqueue.kodi_pl = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
|
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)
|
|
|
|
def get_playqueue_from_type(self, typus):
|
|
"""
|
|
Returns the playqueue according to the typus ('video', 'audio',
|
|
'picture') passed in
|
|
"""
|
|
for playqueue in self.playqueues:
|
|
if playqueue.type == typus:
|
|
break
|
|
else:
|
|
raise ValueError('Wrong type was passed in: %s' % typus)
|
|
return playqueue
|
|
|
|
def get_playqueue_from_playerid(self, kodi_player_id):
|
|
for playqueue in self.playqueues:
|
|
if playqueue.playlistid == kodi_player_id:
|
|
break
|
|
else:
|
|
raise ValueError('Wrong kodi_player_id passed was passed in: %s'
|
|
% kodi_player_id)
|
|
return playqueue
|
|
|
|
@lockmethod.lockthis
|
|
def update_playqueue_from_PMS(self,
|
|
playqueue,
|
|
playqueue_id=None,
|
|
repeat=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
|
|
"""
|
|
log.info('New playqueue received from the PMS, updating!')
|
|
PL.update_playlist_from_PMS(playqueue, playqueue_id, repeat)
|
|
log.debug('Updated playqueue: %s' % playqueue)
|
|
|
|
window('plex_customplaylist', value="true")
|
|
if playqueue.selectedItemOffset not in (None, "0"):
|
|
window('plex_customplaylist.seektime',
|
|
str(ConvertPlexToKodiTime(playqueue.selectedItemOffset)))
|
|
for startpos, item in enumerate(playqueue.items):
|
|
if item.ID == playqueue.selectedItemID:
|
|
break
|
|
else:
|
|
startpos = None
|
|
# Start playback
|
|
if startpos:
|
|
self.player.play(playqueue.kodi_pl, startpos=startpos)
|
|
else:
|
|
self.player.play(playqueue.kodi_pl)
|
|
playqueue.log_Kodi_playlist()
|
|
|
|
@lockmethod.lockthis
|
|
def kodi_onadd(self, data):
|
|
"""
|
|
Called if an item is added to a Kodi playqueue. Data is Kodi JSON-RPC
|
|
output, e.g.
|
|
{
|
|
u'item': {u'type': u'movie', u'id': 3},
|
|
u'playlistid': 1,
|
|
u'position': 0
|
|
}
|
|
"""
|
|
playqueue = self.playqueues[data['playlistid']]
|
|
if playqueue.PKC_playlist_edits:
|
|
old = (data['item'].get('id') if data['item'].get('id')
|
|
else data['item'].get('file'))
|
|
for i, item in enumerate(playqueue.PKC_playlist_edits):
|
|
if old == item:
|
|
log.debug('kodimonitor told us of a PKC edit - ignore')
|
|
del playqueue.PKC_playlist_edits[i]
|
|
return
|
|
if playqueue.ID is None:
|
|
# Need to initialize the queue for the first time
|
|
PL.init_Plex_playlist(playqueue, kodi_item=data['item'])
|
|
else:
|
|
PL.add_playlist_item(playqueue, data['item'], data['position'])
|
|
log.debug('Added a new item to the playqueue: %s' % playqueue)
|
|
|
|
@lockmethod.lockthis
|
|
def kodi_onremove(self, data):
|
|
"""
|
|
Called if an item is removed from a Kodi playqueue. Data is Kodi JSON-
|
|
RPC output, e.g.
|
|
{u'position': 2, u'playlistid': 1}
|
|
"""
|
|
playqueue = self.playqueues[data['playlistid']]
|
|
PL.delete_playlist_item(playqueue, data['position'])
|
|
log.debug('Deleted item at position %s. New playqueue: %s'
|
|
% (data['position'], playqueue))
|
|
|
|
@lockmethod.lockthis
|
|
def _compare_playqueues(self, playqueue, new):
|
|
"""
|
|
Used to poll the Kodi playqueue and update the Plex playqueue if needed
|
|
"""
|
|
if self.threadStopped():
|
|
# Chances are that we got an empty Kodi playlist due to Kodi exit
|
|
return
|
|
old = playqueue.old_kodi_pl
|
|
index = list(range(0, len(old)))
|
|
log.debug('Comparing new Kodi playqueue %s with our play queue %s'
|
|
% (new, playqueue))
|
|
for i, new_item in enumerate(new):
|
|
for j, old_item in enumerate(old):
|
|
if old_item.get('id') is None:
|
|
identical = old_item['file'] == new_item['file']
|
|
else:
|
|
identical = (old_item['id'] == new_item['id'] and
|
|
old_item['type'] == new_item['type'])
|
|
if j == 0 and identical:
|
|
del old[j], index[j]
|
|
break
|
|
elif identical:
|
|
# item now at pos i has been moved from original pos i+j
|
|
PL.move_playlist_item(playqueue, i + j, i)
|
|
# Delete the item we just found
|
|
del old[j], index[j]
|
|
break
|
|
# New elements and left-over elements will be taken care of by the kodi
|
|
# monitor!
|
|
log.debug('New playqueue: %s' % playqueue)
|
|
|
|
def run(self):
|
|
threadStopped = self.threadStopped
|
|
threadSuspended = self.threadSuspended
|
|
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(PL.get_kodi_playlist_items(playqueue)):
|
|
if i == 0:
|
|
PL.init_Plex_playlist(playqueue, kodi_item=item)
|
|
else:
|
|
PL.add_playlist_item(playqueue, item, i)
|
|
while not threadStopped():
|
|
while threadSuspended():
|
|
if threadStopped():
|
|
break
|
|
xbmc.sleep(1000)
|
|
for playqueue in self.playqueues:
|
|
if not playqueue.items:
|
|
# Skip empty playqueues as items can't be modified
|
|
continue
|
|
kodi_playqueue = PL.get_kodi_playlist_items(playqueue)
|
|
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)
|
|
xbmc.sleep(1000)
|
|
log.info("----===## PlayQueue client stopped ##===----")
|