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:
croneter 2018-02-04 12:06:39 +01:00
parent 199939c8b7
commit 3174521475
4 changed files with 99 additions and 111 deletions

View file

@ -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:

View file

@ -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:

View file

@ -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 ##===----")

View file

@ -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