Reintroduce Kodi playlist polling
There is no way around it - Kodi does not tell if the user swaps items in the Kodi playlist, unfortunately
This commit is contained in:
parent
199939c8b7
commit
3174521475
4 changed files with 99 additions and 111 deletions
|
@ -69,7 +69,7 @@ class KodiMonitor(Monitor):
|
|||
"""
|
||||
Will be called when Kodi starts scanning the library
|
||||
"""
|
||||
LOG.debug("Kodi library scan %s running." % library)
|
||||
LOG.debug("Kodi library scan %s running.", library)
|
||||
if library == "video":
|
||||
window('plex_kodiScan', value="true")
|
||||
|
||||
|
@ -77,7 +77,7 @@ class KodiMonitor(Monitor):
|
|||
"""
|
||||
Will be called when Kodi finished scanning the library
|
||||
"""
|
||||
LOG.debug("Kodi library scan %s finished." % library)
|
||||
LOG.debug("Kodi library scan %s finished.", library)
|
||||
if library == "video":
|
||||
window('plex_kodiScan', clear=True)
|
||||
|
||||
|
@ -209,11 +209,6 @@ class KodiMonitor(Monitor):
|
|||
}
|
||||
Will NOT be called if playback initiated by Kodi widgets
|
||||
"""
|
||||
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')
|
||||
return
|
||||
kodi_item = js.get_item(data['playlistid'])
|
||||
if (state.RESUMABLE is True and not kodi_item['file'] and
|
||||
data['position'] == 0 and
|
||||
|
@ -233,22 +228,7 @@ class KodiMonitor(Monitor):
|
|||
thread = Thread(target=playback_triage, kwargs=kwargs)
|
||||
thread.start()
|
||||
return
|
||||
# Have we initiated the playqueue already? If not, ignore this
|
||||
if not playqueue.items:
|
||||
LOG.debug('Playqueue not initiated - ignoring')
|
||||
return
|
||||
# Playlist has been updated; need to tell Plex about it
|
||||
try:
|
||||
if playqueue.id is None:
|
||||
PL.init_Plex_playlist(playqueue, kodi_item=data['item'])
|
||||
else:
|
||||
PL.add_item_to_PMS_playlist(playqueue,
|
||||
data['position'],
|
||||
kodi_item=data['item'])
|
||||
except PL.PlaylistError:
|
||||
pass
|
||||
|
||||
@LOCKER.lockthis
|
||||
def _playlist_onremove(self, data):
|
||||
"""
|
||||
Called if an item is removed from a Kodi playlist. Example data dict:
|
||||
|
@ -257,14 +237,8 @@ class KodiMonitor(Monitor):
|
|||
u'position': 0
|
||||
}
|
||||
"""
|
||||
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')
|
||||
return
|
||||
PL.delete_playlist_item_from_PMS(playqueue, data['position'])
|
||||
pass
|
||||
|
||||
@LOCKER.lockthis
|
||||
def _playlist_onclear(self, data):
|
||||
"""
|
||||
Called if a Kodi playlist is cleared. Example data dict:
|
||||
|
@ -272,11 +246,7 @@ class KodiMonitor(Monitor):
|
|||
u'playlistid': 1,
|
||||
}
|
||||
"""
|
||||
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||
if playqueue.is_kodi_onclear() is False:
|
||||
LOG.debug('PKC already cleared the playqueue - ignoring')
|
||||
return
|
||||
playqueue.clear(kodi=False)
|
||||
pass
|
||||
|
||||
def _get_ids(self, json_item):
|
||||
"""
|
||||
|
@ -399,9 +369,6 @@ class KodiMonitor(Monitor):
|
|||
LOG.info('Could not initialize our playlist')
|
||||
# Avoid errors
|
||||
item = PL.Playlist_Item()
|
||||
# Make sure we've added all items of the Kodi playqueue
|
||||
if playqueue.kodi_pl.size() > 1:
|
||||
self._add_remaining_items_to_playlist(playqueue)
|
||||
# Set the Plex container key (e.g. using the Plex playqueue)
|
||||
container_key = None
|
||||
if info['playlistid'] != -1:
|
||||
|
|
|
@ -45,11 +45,8 @@ class PlaylistObjectBaseclase(object):
|
|||
self.shuffled = 0
|
||||
self.repeat = 0
|
||||
self.plex_transient_token = None
|
||||
# Needed to not add an item twice (first through PKC, then the kodi
|
||||
# monitor)
|
||||
self._onadd_queue = []
|
||||
self._onremove_queue = []
|
||||
self._onclear_queue = []
|
||||
# Need a hack for detecting swaps of elements
|
||||
self.old_kodi_pl = []
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
|
@ -69,63 +66,6 @@ class PlaylistObjectBaseclase(object):
|
|||
answ += '\'%s\': %s, ' % (key, str(getattr(self, key)))
|
||||
return answ + '\'items\': %s}}' % self.items
|
||||
|
||||
def kodi_onadd(self):
|
||||
"""
|
||||
Call this before adding an item to the Kodi playqueue
|
||||
"""
|
||||
self._onadd_queue.append(None)
|
||||
|
||||
def is_kodi_onadd(self):
|
||||
"""
|
||||
Returns False if the last kodimonitor on_add was caused by PKC - so that
|
||||
we are not adding a playlist item twice.
|
||||
|
||||
Calling this function will remove the item from our "checklist"
|
||||
"""
|
||||
try:
|
||||
self._onadd_queue.pop()
|
||||
except IndexError:
|
||||
return True
|
||||
return False
|
||||
|
||||
def kodi_onremove(self):
|
||||
"""
|
||||
Call this before removing an item from the Kodi playqueue
|
||||
"""
|
||||
self._onremove_queue.append(None)
|
||||
|
||||
def is_kodi_onremove(self):
|
||||
"""
|
||||
Returns False if the last kodimonitor on_remove was caused by PKC - so
|
||||
that we are not adding a playlist item twice.
|
||||
|
||||
Calling this function will remove the item from our "checklist"
|
||||
"""
|
||||
try:
|
||||
self._onremove_queue.pop()
|
||||
except IndexError:
|
||||
return True
|
||||
return False
|
||||
|
||||
def kodi_onclear(self):
|
||||
"""
|
||||
Call this before clearing the Kodi playqueue IF it was not empty
|
||||
"""
|
||||
self._onclear_queue.append(None)
|
||||
|
||||
def is_kodi_onclear(self):
|
||||
"""
|
||||
Returns False if the last kodimonitor on_remove was caused by PKC - so
|
||||
that we are not clearing the playlist twice.
|
||||
|
||||
Calling this function will remove the item from our "checklist"
|
||||
"""
|
||||
try:
|
||||
self._onclear_queue.pop()
|
||||
except IndexError:
|
||||
return True
|
||||
return False
|
||||
|
||||
def clear(self, kodi=True):
|
||||
"""
|
||||
Resets the playlist object to an empty playlist.
|
||||
|
@ -135,7 +75,6 @@ class PlaylistObjectBaseclase(object):
|
|||
# kodi monitor's on_clear method will only be called if there were some
|
||||
# items to begin with
|
||||
if kodi and self.kodi_pl.size() != 0:
|
||||
self.kodi_onclear()
|
||||
self.kodi_pl.clear() # Clear Kodi playlist object
|
||||
self.items = []
|
||||
self.id = None
|
||||
|
@ -145,6 +84,7 @@ class PlaylistObjectBaseclase(object):
|
|||
self.shuffled = 0
|
||||
self.repeat = 0
|
||||
self.plex_transient_token = None
|
||||
self.old_kodi_pl = []
|
||||
LOG.debug('Playlist cleared: %s', self)
|
||||
|
||||
|
||||
|
@ -503,10 +443,8 @@ def add_item_to_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
params['item'] = {'%sid' % item.kodi_type: int(item.kodi_id)}
|
||||
else:
|
||||
params['item'] = {'file': item.file}
|
||||
playlist.kodi_onadd()
|
||||
reply = js.playlist_insert(params)
|
||||
if reply.get('error') is not None:
|
||||
playlist.is_kodi_onadd()
|
||||
raise PlaylistError('Could not add item to playlist. Kodi reply. %s',
|
||||
reply)
|
||||
return item
|
||||
|
@ -572,10 +510,8 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
params['item'] = {'%sid' % kodi_type: int(kodi_id)}
|
||||
else:
|
||||
params['item'] = {'file': file}
|
||||
playlist.kodi_onadd()
|
||||
reply = js.playlist_insert(params)
|
||||
if reply.get('error') is not None:
|
||||
playlist.is_kodi_onadd()
|
||||
raise PlaylistError('Could not add item to playlist. Kodi reply. %s',
|
||||
reply)
|
||||
if xml_video_element is not None:
|
||||
|
@ -694,7 +630,6 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
|
|||
LOG.debug('Insert listitem at position %s for Kodi only for %s',
|
||||
pos, playlist)
|
||||
# Add the item into Kodi playlist
|
||||
playlist.kodi_onadd()
|
||||
playlist.kodi_pl.add(url=file, listitem=listitem, index=pos)
|
||||
# We need to add this to our internal queue as well
|
||||
if xml_video_element is not None:
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
Monitors the Kodi playqueue and adjusts the Plex playqueue accordingly
|
||||
"""
|
||||
from logging import getLogger
|
||||
from threading import RLock, Thread
|
||||
from threading import Thread
|
||||
|
||||
from xbmc import Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO
|
||||
from xbmc import Player, PlayList, PLAYLIST_MUSIC, PLAYLIST_VIDEO, sleep
|
||||
|
||||
from utils import window
|
||||
from utils import thread_methods
|
||||
import playlist_func as PL
|
||||
from PlexFunctions import ConvertPlexToKodiTime, GetAllPlexChildren
|
||||
from PlexFunctions import GetAllPlexChildren
|
||||
from PlexAPI import API
|
||||
from plexbmchelper.subscribers import LOCK
|
||||
from playback import play_xml
|
||||
import json_rpc as js
|
||||
import variables as v
|
||||
|
@ -17,8 +18,6 @@ import variables as v
|
|||
###############################################################################
|
||||
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())
|
||||
|
@ -125,3 +124,87 @@ def update_playqueue_from_PMS(playqueue,
|
|||
playqueue.repeat = 0 if not repeat else int(repeat)
|
||||
playqueue.plex_transient_token = transient_token
|
||||
play_xml(playqueue, xml, offset)
|
||||
|
||||
|
||||
@thread_methods(add_suspends=['PMS_STATUS'])
|
||||
class PlayqueueMonitor(Thread):
|
||||
"""
|
||||
Unfortunately, Kodi does not tell if items within a Kodi playqueue
|
||||
(playlist) are swapped. This is what this monitor is for. Don't replace
|
||||
this mechanism till Kodi's implementation of playlists has improved
|
||||
"""
|
||||
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)
|
||||
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):
|
||||
if self.thread_stopped():
|
||||
# Chances are that we got an empty Kodi playlist due to
|
||||
# Kodi exit
|
||||
return
|
||||
try:
|
||||
if (old_item.file.startswith('plugin://') and
|
||||
not old_item.file.startswith(PLUGIN)):
|
||||
# Ignore media by other addons
|
||||
continue
|
||||
except 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 PlayqueueMonitor ##===----")
|
||||
while not thread_stopped():
|
||||
while thread_suspended():
|
||||
if thread_stopped():
|
||||
break
|
||||
sleep(1000)
|
||||
with LOCK:
|
||||
for playqueue in PLAYQUEUES:
|
||||
kodi_pl = js.playlist_get_items(playqueue.playlistid)
|
||||
if playqueue.old_kodi_pl != kodi_pl:
|
||||
# compare old and new playqueue
|
||||
self._compare_playqueues(playqueue, kodi_pl)
|
||||
playqueue.old_kodi_pl = list(kodi_pl)
|
||||
sleep(200)
|
||||
LOG.info("----===## PlayqueueMonitor stopped ##===----")
|
||||
|
|
|
@ -43,6 +43,7 @@ import PlexAPI
|
|||
from PlexCompanion import PlexCompanion
|
||||
from command_pipeline import Monitor_Window
|
||||
from playback_starter import Playback_Starter
|
||||
from playqueue import PlayqueueMonitor
|
||||
from artwork import Image_Cache_Thread
|
||||
from json_rpc import get_setting, set_setting
|
||||
import variables as v
|
||||
|
@ -174,6 +175,7 @@ class Service():
|
|||
self.plexCompanion = PlexCompanion()
|
||||
self.specialMonitor = SpecialMonitor()
|
||||
self.playback_starter = Playback_Starter()
|
||||
self.playqueue = PlayqueueMonitor()
|
||||
if settings('enableTextureCache') == "true":
|
||||
self.image_cache_thread = Image_Cache_Thread()
|
||||
|
||||
|
@ -236,6 +238,7 @@ class Service():
|
|||
if not self.playback_starter_running:
|
||||
self.playback_starter_running = True
|
||||
self.playback_starter.start()
|
||||
self.playqueue.start()
|
||||
if (not self.image_cache_thread_running and
|
||||
settings('enableTextureCache') == "true"):
|
||||
self.image_cache_thread_running = True
|
||||
|
|
Loading…
Reference in a new issue