Beispiel #1
0
    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
Beispiel #12
0
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()
Beispiel #13
0
    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)
Beispiel #14
0
    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")
Beispiel #15
0
        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")
Beispiel #16
0
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()
Beispiel #18
0
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()
Beispiel #20
0
    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")
Beispiel #21
0
    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
Beispiel #23
0
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)

Beispiel #24
0
 def __init__(self):
     xbmclog('In KodiMonitor.__init__()')
     xbmc.Monitor.__init__(self)
     self.ga = GoogleAnalytics()
Beispiel #25
0
 def __init__(self):
     xbmclog('In KodiPlayer.__init__()')
     xbmc.Player.__init__(self)
     self.ga = GoogleAnalytics()