2018-11-19 00:59:17 +11:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import absolute_import, division, unicode_literals
|
2019-01-31 06:36:52 +11:00
|
|
|
from logging import getLogger
|
2018-11-19 00:59:17 +11:00
|
|
|
import Queue
|
|
|
|
from threading import Lock, RLock
|
|
|
|
|
2019-01-31 06:36:52 +11:00
|
|
|
import xbmc
|
|
|
|
|
2018-11-19 00:59:17 +11:00
|
|
|
from .. import utils
|
|
|
|
|
2019-01-31 06:36:52 +11:00
|
|
|
LOG = getLogger('PLEX.app')
|
|
|
|
|
2018-11-19 00:59:17 +11:00
|
|
|
|
|
|
|
class App(object):
|
|
|
|
"""
|
|
|
|
This class is used to store variables across PKC modules
|
|
|
|
"""
|
2018-11-26 17:19:34 +11:00
|
|
|
def __init__(self, entrypoint=False):
|
2019-01-31 06:36:52 +11:00
|
|
|
self.fetch_pms_item_number = None
|
|
|
|
self.force_reload_skin = None
|
2021-02-06 22:20:52 +11:00
|
|
|
# All thread instances
|
|
|
|
self.threads = []
|
2018-11-26 17:19:34 +11:00
|
|
|
if entrypoint:
|
|
|
|
self.load_entrypoint()
|
|
|
|
else:
|
2020-05-07 15:48:50 +10:00
|
|
|
self.reload()
|
2018-11-26 17:19:34 +11:00
|
|
|
# Quit PKC?
|
|
|
|
self.stop_pkc = False
|
2019-01-31 06:36:52 +11:00
|
|
|
# This will suspend the main thread also
|
2018-11-26 17:19:34 +11:00
|
|
|
self.suspend = False
|
2019-05-01 16:56:11 +10:00
|
|
|
# Update Kodi widgets
|
|
|
|
self.update_widgets = False
|
2018-11-26 17:19:34 +11:00
|
|
|
# Need to lock all methods and functions messing with Plex Companion subscribers
|
|
|
|
self.lock_subscriber = RLock()
|
|
|
|
# Need to lock everything messing with Kodi/PKC playqueues
|
|
|
|
self.lock_playqueues = RLock()
|
|
|
|
# Necessary to temporarily hold back librarysync/websocket listener when doing
|
|
|
|
# a full sync
|
|
|
|
self.lock_playlists = Lock()
|
|
|
|
|
|
|
|
# Plex Companion Queue()
|
|
|
|
self.companion_queue = Queue.Queue(maxsize=100)
|
|
|
|
# Websocket_client queue to communicate with librarysync
|
|
|
|
self.websocket_queue = Queue.Queue()
|
|
|
|
# xbmc.Monitor() instance from kodimonitor.py
|
|
|
|
self.monitor = None
|
|
|
|
# xbmc.Player() instance
|
|
|
|
self.player = None
|
2019-01-31 06:36:52 +11:00
|
|
|
# Instance of FanartThread()
|
|
|
|
self.fanart_thread = None
|
|
|
|
# Instance of ImageCachingThread()
|
|
|
|
self.caching_thread = None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_playing(self):
|
2019-11-30 22:50:36 +11:00
|
|
|
return self.player.isPlaying() == 1
|
2019-01-31 06:36:52 +11:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_playing_video(self):
|
2019-11-30 22:50:36 +11:00
|
|
|
return self.player.isPlayingVideo() == 1
|
2019-01-31 06:36:52 +11:00
|
|
|
|
|
|
|
def register_fanart_thread(self, thread):
|
|
|
|
self.fanart_thread = thread
|
|
|
|
self.threads.append(thread)
|
|
|
|
|
|
|
|
def deregister_fanart_thread(self, thread):
|
2019-12-13 17:38:52 +11:00
|
|
|
self.fanart_thread.unblock_callers()
|
2019-01-31 06:36:52 +11:00
|
|
|
self.fanart_thread = None
|
|
|
|
self.threads.remove(thread)
|
|
|
|
|
|
|
|
def suspend_fanart_thread(self, block=True):
|
|
|
|
try:
|
|
|
|
self.fanart_thread.suspend(block=block)
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def resume_fanart_thread(self):
|
|
|
|
try:
|
|
|
|
self.fanart_thread.resume()
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def register_caching_thread(self, thread):
|
|
|
|
self.caching_thread = thread
|
|
|
|
self.threads.append(thread)
|
|
|
|
|
|
|
|
def deregister_caching_thread(self, thread):
|
2019-12-13 17:38:52 +11:00
|
|
|
self.caching_thread.unblock_callers()
|
2019-01-31 06:36:52 +11:00
|
|
|
self.caching_thread = None
|
|
|
|
self.threads.remove(thread)
|
|
|
|
|
|
|
|
def suspend_caching_thread(self, block=True):
|
|
|
|
try:
|
|
|
|
self.caching_thread.suspend(block=block)
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def resume_caching_thread(self):
|
|
|
|
try:
|
|
|
|
self.caching_thread.resume()
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def register_thread(self, thread):
|
|
|
|
"""
|
|
|
|
Hit with thread [backgroundthread.Killablethread instance] to register
|
|
|
|
any and all threads
|
|
|
|
"""
|
|
|
|
self.threads.append(thread)
|
|
|
|
|
|
|
|
def deregister_thread(self, thread):
|
|
|
|
"""
|
|
|
|
Sync thread has done it's work and is e.g. about to die
|
|
|
|
"""
|
2019-12-13 17:38:52 +11:00
|
|
|
thread.unblock_callers()
|
2019-01-31 06:36:52 +11:00
|
|
|
self.threads.remove(thread)
|
|
|
|
|
|
|
|
def suspend_threads(self, block=True):
|
|
|
|
"""
|
|
|
|
Suspend all threads' activity with or without blocking.
|
|
|
|
Returns True only if PKC shutdown requested
|
|
|
|
"""
|
|
|
|
LOG.debug('Suspending threads: %s', self.threads)
|
|
|
|
for thread in self.threads:
|
|
|
|
thread.suspend()
|
|
|
|
if block:
|
|
|
|
while True:
|
|
|
|
for thread in self.threads:
|
2019-11-29 03:49:48 +11:00
|
|
|
if not thread.is_suspended():
|
2019-01-31 06:36:52 +11:00
|
|
|
LOG.debug('Waiting for thread to suspend: %s', thread)
|
2019-02-08 21:21:21 +11:00
|
|
|
# Send suspend signal again in case self.threads
|
|
|
|
# changed
|
2019-11-29 03:49:48 +11:00
|
|
|
thread.suspend(block=True)
|
2019-01-31 06:36:52 +11:00
|
|
|
else:
|
|
|
|
break
|
2020-06-09 17:37:18 +10:00
|
|
|
return self.monitor.abortRequested()
|
2019-01-31 06:36:52 +11:00
|
|
|
|
2020-05-07 15:27:30 +10:00
|
|
|
def resume_threads(self):
|
|
|
|
"""
|
|
|
|
Resume all thread activity with or without blocking.
|
|
|
|
Returns True only if PKC shutdown requested
|
|
|
|
"""
|
|
|
|
LOG.debug('Resuming threads: %s', self.threads)
|
|
|
|
for thread in self.threads:
|
|
|
|
thread.resume()
|
2020-06-09 17:37:18 +10:00
|
|
|
return self.monitor.abortRequested()
|
2020-05-07 15:27:30 +10:00
|
|
|
|
2019-01-31 06:36:52 +11:00
|
|
|
def stop_threads(self, block=True):
|
|
|
|
"""
|
|
|
|
Stop all threads. Will block until all threads are stopped
|
|
|
|
Will NOT quit if PKC should exit!
|
|
|
|
"""
|
|
|
|
LOG.debug('Killing threads: %s', self.threads)
|
|
|
|
for thread in self.threads:
|
2019-11-29 03:49:48 +11:00
|
|
|
thread.cancel()
|
2019-01-31 06:36:52 +11:00
|
|
|
if block:
|
|
|
|
while self.threads:
|
|
|
|
LOG.debug('Waiting for threads to exit: %s', self.threads)
|
|
|
|
if xbmc.sleep(100):
|
|
|
|
return True
|
2018-11-26 17:19:34 +11:00
|
|
|
|
2020-05-07 15:48:50 +10:00
|
|
|
def reload(self):
|
2018-11-19 00:59:17 +11:00
|
|
|
# Number of items to fetch and display in widgets
|
|
|
|
self.fetch_pms_item_number = int(utils.settings('fetch_pms_item_number'))
|
|
|
|
# Hack to force Kodi widget for "in progress" to show up if it was empty
|
|
|
|
# before
|
|
|
|
self.force_reload_skin = utils.settings('forceReloadSkinOnPlaybackStop') == 'true'
|
2018-11-26 03:03:19 +11:00
|
|
|
|
2018-11-26 17:19:34 +11:00
|
|
|
def load_entrypoint(self):
|
|
|
|
self.fetch_pms_item_number = int(utils.settings('fetch_pms_item_number'))
|