mirror of
https://github.com/jellyfin/jellyfin-kodi.git
synced 2024-12-27 11:16:11 +00:00
aac8229f9b
For now, Kodi will need to be restarted to load from the dynamic package every time there is an update to the package.
394 lines
13 KiB
Python
394 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
#################################################################################################
|
|
|
|
import _strptime # Workaround for threads using datetime: _striptime is locked
|
|
import json
|
|
import logging
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
import xbmc
|
|
import xbmcgui
|
|
|
|
import objects
|
|
import connect
|
|
import client
|
|
import library
|
|
import setup
|
|
import monitor
|
|
from libraries import requests
|
|
from views import Views, verify_kodi_defaults
|
|
from helper import _, window, settings, event, dialog, find
|
|
from downloader import get_objects
|
|
from emby import Emby
|
|
|
|
#################################################################################################
|
|
|
|
LOG = logging.getLogger("EMBY."+__name__)
|
|
|
|
#################################################################################################
|
|
|
|
|
|
class Service(xbmc.Monitor):
|
|
|
|
running = True
|
|
library_thread = None
|
|
monitor = None
|
|
play_event = None
|
|
warn = True
|
|
settings = {'last_progress': datetime.today()}
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.settings['profile'] = xbmc.translatePath('special://profile')
|
|
self.settings['mode'] = settings('useDirectPaths')
|
|
self.settings['log_level'] = settings('logLevel') or "1"
|
|
self.settings['enable_context'] = settings('enableContext.bool')
|
|
self.settings['enable_context_transcode'] = settings('enableContextTranscode.bool')
|
|
self.settings['kodi_companion'] = settings('kodiCompanion.bool')
|
|
window('emby_logLevel', value=str(self.settings['log_level']))
|
|
window('emby_kodiProfile', value=self.settings['profile'])
|
|
|
|
if self.settings['enable_context']:
|
|
window('emby_context', value="true")
|
|
if self.settings['enable_context_transcode']:
|
|
window('emby_context_transcode', value="true")
|
|
|
|
LOG.warn("--->>>[ %s ]", client.get_addon_name())
|
|
LOG.warn("Version: %s", client.get_version())
|
|
LOG.warn("KODI Version: %s", xbmc.getInfoLabel('System.BuildVersion'))
|
|
LOG.warn("Platform: %s", client.get_platform())
|
|
LOG.warn("Python Version: %s", sys.version)
|
|
LOG.warn("Using dynamic paths: %s", settings('useDirectPaths') == "0")
|
|
LOG.warn("Log Level: %s", self.settings['log_level'])
|
|
|
|
verify_kodi_defaults()
|
|
Views().get_nodes()
|
|
window('emby.connected.bool', True)
|
|
|
|
xbmc.Monitor.__init__(self)
|
|
self.check_update()
|
|
settings('groupedSets.bool', objects.utils.get_grouped_set())
|
|
|
|
def service(self):
|
|
|
|
''' Keeps the service monitor going.
|
|
Exit on Kodi shutdown or profile switch.
|
|
|
|
if profile switch happens more than once,
|
|
Threads depending on abortRequest will not trigger.
|
|
'''
|
|
self.monitor = monitor.Monitor()
|
|
self.connect = connect.Connect()
|
|
self.start_default()
|
|
|
|
self.settings['mode'] = settings('useDirectPaths')
|
|
|
|
while self.running:
|
|
if window('emby_online.bool'):
|
|
|
|
if self.settings['profile'] != window('emby_kodiProfile'):
|
|
LOG.info("[ profile switch ] %s", self.settings['profile'])
|
|
|
|
break
|
|
|
|
if self.monitor.player.isPlaying():
|
|
difference = datetime.today() - self.settings['last_progress']
|
|
|
|
if difference.seconds > 10:
|
|
|
|
event('ReportProgressRequested', {'Report': difference.seconds > 270})
|
|
self.settings['last_progress'] = datetime.today()
|
|
|
|
if self.waitForAbort(1):
|
|
break
|
|
|
|
self.shutdown()
|
|
|
|
def start_default(self):
|
|
|
|
try:
|
|
self.connect.register()
|
|
setup.Setup()
|
|
except Exception as error:
|
|
LOG.error(error)
|
|
|
|
def stop_default(self):
|
|
|
|
window('emby_online', clear=True)
|
|
Emby().close()
|
|
|
|
if self.library_thread is not None:
|
|
|
|
self.library_thread.stop_client()
|
|
self.library_thread = None
|
|
|
|
|
|
def check_update(self):
|
|
|
|
''' Check for objects build version and compare.
|
|
This pulls a dict that contains all the information for the build needed.
|
|
'''
|
|
LOG.info("--[ check updates/%s ]", objects.version)
|
|
kodi = xbmc.getInfoLabel('System.BuildVersion')
|
|
url = "https://sheets.googleapis.com/v4/spreadsheets/1cKWQCVL0lVONulO2KyGzBilzhGvsyuSjFvrqe8g6nJw/values/A2:B?key=AIzaSyAP-1mcBglk9zIofJlqGpvKXkff3GRMhdI"
|
|
|
|
try:
|
|
versions = {k.lower(): v for k, v in dict(requests.get(url).json()['values']).items()}
|
|
build = find(versions, kodi.lower())
|
|
|
|
if not build:
|
|
raise Exception("build %s incompatible?!" % kodi)
|
|
|
|
label, zipfile = build.split('-', 1)
|
|
|
|
if label == objects.version:
|
|
LOG.info("--[ objects/%s ]", objects.version)
|
|
|
|
return
|
|
|
|
get_objects(zipfile, label + '.zip')
|
|
except Exception as error:
|
|
LOG.info(error)
|
|
|
|
dialog("ok", heading="{emby}", line1=_(33135))
|
|
xbmc.executebuiltin('RestartApp')
|
|
|
|
def onNotification(self, sender, method, data):
|
|
|
|
''' All notifications are sent via NotifyAll built-in or Kodi.
|
|
Central hub.
|
|
'''
|
|
if sender.lower() not in ('plugin.video.emby', 'xbmc'):
|
|
return
|
|
|
|
if sender == 'plugin.video.emby':
|
|
method = method.split('.')[1]
|
|
|
|
if method not in ('ServerUnreachable', 'ServerShuttingDown', 'UserDataChanged', 'ServerConnect',
|
|
'LibraryChanged', 'ServerOnline', 'SyncLibrary', 'RepairLibrary', 'RemoveLibrary',
|
|
'EmbyConnect', 'SyncLibrarySelection', 'RepairLibrarySelection', 'AddServer',
|
|
'Unauthorized', 'UpdateServer', 'UserConfigurationUpdated', 'ServerRestarting'):
|
|
return
|
|
|
|
data = json.loads(data)[0]
|
|
else:
|
|
if method not in ('System.OnQuit', 'System.OnSleep', 'System.OnWake'):
|
|
return
|
|
|
|
data = json.loads(data)
|
|
|
|
LOG.debug("[ %s: %s ] %s", sender, method, json.dumps(data, indent=4))
|
|
|
|
if method == 'ServerOnline':
|
|
if data['ServerId'] is None:
|
|
|
|
window('emby_online.bool', True)
|
|
self.warn = True
|
|
|
|
if self.library_thread is None:
|
|
|
|
self.library_thread = library.Library(self)
|
|
self.library_thread.start()
|
|
|
|
elif method in ('ServerUnreachable', 'ServerShuttingDown'):
|
|
|
|
if self.warn or data.get('ServerId'):
|
|
|
|
self.warn = data.get('ServerId') is not None
|
|
dialog("notification", heading="{emby}", message=_(33146) if data.get('ServerId') is None else _(33149), icon=xbmcgui.NOTIFICATION_ERROR)
|
|
|
|
if data.get('ServerId') is None:
|
|
self.stop_default()
|
|
|
|
if self.waitForAbort(20):
|
|
return
|
|
|
|
self.start_default()
|
|
|
|
elif method == 'Unauthorized':
|
|
dialog("notification", heading="{emby}", message=_(33147) if data['ServerId'] is None else _(33148), icon=xbmcgui.NOTIFICATION_ERROR)
|
|
|
|
elif method == 'ServerRestarting':
|
|
if data.get('ServerId'):
|
|
return
|
|
|
|
if settings('restartMsg.bool'):
|
|
dialog("notification", heading="{emby}", message=_(33006), icon="{emby}")
|
|
|
|
self.stop_default()
|
|
|
|
if self.waitForAbort(10):
|
|
return
|
|
|
|
self.start_default()
|
|
|
|
elif method == 'ServerConnect':
|
|
self.connect.register(data['Id'])
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'EmbyConnect':
|
|
self.connect.setup_login_connect()
|
|
|
|
elif method == 'AddServer':
|
|
|
|
self.connect.setup_manual_server()
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'RemoveServer':
|
|
|
|
self.connect.remove_server(data['Id'])
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'UpdateServer':
|
|
|
|
dialog("ok", heading="{emby}", line1=_(33151))
|
|
self.connect.setup_manual_server()
|
|
|
|
elif method == 'UserDataChanged' and self.library_thread:
|
|
if data.get('ServerId'):
|
|
return
|
|
|
|
self.library_thread.userdata(data['UserDataList'])
|
|
|
|
elif method == 'LibraryChanged' and self.library_thread:
|
|
if data.get('ServerId'):
|
|
return
|
|
|
|
self.library_thread.updated(data['ItemsUpdated'] + data['ItemsAdded'])
|
|
self.library_thread.removed(data['ItemsRemoved'])
|
|
|
|
elif method == 'System.OnQuit':
|
|
window('emby_should_stop.bool', True)
|
|
self.running = False
|
|
|
|
elif method in ('SyncLibrarySelection', 'RepairLibrarySelection'):
|
|
self.library_thread.select_libraries('SyncLibrary' if method == 'SyncLibrarySelection' else 'RepairLibrary')
|
|
|
|
elif method == 'SyncLibrary':
|
|
libraries = data['Id'].split(',')
|
|
|
|
for lib in libraries:
|
|
self.library_thread.add_library(lib)
|
|
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'RepairLibrary':
|
|
libraries = data['Id'].split(',')
|
|
|
|
for lib in libraries:
|
|
self.library_thread.remove_library(lib)
|
|
self.library_thread.add_library(lib)
|
|
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'RemoveLibrary':
|
|
libraries = data['Id'].split(',')
|
|
|
|
for lib in libraries:
|
|
self.library_thread.remove_library(lib)
|
|
|
|
xbmc.executebuiltin("Container.Refresh")
|
|
|
|
elif method == 'System.OnSleep':
|
|
LOG.info("-->[ sleep ]")
|
|
|
|
if self.library_thread is not None:
|
|
|
|
self.library_thread.stop_client()
|
|
self.library_thread = None
|
|
|
|
Emby.close_all()
|
|
|
|
elif method == 'System.OnWake':
|
|
|
|
LOG.info("--<[ sleep ]")
|
|
xbmc.sleep(10000)# Allow network to wake up
|
|
|
|
try:
|
|
self.connect.register()
|
|
except Exception as error:
|
|
LOG.error(error)
|
|
|
|
elif method == 'GUI.OnScreensaverDeactivated':
|
|
|
|
LOG.info("--<[ screensaver ]")
|
|
xbmc.sleep(5000)
|
|
|
|
if self.library_thread is not None:
|
|
self.library_thread.fast_sync()
|
|
|
|
elif method == 'UserConfigurationUpdated':
|
|
|
|
if data.get('ServerId') is None:
|
|
Views().get_views()
|
|
|
|
def onSettingsChanged(self):
|
|
|
|
''' React to setting changes that impact window values.
|
|
'''
|
|
if window('emby_should_stop.bool'):
|
|
return
|
|
|
|
if settings('logLevel') != self.settings['log_level']:
|
|
|
|
log_level = settings('logLevel')
|
|
window('emby_logLevel', str(log_level))
|
|
self.settings['logLevel'] = log_level
|
|
LOG.warn("New log level: %s", log_level)
|
|
|
|
if settings('enableContext.bool') != self.settings['enable_context']:
|
|
|
|
window('emby_context', settings('enableContext'))
|
|
self.settings['enable_context'] = settings('enableContext.bool')
|
|
LOG.warn("New context setting: %s", self.settings['enable_context'])
|
|
|
|
if settings('enableContextTranscode.bool') != self.settings['enable_context_transcode']:
|
|
|
|
window('emby_context_transcode', settings('enableContextTranscode'))
|
|
self.settings['enable_context_transcode'] = settings('enableContextTranscode.bool')
|
|
LOG.warn("New context transcode setting: %s", self.settings['enable_context_transcode'])
|
|
|
|
if settings('useDirectPaths') != self.settings['mode'] and self.library_thread.started:
|
|
|
|
self.settings['mode'] = settings('useDirectPaths')
|
|
LOG.warn("New playback mode setting: %s", self.settings['mode'])
|
|
|
|
if not self.settings.get('mode_warn'):
|
|
|
|
self.settings['mode_warn'] = True
|
|
dialog("yesno", heading="{emby}", line1=_(33118))
|
|
|
|
if settings('kodiCompanion.bool') != self.settings['kodi_companion']:
|
|
self.settings['kodi_companion'] = settings('kodiCompanion.bool')
|
|
|
|
if not self.settings['kodi_companion']:
|
|
dialog("ok", heading="{emby}", line1=_(33138))
|
|
|
|
def shutdown(self):
|
|
|
|
LOG.warn("---<[ EXITING ]")
|
|
|
|
properties = [ # TODO: review
|
|
"emby_state", "emby_serverStatus",
|
|
"emby_syncRunning", "emby_dbCheck",
|
|
"emby_currUser", "emby_dbScan",
|
|
"emby_initialScan", "emby_playbackProps",
|
|
|
|
"emby_play", "emby_online", "emby.connected", "emby_should_stop", "emby.resume",
|
|
"emby.external", "emby.external_check"
|
|
]
|
|
for prop in properties:
|
|
window(prop, clear=True)
|
|
|
|
Emby.close_all()
|
|
|
|
if self.library_thread is not None:
|
|
self.library_thread.stop_client()
|
|
|
|
if self.monitor is not None:
|
|
self.monitor.listener.stop()
|
|
|
|
LOG.warn("---<<<[ %s ]", client.get_addon_name())
|