Attempt to fix locking mechanisms

- Wraper to lock entire function was NOT working
This commit is contained in:
Croneter 2018-06-21 20:43:39 +02:00
parent c440dc7779
commit 9b76795ea4
7 changed files with 183 additions and 204 deletions

View file

@ -138,7 +138,8 @@ class KodiMonitor(xbmc.Monitor):
if method == "Player.OnPlay": if method == "Player.OnPlay":
state.SUSPEND_SYNC = True state.SUSPEND_SYNC = True
self.PlayBackStart(data) with state.LOCK_PLAYQUEUES:
self.PlayBackStart(data)
elif method == "Player.OnStop": elif method == "Player.OnStop":
# Should refresh our video nodes, e.g. on deck # Should refresh our video nodes, e.g. on deck
# xbmc.executebuiltin('ReloadSkin()') # xbmc.executebuiltin('ReloadSkin()')
@ -146,23 +147,28 @@ class KodiMonitor(xbmc.Monitor):
self.hack_replay == data['item']): self.hack_replay == data['item']):
# Hack for add-on paths # Hack for add-on paths
self.hack_replay = None self.hack_replay = None
self._hack_addon_paths_replay_video() with state.LOCK_PLAYQUEUES:
self._hack_addon_paths_replay_video()
elif data.get('end'): elif data.get('end'):
if state.PKC_CAUSED_STOP is True: if state.PKC_CAUSED_STOP is True:
state.PKC_CAUSED_STOP = False state.PKC_CAUSED_STOP = False
LOG.debug('PKC caused this playback stop - ignoring') LOG.debug('PKC caused this playback stop - ignoring')
else: else:
_playback_cleanup(ended=True) with state.LOCK_PLAYQUEUES:
_playback_cleanup(ended=True)
else: else:
_playback_cleanup() with state.LOCK_PLAYQUEUES:
_playback_cleanup()
state.PKC_CAUSED_STOP_DONE = True state.PKC_CAUSED_STOP_DONE = True
state.SUSPEND_SYNC = False state.SUSPEND_SYNC = False
elif method == 'Playlist.OnAdd': elif method == 'Playlist.OnAdd':
self._playlist_onadd(data) with state.LOCK_PLAYQUEUES:
self._playlist_onadd(data)
elif method == 'Playlist.OnRemove': elif method == 'Playlist.OnRemove':
self._playlist_onremove(data) self._playlist_onremove(data)
elif method == 'Playlist.OnClear': elif method == 'Playlist.OnClear':
self._playlist_onclear(data) with state.LOCK_PLAYQUEUES:
self._playlist_onclear(data)
elif method == "VideoLibrary.OnUpdate": elif method == "VideoLibrary.OnUpdate":
# Manually marking as watched/unwatched # Manually marking as watched/unwatched
playcount = data.get('playcount') playcount = data.get('playcount')
@ -208,7 +214,6 @@ class KodiMonitor(xbmc.Monitor):
state.STOP_PKC = True state.STOP_PKC = True
@staticmethod @staticmethod
@state.LOCKER_SUBSCRIBER.lockthis
def _hack_addon_paths_replay_video(): def _hack_addon_paths_replay_video():
""" """
Hack we need for RESUMABLE items because Kodi lost the path of the Hack we need for RESUMABLE items because Kodi lost the path of the
@ -239,7 +244,6 @@ class KodiMonitor(xbmc.Monitor):
thread = Thread(target=playback.playback_triage, kwargs=kwargs) thread = Thread(target=playback.playback_triage, kwargs=kwargs)
thread.start() thread.start()
@state.LOCKER_SUBSCRIBER.lockthis
def _playlist_onadd(self, data): def _playlist_onadd(self, data):
""" """
Called if an item is added to a Kodi playlist. Example data dict: Called if an item is added to a Kodi playlist. Example data dict:
@ -272,7 +276,6 @@ class KodiMonitor(xbmc.Monitor):
""" """
pass pass
@state.LOCKER_SUBSCRIBER.lockthis
def _playlist_onclear(self, data): def _playlist_onclear(self, data):
""" """
Called if a Kodi playlist is cleared. Example data dict: Called if a Kodi playlist is cleared. Example data dict:
@ -348,7 +351,6 @@ class KodiMonitor(xbmc.Monitor):
json_item.get('type'), json_item.get('type'),
json_item.get('file')) json_item.get('file'))
@state.LOCKER_SUBSCRIBER.lockthis
def PlayBackStart(self, data): def PlayBackStart(self, data):
""" """
Called whenever playback is started. Example data: Called whenever playback is started. Example data:
@ -486,7 +488,6 @@ class SpecialMonitor(Thread):
LOG.info("#====---- Special Monitor Stopped ----====#") LOG.info("#====---- Special Monitor Stopped ----====#")
@state.LOCKER_SUBSCRIBER.lockthis
def _playback_cleanup(ended=False): def _playback_cleanup(ended=False):
""" """
PKC cleanup after playback ends/is stopped. Pass ended=True if Kodi PKC cleanup after playback ends/is stopped. Pass ended=True if Kodi

