def login(self, server=None): ga = GoogleAnalytics() ga.sendEventData("Connect", "UserLogin") # Return user or raise error server = server or self.state['Servers'][0] server_address = connectionmanager.getServerAddress( server, server['LastConnectionMode']) users = "" try: users = self.emby.getUsers(server_address) except Exception as error: log.info("Error getting users from server: " + str(error)) if not users: try: return self.login_manual(server_address) except RuntimeError: raise RuntimeError("No user selected") dialog = UsersConnect("script-emby-connect-users.xml", *XML_PATH) dialog.set_server(server_address) dialog.set_users(users) dialog.doModal() if dialog.is_user_selected(): user = dialog.get_user() username = user['Name'] if user['HasPassword']: log.debug("User has password, present manual login") try: return self.login_manual(server_address, username) except RuntimeError: return self.login(server) else: try: user = self.emby.loginUser(server_address, username) except Exception as error: log.info("Error logging in user: "******"No user selected")
def login(self, server=None): ga = GoogleAnalytics() ga.sendEventData("Connect", "UserLogin") # Return user or raise error server = server or self.state['Servers'][0] server_address = connectionmanager.getServerAddress(server, server['LastConnectionMode']) users = ""; try: users = self.emby.getUsers(server_address) except Exception as error: log.info("Error getting users from server: " + str(error)) if not users: try: return self.login_manual(server_address) except RuntimeError: raise RuntimeError("No user selected") dialog = UsersConnect("script-emby-connect-users.xml", *XML_PATH) dialog.set_server(server_address) dialog.set_users(users) dialog.doModal() if dialog.is_user_selected(): user = dialog.get_user() username = user['Name'] if user['HasPassword']: log.debug("User has password, present manual login") try: return self.login_manual(server_address, username) except RuntimeError: return self.login(server) else: try: user = self.emby.loginUser(server_address, username) except Exception as error: log.info("Error logging in user: "******"No user selected")
def _startup(self): serverId = settings('serverId') if (serverId != None): serverId = hashlib.md5(serverId).hexdigest() ga = GoogleAnalytics() ga.sendEventData("Application", "Startup", serverId) try: ga.sendEventData("Version", "OS", platform.platform()) ga.sendEventData("Version", "Python", platform.python_version()) except Exception: pass # Start up events self.warn_auth = True username = self.userclient_thread.get_username() if settings('connectMsg') == "true" and username: # Get additional users add_users = settings('additionalUsers') if add_users: add_users = ", " + ", ".join(add_users.split(',')) dialog(type_="notification", heading="{emby}", message=("%s %s%s" % (lang(33000), username.decode('utf-8'), add_users.decode('utf-8'))), icon="{emby}", time=2000, sound=False) return True
def startSync(self): ga = GoogleAnalytics() # Run at start up - optional to use the server plugin if settings('SyncInstallRunDone') == "true": # Validate views self.refreshViews() completed = False # Verify if server plugin is installed. if settings('serverSync') == "true": # Try to use fast start up url = "{server}/emby/Plugins?format=json" result = self.doUtils(url) for plugin in result: if plugin['Name'] == "Emby.Kodi Sync Queue": log.debug("Found server plugin.") self.isFastSync = True ga.sendEventData("SyncAction", "FastSync") completed = self.fastSync() break if not completed: # Fast sync failed or server plugin is not found ga.sendEventData("SyncAction", "Sync") completed = ManualSync().sync() else: # Install sync is not completed ga.sendEventData("SyncAction", "FullSync") completed = self.fullSync() return completed
def _startup(self): serverId = settings('serverId') if(serverId != None): serverId = hashlib.md5(serverId).hexdigest() ga = GoogleAnalytics() ga.sendEventData("Application", "Startup", serverId) try: ga.sendEventData("Version", "OS", platform.platform()) ga.sendEventData("Version", "Python", platform.python_version()) except Exception: pass # Start up events self.warn_auth = True username = self.userclient_thread.get_username() if settings('connectMsg') == "true" and username: # Get additional users add_users = settings('additionalUsers') if add_users: add_users = ", "+", ".join(add_users.split(',')) dialog(type_="notification", heading="{emby}", message=("%s %s%s" % (lang(33000), username.decode('utf-8'), add_users.decode('utf-8'))), icon="{emby}", time=2000, sound=False) return True
def run(self): while not self.monitor.abortRequested(): if len(self.ambilight_controller.lights) and not ev.is_set(): startReadOut = False vals = {} if self.player.playingvideo: # only if there's actually video # ping metrics server to keep sessions alive while playing # ping every 5 min timeSinceLastPing = time.time() - self.lastMetricPing if (timeSinceLastPing > 300): self.lastMetricPing = time.time() ga = GoogleAnalytics() # Keep the session alive ga.sendEventData("Playback", "Playing", "Video", None, 1) try: pixels = capture.getImage(200) if len(pixels) > 0: screen = image.Screenshot(pixels) hsv_ratios = screen.spectrum_hsv( screen.pixels, self.settings.ambilight_threshold_value, self.settings.ambilight_threshold_saturation, self.settings.color_variation, self.settings.color_bias, len(self.ambilight_controller.lights)) for i in range( len(self.ambilight_controller.lights)): algorithm.transition_colorspace( self, self.ambilight_controller.lights.values() [i], hsv_ratios[i], ) except ZeroDivisionError: pass # Sleep for 0.1s if self.monitor.waitForAbort( 0.1 if self.player.playingvideo else 1): # Abort was requested while waiting. We should exit break
def run(self): try: self.run_internal() except Warning as e: if "restricted" in e: pass elif "401" in e: pass except Exception as e: ga = GoogleAnalytics() errStrings = ga.formatException() ga.sendEventData("Exception", errStrings[0], errStrings[1]) window('emby_dbScan', clear=True) log.exception(e) xbmcgui.Dialog().ok(heading=lang(29999), line1=("Library sync thread has exited! " "You should restart Kodi now. " "Please report this on the forum."), line2=(errStrings[0] + " (" + errStrings[1] + ")"))
def __init__(self): self.client_info = clientinfo.ClientInfo() # Initial logging xbmclog("======== START {} ========".format( self.client_info.get_addon_name())) xbmclog("Python Version: {}".format(sys.version)) xbmclog("Platform: {}".format(self.client_info.get_platform())) xbmclog("KODI Version: {}".format( xbmc.getInfoLabel('System.BuildVersion'))) xbmclog("{} Version: {}".format(self.client_info.get_addon_name(), self.client_info.get_version())) self.ga = GoogleAnalytics() try: self.ga.sendEventData("Application", "Startup") except Exception as error: xbmclog(error) self.connected = False
def shutdown(self): if self.settings: del self.settings if self.monitor: del self.monitor if self.player: del self.player xbmclog("======== SERVICE SHUTDOWN ========") if not self.ga: self.ga = GoogleAnalytics() if self.startup: uptime = time.time() - self.startup uptime = int(uptime / 60) xbmclog("Shutting down after {} minutes".format(uptime)) # TODO - Change to custom metrics self.ga.sendEventData("Metrics", "Uptime", eventValue=uptime) self.ga.sendEventData("Application", "Shutdown")
def run(self): try: self.run_internal() except Warning as e: if "restricted" in e: pass elif "401" in e: pass except Exception as e: ga = GoogleAnalytics() errStrings = ga.formatException() if not (hasattr(e, 'quiet') and e.quiet): ga.sendEventData("Exception", errStrings[0], errStrings[1]) window('emby_dbScan', clear=True) log.exception(e) xbmcgui.Dialog().ok( heading=lang(29999), line1=( "Library sync thread has exited! " "You should restart Kodi now. " "Please report this on the forum."), line2=(errStrings[0] + " (" + errStrings[1] + ")"))
def startSync(self): ga = GoogleAnalytics() # Run at start up - optional to use the server plugin if settings('SyncInstallRunDone') == "true": # Validate views self.refreshViews() completed = False # Verify if server plugin is installed. if settings('serverSync') == "true": # Try to use fast start up url = "{server}/emby/Plugins?format=json" try: result = self.doUtils(url) except Exception as error: log.info("Error getting plugin list form server: " + str(error)) result = [] for plugin in result: if plugin['Name'] == "Emby.Kodi Sync Queue": log.debug("Found server plugin.") self.isFastSync = True ga.sendEventData("SyncAction", "FastSync") completed = self.fastSync() break if not completed: # Fast sync failed or server plugin is not found ga.sendEventData("SyncAction", "Sync") completed = ManualSync().sync() else: # Install sync is not completed ga.sendEventData("SyncAction", "FullSync") completed = self.fullSync() return completed
from service_entry import Service from utils import settings from ga_client import GoogleAnalytics ################################################################################################# loghandler.config() log = logging.getLogger("EMBY.service") DELAY = int(settings('startupDelay') or 0) ################################################################################################# if __name__ == "__main__": log.warn("Delaying emby startup by: %s sec...", DELAY) service = Service() try: if DELAY and xbmc.Monitor().waitForAbort(DELAY): raise RuntimeError("Abort event while waiting to start Emby for kodi") # Start the service service.service_entry_point() except Exception as error: if not (hasattr(error, 'quiet') and error.quiet): ga = GoogleAnalytics() errStrings = ga.formatException() ga.sendEventData("Exception", errStrings[0], errStrings[1]) log.exception(error) log.info("Forcing shutdown") service.shutdown()
def onPlayBackStarted(self): # Will be called when xbmc starts playing a file self.stopAll() # Get current file try: currentFile = self.xbmcplayer.getPlayingFile() xbmc.sleep(300) except: currentFile = "" count = 0 while not currentFile: xbmc.sleep(100) try: currentFile = self.xbmcplayer.getPlayingFile() except: pass if count == 5: # try 5 times log.info("Cancelling playback report...") break else: count += 1 if currentFile: self.currentFile = currentFile # We may need to wait for info to be set in kodi monitor itemId = window("emby_%s.itemid" % currentFile) tryCount = 0 while not itemId: xbmc.sleep(200) itemId = window("emby_%s.itemid" % currentFile) if tryCount == 20: # try 20 times or about 10 seconds log.info( "Could not find itemId, cancelling playback report...") break else: tryCount += 1 else: log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId)) # Only proceed if an itemId was found. embyitem = "emby_%s" % currentFile runtime = window("%s.runtime" % embyitem) refresh_id = window("%s.refreshid" % embyitem) playMethod = window("%s.playmethod" % embyitem) itemType = window("%s.type" % embyitem) window('emby_skipWatched%s' % itemId, value="true") customseek = window('emby_customPlaylist.seektime') if window('emby_customPlaylist') == "true" and customseek: # Start at, when using custom playlist (play to Kodi from webclient) log.info("Seeking to: %s" % customseek) self.xbmcplayer.seekTime(int(customseek) / 10000000.0) window('emby_customPlaylist.seektime', clear=True) seekTime = self.xbmcplayer.getTime() # Get playback volume volume_query = { "jsonrpc": "2.0", "id": 1, "method": "Application.GetProperties", "params": { "properties": ["volume", "muted"] } } result = xbmc.executeJSONRPC(json.dumps(volume_query)) result = json.loads(result) result = result.get('result') volume = result.get('volume') muted = result.get('muted') # Postdata structure to send to Emby server url = "{server}/emby/Sessions/Playing" postdata = { 'QueueableMediaTypes': "Video", 'CanSeek': True, 'ItemId': itemId, 'MediaSourceId': itemId, 'PlayMethod': playMethod, 'VolumeLevel': volume, 'PositionTicks': int(seekTime * 10000000), 'IsMuted': muted } # Get the current audio track and subtitles if playMethod == "Transcode": # property set in PlayUtils.py postdata['AudioStreamIndex'] = window( "%sAudioStreamIndex" % currentFile) postdata['SubtitleStreamIndex'] = window( "%sSubtitleStreamIndex" % currentFile) else: # Get the current kodi audio and subtitles and convert to Emby equivalent tracks_query = { "jsonrpc": "2.0", "id": 1, "method": "Player.GetProperties", "params": { "playerid": 1, "properties": [ "currentsubtitle", "currentaudiostream", "subtitleenabled" ] } } result = xbmc.executeJSONRPC(json.dumps(tracks_query)) result = json.loads(result) result = result.get('result') try: # Audio tracks indexAudio = result['currentaudiostream']['index'] except (KeyError, TypeError): indexAudio = 0 try: # Subtitles tracks indexSubs = result['currentsubtitle']['index'] except (KeyError, TypeError): indexSubs = 0 try: # If subtitles are enabled subsEnabled = result['subtitleenabled'] except (KeyError, TypeError): subsEnabled = "" # Postdata for the audio postdata['AudioStreamIndex'] = indexAudio + 1 # Postdata for the subtitles if subsEnabled and len( xbmc.Player().getAvailableSubtitleStreams()) > 0: # Number of audiotracks to help get Emby Index audioTracks = len( xbmc.Player().getAvailableAudioStreams()) mapping = window("%s.indexMapping" % embyitem) if mapping: # Set in playbackutils.py log.debug( "Mapping for external subtitles index: %s" % mapping) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): # If the current subtitle is in the mapping postdata[ 'SubtitleStreamIndex'] = externalIndex[str( indexSubs)] else: # Internal subtitle currently selected subindex = indexSubs - len( externalIndex) + audioTracks + 1 postdata['SubtitleStreamIndex'] = subindex else: # Direct paths enabled scenario or no external subtitles set postdata[ 'SubtitleStreamIndex'] = indexSubs + audioTracks + 1 else: postdata['SubtitleStreamIndex'] = "" # Post playback to server log.debug("Sending POST play started: %s." % postdata) self.doUtils(url, postBody=postdata, action_type="POST") # Ensure we do have a runtime try: runtime = int(runtime) except ValueError: runtime = self.xbmcplayer.getTotalTime() log.info("Runtime is missing, Kodi runtime: %s" % runtime) # Save data map for updates and position calls data = { 'runtime': runtime, 'item_id': itemId, 'refresh_id': refresh_id, 'currentfile': currentFile, 'AudioStreamIndex': postdata['AudioStreamIndex'], 'SubtitleStreamIndex': postdata['SubtitleStreamIndex'], 'playmethod': playMethod, 'Type': itemType, 'currentPosition': int(seekTime) } self.played_info[currentFile] = data log.info("ADDING_FILE: %s" % self.played_info) ga = GoogleAnalytics() ga.sendEventData("PlayAction", itemType, playMethod) ga.sendScreenView(itemType)
def stopAll(self): if not self.played_info: return log.info("Played_information: %s" % self.played_info) # Process each items for item in self.played_info: data = self.played_info.get(item) if data: log.debug("Item path: %s" % item) log.debug("Item data: %s" % data) runtime = data['runtime'] currentPosition = data['currentPosition'] itemid = data['item_id'] refresh_id = data['refresh_id'] currentFile = data['currentfile'] media_type = data['Type'] playMethod = data['playmethod'] # Prevent manually mark as watched in Kodi monitor window('emby_skipWatched%s' % itemid, value="true") self.stopPlayback(data) if currentPosition and runtime: try: percentComplete = (currentPosition * 10000000) / int(runtime) except ZeroDivisionError: # Runtime is 0. percentComplete = 0 markPlayedAt = float(settings('markPlayed')) / 100 log.info("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt)) # Send the delete action to the server. offerDelete = False if media_type == "Episode" and settings( 'deleteTV') == "true": offerDelete = True elif media_type == "Movie" and settings( 'deleteMovies') == "true": offerDelete = True if settings('offerDelete') != "true": # Delete could be disabled, even if the subsetting is enabled. offerDelete = False if percentComplete >= markPlayedAt and offerDelete: resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000) if resp: url = "{server}/emby/Items/%s?format=json" % itemid log.info("Deleting request: %s" % itemid) self.doUtils(url, action_type="DELETE") else: log.info("User skipped deletion.") # Stop transcoding if playMethod == "Transcode": log.info("Transcoding for %s terminated." % itemid) deviceId = self.clientInfo.get_device_id() url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId self.doUtils(url, action_type="DELETE") path = xbmc.translatePath( "special://profile/addon_data/plugin.video.emby/temp/" ).decode('utf-8') dirs, files = xbmcvfs.listdir(path) for file in files: xbmcvfs.delete("%s%s" % (path, file)) self.played_info.clear() ga = GoogleAnalytics() ga.sendEventData("PlayAction", "Stopped")
elif window('emby_dbScan') != "true": import librarysync library_sync = librarysync.LibrarySync() if mode == 'manualsync': librarysync.ManualSync().sync() elif mode == 'fastsync': library_sync.startSync() else: library_sync.fullSync(repair=True) else: log.warn("Database scan is already running") if __name__ == "__main__": log.info("plugin.video.emby started") try: Main() except Exception as error: if not (hasattr(error, 'quiet') and error.quiet): ga = GoogleAnalytics() errStrings = ga.formatException() ga.sendEventData("Exception", errStrings[0], errStrings[1]) log.exception(error) raise log.info("plugin.video.emby stopped")
class Player(xbmc.Player): duration = 0 playingvideo = False playlistlen = 0 movie = False hue_service = None ga = None def __init__(self): xbmclog('In KodiPlayer.__init__()') xbmc.Player.__init__(self) self.ga = GoogleAnalytics() def onAVStarted(self): xbmclog('In KodiPlayer.onAVStarted()') if self.isPlayingVideo(): self.playingvideo = True self.duration = self.getTotalTime() self.ga.sendEventData("Playback", "Started", "Video") self.ga.sendScreenView("Video") playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) self.playlistlen = playlist.size() self.playlistpos = playlist.getposition() self.hue_service.state_changed("started", self.duration) def onPlayBackPaused(self): xbmclog('In KodiPlayer.onPlayBackPaused()') if self.isPlayingVideo(): self.ga.sendEventData("Playback", "Paused", "Video") self.hue_service.state_changed("paused", self.duration) self.playingvideo = False def onPlayBackResumed(self): xbmclog('In KodiPlayer.onPlayBackResume()') if self.isPlayingVideo(): self.ga.sendEventData("Playback", "Resumed", "Video") self.hue_service.state_changed("resumed", self.duration) self.playingvideo = True if self.duration == 0: self.duration = self.getTotalTime() def onPlayBackStopped(self): xbmclog('In KodiPlayer.onPlayBackStopped()') self.ga.sendEventData("Playback", "Stopped", "Video") self.hue_service.state_changed("stopped", self.duration) self.playingvideo = False self.playlistlen = 0 def onPlayBackEnded(self): xbmclog('In KodiPlayer.onPlayBackEnded()') self.ga.sendEventData("Playback", "Ended", "Video") # If there are upcoming plays, ignore if self.playlistpos < self.playlistlen - 1: return self.playingvideo = False self.hue_service.state_changed("stopped", self.duration)
def service_entry_point(self): # Important: Threads depending on abortRequest will not trigger # if profile switch happens more than once. self.monitor = kodimonitor.KodiMonitor() self.kodi_player = player.Player() kodi_profile = xbmc.translatePath('special://profile') # Server auto-detect initialsetup.InitialSetup().setup() # Initialize important threads self.userclient_thread = userclient.UserClient() user_client = self.userclient_thread self.websocket_thread = wsc.WebSocketClient() self.library_thread = librarysync.LibrarySync() while not self.monitor.abortRequested(): if window('emby_kodiProfile') != kodi_profile: # Profile change happened, terminate this thread and others log.info( "Kodi profile was: %s and changed to: %s. Terminating old Emby thread.", kodi_profile, window('emby_kodiProfile')) exc = Exception("Kodi profile changed detected") exc.quiet = True raise exc # Before proceeding, need to make sure: # 1. Server is online # 2. User is set # 3. User has access to the server if window('emby_online') == "true": # Emby server is online # Verify if user is set and has access to the server if user_client.get_user( ) is not None and user_client.get_access(): # If an item is playing if self.kodi_player.isPlaying(): # ping metrics server to keep sessions alive while playing # ping every 5 min timeSinceLastPing = time.time() - self.lastMetricPing if (timeSinceLastPing > 300): self.lastMetricPing = time.time() ga = GoogleAnalytics() ga.sendEventData("PlayAction", "PlayPing") self._report_progress() elif not self.startup: self.startup = self._startup() if not self.websocket_running: # Start the Websocket Client self.websocket_running = True self.websocket_thread.start() if not self.library_running: # Start the syncing thread self.library_running = True self.library_thread.start() else: if (user_client.get_user() is None) and self.warn_auth: # Alert user is not authenticated and suppress future warning self.warn_auth = False log.info("Not authenticated yet.") # User access is restricted. # Keep verifying until access is granted # unless server goes offline or Kodi is shut down. self._access_check() else: # Wait until Emby server is online # or Kodi is shut down. self._server_online_check() if self.monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break ##### Emby thread is terminating. ##### self.shutdown()
class KodiMonitor(xbmc.Monitor): hue_service = None ga = None def __init__(self): xbmclog('In KodiMonitor.__init__()') xbmc.Monitor.__init__(self) self.ga = GoogleAnalytics() def onSettingsChanged(self): xbmclog('In onSettingsChanged()') self.ga.sendScreenView("Configurations") self.hue_service.ga.sendEventData("Configurations", "Update") self.hue_service.settings.readxml() xbmclog("Updated settings: \n{}".format(self.hue_service.settings)) self.hue_service.update_controllers() def onNotification(self, sender, method, data): xbmclog('In onNotification(sender={}, method={}, data={})'.format( sender, method, data)) if sender == clientinfo.ClientInfo().get_addon_id(): if 'discover' in method: self.ga.sendScreenView("Configurations/Discover") self.hue_service.ga.sendEventData("Configurations", "Discover") ui.discover_lights(self.hue_service) self.hue_service.update_controllers() if 'start_setup_theater_lights' in method: self.ga.sendScreenView("Configurations/SetupGroup/Theater") self.hue_service.ga.sendEventData("Configurations", "Setup Group", "Theater") ret = ui.multiselect_lights( 'Select Theater Lights', ','.join([ self.hue_service.settings.ambilight_group, self.hue_service.settings.static_group ]), self.hue_service.settings.theater_group) self.hue_service.settings.update(theater_group=ret) self.hue_service.update_controllers() if 'start_setup_theater_subgroup' in method: self.ga.sendScreenView( "Configurations/SetupGroup/TheaterSubgroup") self.hue_service.ga.sendEventData("Configurations", "Setup Group", "Theater Subgroup") ret = ui.multiselect_lights( 'Select Theater Subgroup', ','.join([ self.hue_service.settings.ambilight_group, self.hue_service.settings.static_group ]), self.hue_service.settings.theater_subgroup) self.hue_service.settings.update(theater_subgroup=ret) self.hue_service.update_controllers() if 'start_setup_ambilight_lights' in method: self.ga.sendScreenView("Configurations/SetupGroup/Ambilight") self.hue_service.ga.sendEventData("Configurations", "Setup Group", "Ambilight") ret = ui.multiselect_lights( 'Select Ambilight Lights', ','.join([ self.hue_service.settings.theater_group, self.hue_service.settings.static_group ]), self.hue_service.settings.ambilight_group) self.hue_service.settings.update(ambilight_group=ret) self.hue_service.update_controllers() if 'start_setup_static_lights' in method: self.ga.sendScreenView("Configurations/SetupGroup/Static") self.hue_service.ga.sendEventData("Configurations", "Setup Group", "Static") ret = ui.multiselect_lights( 'Select Static Lights', ','.join([ self.hue_service.settings.theater_group, self.hue_service.settings.ambilight_group ]), self.hue_service.settings.static_group) self.hue_service.settings.update(static_group=ret) self.hue_service.update_controllers() if 'reset_settings' in method: self.ga.sendScreenView("Configurations/Reset") self.hue_service.ga.sendEventData("Configurations", "Reset") os.unlink(os.path.join(__addondir__, "settings.xml"))
def service_entry_point(self): # Important: Threads depending on abortRequest will not trigger # if profile switch happens more than once. self.monitor = kodimonitor.KodiMonitor() self.kodi_player = player.Player() kodi_profile = xbmc.translatePath('special://profile') # Server auto-detect initialsetup.InitialSetup().setup() # Initialize important threads self.userclient_thread = userclient.UserClient() user_client = self.userclient_thread self.websocket_thread = wsc.WebSocketClient() self.library_thread = librarysync.LibrarySync() while not self.monitor.abortRequested(): if window('emby_kodiProfile') != kodi_profile: # Profile change happened, terminate this thread and others log.info("Kodi profile was: %s and changed to: %s. Terminating old Emby thread.", kodi_profile, window('emby_kodiProfile')) exc = Exception("Kodi profile changed detected") exc.quiet = True raise exc # Before proceeding, need to make sure: # 1. Server is online # 2. User is set # 3. User has access to the server if window('emby_online') == "true": # Emby server is online # Verify if user is set and has access to the server if user_client.get_user() is not None and user_client.get_access(): # If an item is playing if self.kodi_player.isPlaying(): # ping metrics server to keep sessions alive while playing # ping every 5 min timeSinceLastPing = time.time() - self.lastMetricPing if(timeSinceLastPing > 300): self.lastMetricPing = time.time() ga = GoogleAnalytics() ga.sendEventData("PlayAction", "PlayPing") self._report_progress() elif not self.startup: self.startup = self._startup() if not self.websocket_running: # Start the Websocket Client self.websocket_running = True self.websocket_thread.start() if not self.library_running: # Start the syncing thread self.library_running = True self.library_thread.start() else: if (user_client.get_user() is None) and self.warn_auth: # Alert user is not authenticated and suppress future warning self.warn_auth = False log.info("Not authenticated yet.") # User access is restricted. # Keep verifying until access is granted # unless server goes offline or Kodi is shut down. self._access_check() else: # Wait until Emby server is online # or Kodi is shut down. self._server_online_check() if self.monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break ##### Emby thread is terminating. ##### self.shutdown()
def stopAll(self): if not self.played_info: return log.info("Played_information: %s" % self.played_info) # Process each items for item in self.played_info: data = self.played_info.get(item) if data: log.debug("Item path: %s" % item) log.debug("Item data: %s" % data) runtime = data['runtime'] currentPosition = data['currentPosition'] itemid = data['item_id'] refresh_id = data['refresh_id'] currentFile = data['currentfile'] media_type = data['Type'] playMethod = data['playmethod'] # Prevent manually mark as watched in Kodi monitor window('emby_skipWatched%s' % itemid, value="true") self.stopPlayback(data) if currentPosition and runtime: try: percentComplete = (currentPosition * 10000000) / int(runtime) except ZeroDivisionError: # Runtime is 0. percentComplete = 0 markPlayedAt = float(settings('markPlayed')) / 100 log.info("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt)) # Send the delete action to the server. offerDelete = False if media_type == "Episode" and settings('deleteTV') == "true": offerDelete = True elif media_type == "Movie" and settings('deleteMovies') == "true": offerDelete = True if settings('offerDelete') != "true": # Delete could be disabled, even if the subsetting is enabled. offerDelete = False if percentComplete >= markPlayedAt and offerDelete: resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000) if resp: url = "{server}/emby/Items/%s?format=json" % itemid log.info("Deleting request: %s" % itemid) self.doUtils(url, action_type="DELETE") else: log.info("User skipped deletion.") # Stop transcoding if playMethod == "Transcode": log.info("Transcoding for %s terminated." % itemid) deviceId = self.clientInfo.get_device_id() url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId self.doUtils(url, action_type="DELETE") path = xbmc.translatePath( "special://profile/addon_data/plugin.video.emby/temp/").decode('utf-8') dirs, files = xbmcvfs.listdir(path) for file in files: xbmcvfs.delete("%s%s" % (path, file)) self.played_info.clear() ga = GoogleAnalytics() ga.sendEventData("PlayAction", "Stopped")
def onPlayBackStarted(self): # Will be called when xbmc starts playing a file self.stopAll() # Get current file try: currentFile = self.xbmcplayer.getPlayingFile() xbmc.sleep(300) except: currentFile = "" count = 0 while not currentFile: xbmc.sleep(100) try: currentFile = self.xbmcplayer.getPlayingFile() except: pass if count == 5: # try 5 times log.info("Cancelling playback report...") break else: count += 1 # if we did not get the current file return if currentFile == "": return # process the playing file self.currentFile = currentFile # We may need to wait for info to be set in kodi monitor itemId = window("emby_%s.itemid" % currentFile) tryCount = 0 while not itemId: xbmc.sleep(200) itemId = window("emby_%s.itemid" % currentFile) if tryCount == 20: # try 20 times or about 10 seconds log.info("Could not find itemId, cancelling playback report...") break else: tryCount += 1 else: log.info("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId)) # Only proceed if an itemId was found. embyitem = "emby_%s" % currentFile runtime = window("%s.runtime" % embyitem) refresh_id = window("%s.refreshid" % embyitem) playMethod = window("%s.playmethod" % embyitem) itemType = window("%s.type" % embyitem) window('emby_skipWatched%s' % itemId, value="true") customseek = window('emby_customPlaylist.seektime') if window('emby_customPlaylist') == "true" and customseek: # Start at, when using custom playlist (play to Kodi from webclient) log.info("Seeking to: %s" % customseek) self.xbmcplayer.seekTime(int(customseek)/10000000.0) window('emby_customPlaylist.seektime', clear=True) try: seekTime = self.xbmcplayer.getTime() except: # at this point we should be playing and if not then bail out return # Get playback volume volume_query = { "jsonrpc": "2.0", "id": 1, "method": "Application.GetProperties", "params": { "properties": ["volume", "muted"] } } result = xbmc.executeJSONRPC(json.dumps(volume_query)) result = json.loads(result) result = result.get('result') volume = result.get('volume') muted = result.get('muted') # Postdata structure to send to Emby server url = "{server}/emby/Sessions/Playing" postdata = { 'QueueableMediaTypes': "Video", 'CanSeek': True, 'ItemId': itemId, 'MediaSourceId': itemId, 'PlayMethod': playMethod, 'VolumeLevel': volume, 'PositionTicks': int(seekTime * 10000000), 'IsMuted': muted } # Get the current audio track and subtitles if playMethod == "Transcode": # property set in PlayUtils.py postdata['AudioStreamIndex'] = window("%sAudioStreamIndex" % currentFile) postdata['SubtitleStreamIndex'] = window("%sSubtitleStreamIndex" % currentFile) else: # Get the current kodi audio and subtitles and convert to Emby equivalent tracks_query = { "jsonrpc": "2.0", "id": 1, "method": "Player.GetProperties", "params": { "playerid": 1, "properties": ["currentsubtitle","currentaudiostream","subtitleenabled"] } } result = xbmc.executeJSONRPC(json.dumps(tracks_query)) result = json.loads(result) result = result.get('result') try: # Audio tracks indexAudio = result['currentaudiostream']['index'] except (KeyError, TypeError): indexAudio = 0 try: # Subtitles tracks indexSubs = result['currentsubtitle']['index'] except (KeyError, TypeError): indexSubs = 0 try: # If subtitles are enabled subsEnabled = result['subtitleenabled'] except (KeyError, TypeError): subsEnabled = "" # Postdata for the audio postdata['AudioStreamIndex'] = indexAudio + 1 # Postdata for the subtitles if subsEnabled and len(xbmc.Player().getAvailableSubtitleStreams()) > 0: # Number of audiotracks to help get Emby Index audioTracks = len(xbmc.Player().getAvailableAudioStreams()) mapping = window("%s.indexMapping" % embyitem) if mapping: # Set in playbackutils.py log.debug("Mapping for external subtitles index: %s" % mapping) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): # If the current subtitle is in the mapping postdata['SubtitleStreamIndex'] = externalIndex[str(indexSubs)] else: # Internal subtitle currently selected subindex = indexSubs - len(externalIndex) + audioTracks + 1 postdata['SubtitleStreamIndex'] = subindex else: # Direct paths enabled scenario or no external subtitles set postdata['SubtitleStreamIndex'] = indexSubs + audioTracks + 1 else: postdata['SubtitleStreamIndex'] = "" # Post playback to server log.debug("Sending POST play started: %s." % postdata) self.doUtils(url, postBody=postdata, action_type="POST") # Ensure we do have a runtime try: runtime = int(runtime) except ValueError: try: runtime = int(self.xbmcplayer.getTotalTime()) log.info("Runtime is missing, Kodi runtime: %s" % runtime) except: runtime = 0 log.info("Runtime is missing, Using Zero") # Save data map for updates and position calls data = { 'runtime': runtime, 'item_id': itemId, 'refresh_id': refresh_id, 'currentfile': currentFile, 'AudioStreamIndex': postdata['AudioStreamIndex'], 'SubtitleStreamIndex': postdata['SubtitleStreamIndex'], 'playmethod': playMethod, 'Type': itemType, 'currentPosition': int(seekTime) } self.played_info[currentFile] = data log.info("ADDING_FILE: %s" % self.played_info) ga = GoogleAnalytics() ga.sendEventData("PlayAction", itemType, playMethod) ga.sendScreenView(itemType)
class Service(object): startup = False ga = None theater_controller = None ambilight_controller = None static_controller = None settings = None monitor = None player = None lastMetricPing = time.time() def __init__(self): self.client_info = clientinfo.ClientInfo() # Initial logging xbmclog("======== START {} ========".format( self.client_info.get_addon_name())) xbmclog("Python Version: {}".format(sys.version)) xbmclog("Platform: {}".format(self.client_info.get_platform())) xbmclog("KODI Version: {}".format( xbmc.getInfoLabel('System.BuildVersion'))) xbmclog("{} Version: {}".format(self.client_info.get_addon_name(), self.client_info.get_version())) self.ga = GoogleAnalytics() try: self.ga.sendEventData("Application", "Startup") except Exception as error: xbmclog(error) self.connected = False def service_entry_point(self): xbmclog('In Service.service_entry_point()') if not self.startup: self.startup = self._startup() if self.player == None: xbmclog('Could not instantiate player') return # run() until abortRequested to update ambilight lights self.run() # Kodi requested abort self.shutdown() def _startup(self): self.settings = Settings(self) xbmclog("Current settings: \n{}".format(self.settings)) # Important: Threads depending on abortRequest will not trigger # if profile switch happens more than once. self.monitor = kodimonitor.KodiMonitor() self.monitor.hue_service = self self.player = player.Player() self.player.hue_service = self self.update_controllers() if self.settings.misc_initialflash: self.ambilight_controller.flash_lights() self.theater_controller.flash_lights() self.static_controller.flash_lights() xbmclog("======== SERVICE STARTUP ========") return time.time() def shutdown(self): if self.settings: del self.settings if self.monitor: del self.monitor if self.player: del self.player xbmclog("======== SERVICE SHUTDOWN ========") if not self.ga: self.ga = GoogleAnalytics() if self.startup: uptime = time.time() - self.startup uptime = int(uptime / 60) xbmclog("Shutting down after {} minutes".format(uptime)) # TODO - Change to custom metrics self.ga.sendEventData("Metrics", "Uptime", eventValue=uptime) self.ga.sendEventData("Application", "Shutdown") def update_controllers(self): if (self.ambilight_controller == None or (self.ambilight_controller != None and set(self.settings.ambilight_group.split(',')) != set( self.ambilight_controller.lights.keys()))): self.ambilight_controller = AmbilightController( bridge.get_lights_by_ids( self.settings.ambilight_group.split(',')), self.settings) if (self.theater_controller == None or (self.theater_controller != None and set(self.settings.theater_group.split(',')) != set( self.theater_controller.lights.keys()))): self.theater_controller = TheaterController( bridge.get_lights_by_ids( self.settings.theater_group.split(',')), self.settings) if (self.static_controller == None or (self.static_controller != None and set(self.settings.static_group.split(',')) != set( self.static_controller.lights.keys()))): self.static_controller = StaticController( bridge.get_lights_by_ids( self.settings.static_group.split(',')), self.settings) xbmclog('In Hue.update_controllers() instantiated following ' 'controllers {} {} {}'.format( self.theater_controller, self.ambilight_controller, self.static_controller, )) def state_changed(self, state, duration): xbmclog('In state_changed(state={}, duration={})'.format( state, duration)) if (xbmc.getCondVisibility('Window.IsActive(screensaver-atv4.xml)') or xbmc.getCondVisibility( 'Window.IsActive(screensaver-video-main.xml)')): return if duration < self.settings.misc_disableshort_threshold and self.settings.misc_disableshort: return if state == "started": # start capture when playback starts capture_width = 32 # 100 capture_height = capture_width / capture.getAspectRatio() if capture_height == 0: capture_height = capture_width # fix for divide by zero. capture.capture(int(capture_width), int(capture_height)) if state == "started" or state == "resumed": ev.set() resume = False if state == "resumed": resume = True self.theater_controller.on_playback_start(resume) self.ambilight_controller.on_playback_start(resume) self.static_controller.on_playback_start(resume) ev.clear() elif state == "paused": ev.set() self.theater_controller.on_playback_pause() self.ambilight_controller.on_playback_pause() self.static_controller.on_playback_pause() elif state == "stopped": ev.set() self.theater_controller.on_playback_stop() self.ambilight_controller.on_playback_stop() self.static_controller.on_playback_stop() def run(self): while not self.monitor.abortRequested(): if len(self.ambilight_controller.lights) and not ev.is_set(): startReadOut = False vals = {} if self.player.playingvideo: # only if there's actually video # ping metrics server to keep sessions alive while playing # ping every 5 min timeSinceLastPing = time.time() - self.lastMetricPing if (timeSinceLastPing > 300): self.lastMetricPing = time.time() ga = GoogleAnalytics() # Keep the session alive ga.sendEventData("Playback", "Playing", "Video", None, 1) try: pixels = capture.getImage(200) if len(pixels) > 0: screen = image.Screenshot(pixels) hsv_ratios = screen.spectrum_hsv( screen.pixels, self.settings.ambilight_threshold_value, self.settings.ambilight_threshold_saturation, self.settings.color_variation, self.settings.color_bias, len(self.ambilight_controller.lights)) for i in range( len(self.ambilight_controller.lights)): algorithm.transition_colorspace( self, self.ambilight_controller.lights.values() [i], hsv_ratios[i], ) except ZeroDivisionError: pass # Sleep for 0.1s if self.monitor.waitForAbort( 0.1 if self.player.playingvideo else 1): # Abort was requested while waiting. We should exit break
import time import lights from lifxlan import * from tools import * from ga_client import GoogleAnalytics # TODO - clean up, remove bridge ip, user references lan = LifxLAN() ga = GoogleAnalytics() lights_cache = None def discover(): show_busy_dialog() lifx_lights = get_lights(refresh=True) if lifx_lights and len(lifx_lights) > 0: xbmclog("discover() - Found {0} Lifx lights".format( str(len(lifx_lights)))) notify("Kodi Lifx", "Found {0} Lifx lights".format(str(len(lifx_lights)))) else: xbmclog("discover() - No Lifx lights found") notify("Kodi Lifx", "No Lifx lights found") hide_busy_dialog() return len(lifx_lights)
def __init__(self): xbmclog('In KodiMonitor.__init__()') xbmc.Monitor.__init__(self) self.ga = GoogleAnalytics()
def __init__(self): xbmclog('In KodiPlayer.__init__()') xbmc.Player.__init__(self) self.ga = GoogleAnalytics()