# -*- 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 data['item'].get('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