View file

@ -30,7 +30,6 @@ NULL_VIDEO = join(v.ADDON_FOLDER, 'addons', v.ADDON_ID, 'empty_video.mp4')
############################################################################### ###############################################################################
@state.LOCKER_SUBSCRIBER.lockthis
def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True): def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
""" """
Hit this function for addon path playback, Plex trailers, etc. Hit this function for addon path playback, Plex trailers, etc.
@ -107,11 +106,12 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
initiate = True initiate = True
else: else:
initiate = False initiate = False
if initiate: with state.LOCK_PLAYQUEUES:
_playback_init(plex_id, plex_type, playqueue, pos) if initiate:
else: _playback_init(plex_id, plex_type, playqueue, pos)
# kick off playback on second pass else:
_conclude_playback(playqueue, pos) # kick off playback on second pass
_conclude_playback(playqueue, pos)
def _playlist_playback(plex_id, plex_type): def _playlist_playback(plex_id, plex_type):

View file

@ -267,41 +267,49 @@ def _kodi_playlist_identical(xml_element):
pass pass
@state.LOCKER_PLAYLISTS.lockthis
def process_websocket(plex_id, updated_at, state): def process_websocket(plex_id, updated_at, state):
""" """
Hit by librarysync to process websocket messages concerning playlists Hit by librarysync to process websocket messages concerning playlists
""" """
create = False create = False
playlist = playlist_object_from_db(plex_id=plex_id) playlist = playlist_object_from_db(plex_id=plex_id)
try: with state.LOCK_PLAYLISTS:
if playlist and state == 9: try:
LOG.debug('Plex deletion of playlist detected: %s', playlist) if playlist and state == 9:
delete_kodi_playlist(playlist) LOG.debug('Plex deletion of playlist detected: %s', playlist)
elif playlist and playlist.plex_updatedat == updated_at: delete_kodi_playlist(playlist)
LOG.debug('Playlist with id %s already synced: %s', elif playlist and playlist.plex_updatedat == updated_at:
plex_id, playlist) LOG.debug('Playlist with id %s already synced: %s',
elif playlist: plex_id, playlist)
LOG.debug('Change of Plex playlist detected: %s', playlist) elif playlist:
delete_kodi_playlist(playlist) LOG.debug('Change of Plex playlist detected: %s', playlist)
create = True delete_kodi_playlist(playlist)
elif not playlist and not state == 9: create = True
LOG.debug('Creation of new Plex playlist detected: %s', plex_id) elif not playlist and not state == 9:
create = True LOG.debug('Creation of new Plex playlist detected: %s',
# To the actual work plex_id)
if create: create = True
create_kodi_playlist(plex_id=plex_id, updated_at=updated_at) # To the actual work
except PL.PlaylistError: if create:
pass create_kodi_playlist(plex_id=plex_id, updated_at=updated_at)
except PL.PlaylistError:
pass
@state.LOCKER_PLAYLISTS.lockthis
def full_sync(): def full_sync():
""" """
Full sync of playlists between Kodi and Plex. Returns True is successful, Full sync of playlists between Kodi and Plex. Returns True is successful,
False otherwise False otherwise
""" """
LOG.info('Starting playlist full sync') LOG.info('Starting playlist full sync')
with state.LOCK_PLAYLISTS:
return _full_sync()
def _full_sync():
"""
Need to lock because we're messing with playlists
"""
# Get all Plex playlists # Get all Plex playlists
xml = PL.get_all_playlists() xml = PL.get_all_playlists()
if xml is None: if xml is None:

View file

@ -34,7 +34,7 @@ def init_playqueues():
LOG.debug('Playqueues have already been initialized') LOG.debug('Playqueues have already been initialized')
return return
# Initialize Kodi playqueues # Initialize Kodi playqueues
with state.LOCK_SUBSCRIBER: with state.LOCK_PLAYQUEUES:
for i in (0, 1, 2): for i in (0, 1, 2):
# Just in case the Kodi response is not sorted correctly # Just in case the Kodi response is not sorted correctly
for queue in js.get_playlists(): for queue in js.get_playlists():
@ -62,14 +62,13 @@ def get_playqueue_from_type(kodi_playlist_type):
Returns the playqueue according to the kodi_playlist_type ('video', Returns the playqueue according to the kodi_playlist_type ('video',
'audio', 'picture') passed in 'audio', 'picture') passed in
""" """
with state.LOCK_SUBSCRIBER: for playqueue in PLAYQUEUES:
for playqueue in PLAYQUEUES: if playqueue.type == kodi_playlist_type:
if playqueue.type == kodi_playlist_type: break
break else:
else: raise ValueError('Wrong playlist type passed in: %s',
raise ValueError('Wrong playlist type passed in: %s', kodi_playlist_type)
kodi_playlist_type) return playqueue
return playqueue
def init_playqueue_from_plex_children(plex_id, transient_token=None): def init_playqueue_from_plex_children(plex_id, transient_token=None):
@ -190,7 +189,7 @@ class PlayqueueMonitor(Thread):
if stopped(): if stopped():
break break
xbmc.sleep(1000) xbmc.sleep(1000)
with state.LOCK_SUBSCRIBER: with state.LOCK_PLAYQUEUES:
for playqueue in PLAYQUEUES: for playqueue in PLAYQUEUES:
kodi_pl = js.playlist_get_items(playqueue.playlistid) kodi_pl = js.playlist_get_items(playqueue.playlistid)
if playqueue.old_kodi_pl != kodi_pl: if playqueue.old_kodi_pl != kodi_pl:

View file

@ -43,7 +43,7 @@ def update_playqueue_from_PMS(playqueue,
# Safe transient token from being deleted # Safe transient token from being deleted
if transient_token is None: if transient_token is None:
transient_token = playqueue.plex_transient_token transient_token = playqueue.plex_transient_token
with state.LOCK_SUBSCRIBER: with state.LOCK_PLAYQUEUES:
xml = PL.get_PMS_playlist(playqueue, playqueue_id) xml = PL.get_PMS_playlist(playqueue, playqueue_id)
playqueue.clear() playqueue.clear()
try: try:
@ -74,7 +74,6 @@ class PlexCompanion(Thread):
self.subscription_manager = None self.subscription_manager = None
Thread.__init__(self) Thread.__init__(self)
@state.LOCKER_SUBSCRIBER.lockthis
def _process_alexa(self, data): def _process_alexa(self, data):
xml = PF.GetPlexMetadata(data['key']) xml = PF.GetPlexMetadata(data['key'])
try: try:
@ -131,7 +130,6 @@ class PlexCompanion(Thread):
executebuiltin('RunPlugin(plugin://%s?%s)' executebuiltin('RunPlugin(plugin://%s?%s)'
% (v.ADDON_ID, urlencode(params))) % (v.ADDON_ID, urlencode(params)))
@state.LOCKER_SUBSCRIBER.lockthis
def _process_playlist(self, data): def _process_playlist(self, data):
# Get the playqueue ID # Get the playqueue ID
_, container_key, query = PF.ParseContainerKey(data['containerKey']) _, container_key, query = PF.ParseContainerKey(data['containerKey'])
@ -156,7 +154,6 @@ class PlexCompanion(Thread):
offset=data.get('offset'), offset=data.get('offset'),
transient_token=data.get('token')) transient_token=data.get('token'))
@state.LOCKER_SUBSCRIBER.lockthis
def _process_streams(self, data): def _process_streams(self, data):
""" """
Plex Companion client adjusted audio or subtitle stream Plex Companion client adjusted audio or subtitle stream
@ -178,7 +175,6 @@ class PlexCompanion(Thread):
else: else:
LOG.error('Unknown setStreams command: %s', data) LOG.error('Unknown setStreams command: %s', data)
@state.LOCKER_SUBSCRIBER.lockthis
def _process_refresh(self, data): def _process_refresh(self, data):
""" """
example data: {'playQueueID': '8475', 'commandID': '11'} example data: {'playQueueID': '8475', 'commandID': '11'}
@ -217,14 +213,17 @@ class PlexCompanion(Thread):
LOG.debug('Processing: %s', task) LOG.debug('Processing: %s', task)
data = task['data'] data = task['data']
if task['action'] == 'alexa': if task['action'] == 'alexa':
self._process_alexa(data) with state.LOCK_PLAYQUEUES:
self._process_alexa(data)
elif (task['action'] == 'playlist' and elif (task['action'] == 'playlist' and
data.get('address') == 'node.plexapp.com'): data.get('address') == 'node.plexapp.com'):
self._process_node(data) self._process_node(data)
elif task['action'] == 'playlist': elif task['action'] == 'playlist':
self._process_playlist(data) with state.LOCK_PLAYQUEUES:
self._process_playlist(data)
elif task['action'] == 'refreshPlayQueue': elif task['action'] == 'refreshPlayQueue':
self._process_refresh(data) with state.LOCK_PLAYQUEUES:
self._process_refresh(data)
elif task['action'] == 'setStreams': elif task['action'] == 'setStreams':
try: try:
self._process_streams(data) self._process_streams(data)

