jellyfin-kodi/resources/lib/kodimonitor.py

267 lines
9.8 KiB
Python

# -*- coding: utf-8 -*-
#################################################################################################
import json
import logging
import threading
import xbmc
import xbmcgui
import downloadutils
import embydb_functions as embydb
import playbackutils as pbutils
from utils import window, settings, create_id
#from ga_client import log_error
from database import DatabaseConn
#################################################################################################
log = logging.getLogger("EMBY."+__name__)
KODI = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
#################################################################################################
class KodiMonitor(xbmc.Monitor):
retry = True
special_monitor = None
def __init__(self):
xbmc.Monitor.__init__(self)
self.special_monitor = SpecialMonitor().start()
self.download = downloadutils.DownloadUtils().downloadUrl
log.info("Kodi monitor started")
def onScanStarted(self, library):
log.debug("Kodi library scan %s running", library)
if library == "video":
window('emby_kodiScan', value="true")
def onScanFinished(self, library):
log.info("Kodi library scan %s finished", library)
if library == "video":
window('emby_kodiScan', clear=True)
def onSettingsChanged(self):
# Monitor emby settings
current_log_level = settings('logLevel')
if window('emby_logLevel') != current_log_level:
# The log level changed, set new prop
log.info("New log level: %s", current_log_level)
window('emby_logLevel', value=current_log_level)
current_context = "true" if settings('enableContext') == "true" else ""
if window('emby_context') != current_context:
log.info("New context setting: %s", current_context)
window('emby_context', value=current_context)
current_context = "true" if settings('enableContextTranscode') == "true" else ""
if window('emby_context_transcode') != current_context:
log.info("New context transcode setting: %s", current_context)
window('emby_context_transcode', value=current_context)
#@log_error()
def onNotification(self, sender, method, data):
if method not in ('Playlist.OnAdd', 'Player.OnStop', 'Player.OnClear'):
log.info("Method: %s Data: %s", method, data)
try:
if data:
data = json.loads(data, 'utf-8')
except:
log.info("Error parsing message data: %s", data)
return
if method == 'Player.OnPlay':
self.retry = True
self._on_play_(data)
elif method == 'VideoLibrary.OnUpdate':
self._video_update(data)
elif method == 'System.OnSleep':
# Connection is going to sleep
log.info("Marking the server as offline. System.OnSleep activated.")
window('emby_online', value="sleep")
elif method == 'System.OnWake':
self._system_wake()
elif method == 'GUI.OnScreensaverDeactivated':
self._screensaver_deactivated()
def _on_play_(self, data):
# Set up report progress for emby playback
try:
kodi_id = None
if KODI >= 17 and xbmc.Player().isPlayingVideo():
''' Seems to misbehave when playback is not terminated prior to playing new content.
The kodi id remains that of the previous title. Maybe onPlay happens before
this information is updated. Added a failsafe further below.
'''
item = xbmc.Player().getVideoInfoTag()
kodi_id = item.getDbId()
item_type = item.getMediaType()
if kodi_id is None or int(kodi_id) == -1 or 'item' in data and 'id' in data['item'] and data['item']['id'] != kodi_id:
item = data['item']
kodi_id = item['id']
item_type = item['type']
log.info("kodi_id: %s item_type: %s", kodi_id, item_type)
except (KeyError, TypeError):
log.info("Item is invalid for playstate update")
# Retry once, sometimes xbmc.Player().isPlayingVideo() will return false when played from widget.
if self.retry:
self.retry = False
xbmc.sleep(200)
return self._on_play_(data)
else:
if ((settings('useDirectPaths') == "1" and not item_type == "song") or
(item_type == "song" and settings('enableMusic') == "true")):
# Set up properties for player
item_id = self._get_item_id(kodi_id, item_type)
if item_id:
url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % item_id
result = self.download(url)
log.debug("Item: %s", result)
playurl = None
count = 0
while not playurl and count < 2:
try:
playurl = xbmc.Player().getPlayingFile()
except RuntimeError:
count += 1
xbmc.sleep(200)
else:
window('emby_%s.play.json' % playurl, {
'playmethod': "DirectStream" if item_type == "song" and settings('streamMusic') == "true" else "DirectPlay",
'playsession_id': str(create_id()).replace("-", "")
})
listitem = xbmcgui.ListItem()
pbutils.PlaybackUtils(result).set_properties(playurl, listitem)
def _video_update(self, data):
# Manually marking as watched/unwatched
try:
item = data['item']
kodi_id = item['id']
item_type = item['type']
except (KeyError, TypeError):
log.info("Item is invalid for playstate update")
else:
# Send notification to the server.
item_id = self._get_item_id(kodi_id, item_type)
if item_id:
# Stop from manually marking as watched unwatched, with actual playback.
if window('emby.skip.%s' % item_id) == "true":
# property is set in player.py
window('emby.skip.%s' % item_id, clear=True)
else:
# notify the server
url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % item_id
if data.get('playcount') != 0:
self.download(url, action_type="POST")
log.info("Mark as watched for itemid: %s", item_id)
else:
self.download(url, action_type="DELETE")
log.info("Mark as unwatched for itemid: %s", item_id)
@classmethod
def _system_wake(cls):
# Allow network to wake up
xbmc.sleep(10000)
window('emby_online', value="false")
window('emby_onWake', value="true")
@classmethod
def _screensaver_deactivated(cls):
if settings('dbSyncScreensaver') == "true":
xbmc.sleep(5000)
window('emby_onWake', value="true")
@classmethod
def _get_item_id(cls, kodi_id, item_type):
item_id = None
with DatabaseConn('emby') as cursor:
emby_db = embydb.Embydb_Functions(cursor)
db_item = emby_db.getItem_byKodiId(kodi_id, item_type)
try:
item_id = db_item[0]
except TypeError:
log.info("Could not retrieve item Id")
return item_id
class SpecialMonitor(threading.Thread):
_stop_thread = False
external_count = 0
def run(self):
''' Detect the resume dialog for widgets.
Detect external players.
'''
monitor = xbmc.Monitor()
log.warn("----====# Starting Special Monitor #====----")
while not self._stop_thread:
player = xbmc.Player()
isPlaying = player.isPlaying()
if (not isPlaying and xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and
xbmc.getInfoLabel('Control.GetLabel(1002)').decode('utf-8') == xbmc.getLocalizedString(12021)):
control = int(xbmcgui.Window(10106).getFocusId())
if control == 1002: # Start from beginning
log.info("Resume dialog: Start from beginning selected.")
window('emby.resume', clear=True)
else:
log.info("Resume dialog: Resume selected.")
window('emby.resume', value="true")
elif isPlaying and not window('emby.external_check'):
time = player.getTime()
if time > 1: # Not external player.
window('emby.external_check', value="true")
self.external_count = 0
elif self.external_count == 120:
log.info("External player detected.")
window('emby.external', value="true")
window('emby.external_check', value="true")
self.external_count = 0
elif time == 0:
self.external_count += 1
if monitor.waitForAbort(0.5):
# Abort was requested while waiting. We should exit
break
log.warn("#====---- Special Monitor Stopped ----====#")
def stop_monitor(self):
self._stop_thread = True