View file

@ -145,7 +145,6 @@ class SubscriptionMgr(object):
position = info['position'] position = info['position']
return position return position
@state.LOCKER_SUBSCRIBER.lockthis
def msg(self, players): def msg(self, players):
""" """
Returns a timeline xml as str Returns a timeline xml as str
@ -185,94 +184,98 @@ class SubscriptionMgr(object):
return answ return answ
def _timeline_dict(self, player, ptype): def _timeline_dict(self, player, ptype):
playerid = player['playerid'] with state.LOCK_PLAYQUEUES:
info = state.PLAYER_STATES[playerid] playerid = player['playerid']
playqueue = PQ.PLAYQUEUES[playerid] info = state.PLAYER_STATES[playerid]
position = self._get_correct_position(info, playqueue) playqueue = PQ.PLAYQUEUES[playerid]
try: position = self._get_correct_position(info, playqueue)
item = playqueue.items[position] try:
except IndexError: item = playqueue.items[position]
# E.g. for direct path playback for single item except IndexError:
return { # E.g. for direct path playback for single item
return {
'controllable': CONTROLLABLE[ptype],
'type': ptype,
'state': 'stopped'
}
if ptype in (v.PLEX_PLAYLIST_TYPE_VIDEO,
v.PLEX_PLAYLIST_TYPE_PHOTO):
self.location = 'fullScreenVideo'
self.stop_sent_to_web = False
pbmc_server = utils.window('pms_server')
if pbmc_server:
(self.protocol, self.server, self.port) = pbmc_server.split(':')
self.server = self.server.replace('/', '')
status = 'paused' if int(info['speed']) == 0 else 'playing'
duration = utils.kodi_time_to_millis(info['totaltime'])
shuffle = '1' if info['shuffled'] else '0'
mute = '1' if info['muted'] is True else '0'
answ = {
'controllable': CONTROLLABLE[ptype], 'controllable': CONTROLLABLE[ptype],
'protocol': self.protocol,
'address': self.server,
'port': self.port,
'machineIdentifier': utils.window('plex_machineIdentifier'),
'state': status,
'type': ptype, 'type': ptype,
'state': 'stopped' 'itemType': ptype,
'time': utils.kodi_time_to_millis(info['time']),
'duration': duration,
'seekRange': '0-%s' % duration,
'shuffle': shuffle,
'repeat': v.PLEX_REPEAT_FROM_KODI_REPEAT[info['repeat']],
'volume': info['volume'],
'mute': mute,
'mediaIndex': 0, # Still to implement from here
'partIndex': 0,
'partCount': 1,
'providerIdentifier': 'com.plexapp.plugins.library',
} }
if ptype in (v.PLEX_PLAYLIST_TYPE_VIDEO, v.PLEX_PLAYLIST_TYPE_PHOTO): # Get the plex id from the PKC playqueue not info, as Kodi jumps to
self.location = 'fullScreenVideo' # next playqueue element way BEFORE kodi monitor onplayback is
self.stop_sent_to_web = False # called
pbmc_server = utils.window('pms_server') if item.plex_id:
if pbmc_server: answ['key'] = '/library/metadata/%s' % item.plex_id
(self.protocol, self.server, self.port) = pbmc_server.split(':') answ['ratingKey'] = item.plex_id
self.server = self.server.replace('/', '') # PlayQueue stuff
status = 'paused' if int(info['speed']) == 0 else 'playing' if info['container_key']:
duration = utils.kodi_time_to_millis(info['totaltime']) answ['containerKey'] = info['container_key']
shuffle = '1' if info['shuffled'] else '0' if (info['container_key'] is not None and
mute = '1' if info['muted'] is True else '0' info['container_key'].startswith('/playQueues')):
answ = { answ['playQueueID'] = playqueue.id
'controllable': CONTROLLABLE[ptype], answ['playQueueVersion'] = playqueue.version
'protocol': self.protocol, answ['playQueueItemID'] = item.id
'address': self.server, if playqueue.items[position].guid:
'port': self.port, answ['guid'] = item.guid
'machineIdentifier': utils.window('plex_machineIdentifier'), # Temp. token set?
'state': status, if state.PLEX_TRANSIENT_TOKEN:
'type': ptype, answ['token'] = state.PLEX_TRANSIENT_TOKEN
'itemType': ptype, elif playqueue.plex_transient_token:
'time': utils.kodi_time_to_millis(info['time']), answ['token'] = playqueue.plex_transient_token
'duration': duration, # Process audio and subtitle streams
'seekRange': '0-%s' % duration, if ptype == v.PLEX_PLAYLIST_TYPE_VIDEO:
'shuffle': shuffle, strm_id = self._plex_stream_index(playerid, 'audio')
'repeat': v.PLEX_REPEAT_FROM_KODI_REPEAT[info['repeat']], if strm_id:
'volume': info['volume'], answ['audioStreamID'] = strm_id
'mute': mute, else:
'mediaIndex': 0, # Still to implement from here LOG.error('We could not select a Plex audiostream')
'partIndex': 0, strm_id = self._plex_stream_index(playerid, 'video')
'partCount': 1, if strm_id:
'providerIdentifier': 'com.plexapp.plugins.library', answ['videoStreamID'] = strm_id
} else:
# Get the plex id from the PKC playqueue not info, as Kodi jumps to next LOG.error('We could not select a Plex videostream')
# playqueue element way BEFORE kodi monitor onplayback is called if info['subtitleenabled']:
if item.plex_id: try:
answ['key'] = '/library/metadata/%s' % item.plex_id strm_id = self._plex_stream_index(playerid, 'subtitle')
answ['ratingKey'] = item.plex_id except KeyError:
# PlayQueue stuff # subtitleenabled can be True while currentsubtitle can
if info['container_key']: # still be {}
answ['containerKey'] = info['container_key'] strm_id = None
if (info['container_key'] is not None and if strm_id is not None:
info['container_key'].startswith('/playQueues')): # If None, then the subtitle is only present on Kodi
answ['playQueueID'] = playqueue.id # side
answ['playQueueVersion'] = playqueue.version answ['subtitleStreamID'] = strm_id
answ['playQueueItemID'] = item.id return answ
if playqueue.items[position].guid:
answ['guid'] = item.guid
# Temp. token set?
if state.PLEX_TRANSIENT_TOKEN:
answ['token'] = state.PLEX_TRANSIENT_TOKEN
elif playqueue.plex_transient_token:
answ['token'] = playqueue.plex_transient_token
# Process audio and subtitle streams
if ptype == v.PLEX_PLAYLIST_TYPE_VIDEO:
strm_id = self._plex_stream_index(playerid, 'audio')
if strm_id:
answ['audioStreamID'] = strm_id
else:
LOG.error('We could not select a Plex audiostream')
strm_id = self._plex_stream_index(playerid, 'video')
if strm_id:
answ['videoStreamID'] = strm_id
else:
LOG.error('We could not select a Plex videostream')
if info['subtitleenabled']:
try:
strm_id = self._plex_stream_index(playerid, 'subtitle')
except KeyError:
# subtitleenabled can be True while currentsubtitle can
# still be {}
strm_id = None
if strm_id is not None:
# If None, then the subtitle is only present on Kodi side
answ['subtitleStreamID'] = strm_id
return answ
def signal_stop(self): def signal_stop(self):
""" """
@ -297,14 +300,14 @@ class SubscriptionMgr(object):
return playqueue.items[position].plex_stream_index( return playqueue.items[position].plex_stream_index(
info[STREAM_DETAILS[stream_type]]['index'], stream_type) info[STREAM_DETAILS[stream_type]]['index'], stream_type)
@state.LOCKER_SUBSCRIBER.lockthis
def update_command_id(self, uuid, command_id): def update_command_id(self, uuid, command_id):
""" """
Updates the Plex Companien client with the machine identifier uuid with Updates the Plex Companien client with the machine identifier uuid with
command_id command_id
""" """
if command_id and self.subscribers.get(uuid): with state.LOCK_SUBSCRIBER:
self.subscribers[uuid].command_id = int(command_id) if command_id and self.subscribers.get(uuid):
self.subscribers[uuid].command_id = int(command_id)
def _playqueue_init_done(self, players): def _playqueue_init_done(self, players):
""" """
@ -327,29 +330,29 @@ class SubscriptionMgr(object):
return False return False
return True return True
@state.LOCKER_SUBSCRIBER.lockthis
def notify(self): def notify(self):
""" """
Causes PKC to tell the PMS and Plex Companion players to receive a Causes PKC to tell the PMS and Plex Companion players to receive a
notification what's being played. notification what's being played.
""" """
self._cleanup() with state.LOCK_SUBSCRIBER:
# Get all the active/playing Kodi players (video, audio, pictures) self._cleanup()
players = js.get_players() # Get all the active/playing Kodi players (video, audio, pictures)
# Update the PKC info with what's playing on the Kodi side players = js.get_players()
for player in players.values(): # Update the PKC info with what's playing on the Kodi side
update_player_info(player['playerid']) for player in players.values():
# Check whether we can use the CURRENT info or whether PKC is still update_player_info(player['playerid'])
# initializing # Check whether we can use the CURRENT info or whether PKC is still
if self._playqueue_init_done(players) is False: # initializing
LOG.debug('PKC playqueue is still initializing - skipping update') if self._playqueue_init_done(players) is False:
return LOG.debug('PKC playqueue is still initializing - skip update')
self._notify_server(players) return
if self.subscribers: self._notify_server(players)
msg = self.msg(players) if self.subscribers:
for subscriber in self.subscribers.values(): msg = self.msg(players)
subscriber.send_update(msg) for subscriber in self.subscribers.values():
self.lastplayers = players subscriber.send_update(msg)
self.lastplayers = players
def _notify_server(self, players): def _notify_server(self, players):
for typus, player in players.iteritems(): for typus, player in players.iteritems():
@ -410,7 +413,6 @@ class SubscriptionMgr(object):
LOG.debug("Sent server notification with parameters: %s to %s", LOG.debug("Sent server notification with parameters: %s to %s",
xargs, url) xargs, url)
@state.LOCKER_SUBSCRIBER.lockthis
def add_subscriber(self, protocol, host, port, uuid, command_id): def add_subscriber(self, protocol, host, port, uuid, command_id):
""" """
Adds a new Plex Companion subscriber to PKC. Adds a new Plex Companion subscriber to PKC.
@ -422,20 +424,21 @@ class SubscriptionMgr(object):
command_id, command_id,
self, self,
self.request_mgr) self.request_mgr)
self.subscribers[subscriber.uuid] = subscriber with state.LOCK_SUBSCRIBER:
self.subscribers[subscriber.uuid] = subscriber
return subscriber return subscriber
@state.LOCKER_SUBSCRIBER.lockthis
def remove_subscriber(self, uuid): def remove_subscriber(self, uuid):
""" """
Removes a connected Plex Companion subscriber with machine identifier Removes a connected Plex Companion subscriber with machine identifier
uuid from PKC notifications. uuid from PKC notifications.
(Calls the cleanup() method of the subscriber) (Calls the cleanup() method of the subscriber)
""" """
for subscriber in self.subscribers.values(): with state.LOCK_SUBSCRIBER:
if subscriber.uuid == uuid or subscriber.host == uuid: for subscriber in self.subscribers.values():
subscriber.cleanup() if subscriber.uuid == uuid or subscriber.host == uuid:
del self.subscribers[subscriber.uuid] subscriber.cleanup()
del self.subscribers[subscriber.uuid]
def _cleanup(self): def _cleanup(self):
for subscriber in self.subscribers.values(): for subscriber in self.subscribers.values():

View file

@ -1,48 +1,17 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# THREAD SAFE # THREAD SAFE
from threading import Lock, RLock from threading import Lock, RLock
from functools import wraps
class LockFunction(object):
"""
Decorator for class methods and functions to lock them with lock.
Initialize this class first
lockfunction = LockFunction(lock), where lock is a threading.Lock() object
To then lock a function or method:
@lockfunction.lockthis
def some_function(args, kwargs)
"""
def __init__(self, lock):
self.lock = lock
def lockthis(self, func):
"""
Use this method to actually lock a function or method
"""
@wraps(func)
def wrapper(*args, **kwargs):
"""
Wrapper construct
"""
with self.lock:
result = func(*args, **kwargs)
return result
return wrapper
# LOCKS # LOCKS
#################### ####################
# Need to lock all methods and functions messing with subscribers # Need to lock all methods and functions messing with Plex Companion subscribers
LOCK_SUBSCRIBER = RLock() LOCK_SUBSCRIBER = RLock()
LOCKER_SUBSCRIBER = LockFunction(LOCK_SUBSCRIBER) # Need to lock everything messing with Kodi/PKC playqueues
LOCK_PLAYQUEUES = RLock()
# Necessary to temporarily hold back librarysync/websocket listener when doing # Necessary to temporarily hold back librarysync/websocket listener when doing
# a full sync # a full sync
LOCK_PLAYLISTS = Lock() LOCK_PLAYLISTS = Lock()
LOCKER_PLAYLISTS = LockFunction(LOCK_PLAYLISTS)
# Quit PKC # Quit PKC
STOP_PKC = False STOP_PKC = False