Exemplo n.º 1
0
def checkGithub():
    plexcs.COMMITS_BEHIND = 0

    # Get the latest version available from github
    logger.info('Retrieving latest version information from GitHub')
    url = 'https://api.github.com/repos/%s/plex-cs/commits/%s' % (
        plexcs.CONFIG.GIT_USER, plexcs.CONFIG.GIT_BRANCH)
    version = request.request_json(url,
                                   timeout=20,
                                   validator=lambda x: type(x) == dict)

    if version is None:
        logger.warn(
            'Could not get the latest version from GitHub. Are you running a local development version?'
        )
        return plexcs.CURRENT_VERSION

    plexcs.LATEST_VERSION = version['sha']
    logger.debug("Latest version is %s", plexcs.LATEST_VERSION)

    # See how many commits behind we are
    if not plexcs.CURRENT_VERSION:
        logger.info(
            'You are running an unknown version of Plex:CS. Run the updater to identify your version'
        )
        return plexcs.LATEST_VERSION

    if plexcs.LATEST_VERSION == plexcs.CURRENT_VERSION:
        logger.info('Plex:CS is up to date')
        return plexcs.LATEST_VERSION

    logger.info(
        'Comparing currently installed version with latest GitHub version')
    url = 'https://api.github.com/repos/%s/plex-cs/compare/%s...%s' % (
        plexcs.CONFIG.GIT_USER, plexcs.LATEST_VERSION, plexcs.CURRENT_VERSION)
    commits = request.request_json(url,
                                   timeout=20,
                                   whitelist_status_code=404,
                                   validator=lambda x: type(x) == dict)

    if commits is None:
        logger.warn('Could not get commits behind from GitHub.')
        return plexcs.LATEST_VERSION

    try:
        plexcs.COMMITS_BEHIND = int(commits['behind_by'])
        logger.debug("In total, %d commits behind", plexcs.COMMITS_BEHIND)
    except KeyError:
        logger.info(
            'Cannot compare versions. Are you running a local development version?'
        )
        plexcs.COMMITS_BEHIND = 0

    if plexcs.COMMITS_BEHIND > 0:
        logger.info('New version is available. You are %s commits behind' %
                    plexcs.COMMITS_BEHIND)
    elif plexcs.COMMITS_BEHIND == 0:
        logger.info('Plex:CS is up to date')

    return plexcs.LATEST_VERSION
Exemplo n.º 2
0
    def action(self, query, args=None, return_last_id=False):
        if query is None:
            return

        with db_lock:
            sql_result = None
            attempts = 0

            while attempts < 5:
                try:
                    with self.connection as c:
                        if args is None:
                            sql_result = c.execute(query)
                        else:
                            sql_result = c.execute(query, args)
                    # Our transaction was successful, leave the loop
                    break

                except sqlite3.OperationalError as e:
                    if "unable to open database file" in e.message or "database is locked" in e.message:
                        logger.warn('Database Error: %s', e)
                        attempts += 1
                        time.sleep(1)
                    else:
                        logger.error('Database error: %s', e)
                        raise

                except sqlite3.DatabaseError as e:
                    logger.error('Fatal Error executing %s :: %s', query, e)
                    raise

            return sql_result
Exemplo n.º 3
0
def refresh_users():
    logger.info("Requesting users list refresh...")
    result = PlexTV().get_full_users_list()
    monitor_db = database.MonitorDatabase()

    if len(result) > 0:
        for item in result:
            control_value_dict = {"user_id": item['user_id']}
            new_value_dict = {"username": item['username'],
                              "thumb": item['thumb'],
                              "email": item['email'],
                              "is_home_user": item['is_home_user'],
                              "is_allow_sync": item['is_allow_sync'],
                              "is_restricted": item['is_restricted']
                              }

            # Check if we've set a custom avatar if so don't overwrite it.
            if item['user_id']:
                avatar_urls = monitor_db.select('SELECT thumb, custom_avatar_url '
                                                'FROM users WHERE user_id = ?',
                                                [item['user_id']])
                if avatar_urls:
                    if not avatar_urls[0]['custom_avatar_url'] or \
                            avatar_urls[0]['custom_avatar_url'] == avatar_urls[0]['thumb']:
                        new_value_dict['custom_avatar_url'] = item['thumb']
                else:
                    new_value_dict['custom_avatar_url'] = item['thumb']

            monitor_db.upsert('users', new_value_dict, control_value_dict)

        logger.info("Users list refreshed.")
    else:
        logger.warn("Unable to refresh users list.")
Exemplo n.º 4
0
def check_server_response():

    with monitor_lock:
        pms_connect = pmsconnect.PmsConnect()
        server_response = pms_connect.get_server_response()

        global ext_ping_count
        
        # Check for remote access
        if server_response:
        
            mapping_state = server_response['mapping_state']
            mapping_error = server_response['mapping_error']

            # Check if the port is mapped
            if not mapping_state == 'mapped':
                ext_ping_count += 1
                logger.warn(u"Plex:CS Monitor :: Plex remote access port not mapped, ping attempt %s." \
                            % str(ext_ping_count))
            # Check if the port is open
            elif mapping_error == 'unreachable':
                ext_ping_count += 1
                logger.warn(u"Plex:CS Monitor :: Plex remote access port mapped, but mapping failed, ping attempt %s." \
                            % str(ext_ping_count))
            # Reset external ping counter
            else:
                ext_ping_count = 0

        if ext_ping_count == 3:
            # Fire off notifications
            threading.Thread(target=notification_handler.notify_timeline,
                                kwargs=dict(notify_action='extdown')).start()
Exemplo n.º 5
0
    def discover(self):
        """ Query plex for all servers online. Returns the ones you own in a selectize format """
        result = self.get_plextv_resources(include_https=True, output_format='raw')
        servers = xmltodict.parse(result, process_namespaces=True, attr_prefix='')
        clean_servers = []

        try:
            if servers:
                # Fix if its only one "device"
                if int(servers['MediaContainer']['size']) == 1:
                    servers['MediaContainer']['Device'] = [servers['MediaContainer']['Device']]

                for server in servers['MediaContainer']['Device']:
                    # Only grab servers online and own
                    if server.get('presence', None) == '1' and server.get('owned', None) == '1' and server.get('provides', None) == 'server':
                        # If someone only has one connection..
                        if isinstance(server['Connection'], dict):
                            server['Connection'] = [server['Connection']]

                        for s in server['Connection']:
                            # to avoid circular ref
                            d = {}
                            d.update(s)
                            d.update(server)
                            d['label'] = d['name']
                            d['value'] = d['address']
                            del d['Connection']
                            clean_servers.append(d)

        except Exception as e:
            logger.warn('Failed to get servers from plex %s' % e)
            return clean_servers

        return json.dumps(clean_servers, indent=4)
Exemplo n.º 6
0
def shutdown(restart=False, update=False):
    cherrypy.engine.exit()
    SCHED.shutdown(wait=False)

    CONFIG.write()

    if not restart and not update:
        logger.info('Plex:CS is shutting down...')

    if update:
        logger.info('Plex:CS is updating...')
        try:
            versioncheck.update()
        except Exception as e:
            logger.warn('Plex:CS failed to update: %s. Restarting.', e)

    if CREATEPID:
        logger.info('Removing pidfile %s', PIDFILE)
        os.remove(PIDFILE)

    if restart:
        logger.info('Plex:CS is restarting...')
        popen_list = [sys.executable, FULL_PATH]
        popen_list += ARGS
        if '--nolaunch' not in popen_list:
            popen_list += ['--nolaunch']
        logger.info('Restarting Plex:CS with %s', popen_list)
        subprocess.Popen(popen_list, cwd=os.getcwd())

    os._exit(0)
Exemplo n.º 7
0
    def action(self, query, args=None, return_last_id=False):
        if query is None:
            return

        with db_lock:
            sql_result = None
            attempts = 0

            while attempts < 5:
                try:
                    with self.connection as c:
                        if args is None:
                            sql_result = c.execute(query)
                        else:
                            sql_result = c.execute(query, args)
                    # Our transaction was successful, leave the loop
                    break

                except sqlite3.OperationalError as e:
                    if "unable to open database file" in e.message or "database is locked" in e.message:
                        logger.warn('Database Error: %s', e)
                        attempts += 1
                        time.sleep(1)
                    else:
                        logger.error('Database error: %s', e)
                        raise

                except sqlite3.DatabaseError as e:
                    logger.error('Fatal Error executing %s :: %s', query, e)
                    raise

            return sql_result
Exemplo n.º 8
0
def run():
    from websocket import create_connection

    uri = 'ws://%s:%s/:/websockets/notifications' % (
        plexcs.CONFIG.PMS_IP,
        plexcs.CONFIG.PMS_PORT
    )

    # Set authentication token (if one is available)
    if plexcs.CONFIG.PMS_TOKEN:
        uri += '?X-Plex-Token=' + plexcs.CONFIG.PMS_TOKEN

    ws_connected = False
    reconnects = 0

    # Try an open the websocket connection - if it fails after 15 retries fallback to polling
    while not ws_connected and reconnects <= 15:
        try:
            logger.info(u'Plex:CS WebSocket :: Opening websocket, connection attempt %s.' % str(reconnects + 1))
            ws = create_connection(uri)
            reconnects = 0
            ws_connected = True
            logger.info(u'Plex:CS WebSocket :: Ready')
        except IOError as e:
            logger.error(u'Plex:CS WebSocket :: %s.' % e)
            reconnects += 1
            time.sleep(5)

    while ws_connected:
        try:
            process(*receive(ws))

            # successfully received data, reset reconnects counter
            reconnects = 0
        except websocket.WebSocketConnectionClosedException:
            if reconnects <= 15:
                reconnects += 1

                # Sleep 5 between connection attempts
                if reconnects > 1:
                    time.sleep(5)

                logger.warn(u'Plex:CS WebSocket :: Connection has closed, reconnecting...')
                try:
                    ws = create_connection(uri)
                except IOError as e:
                    logger.info(u'Plex:CS WebSocket :: %s.' % e)

            else:
                ws_connected = False
                break

    if not ws_connected:
        logger.error(u'Plex:CS WebSocket :: Connection unavailable, falling back to polling.')
        plexcs.POLLING_FAILOVER = True
        plexcs.initialize_scheduler()

    logger.debug(u'Plex:CS WebSocket :: Leaving thread.')
Exemplo n.º 9
0
    def _getPlayby(self,
                   time_range='30',
                   y_axis='plays',
                   playtype='total_plays_per_month',
                   **kwargs):

        graph = graphs.Graphs()
        if playtype == 'total_plays_per_month':
            result = graph.get_total_plays_per_month(y_axis=y_axis)

        elif playtype == 'total_plays_per_day':
            result = graph.get_total_plays_per_day(time_range=time_range,
                                                   y_axis=y_axis)

        elif playtype == 'total_plays_per_hourofday':
            result = graph.get_total_plays_per_hourofday(time_range=time_range,
                                                         y_axis=y_axis)

        elif playtype == 'total_plays_per_dayofweek':
            result = graph.get_total_plays_per_dayofweek(time_range=time_range,
                                                         y_axis=y_axis)

        elif playtype == 'stream_type_by_top_10_users':
            result = graph.get_stream_type_by_top_10_users(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'stream_type_by_top_10_platforms':
            result = graph.get_stream_type_by_top_10_platforms(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_stream_resolution':
            result = graph.get_total_plays_by_stream_resolution(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_source_resolution':
            result = graph.get_total_plays_by_source_resolution(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_per_stream_type':
            result = graph.get_total_plays_per_stream_type(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_top_10_users':
            result = graph.get_total_plays_by_top_10_users(
                time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_top_10_platforms':
            result = graph.get_total_plays_by_top_10_platforms(
                time_range=time_range, y_axis=y_axis)

        if result:
            self.data = result
            return result
        else:
            logger.warn('Unable to retrieve %s from db' % playtype)
Exemplo n.º 10
0
    def _getMetadata(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_metadata(rating_key, 'dict')

        if result:
            self.data = result
            return result
        else:
            self.msg = 'Unable to retrive metadata %s' % rating_key
            logger.warn('Unable to retrieve data.')
Exemplo n.º 11
0
    def _getMetadata(self, rating_key='', **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        result = pms_connect.get_metadata(rating_key, 'dict')

        if result:
            self.data = result
            return result
        else:
            self.msg = 'Unable to retrive metadata %s' % rating_key
            logger.warn('Unable to retrieve data.')
Exemplo n.º 12
0
    def get_token(self):
        plextv_response = self.get_plex_auth(output_format='xml')

        if plextv_response:
            xml_head = plextv_response.getElementsByTagName('user')
            if not xml_head:
                logger.warn("Error parsing XML for Plex.tv token")
                return []

            auth_token = xml_head[0].getAttribute('authenticationToken')

            return auth_token
        else:
            return []
Exemplo n.º 13
0
def daemonize():
    if threading.activeCount() != 1:
        logger.warn(
            'There are %r active threads. Daemonizing may cause'
            ' strange behavior.',
            threading.enumerate())

    sys.stdout.flush()
    sys.stderr.flush()

    # Do first fork
    try:
        pid = os.fork()  # @UndefinedVariable - only available in UNIX
        if pid != 0:
            sys.exit(0)
    except OSError as e:
        raise RuntimeError("1st fork failed: %s [%d]", e.strerror, e.errno)

    os.setsid()

    # Make sure I can read my own files and shut out others
    prev = os.umask(0)  # @UndefinedVariable - only available in UNIX
    os.umask(prev and int('077', 8))

    # Make the child a session-leader by detaching from the terminal
    try:
        pid = os.fork()  # @UndefinedVariable - only available in UNIX
        if pid != 0:
            sys.exit(0)
    except OSError as e:
        raise RuntimeError("2nd fork failed: %s [%d]", e.strerror, e.errno)

    dev_null = file('/dev/null', 'r')
    os.dup2(dev_null.fileno(), sys.stdin.fileno())

    si = open('/dev/null', "r")
    so = open('/dev/null', "a+")
    se = open('/dev/null', "a+")

    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())

    pid = os.getpid()
    logger.info('Daemonized to PID: %d', pid)

    if CREATEPID:
        logger.info("Writing PID %d to %s", pid, PIDFILE)
        with file(PIDFILE, 'w') as fp:
            fp.write("%s\n" % pid)
Exemplo n.º 14
0
def checkGithub():
    plexcs.COMMITS_BEHIND = 0

    # Get the latest version available from github
    logger.info("Retrieving latest version information from GitHub")
    url = "https://api.github.com/repos/%s/plex-cs/commits/%s" % (plexcs.CONFIG.GIT_USER, plexcs.CONFIG.GIT_BRANCH)
    version = request.request_json(url, timeout=20, validator=lambda x: type(x) == dict)

    if version is None:
        logger.warn("Could not get the latest version from GitHub. Are you running a local development version?")
        return plexcs.CURRENT_VERSION

    plexcs.LATEST_VERSION = version["sha"]
    logger.debug("Latest version is %s", plexcs.LATEST_VERSION)

    # See how many commits behind we are
    if not plexcs.CURRENT_VERSION:
        logger.info("You are running an unknown version of Plex:CS. Run the updater to identify your version")
        return plexcs.LATEST_VERSION

    if plexcs.LATEST_VERSION == plexcs.CURRENT_VERSION:
        logger.info("Plex:CS is up to date")
        return plexcs.LATEST_VERSION

    logger.info("Comparing currently installed version with latest GitHub version")
    url = "https://api.github.com/repos/%s/plex-cs/compare/%s...%s" % (
        plexcs.CONFIG.GIT_USER,
        plexcs.LATEST_VERSION,
        plexcs.CURRENT_VERSION,
    )
    commits = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == dict)

    if commits is None:
        logger.warn("Could not get commits behind from GitHub.")
        return plexcs.LATEST_VERSION

    try:
        plexcs.COMMITS_BEHIND = int(commits["behind_by"])
        logger.debug("In total, %d commits behind", plexcs.COMMITS_BEHIND)
    except KeyError:
        logger.info("Cannot compare versions. Are you running a local development version?")
        plexcs.COMMITS_BEHIND = 0

    if plexcs.COMMITS_BEHIND > 0:
        logger.info("New version is available. You are %s commits behind" % plexcs.COMMITS_BEHIND)
    elif plexcs.COMMITS_BEHIND == 0:
        logger.info("Plex:CS is up to date")

    return plexcs.LATEST_VERSION
Exemplo n.º 15
0
    def _getSync(self, machine_id=None, user_id=None, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        server_id = pms_connect.get_server_identity()

        plex_tv = plextv.PlexTV()
        if not machine_id:
            result = plex_tv.get_synced_items(machine_id=server_id['machine_identifier'], user_id=user_id)
        else:
            result = plex_tv.get_synced_items(machine_id=machine_id, user_id=user_id)

        if result:
            self.data = result
            return result
        else:
            self.msg = 'Unable to retrieve sync data for user'
            logger.warn('Unable to retrieve sync data for user.')
Exemplo n.º 16
0
    def get_server_times(self):
        servers = self.get_plextv_server_list(output_format='xml')
        server_times = []

        try:
            xml_head = servers.getElementsByTagName('Server')
        except:
            logger.warn("Error parsing XML for Plex servers.")
            return []

        for a in xml_head:
            if helpers.get_xml_attr(a, 'machineIdentifier') == plexcs.CONFIG.PMS_IDENTIFIER:
                server_times.append({"created_at": helpers.get_xml_attr(a, 'createdAt'),
                                     "updated_at": helpers.get_xml_attr(a, 'updatedAt')
                                     })
                break

        return server_times
Exemplo n.º 17
0
    def _getSync(self, machine_id=None, user_id=None, **kwargs):

        pms_connect = pmsconnect.PmsConnect()
        server_id = pms_connect.get_server_identity()

        plex_tv = plextv.PlexTV()
        if not machine_id:
            result = plex_tv.get_synced_items(
                machine_id=server_id['machine_identifier'], user_id=user_id)
        else:
            result = plex_tv.get_synced_items(machine_id=machine_id,
                                              user_id=user_id)

        if result:
            self.data = result
            return result
        else:
            self.msg = 'Unable to retrieve sync data for user'
            logger.warn('Unable to retrieve sync data for user.')
Exemplo n.º 18
0
    def _getPlayby(self, time_range='30', y_axis='plays', playtype='total_plays_per_month', **kwargs):

        graph = graphs.Graphs()
        if playtype == 'total_plays_per_month':
            result = graph.get_total_plays_per_month(y_axis=y_axis)

        elif playtype == 'total_plays_per_day':
            result = graph.get_total_plays_per_day(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_per_hourofday':
            result = graph.get_total_plays_per_hourofday(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_per_dayofweek':
            result = graph.get_total_plays_per_dayofweek(time_range=time_range, y_axis=y_axis)

        elif playtype == 'stream_type_by_top_10_users':
            result = graph.get_stream_type_by_top_10_users(time_range=time_range, y_axis=y_axis)

        elif playtype == 'stream_type_by_top_10_platforms':
            result = graph.get_stream_type_by_top_10_platforms(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_stream_resolution':
            result = graph.get_total_plays_by_stream_resolution(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_source_resolution':
            result = graph.get_total_plays_by_source_resolution(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_per_stream_type':
            result = graph.get_total_plays_per_stream_type(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_top_10_users':
            result = graph.get_total_plays_by_top_10_users(time_range=time_range, y_axis=y_axis)

        elif playtype == 'total_plays_by_top_10_platforms':
            result = graph.get_total_plays_by_top_10_platforms(time_range=time_range, y_axis=y_axis)

        if result:
            self.data = result
            return result
        else:
            logger.warn('Unable to retrieve %s from db' % playtype)
Exemplo n.º 19
0
    def get_user_player_stats(self, user=None, user_id=None):
        monitor_db = database.MonitorDatabase()

        player_stats = []
        result_id = 0

        try:
            if user_id:
                query = 'SELECT player, COUNT(player) as player_count, platform ' \
                        'FROM session_history ' \
                        'WHERE user_id = ? ' \
                        'GROUP BY player ' \
                        'ORDER BY player_count DESC'
                result = monitor_db.select(query, args=[user_id])
            else:
                query = 'SELECT player, COUNT(player) as player_count, platform ' \
                        'FROM session_history ' \
                        'WHERE user = ? ' \
                        'GROUP BY player ' \
                        'ORDER BY player_count DESC'
                result = monitor_db.select(query, args=[user])
        except:
            logger.warn("Unable to execute database query.")
            return None

        for item in result:
            # Rename Mystery platform names
            platform_type = common.PLATFORM_NAME_OVERRIDES.get(
                item['platform'], item['platform'])

            row = {
                'player_name': item['player'],
                'platform_type': platform_type,
                'total_plays': item['player_count'],
                'result_id': result_id
            }
            player_stats.append(row)
            result_id += 1

        return player_stats
Exemplo n.º 20
0
def process(opcode, data):
    from plexcs import activity_handler

    if opcode not in opcode_data:
        return False

    try:
        info = json.loads(data)
    except Exception as ex:
        logger.warn(u'Plex:CS WebSocket :: Error decoding message from websocket: %s' % ex)
        logger.debug(data)
        return False

    type = info.get('type')

    if not type:
        return False

    if type == 'playing':
        # logger.debug('%s.playing %s' % (name, info))
        try:
            time_line = info.get('_children')
        except:
            logger.debug(u"Plex:CS WebSocket :: Session found but unable to get timeline data.")
            return False

        activity = activity_handler.ActivityHandler(timeline=time_line[0])
        activity.process()

    #if type == 'timeline':
    #    try:
    #        time_line = info.get('_children')
    #    except:
    #        logger.debug(u"Plex:CS WebSocket :: Timeline event found but unable to get timeline data.")
    #        return False

    #    activity = activity_handler.TimelineHandler(timeline=time_line[0])
    #    activity.process()

    return True
Exemplo n.º 21
0
    def get_user_player_stats(self, user=None, user_id=None):
        monitor_db = database.MonitorDatabase()

        player_stats = []
        result_id = 0

        try:
            if user_id:
                query = 'SELECT player, COUNT(player) as player_count, platform ' \
                        'FROM session_history ' \
                        'WHERE user_id = ? ' \
                        'GROUP BY player ' \
                        'ORDER BY player_count DESC'
                result = monitor_db.select(query, args=[user_id])
            else:
                query = 'SELECT player, COUNT(player) as player_count, platform ' \
                        'FROM session_history ' \
                        'WHERE user = ? ' \
                        'GROUP BY player ' \
                        'ORDER BY player_count DESC'
                result = monitor_db.select(query, args=[user])
        except:
            logger.warn("Unable to execute database query.")
            return None

        for item in result:
            # Rename Mystery platform names
            platform_type = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])

            row = {'player_name': item['player'],
                   'platform_type': platform_type,
                   'total_plays': item['player_count'],
                   'result_id': result_id
                   }
            player_stats.append(row)
            result_id += 1

        return player_stats
Exemplo n.º 22
0
def get_real_pms_url():
    logger.info("Requesting URLs for server...")

    # Reset any current PMS_URL value
    plexcs.CONFIG.__setattr__('PMS_URL', '')
    plexcs.CONFIG.write()

    fallback_url = 'http://' + plexcs.CONFIG.PMS_IP + ':' + str(plexcs.CONFIG.PMS_PORT)

    if plexcs.CONFIG.PMS_SSL:
        result = PlexTV().get_server_urls(include_https=True)
        process_urls = True
    elif plexcs.CONFIG.PMS_IS_REMOTE:
        result = PlexTV().get_server_urls(include_https=False)
        process_urls = True
    else:
        process_urls = False

    if process_urls:
        if len(result) > 0:
            for item in result:
                if plexcs.CONFIG.PMS_IS_REMOTE and item['local'] == '0':
                        plexcs.CONFIG.__setattr__('PMS_URL', item['uri'])
                        plexcs.CONFIG.write()
                        logger.info("Server URL retrieved.")
                if not plexcs.CONFIG.PMS_IS_REMOTE and item['local'] == '1':
                        plexcs.CONFIG.__setattr__('PMS_URL', item['uri'])
                        plexcs.CONFIG.write()
                        logger.info("Server URL retrieved.")
        else:
            plexcs.CONFIG.__setattr__('PMS_URL', fallback_url)
            plexcs.CONFIG.write()
            logger.warn("Unable to retrieve server URLs. Using user-defined value.")
    else:
        plexcs.CONFIG.__setattr__('PMS_URL', fallback_url)
        plexcs.CONFIG.write()
Exemplo n.º 23
0
def parse_xml(unparsed=None):
    from plexcs import logger

    if unparsed:
        try:
            xml_parse = minidom.parseString(unparsed)
            return xml_parse
        except Exception as e:
            logger.warn("Error parsing XML. %s" % e)
            return []
        except:
            logger.warn("Error parsing XML.")
            return []
    else:
        logger.warn("XML parse request made but no data received.")
        return []
Exemplo n.º 24
0
    def get_server_urls(self, include_https=True):

        if plexcs.CONFIG.PMS_IDENTIFIER:
            server_id = plexcs.CONFIG.PMS_IDENTIFIER
        else:
            logger.error('Plex:CS PlexTV connector :: Unable to retrieve server identity.')
            return []

        plextv_resources = self.get_plextv_resources(include_https=include_https)
        server_urls = []

        try:
            xml_parse = minidom.parseString(plextv_resources)
        except Exception as e:
            logger.warn("Error parsing XML for Plex resources: %s" % e)
            return []
        except:
            logger.warn("Error parsing XML for Plex resources.")
            return []

        try:
            xml_head = xml_parse.getElementsByTagName('Device')
        except:
            logger.warn("Error parsing XML for Plex resources.")
            return []

        for a in xml_head:
            if helpers.get_xml_attr(a, 'clientIdentifier') == server_id:
                connections = a.getElementsByTagName('Connection')
                for connection in connections:
                    server_details = {"protocol": helpers.get_xml_attr(connection, 'protocol'),
                                      "address": helpers.get_xml_attr(connection, 'address'),
                                      "port": helpers.get_xml_attr(connection, 'port'),
                                      "uri": helpers.get_xml_attr(connection, 'uri'),
                                      "local": helpers.get_xml_attr(connection, 'local')
                                      }

                    server_urls.append(server_details)

        return server_urls
Exemplo n.º 25
0
    def get_user_list(self, kwargs=None):
        data_tables = datatables.DataTables()

        custom_where = ['users.deleted_user', 0]

        columns = ['session_history.id',
                   'users.user_id as user_id',
                   'users.custom_avatar_url as user_thumb',
                   '(case when users.friendly_name is null then users.username else \
                    users.friendly_name end) as friendly_name',
                   'MAX(session_history.started) as last_seen',
                   'session_history.ip_address as ip_address',
                   'COUNT(session_history.id) as plays',
                   'session_history.platform as platform',
                   'session_history.player as player',
                   'session_history_metadata.full_title as last_watched',
                   'session_history_metadata.thumb',
                   'session_history_metadata.parent_thumb',
                   'session_history_metadata.grandparent_thumb',
                   'session_history_metadata.media_type',
                   'session_history.rating_key as rating_key',
                   'session_history_media_info.video_decision',
                   'users.username as user',
                   'users.do_notify as do_notify',
                   'users.keep_history as keep_history'
                   ]
        try:
            query = data_tables.ssp_query(table_name='users',
                                          columns=columns,
                                          custom_where=[custom_where],
                                          group_by=['users.user_id'],
                                          join_types=['LEFT OUTER JOIN',
                                                      'LEFT OUTER JOIN',
                                                      'LEFT OUTER JOIN'],
                                          join_tables=['session_history',
                                                       'session_history_metadata',
                                                       'session_history_media_info'],
                                          join_evals=[['session_history.user_id', 'users.user_id'],
                                                      ['session_history.id', 'session_history_metadata.id'],
                                                      ['session_history.id', 'session_history_media_info.id']],
                                          kwargs=kwargs)
        except:
            logger.warn("Unable to execute database query.")
            return {'recordsFiltered': 0,
                    'recordsTotal': 0,
                    'draw': 0,
                    'data': 'null',
                    'error': 'Unable to execute database query.'}

        users = query['result']

        rows = []
        for item in users:
            if item["media_type"] == 'episode' and item["parent_thumb"]:
                thumb = item["parent_thumb"]
            elif item["media_type"] == 'episode':
                thumb = item["grandparent_thumb"]
            else:
                thumb = item["thumb"]

            if not item['user_thumb'] or item['user_thumb'] == '':
                user_thumb = common.DEFAULT_USER_THUMB
            else:
                user_thumb = item['user_thumb']

            # Rename Mystery platform names
            platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])

            row = {"id": item['id'],
                   "plays": item['plays'],
                   "last_seen": item['last_seen'],
                   "friendly_name": item['friendly_name'],
                   "ip_address": item['ip_address'],
                   "platform": platform,
                   "player": item["player"],
                   "last_watched": item['last_watched'],
                   "thumb": thumb,
                   "media_type": item['media_type'],
                   "rating_key": item['rating_key'],
                   "video_decision": item['video_decision'],
                   "user_thumb": user_thumb,
                   "user": item["user"],
                   "user_id": item['user_id'],
                   "do_notify": helpers.checked(item['do_notify']),
                   "keep_history": helpers.checked(item['keep_history'])
                   }

            rows.append(row)

        dict = {'recordsFiltered': query['filteredCount'],
                'recordsTotal': query['totalCount'],
                'data': rows,
                'draw': query['draw']
        }

        return dict
Exemplo n.º 26
0
    def get_user_details(self, user=None, user_id=None):
        from plexcs import plextv

        monitor_db = database.MonitorDatabase()

        if user:
            query = 'SELECT user_id, username, friendly_name, email, ' \
                    'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                    'FROM users ' \
                    'WHERE username = ? ' \
                    'UNION ALL ' \
                    'SELECT null, user, null, null, null, null, null, null, null ' \
                    'FROM session_history ' \
                    'WHERE user = ? ' \
                    'GROUP BY user ' \
                    'LIMIT 1'
            result = monitor_db.select(query, args=[user, user])
        elif user_id:
            query = 'SELECT user_id, username, friendly_name, email, ' \
                    'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                    'FROM users ' \
                    'WHERE user_id = ? ' \
                    'UNION ALL ' \
                    'SELECT user_id, user, null, null, null, null, null, null, null ' \
                    'FROM session_history ' \
                    'WHERE user_id = ? ' \
                    'GROUP BY user ' \
                    'LIMIT 1'
            result = monitor_db.select(query, args=[user_id, user_id])
        else:
            result = None

        if result:
            user_details = {}
            for item in result:
                if not item['friendly_name']:
                    friendly_name = item['username']
                else:
                    friendly_name = item['friendly_name']
                if not item['thumb'] or item['thumb'] == '':
                    user_thumb = common.DEFAULT_USER_THUMB
                else:
                    user_thumb = item['thumb']

                user_details = {"user_id": item['user_id'],
                                "username": item['username'],
                                "friendly_name": friendly_name,
                                "email": item['email'],
                                "thumb": user_thumb,
                                "is_home_user": item['is_home_user'],
                                "is_allow_sync": item['is_allow_sync'],
                                "is_restricted": item['is_restricted'],
                                "do_notify": item['do_notify']
                                }
            return user_details
        else:
            logger.warn(u"Plex:CS :: Unable to retrieve user from local database. Requesting user list refresh.")
            # Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
            if user:
                # Refresh users
                plextv.refresh_users()
                query = 'SELECT user_id, username, friendly_name, email, ' \
                        'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                        'FROM users ' \
                        'WHERE username = ? ' \
                        'UNION ALL ' \
                        'SELECT null, user, null, null, null, null, null, null, null ' \
                        'FROM session_history ' \
                        'WHERE user = ? ' \
                        'GROUP BY user ' \
                        'LIMIT 1'
                result = monitor_db.select(query, args=[user, user])
            elif user_id:
                # Refresh users
                plextv.refresh_users()
                query = 'SELECT user_id, username, friendly_name, email, ' \
                        'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                        'FROM users ' \
                        'WHERE user_id = ? ' \
                        'UNION ALL ' \
                        'SELECT user_id, user, null, null, null, null, null, null, null ' \
                        'FROM session_history ' \
                        'WHERE user_id = ? ' \
                        'GROUP BY user ' \
                        'LIMIT 1'
                result = monitor_db.select(query, args=[user_id, user_id])
            else:
                result = None

            if result:
                user_details = {}
                for item in result:
                    if not item['friendly_name']:
                        friendly_name = item['username']
                    else:
                        friendly_name = item['friendly_name']
                    if not item['thumb'] or item['thumb'] == '':
                        user_thumb = common.DEFAULT_USER_THUMB
                    else:
                        user_thumb = item['thumb']

                    user_details = {"user_id": item['user_id'],
                                    "username": item['username'],
                                    "friendly_name": friendly_name,
                                    "email": item['email'],
                                    "thumb": user_thumb,
                                    "is_home_user": item['is_home_user'],
                                    "is_allow_sync": item['is_allow_sync'],
                                    "is_restricted": item['is_restricted'],
                                    "do_notify": item['do_notify']
                                    }
                return user_details
            else:
                # If there is no user data we must return something
                # Use "Local" user to retain compatibility with PlexWatch database value
                return {"user_id": None,
                        "username": '******',
                        "friendly_name": 'Local',
                        "email": '',
                        "thumb": '',
                        "is_home_user": 0,
                        "is_allow_sync": 0,
                        "is_restricted": 0,
                        "do_notify": 0
                        }
Exemplo n.º 27
0
def extract_plexwatch_xml(xml=None):
    output = {}
    clean_xml = helpers.latinToAscii(xml)
    try:
        xml_parse = minidom.parseString(clean_xml)
    except:
        logger.warn("Error parsing XML for Plexwatch database.")
        return None

    xml_head = xml_parse.getElementsByTagName('opt')
    if not xml_head:
        logger.warn("Error parsing XML for Plexwatch database.")
        return None

    for a in xml_head:
        added_at = helpers.get_xml_attr(a, 'addedAt')
        art = helpers.get_xml_attr(a, 'art')
        duration = helpers.get_xml_attr(a, 'duration')
        grandparent_thumb = helpers.get_xml_attr(a, 'grandparentThumb')
        grandparent_title = helpers.get_xml_attr(a, 'grandparentTitle')
        guid = helpers.get_xml_attr(a, 'guid')
        media_index = helpers.get_xml_attr(a, 'index')
        originally_available_at = helpers.get_xml_attr(a, 'originallyAvailableAt')
        last_viewed_at = helpers.get_xml_attr(a, 'lastViewedAt')
        parent_media_index = helpers.get_xml_attr(a, 'parentIndex')
        parent_thumb = helpers.get_xml_attr(a, 'parentThumb')
        rating = helpers.get_xml_attr(a, 'rating')
        thumb = helpers.get_xml_attr(a, 'thumb')
        media_type = helpers.get_xml_attr(a, 'type')
        updated_at = helpers.get_xml_attr(a, 'updatedAt')
        view_offset = helpers.get_xml_attr(a, 'viewOffset')
        year = helpers.get_xml_attr(a, 'year')
        parent_title = helpers.get_xml_attr(a, 'parentTitle')
        studio = helpers.get_xml_attr(a, 'studio')
        title = helpers.get_xml_attr(a, 'title')
        tagline = helpers.get_xml_attr(a, 'tagline')

        directors = []
        if a.getElementsByTagName('Director'):
            director_elem = a.getElementsByTagName('Director')
            for b in director_elem:
                directors.append(helpers.get_xml_attr(b, 'tag'))

        aspect_ratio = ''
        audio_channels = None
        audio_codec = ''
        bitrate = None
        container = ''
        height = None
        video_codec = ''
        video_framerate = ''
        video_resolution = ''
        width = None

        if a.getElementsByTagName('Media'):
            media_elem = a.getElementsByTagName('Media')
            for c in media_elem:
                aspect_ratio = helpers.get_xml_attr(c, 'aspectRatio')
                audio_channels = helpers.get_xml_attr(c, 'audioChannels')
                audio_codec = helpers.get_xml_attr(c, 'audioCodec')
                bitrate = helpers.get_xml_attr(c, 'bitrate')
                container = helpers.get_xml_attr(c, 'container')
                height = helpers.get_xml_attr(c, 'height')
                video_codec = helpers.get_xml_attr(c, 'videoCodec')
                video_framerate = helpers.get_xml_attr(c, 'videoFrameRate')
                video_resolution = helpers.get_xml_attr(c, 'videoResolution')
                width = helpers.get_xml_attr(c, 'width')

        machine_id = ''
        platform = ''
        player = ''

        if a.getElementsByTagName('Player'):
            player_elem = a.getElementsByTagName('Player')
            for d in player_elem:
                machine_id = helpers.get_xml_attr(d, 'machineIdentifier')
                platform = helpers.get_xml_attr(d, 'platform')
                player = helpers.get_xml_attr(d, 'title')

        transcode_audio_channels = None
        transcode_audio_codec = ''
        audio_decision = 'direct play'
        transcode_container = ''
        transcode_height = None
        transcode_protocol = ''
        transcode_video_codec = ''
        video_decision = 'direct play'
        transcode_width = None

        if a.getElementsByTagName('TranscodeSession'):
            transcode_elem = a.getElementsByTagName('TranscodeSession')
            for e in transcode_elem:
                transcode_audio_channels = helpers.get_xml_attr(e, 'audioChannels')
                transcode_audio_codec = helpers.get_xml_attr(e, 'audioCodec')
                audio_decision = helpers.get_xml_attr(e, 'audioDecision')
                transcode_container = helpers.get_xml_attr(e, 'container')
                transcode_height = helpers.get_xml_attr(e, 'height')
                transcode_protocol = helpers.get_xml_attr(e, 'protocol')
                transcode_video_codec = helpers.get_xml_attr(e, 'videoCodec')
                video_decision = helpers.get_xml_attr(e, 'videoDecision')
                transcode_width = helpers.get_xml_attr(e, 'width')

        user_id = None

        if a.getElementsByTagName('User'):
            user_elem = a.getElementsByTagName('User')
            for f in user_elem:
                user_id = helpers.get_xml_attr(f, 'id')

        writers = []
        if a.getElementsByTagName('Writer'):
            writer_elem = a.getElementsByTagName('Writer')
            for g in writer_elem:
                writers.append(helpers.get_xml_attr(g, 'tag'))

        actors = []
        if a.getElementsByTagName('Role'):
            actor_elem = a.getElementsByTagName('Role')
            for h in actor_elem:
                actors.append(helpers.get_xml_attr(h, 'tag'))

        genres = []
        if a.getElementsByTagName('Genre'):
            genre_elem = a.getElementsByTagName('Genre')
            for i in genre_elem:
                genres.append(helpers.get_xml_attr(i, 'tag'))

        output = {'added_at': added_at,
                  'art': art,
                  'duration': duration,
                  'grandparent_thumb': grandparent_thumb,
                  'grandparent_title': grandparent_title,
                  'parent_title': parent_title,
                  'title': title,
                  'tagline': tagline,
                  'guid': guid,
                  'media_index': media_index,
                  'originally_available_at': originally_available_at,
                  'last_viewed_at': last_viewed_at,
                  'parent_media_index': parent_media_index,
                  'parent_thumb': parent_thumb,
                  'rating': rating,
                  'thumb': thumb,
                  'media_type': media_type,
                  'updated_at': updated_at,
                  'view_offset': view_offset,
                  'year': year,
                  'directors': directors,
                  'aspect_ratio': aspect_ratio,
                  'audio_channels': audio_channels,
                  'audio_codec': audio_codec,
                  'bitrate': bitrate,
                  'container': container,
                  'height': height,
                  'video_codec': video_codec,
                  'video_framerate': video_framerate,
                  'video_resolution': video_resolution,
                  'width': width,
                  'machine_id': machine_id,
                  'platform': platform,
                  'player': player,
                  'transcode_audio_channels': transcode_audio_channels,
                  'transcode_audio_codec': transcode_audio_codec,
                  'audio_decision': audio_decision,
                  'transcode_container': transcode_container,
                  'transcode_height': transcode_height,
                  'transcode_protocol': transcode_protocol,
                  'transcode_video_codec': transcode_video_codec,
                  'video_decision': video_decision,
                  'transcode_width': transcode_width,
                  'user_id': user_id,
                  'writers': writers,
                  'actors': actors,
                  'genres': genres,
                  'studio': studio
                  }

    return output
Exemplo n.º 28
0
    def get_user_unique_ips(self, kwargs=None, custom_where=None):
        data_tables = datatables.DataTables()

        # Change custom_where column name due to ambiguous column name after JOIN
        custom_where[0][0] = 'custom_user_id' if custom_where[0][
            0] == 'user_id' else custom_where[0][0]

        columns = [
            'session_history.id', 'session_history.started as last_seen',
            'session_history.ip_address as ip_address',
            'COUNT(session_history.id) as play_count',
            'session_history.platform as platform',
            'session_history.player as player',
            'session_history_metadata.full_title as last_watched',
            'session_history_metadata.thumb',
            'session_history_metadata.parent_thumb',
            'session_history_metadata.grandparent_thumb',
            'session_history_metadata.media_type',
            'session_history.rating_key as rating_key',
            'session_history_media_info.video_decision',
            'session_history.user as user',
            'session_history.user_id as custom_user_id',
            '(case when users.friendly_name is null then users.username else \
                    users.friendly_name end) as friendly_name'
        ]

        try:
            query = data_tables.ssp_query(
                table_name='session_history',
                columns=columns,
                custom_where=custom_where,
                group_by=['ip_address'],
                join_types=['JOIN', 'JOIN', 'JOIN'],
                join_tables=[
                    'users', 'session_history_metadata',
                    'session_history_media_info'
                ],
                join_evals=[
                    ['session_history.user_id', 'users.user_id'],
                    ['session_history.id', 'session_history_metadata.id'],
                    ['session_history.id', 'session_history_media_info.id']
                ],
                kwargs=kwargs)
        except:
            logger.warn("Unable to execute database query.")
            return {
                'recordsFiltered': 0,
                'recordsTotal': 0,
                'draw': 0,
                'data': 'null',
                'error': 'Unable to execute database query.'
            }

        results = query['result']

        rows = []
        for item in results:
            if item["media_type"] == 'episode' and item["parent_thumb"]:
                thumb = item["parent_thumb"]
            elif item["media_type"] == 'episode':
                thumb = item["grandparent_thumb"]
            else:
                thumb = item["thumb"]

            # Rename Mystery platform names
            platform = common.PLATFORM_NAME_OVERRIDES.get(
                item["platform"], item["platform"])

            row = {
                "id": item['id'],
                "last_seen": item['last_seen'],
                "ip_address": item['ip_address'],
                "play_count": item['play_count'],
                "platform": platform,
                "player": item['player'],
                "last_watched": item['last_watched'],
                "thumb": thumb,
                "media_type": item['media_type'],
                "rating_key": item['rating_key'],
                "video_decision": item['video_decision'],
                "friendly_name": item['friendly_name']
            }

            rows.append(row)

        dict = {
            'recordsFiltered': query['filteredCount'],
            'recordsTotal': query['totalCount'],
            'data': rows,
            'draw': query['draw']
        }

        return dict
Exemplo n.º 29
0
    def get_user_unique_ips(self, kwargs=None, custom_where=None):
        data_tables = datatables.DataTables()

        # Change custom_where column name due to ambiguous column name after JOIN
        custom_where[0][0] = 'custom_user_id' if custom_where[0][0] == 'user_id' else custom_where[0][0]

        columns = ['session_history.id',
                   'session_history.started as last_seen',
                   'session_history.ip_address as ip_address',
                   'COUNT(session_history.id) as play_count',
                   'session_history.platform as platform',
                   'session_history.player as player',
                   'session_history_metadata.full_title as last_watched',
                   'session_history_metadata.thumb',
                   'session_history_metadata.parent_thumb',
                   'session_history_metadata.grandparent_thumb',
                   'session_history_metadata.media_type',
                   'session_history.rating_key as rating_key',
                   'session_history_media_info.video_decision',
                   'session_history.user as user',
                   'session_history.user_id as custom_user_id',
                   '(case when users.friendly_name is null then users.username else \
                    users.friendly_name end) as friendly_name'
                   ]

        try:
            query = data_tables.ssp_query(table_name='session_history',
                                          columns=columns,
                                          custom_where=custom_where,
                                          group_by=['ip_address'],
                                          join_types=['JOIN',
                                                      'JOIN',
                                                      'JOIN'],
                                          join_tables=['users',
                                                       'session_history_metadata',
                                                       'session_history_media_info'],
                                          join_evals=[['session_history.user_id', 'users.user_id'],
                                                      ['session_history.id', 'session_history_metadata.id'],
                                                      ['session_history.id', 'session_history_media_info.id']],
                                          kwargs=kwargs)
        except:
            logger.warn("Unable to execute database query.")
            return {'recordsFiltered': 0,
                    'recordsTotal': 0,
                    'draw': 0,
                    'data': 'null',
                    'error': 'Unable to execute database query.'}

        results = query['result']

        rows = []
        for item in results:
            if item["media_type"] == 'episode' and item["parent_thumb"]:
                thumb = item["parent_thumb"]
            elif item["media_type"] == 'episode':
                thumb = item["grandparent_thumb"]
            else:
                thumb = item["thumb"]

            # Rename Mystery platform names
            platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])

            row = {"id": item['id'],
                   "last_seen": item['last_seen'],
                   "ip_address": item['ip_address'],
                   "play_count": item['play_count'],
                   "platform": platform,
                   "player": item['player'],
                   "last_watched": item['last_watched'],
                   "thumb": thumb,
                   "media_type": item['media_type'],
                   "rating_key": item['rating_key'],
                   "video_decision": item['video_decision'],
                   "friendly_name": item['friendly_name']
                   }

            rows.append(row)

        dict = {'recordsFiltered': query['filteredCount'],
                'recordsTotal': query['totalCount'],
                'data': rows,
                'draw': query['draw']
        }

        return dict
Exemplo n.º 30
0
    def get_user_details(self, user=None, user_id=None):
        from plexcs import plextv

        monitor_db = database.MonitorDatabase()

        if user:
            query = 'SELECT user_id, username, friendly_name, email, ' \
                    'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                    'FROM users ' \
                    'WHERE username = ? ' \
                    'UNION ALL ' \
                    'SELECT null, user, null, null, null, null, null, null, null ' \
                    'FROM session_history ' \
                    'WHERE user = ? ' \
                    'GROUP BY user ' \
                    'LIMIT 1'
            result = monitor_db.select(query, args=[user, user])
        elif user_id:
            query = 'SELECT user_id, username, friendly_name, email, ' \
                    'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                    'FROM users ' \
                    'WHERE user_id = ? ' \
                    'UNION ALL ' \
                    'SELECT user_id, user, null, null, null, null, null, null, null ' \
                    'FROM session_history ' \
                    'WHERE user_id = ? ' \
                    'GROUP BY user ' \
                    'LIMIT 1'
            result = monitor_db.select(query, args=[user_id, user_id])
        else:
            result = None

        if result:
            user_details = {}
            for item in result:
                if not item['friendly_name']:
                    friendly_name = item['username']
                else:
                    friendly_name = item['friendly_name']
                if not item['thumb'] or item['thumb'] == '':
                    user_thumb = common.DEFAULT_USER_THUMB
                else:
                    user_thumb = item['thumb']

                user_details = {
                    "user_id": item['user_id'],
                    "username": item['username'],
                    "friendly_name": friendly_name,
                    "email": item['email'],
                    "thumb": user_thumb,
                    "is_home_user": item['is_home_user'],
                    "is_allow_sync": item['is_allow_sync'],
                    "is_restricted": item['is_restricted'],
                    "do_notify": item['do_notify']
                }
            return user_details
        else:
            logger.warn(
                u"Plex:CS :: Unable to retrieve user from local database. Requesting user list refresh."
            )
            # Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
            if user:
                # Refresh users
                plextv.refresh_users()
                query = 'SELECT user_id, username, friendly_name, email, ' \
                        'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                        'FROM users ' \
                        'WHERE username = ? ' \
                        'UNION ALL ' \
                        'SELECT null, user, null, null, null, null, null, null, null ' \
                        'FROM session_history ' \
                        'WHERE user = ? ' \
                        'GROUP BY user ' \
                        'LIMIT 1'
                result = monitor_db.select(query, args=[user, user])
            elif user_id:
                # Refresh users
                plextv.refresh_users()
                query = 'SELECT user_id, username, friendly_name, email, ' \
                        'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
                        'FROM users ' \
                        'WHERE user_id = ? ' \
                        'UNION ALL ' \
                        'SELECT user_id, user, null, null, null, null, null, null, null ' \
                        'FROM session_history ' \
                        'WHERE user_id = ? ' \
                        'GROUP BY user ' \
                        'LIMIT 1'
                result = monitor_db.select(query, args=[user_id, user_id])
            else:
                result = None

            if result:
                user_details = {}
                for item in result:
                    if not item['friendly_name']:
                        friendly_name = item['username']
                    else:
                        friendly_name = item['friendly_name']
                    if not item['thumb'] or item['thumb'] == '':
                        user_thumb = common.DEFAULT_USER_THUMB
                    else:
                        user_thumb = item['thumb']

                    user_details = {
                        "user_id": item['user_id'],
                        "username": item['username'],
                        "friendly_name": friendly_name,
                        "email": item['email'],
                        "thumb": user_thumb,
                        "is_home_user": item['is_home_user'],
                        "is_allow_sync": item['is_allow_sync'],
                        "is_restricted": item['is_restricted'],
                        "do_notify": item['do_notify']
                    }
                return user_details
            else:
                # If there is no user data we must return something
                # Use "Local" user to retain compatibility with PlexWatch database value
                return {
                    "user_id": None,
                    "username": '******',
                    "friendly_name": 'Local',
                    "email": '',
                    "thumb": '',
                    "is_home_user": 0,
                    "is_allow_sync": 0,
                    "is_restricted": 0,
                    "do_notify": 0
                }
Exemplo n.º 31
0
    def get_total_plays_per_stream_type(self, time_range='30', y_axis='plays'):
        monitor_db = database.MonitorDatabase()

        if not time_range.isdigit():
            time_range = '30'

        try:
            if y_axis == 'plays':
                query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
                        'SUM(case when session_history_media_info.video_decision = "direct play" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play") ' \
                        'then 1 else 0 end) as dp_count, ' \
                        'SUM(case when session_history_media_info.video_decision = "copy" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy") ' \
                        'then 1 else 0 end) as ds_count, ' \
                        'SUM(case when session_history_media_info.video_decision = "transcode" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode") ' \
                        'then 1 else 0 end) as tc_count ' \
                        'FROM session_history ' \
                        'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
                        'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
                        'datetime("now", "-%s days", "localtime")) AND ' \
                        '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
                        'GROUP BY date_played ' \
                        'ORDER BY started ASC' % time_range

                result = monitor_db.select(query)
            else:
                query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
                        'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \
                        'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
                        ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
                        'SUM(case when (session_history_media_info.video_decision = "copy" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \
                        'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
                        ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
                        'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
                        'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \
                        'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
                        ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count ' \
                        'FROM session_history ' \
                        'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
                        'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
                        'datetime("now", "-%s days", "localtime") AND ' \
                        '(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
                        'GROUP BY date_played ' \
                        'ORDER BY started ASC' % time_range

                result = monitor_db.select(query)
        except:
            logger.warn("Unable to execute database query.")
            return None

        # create our date range as some days may not have any data
        # but we still want to display them
        base = datetime.date.today()
        date_list = [base - datetime.timedelta(days=x) for x in range(0, int(time_range))]

        categories = []
        series_1 = []
        series_2 = []
        series_3 = []

        for date_item in sorted(date_list):
            date_string = date_item.strftime('%Y-%m-%d')
            categories.append(date_string)
            series_1_value = 0
            series_2_value = 0
            series_3_value = 0
            for item in result:
                if date_string == item['date_played']:
                    series_1_value = item['dp_count']
                    series_2_value = item['ds_count']
                    series_3_value = item['tc_count']
                    break
                else:
                    series_1_value = 0
                    series_2_value = 0
                    series_3_value = 0

            series_1.append(series_1_value)
            series_2.append(series_2_value)
            series_3.append(series_3_value)

        series_1_output = {'name': 'Direct Play',
                           'data': series_1}
        series_2_output = {'name': 'Direct Stream',
                           'data': series_2}
        series_3_output = {'name': 'Transcode',
                           'data': series_3}

        output = {'categories': categories,
                  'series': [series_1_output, series_2_output, series_3_output]}
        return output
Exemplo n.º 32
0
    def get_synced_items(self, machine_id=None, user_id=None):
        sync_list = self.get_plextv_sync_lists(machine_id)
        user_data = users.Users()

        synced_items = []

        try:
            xml_parse = minidom.parseString(sync_list)
        except Exception as e:
            logger.warn("Error parsing XML for Plex sync lists: %s" % e)
            return []
        except:
            logger.warn("Error parsing XML for Plex sync lists.")
            return []

        xml_head = xml_parse.getElementsByTagName('SyncList')

        if not xml_head:
            logger.warn("Error parsing XML for Plex sync lists.")
        else:
            for a in xml_head:
                client_id = helpers.get_xml_attr(a, 'id')
                sync_device = a.getElementsByTagName('Device')
                for device in sync_device:
                    device_user_id = helpers.get_xml_attr(device, 'userID')
                    try:
                        device_username = user_data.get_user_details(user_id=device_user_id)['username']
                        device_friendly_name = user_data.get_user_details(user_id=device_user_id)['friendly_name']
                    except:
                        device_username = ''
                        device_friendly_name = ''
                    device_name = helpers.get_xml_attr(device, 'name')
                    device_product = helpers.get_xml_attr(device, 'product')
                    device_product_version = helpers.get_xml_attr(device, 'productVersion')
                    device_platform = helpers.get_xml_attr(device, 'platform')
                    device_platform_version = helpers.get_xml_attr(device, 'platformVersion')
                    device_type = helpers.get_xml_attr(device, 'device')
                    device_model = helpers.get_xml_attr(device, 'model')
                    device_last_seen = helpers.get_xml_attr(device, 'lastSeenAt')

                # Filter by user_id
                if user_id and user_id != device_user_id:
                    continue

                for synced in a.getElementsByTagName('SyncItems'):
                    sync_item = synced.getElementsByTagName('SyncItem')
                    for item in sync_item:
                        sync_id = helpers.get_xml_attr(item, 'id')
                        sync_version = helpers.get_xml_attr(item, 'version')
                        sync_root_title = helpers.get_xml_attr(item, 'rootTitle')
                        sync_title = helpers.get_xml_attr(item, 'title')
                        sync_metadata_type = helpers.get_xml_attr(item, 'metadataType')
                        sync_content_type = helpers.get_xml_attr(item, 'contentType')

                        for status in item.getElementsByTagName('Status'):
                            status_failure_code = helpers.get_xml_attr(status, 'failureCode')
                            status_failure = helpers.get_xml_attr(status, 'failure')
                            status_state = helpers.get_xml_attr(status, 'state')
                            status_item_count = helpers.get_xml_attr(status, 'itemsCount')
                            status_item_complete_count = helpers.get_xml_attr(status, 'itemsCompleteCount')
                            status_item_downloaded_count = helpers.get_xml_attr(status, 'itemsDownloadedCount')
                            status_item_ready_count = helpers.get_xml_attr(status, 'itemsReadyCount')
                            status_item_successful_count = helpers.get_xml_attr(status, 'itemsSuccessfulCount')
                            status_total_size = helpers.get_xml_attr(status, 'totalSize')
                            status_item_download_percent_complete = helpers.get_percent(
                                status_item_downloaded_count, status_item_count)

                        for settings in item.getElementsByTagName('MediaSettings'):
                            settings_audio_boost = helpers.get_xml_attr(settings, 'audioBoost')
                            settings_music_bitrate = helpers.get_xml_attr(settings, 'musicBitrate')
                            settings_photo_quality = helpers.get_xml_attr(settings, 'photoQuality')
                            settings_photo_resolution = helpers.get_xml_attr(settings, 'photoResolution')
                            settings_video_quality = helpers.get_xml_attr(settings, 'videoQuality')
                            settings_video_resolution = helpers.get_xml_attr(settings, 'videoResolution')

                        if helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri').endswith('%2Fchildren'):
                            clean_uri = helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri')[:-11]
                        else:
                            clean_uri = helpers.get_xml_attr(item.getElementsByTagName('Location')[0], 'uri')

                        rating_key = clean_uri.rpartition('%2F')[-1]

                        sync_details = {"device_name": helpers.sanitize(device_name),
                                        "platform": helpers.sanitize(device_platform),
                                        "username": helpers.sanitize(device_username),
                                        "friendly_name": helpers.sanitize(device_friendly_name),
                                        "user_id": device_user_id,
                                        "root_title": helpers.sanitize(sync_root_title),
                                        "title": helpers.sanitize(sync_title),
                                        "metadata_type": sync_metadata_type,
                                        "content_type": sync_content_type,
                                        "rating_key": rating_key,
                                        "state": status_state,
                                        "item_count": status_item_count,
                                        "item_complete_count": status_item_complete_count,
                                        "item_downloaded_count": status_item_downloaded_count,
                                        "item_downloaded_percent_complete": status_item_download_percent_complete,
                                        "music_bitrate": settings_music_bitrate,
                                        "photo_quality": settings_photo_quality,
                                        "video_quality": settings_video_quality,
                                        "total_size": status_total_size,
                                        "failure": status_failure,
                                        "sync_id": sync_id
                                        }

                        synced_items.append(sync_details)

        return synced_items
Exemplo n.º 33
0
    def get_full_users_list(self):
        friends_list = self.get_plextv_friends()
        own_account = self.get_plextv_user_details()
        users_list = []

        try:
            xml_parse = minidom.parseString(own_account)
        except Exception as e:
            logger.warn("Error parsing XML for Plex account details: %s" % e)
            return []
        except:
            logger.warn("Error parsing XML for Plex account details.")
            return []

        xml_head = xml_parse.getElementsByTagName('user')
        if not xml_head:
            logger.warn("Error parsing XML for Plex account details.")
        else:
            for a in xml_head:
                own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
                               "username": helpers.get_xml_attr(a, 'username'),
                               "thumb": helpers.get_xml_attr(a, 'thumb'),
                               "email": helpers.get_xml_attr(a, 'email'),
                               "is_home_user": helpers.get_xml_attr(a, 'home'),
                               "is_allow_sync": None,
                               "is_restricted": helpers.get_xml_attr(a, 'restricted')
                               }

                users_list.append(own_details)

        try:
            xml_parse = minidom.parseString(friends_list)
        except Exception as e:
            logger.warn("Error parsing XML for Plex friends list: %s" % e)
        except:
            logger.warn("Error parsing XML for Plex friends list.")

        xml_head = xml_parse.getElementsByTagName('User')
        if not xml_head:
            logger.warn("Error parsing XML for Plex friends list.")
        else:
            for a in xml_head:
                friend = {"user_id": helpers.get_xml_attr(a, 'id'),
                          "username": helpers.get_xml_attr(a, 'title'),
                          "thumb": helpers.get_xml_attr(a, 'thumb'),
                          "email": helpers.get_xml_attr(a, 'email'),
                          "is_home_user": helpers.get_xml_attr(a, 'home'),
                          "is_allow_sync": helpers.get_xml_attr(a, 'allowSync'),
                          "is_restricted": helpers.get_xml_attr(a, 'restricted')
                          }

                users_list.append(friend)

        return users_list
Exemplo n.º 34
0
def check_active_sessions(ws_request=False):

    with monitor_lock:
        pms_connect = pmsconnect.PmsConnect()
        session_list = pms_connect.get_current_activity()
        monitor_db = database.MonitorDatabase()
        monitor_process = activity_processor.ActivityProcessor()
        # logger.debug(u"Plex:CS Monitor :: Checking for active streams.")

        global int_ping_count

        if session_list:
            int_ping_count = 0

            media_container = session_list['sessions']

            # Check our temp table for what we must do with the new streams
            db_streams = monitor_db.select('SELECT started, session_key, rating_key, media_type, title, parent_title, '
                                           'grandparent_title, user_id, user, friendly_name, ip_address, player, '
                                           'platform, machine_id, parent_rating_key, grandparent_rating_key, state, '
                                           'view_offset, duration, video_decision, audio_decision, width, height, '
                                           'container, video_codec, audio_codec, bitrate, video_resolution, '
                                           'video_framerate, aspect_ratio, audio_channels, transcode_protocol, '
                                           'transcode_container, transcode_video_codec, transcode_audio_codec, '
                                           'transcode_audio_channels, transcode_width, transcode_height, '
                                           'paused_counter, last_paused '
                                           'FROM sessions')
            for stream in db_streams:
                if any(d['session_key'] == str(stream['session_key']) and d['rating_key'] == str(stream['rating_key'])
                       for d in media_container):
                    # The user's session is still active
                    for session in media_container:
                        if session['session_key'] == str(stream['session_key']) and \
                                session['rating_key'] == str(stream['rating_key']):
                            # The user is still playing the same media item
                            # Here we can check the play states
                            if session['state'] != stream['state']:
                                if session['state'] == 'paused':
                                    # Push any notifications -
                                    # Push it on it's own thread so we don't hold up our db actions
                                    threading.Thread(target=notification_handler.notify,
                                                     kwargs=dict(stream_data=stream, notify_action='pause')).start()

                                if session['state'] == 'playing' and stream['state'] == 'paused':
                                    # Push any notifications -
                                    # Push it on it's own thread so we don't hold up our db actions
                                    threading.Thread(target=notification_handler.notify,
                                                     kwargs=dict(stream_data=stream, notify_action='resume')).start()

                            if stream['state'] == 'paused' and not ws_request:
                                # The stream is still paused so we need to increment the paused_counter
                                # Using the set config parameter as the interval, probably not the most accurate but
                                # it will have to do for now. If it's a websocket request don't use this method.
                                paused_counter = int(stream['paused_counter']) + plexcs.CONFIG.MONITORING_INTERVAL
                                monitor_db.action('UPDATE sessions SET paused_counter = ? '
                                                  'WHERE session_key = ? AND rating_key = ?',
                                                  [paused_counter, stream['session_key'], stream['rating_key']])

                            if session['state'] == 'buffering' and plexcs.CONFIG.BUFFER_THRESHOLD > 0:
                                # The stream is buffering so we need to increment the buffer_count
                                # We're going just increment on every monitor ping,
                                # would be difficult to keep track otherwise
                                monitor_db.action('UPDATE sessions SET buffer_count = buffer_count + 1 '
                                                  'WHERE session_key = ? AND rating_key = ?',
                                                  [stream['session_key'], stream['rating_key']])

                                # Check the current buffer count and last buffer to determine if we should notify
                                buffer_values = monitor_db.select('SELECT buffer_count, buffer_last_triggered '
                                                                  'FROM sessions '
                                                                  'WHERE session_key = ? AND rating_key = ?',
                                                                  [stream['session_key'], stream['rating_key']])

                                if buffer_values[0]['buffer_count'] >= plexcs.CONFIG.BUFFER_THRESHOLD:
                                    # Push any notifications -
                                    # Push it on it's own thread so we don't hold up our db actions
                                    # Our first buffer notification
                                    if buffer_values[0]['buffer_count'] == plexcs.CONFIG.BUFFER_THRESHOLD:
                                        logger.info(u"Plex:CS Monitor :: User '%s' has triggered a buffer warning."
                                                    % stream['user'])
                                        # Set the buffer trigger time
                                        monitor_db.action('UPDATE sessions '
                                                          'SET buffer_last_triggered = strftime("%s","now") '
                                                          'WHERE session_key = ? AND rating_key = ?',
                                                          [stream['session_key'], stream['rating_key']])

                                        threading.Thread(target=notification_handler.notify,
                                                         kwargs=dict(stream_data=stream, notify_action='buffer')).start()
                                    else:
                                        # Subsequent buffer notifications after wait time
                                        if int(time.time()) > buffer_values[0]['buffer_last_triggered'] + \
                                                plexcs.CONFIG.BUFFER_WAIT:
                                            logger.info(u"Plex:CS Monitor :: User '%s' has triggered multiple buffer warnings."
                                                    % stream['user'])
                                            # Set the buffer trigger time
                                            monitor_db.action('UPDATE sessions '
                                                              'SET buffer_last_triggered = strftime("%s","now") '
                                                              'WHERE session_key = ? AND rating_key = ?',
                                                              [stream['session_key'], stream['rating_key']])

                                            threading.Thread(target=notification_handler.notify,
                                                             kwargs=dict(stream_data=stream, notify_action='buffer')).start()

                                logger.debug(u"Plex:CS Monitor :: Stream buffering. Count is now %s. Last triggered %s."
                                             % (buffer_values[0]['buffer_count'],
                                                buffer_values[0]['buffer_last_triggered']))

                            # Check if the user has reached the offset in the media we defined as the "watched" percent
                            # Don't trigger if state is buffer as some clients push the progress to the end when
                            # buffering on start.
                            if session['view_offset'] and session['duration'] and session['state'] != 'buffering':
                                if helpers.get_percent(session['view_offset'],
                                                       session['duration']) > plexcs.CONFIG.NOTIFY_WATCHED_PERCENT:
                                    # Push any notifications -
                                    # Push it on it's own thread so we don't hold up our db actions
                                    threading.Thread(target=notification_handler.notify,
                                                     kwargs=dict(stream_data=stream, notify_action='watched')).start()

                else:
                    # The user has stopped playing a stream
                    logger.debug(u"Plex:CS Monitor :: Removing sessionKey %s ratingKey %s from session queue"
                                 % (stream['session_key'], stream['rating_key']))
                    monitor_db.action('DELETE FROM sessions WHERE session_key = ? AND rating_key = ?',
                                      [stream['session_key'], stream['rating_key']])

                    # Check if the user has reached the offset in the media we defined as the "watched" percent
                    if stream['view_offset'] and stream['duration']:
                        if helpers.get_percent(stream['view_offset'],
                                               stream['duration']) > plexcs.CONFIG.NOTIFY_WATCHED_PERCENT:
                            # Push any notifications -
                            # Push it on it's own thread so we don't hold up our db actions
                            threading.Thread(target=notification_handler.notify,
                                             kwargs=dict(stream_data=stream, notify_action='watched')).start()

                    # Push any notifications - Push it on it's own thread so we don't hold up our db actions
                    threading.Thread(target=notification_handler.notify,
                                     kwargs=dict(stream_data=stream, notify_action='stop')).start()

                    # Write the item history on playback stop
                    monitor_process.write_session_history(session=stream)

            # Process the newly received session data
            for session in media_container:
                monitor_process.write_session(session)
        else:
            logger.debug(u"Plex:CS Monitor :: Unable to read session list.")

            int_ping_count += 1
            logger.warn(u"Plex:CS Monitor :: Unable to get an internal response from the server, ping attempt %s." \
                        % str(int_ping_count))

        if int_ping_count == 3:
            # Fire off notifications
            threading.Thread(target=notification_handler.notify_timeline,
                                kwargs=dict(notify_action='intdown')).start()
Exemplo n.º 35
0
def extract_plexwatch_xml(xml=None):
    output = {}
    clean_xml = helpers.latinToAscii(xml)
    try:
        xml_parse = minidom.parseString(clean_xml)
    except:
        logger.warn("Error parsing XML for Plexwatch database.")
        return None

    xml_head = xml_parse.getElementsByTagName('opt')
    if not xml_head:
        logger.warn("Error parsing XML for Plexwatch database.")
        return None

    for a in xml_head:
        added_at = helpers.get_xml_attr(a, 'addedAt')
        art = helpers.get_xml_attr(a, 'art')
        duration = helpers.get_xml_attr(a, 'duration')
        grandparent_thumb = helpers.get_xml_attr(a, 'grandparentThumb')
        grandparent_title = helpers.get_xml_attr(a, 'grandparentTitle')
        guid = helpers.get_xml_attr(a, 'guid')
        media_index = helpers.get_xml_attr(a, 'index')
        originally_available_at = helpers.get_xml_attr(
            a, 'originallyAvailableAt')
        last_viewed_at = helpers.get_xml_attr(a, 'lastViewedAt')
        parent_media_index = helpers.get_xml_attr(a, 'parentIndex')
        parent_thumb = helpers.get_xml_attr(a, 'parentThumb')
        rating = helpers.get_xml_attr(a, 'rating')
        thumb = helpers.get_xml_attr(a, 'thumb')
        media_type = helpers.get_xml_attr(a, 'type')
        updated_at = helpers.get_xml_attr(a, 'updatedAt')
        view_offset = helpers.get_xml_attr(a, 'viewOffset')
        year = helpers.get_xml_attr(a, 'year')
        parent_title = helpers.get_xml_attr(a, 'parentTitle')
        studio = helpers.get_xml_attr(a, 'studio')
        title = helpers.get_xml_attr(a, 'title')
        tagline = helpers.get_xml_attr(a, 'tagline')

        directors = []
        if a.getElementsByTagName('Director'):
            director_elem = a.getElementsByTagName('Director')
            for b in director_elem:
                directors.append(helpers.get_xml_attr(b, 'tag'))

        aspect_ratio = ''
        audio_channels = None
        audio_codec = ''
        bitrate = None
        container = ''
        height = None
        video_codec = ''
        video_framerate = ''
        video_resolution = ''
        width = None

        if a.getElementsByTagName('Media'):
            media_elem = a.getElementsByTagName('Media')
            for c in media_elem:
                aspect_ratio = helpers.get_xml_attr(c, 'aspectRatio')
                audio_channels = helpers.get_xml_attr(c, 'audioChannels')
                audio_codec = helpers.get_xml_attr(c, 'audioCodec')
                bitrate = helpers.get_xml_attr(c, 'bitrate')
                container = helpers.get_xml_attr(c, 'container')
                height = helpers.get_xml_attr(c, 'height')
                video_codec = helpers.get_xml_attr(c, 'videoCodec')
                video_framerate = helpers.get_xml_attr(c, 'videoFrameRate')
                video_resolution = helpers.get_xml_attr(c, 'videoResolution')
                width = helpers.get_xml_attr(c, 'width')

        machine_id = ''
        platform = ''
        player = ''

        if a.getElementsByTagName('Player'):
            player_elem = a.getElementsByTagName('Player')
            for d in player_elem:
                machine_id = helpers.get_xml_attr(d, 'machineIdentifier')
                platform = helpers.get_xml_attr(d, 'platform')
                player = helpers.get_xml_attr(d, 'title')

        transcode_audio_channels = None
        transcode_audio_codec = ''
        audio_decision = 'direct play'
        transcode_container = ''
        transcode_height = None
        transcode_protocol = ''
        transcode_video_codec = ''
        video_decision = 'direct play'
        transcode_width = None

        if a.getElementsByTagName('TranscodeSession'):
            transcode_elem = a.getElementsByTagName('TranscodeSession')
            for e in transcode_elem:
                transcode_audio_channels = helpers.get_xml_attr(
                    e, 'audioChannels')
                transcode_audio_codec = helpers.get_xml_attr(e, 'audioCodec')
                audio_decision = helpers.get_xml_attr(e, 'audioDecision')
                transcode_container = helpers.get_xml_attr(e, 'container')
                transcode_height = helpers.get_xml_attr(e, 'height')
                transcode_protocol = helpers.get_xml_attr(e, 'protocol')
                transcode_video_codec = helpers.get_xml_attr(e, 'videoCodec')
                video_decision = helpers.get_xml_attr(e, 'videoDecision')
                transcode_width = helpers.get_xml_attr(e, 'width')

        user_id = None

        if a.getElementsByTagName('User'):
            user_elem = a.getElementsByTagName('User')
            for f in user_elem:
                user_id = helpers.get_xml_attr(f, 'id')

        writers = []
        if a.getElementsByTagName('Writer'):
            writer_elem = a.getElementsByTagName('Writer')
            for g in writer_elem:
                writers.append(helpers.get_xml_attr(g, 'tag'))

        actors = []
        if a.getElementsByTagName('Role'):
            actor_elem = a.getElementsByTagName('Role')
            for h in actor_elem:
                actors.append(helpers.get_xml_attr(h, 'tag'))

        genres = []
        if a.getElementsByTagName('Genre'):
            genre_elem = a.getElementsByTagName('Genre')
            for i in genre_elem:
                genres.append(helpers.get_xml_attr(i, 'tag'))

        output = {
            'added_at': added_at,
            'art': art,
            'duration': duration,
            'grandparent_thumb': grandparent_thumb,
            'grandparent_title': grandparent_title,
            'parent_title': parent_title,
            'title': title,
            'tagline': tagline,
            'guid': guid,
            'media_index': media_index,
            'originally_available_at': originally_available_at,
            'last_viewed_at': last_viewed_at,
            'parent_media_index': parent_media_index,
            'parent_thumb': parent_thumb,
            'rating': rating,
            'thumb': thumb,
            'media_type': media_type,
            'updated_at': updated_at,
            'view_offset': view_offset,
            'year': year,
            'directors': directors,
            'aspect_ratio': aspect_ratio,
            'audio_channels': audio_channels,
            'audio_codec': audio_codec,
            'bitrate': bitrate,
            'container': container,
            'height': height,
            'video_codec': video_codec,
            'video_framerate': video_framerate,
            'video_resolution': video_resolution,
            'width': width,
            'machine_id': machine_id,
            'platform': platform,
            'player': player,
            'transcode_audio_channels': transcode_audio_channels,
            'transcode_audio_codec': transcode_audio_codec,
            'audio_decision': audio_decision,
            'transcode_container': transcode_container,
            'transcode_height': transcode_height,
            'transcode_protocol': transcode_protocol,
            'transcode_video_codec': transcode_video_codec,
            'video_decision': video_decision,
            'transcode_width': transcode_width,
            'user_id': user_id,
            'writers': writers,
            'actors': actors,
            'genres': genres,
            'studio': studio
        }

    return output
Exemplo n.º 36
0
    def get_user_list(self, kwargs=None):
        data_tables = datatables.DataTables()

        custom_where = ['users.deleted_user', 0]

        columns = [
            'session_history.id', 'users.user_id as user_id',
            'users.custom_avatar_url as user_thumb',
            '(case when users.friendly_name is null then users.username else \
                    users.friendly_name end) as friendly_name',
            'MAX(session_history.started) as last_seen',
            'session_history.ip_address as ip_address',
            'COUNT(session_history.id) as plays',
            'session_history.platform as platform',
            'session_history.player as player',
            'session_history_metadata.full_title as last_watched',
            'session_history_metadata.thumb',
            'session_history_metadata.parent_thumb',
            'session_history_metadata.grandparent_thumb',
            'session_history_metadata.media_type',
            'session_history.rating_key as rating_key',
            'session_history_media_info.video_decision',
            'users.username as user', 'users.do_notify as do_notify',
            'users.keep_history as keep_history'
        ]
        try:
            query = data_tables.ssp_query(
                table_name='users',
                columns=columns,
                custom_where=[custom_where],
                group_by=['users.user_id'],
                join_types=[
                    'LEFT OUTER JOIN', 'LEFT OUTER JOIN', 'LEFT OUTER JOIN'
                ],
                join_tables=[
                    'session_history', 'session_history_metadata',
                    'session_history_media_info'
                ],
                join_evals=[
                    ['session_history.user_id', 'users.user_id'],
                    ['session_history.id', 'session_history_metadata.id'],
                    ['session_history.id', 'session_history_media_info.id']
                ],
                kwargs=kwargs)
        except:
            logger.warn("Unable to execute database query.")
            return {
                'recordsFiltered': 0,
                'recordsTotal': 0,
                'draw': 0,
                'data': 'null',
                'error': 'Unable to execute database query.'
            }

        users = query['result']

        rows = []
        for item in users:
            if item["media_type"] == 'episode' and item["parent_thumb"]:
                thumb = item["parent_thumb"]
            elif item["media_type"] == 'episode':
                thumb = item["grandparent_thumb"]
            else:
                thumb = item["thumb"]

            if not item['user_thumb'] or item['user_thumb'] == '':
                user_thumb = common.DEFAULT_USER_THUMB
            else:
                user_thumb = item['user_thumb']

            # Rename Mystery platform names
            platform = common.PLATFORM_NAME_OVERRIDES.get(
                item["platform"], item["platform"])

            row = {
                "id": item['id'],
                "plays": item['plays'],
                "last_seen": item['last_seen'],
                "friendly_name": item['friendly_name'],
                "ip_address": item['ip_address'],
                "platform": platform,
                "player": item["player"],
                "last_watched": item['last_watched'],
                "thumb": thumb,
                "media_type": item['media_type'],
                "rating_key": item['rating_key'],
                "video_decision": item['video_decision'],
                "user_thumb": user_thumb,
                "user": item["user"],
                "user_id": item['user_id'],
                "do_notify": helpers.checked(item['do_notify']),
                "keep_history": helpers.checked(item['keep_history'])
            }

            rows.append(row)

        dict = {
            'recordsFiltered': query['filteredCount'],
            'recordsTotal': query['totalCount'],
            'data': rows,
            'draw': query['draw']
        }

        return dict
Exemplo n.º 37
0
def main():
    """
    Plex:CS application entry point. Parses arguments, setups encoding and
    initializes the application.
    """

    # Fixed paths to Plex:CS
    if hasattr(sys, 'frozen'):
        plexcs.FULL_PATH = os.path.abspath(sys.executable)
    else:
        plexcs.FULL_PATH = os.path.abspath(__file__)

    plexcs.PROG_DIR = os.path.dirname(plexcs.FULL_PATH)
    plexcs.ARGS = sys.argv[1:]

    # From sickbeard
    plexcs.SYS_PLATFORM = sys.platform
    plexcs.SYS_ENCODING = None

    try:
        locale.setlocale(locale.LC_ALL, "")
        plexcs.SYS_ENCODING = locale.getpreferredencoding()
    except (locale.Error, IOError):
        pass

    # for OSes that are poorly configured I'll just force UTF-8
    if not plexcs.SYS_ENCODING or plexcs.SYS_ENCODING in ('ANSI_X3.4-1968',
                                                          'US-ASCII', 'ASCII'):
        plexcs.SYS_ENCODING = 'UTF-8'

    # Set up and gather command line arguments
    parser = argparse.ArgumentParser(
        description=
        'A Python based monitoring and tracking tool for Plex Media Server.')

    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        help='Increase console logging verbosity')
    parser.add_argument('-q',
                        '--quiet',
                        action='store_true',
                        help='Turn off console logging')
    parser.add_argument('-d',
                        '--daemon',
                        action='store_true',
                        help='Run as a daemon')
    parser.add_argument('-p',
                        '--port',
                        type=int,
                        help='Force Plex:CS to run on a specified port')
    parser.add_argument(
        '--datadir', help='Specify a directory where to store your data files')
    parser.add_argument('--config', help='Specify a config file to use')
    parser.add_argument('--nolaunch',
                        action='store_true',
                        help='Prevent browser from launching on startup')
    parser.add_argument(
        '--pidfile',
        help='Create a pid file (only relevant when running as a daemon)')

    args = parser.parse_args()

    if args.verbose:
        plexcs.VERBOSE = True
    if args.quiet:
        plexcs.QUIET = True

    # Do an intial setup of the logger.
    logger.initLogger(console=not plexcs.QUIET,
                      log_dir=False,
                      verbose=plexcs.VERBOSE)

    if args.daemon:
        if sys.platform == 'win32':
            sys.stderr.write(
                "Daemonizing not supported under Windows, starting normally\n")
        else:
            plexcs.DAEMON = True
            plexcs.QUIET = True

    if args.pidfile:
        plexcs.PIDFILE = str(args.pidfile)

        # If the pidfile already exists, plexcs may still be running, so
        # exit
        if os.path.exists(plexcs.PIDFILE):
            raise SystemExit("PID file '%s' already exists. Exiting." %
                             plexcs.PIDFILE)

        # The pidfile is only useful in daemon mode, make sure we can write the
        # file properly
        if plexcs.DAEMON:
            plexcs.CREATEPID = True

            try:
                with open(plexcs.PIDFILE, 'w') as fp:
                    fp.write("pid\n")
            except IOError as e:
                raise SystemExit("Unable to write PID file: %s", e)
        else:
            logger.warn("Not running in daemon mode. PID file creation " \
                        "disabled.")

    # Determine which data directory and config file to use
    if args.datadir:
        plexcs.DATA_DIR = args.datadir
    else:
        plexcs.DATA_DIR = plexcs.PROG_DIR

    if args.config:
        config_file = args.config
    else:
        config_file = os.path.join(plexcs.DATA_DIR, 'config.ini')

    # Try to create the DATA_DIR if it doesn't exist
    if not os.path.exists(plexcs.DATA_DIR):
        try:
            os.makedirs(plexcs.DATA_DIR)
        except OSError:
            raise SystemExit('Could not create data directory: ' +
                             plexcs.DATA_DIR + '. Exiting....')

    # Make sure the DATA_DIR is writeable
    if not os.access(plexcs.DATA_DIR, os.W_OK):
        raise SystemExit('Cannot write to the data directory: ' +
                         plexcs.DATA_DIR + '. Exiting...')

    # Put the database in the DATA_DIR
    plexcs.DB_FILE = os.path.join(plexcs.DATA_DIR, 'plexcs.db')

    # Read config and start logging
    plexcs.initialize(config_file)

    if plexcs.DAEMON:
        plexcs.daemonize()

    # Force the http port if neccessary
    if args.port:
        http_port = args.port
        logger.info('Using forced web server port: %i', http_port)
    else:
        http_port = int(plexcs.CONFIG.HTTP_PORT)

    # Check if pyOpenSSL is installed. It is required for certificate generation
    # and for CherryPy.
    if plexcs.CONFIG.ENABLE_HTTPS:
        try:
            import OpenSSL
        except ImportError:
            logger.warn("The pyOpenSSL module is missing. Install this " \
                        "module to enable HTTPS. HTTPS will be disabled.")
            plexcs.CONFIG.ENABLE_HTTPS = False

    # Try to start the server. Will exit here is address is already in use.
    web_config = {
        'http_port': http_port,
        'http_host': plexcs.CONFIG.HTTP_HOST,
        'http_root': plexcs.CONFIG.HTTP_ROOT,
        'http_proxy': plexcs.CONFIG.HTTP_PROXY,
        'enable_https': plexcs.CONFIG.ENABLE_HTTPS,
        'https_cert': plexcs.CONFIG.HTTPS_CERT,
        'https_key': plexcs.CONFIG.HTTPS_KEY,
        'http_username': plexcs.CONFIG.HTTP_USERNAME,
        'http_password': plexcs.CONFIG.HTTP_PASSWORD,
    }
    webstart.initialize(web_config)

    # Start the background threads
    plexcs.start()

    # Open connection for websocket
    if plexcs.CONFIG.MONITORING_USE_WEBSOCKET:
        try:
            web_socket.start_thread()
        except:
            logger.warn(u"Websocket :: Unable to open connection.")
            # Fallback to polling
            plexcs.POLLING_FAILOVER = True
            plexcs.initialize_scheduler()

    # Open webbrowser
    if plexcs.CONFIG.LAUNCH_BROWSER and not args.nolaunch:
        plexcs.launch_browser(plexcs.CONFIG.HTTP_HOST, http_port,
                              plexcs.CONFIG.HTTP_ROOT)

    # Wait endlessy for a signal to happen
    while True:
        if not plexcs.SIGNAL:
            try:
                time.sleep(1)
            except KeyboardInterrupt:
                plexcs.SIGNAL = 'shutdown'
        else:
            logger.info('Received signal: %s', plexcs.SIGNAL)

            if plexcs.SIGNAL == 'shutdown':
                plexcs.shutdown()
            elif plexcs.SIGNAL == 'restart':
                plexcs.shutdown(restart=True)
            else:
                plexcs.shutdown(restart=True, update=True)

            plexcs.SIGNAL = None
Exemplo n.º 38
0
    def make_request(self,
                     uri=None, proto='HTTP',
                     request_type='GET',
                     headers=None,
                     output_format='raw',
                     return_type=False,
                     no_token=False):

        valid_request_types = ['GET', 'POST', 'PUT', 'DELETE']

        if request_type.upper() not in valid_request_types:
            logger.debug(u"HTTP request made but unsupported request type given.")
            return None

        if uri:
            if proto.upper() == 'HTTPS':
                if not self.ssl_verify and hasattr(ssl, '_create_unverified_context'):
                    context = ssl._create_unverified_context()
                    handler = HTTPSConnection(host=self.host, port=self.port, timeout=10, context=context)
                    logger.warn(u"Plex:CS HTTP Handler :: Unverified HTTPS request made. This connection is not secure.")
                else:
                    handler = HTTPSConnection(host=self.host, port=self.port, timeout=10)
            else:
                handler = HTTPConnection(host=self.host, port=self.port, timeout=10)

            token_string = ''
            if not no_token:
                if uri.find('?') > 0:
                    token_string = '&X-Plex-Token=' + self.token
                else:
                    token_string = '?X-Plex-Token=' + self.token

            try:
                if headers:
                    handler.request(request_type, uri + token_string, headers=headers)
                else:
                    handler.request(request_type, uri + token_string)
                response = handler.getresponse()
                request_status = response.status
                request_content = response.read()
                content_type = response.getheader('content-type')
            except IOError as e:
                logger.warn(u"Failed to access uri endpoint %s with error %s" % (uri, e))
                return None
            except Exception as e:
                logger.warn(u"Failed to access uri endpoint %s. Is your server maybe accepting SSL connections only? %s" % (uri, e))
                return None
            except:
                logger.warn(u"Failed to access uri endpoint %s with Uncaught exception." % uri)
                return None

            if request_status == 200:
                try:
                    if output_format == 'dict':
                        output = helpers.convert_xml_to_dict(request_content)
                    elif output_format == 'json':
                        output = helpers.convert_xml_to_json(request_content)
                    elif output_format == 'xml':
                        output = helpers.parse_xml(request_content)
                    else:
                        output = request_content

                    if return_type:
                        return output, content_type

                    return output

                except Exception as e:
                    logger.warn(u"Failed format response from uri %s to %s error %s" % (uri, output_format, e))
                    return None

            else:
                logger.warn(u"Failed to access uri endpoint %s. Status code %r" % (uri, request_status))
                return None
        else:
            logger.debug(u"HTTP request made but no enpoint given.")
            return None
Exemplo n.º 39
0
def main():
    """
    Plex:CS application entry point. Parses arguments, setups encoding and
    initializes the application.
    """

    # Fixed paths to Plex:CS
    if hasattr(sys, 'frozen'):
        plexcs.FULL_PATH = os.path.abspath(sys.executable)
    else:
        plexcs.FULL_PATH = os.path.abspath(__file__)

    plexcs.PROG_DIR = os.path.dirname(plexcs.FULL_PATH)
    plexcs.ARGS = sys.argv[1:]

    # From sickbeard
    plexcs.SYS_PLATFORM = sys.platform
    plexcs.SYS_ENCODING = None

    try:
        locale.setlocale(locale.LC_ALL, "")
        plexcs.SYS_ENCODING = locale.getpreferredencoding()
    except (locale.Error, IOError):
        pass

    # for OSes that are poorly configured I'll just force UTF-8
    if not plexcs.SYS_ENCODING or plexcs.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
        plexcs.SYS_ENCODING = 'UTF-8'

    # Set up and gather command line arguments
    parser = argparse.ArgumentParser(
        description='A Python based monitoring and tracking tool for Plex Media Server.')

    parser.add_argument(
        '-v', '--verbose', action='store_true', help='Increase console logging verbosity')
    parser.add_argument(
        '-q', '--quiet', action='store_true', help='Turn off console logging')
    parser.add_argument(
        '-d', '--daemon', action='store_true', help='Run as a daemon')
    parser.add_argument(
        '-p', '--port', type=int, help='Force Plex:CS to run on a specified port')
    parser.add_argument(
        '--datadir', help='Specify a directory where to store your data files')
    parser.add_argument('--config', help='Specify a config file to use')
    parser.add_argument('--nolaunch', action='store_true',
                        help='Prevent browser from launching on startup')
    parser.add_argument(
        '--pidfile', help='Create a pid file (only relevant when running as a daemon)')

    args = parser.parse_args()

    if args.verbose:
        plexcs.VERBOSE = True
    if args.quiet:
        plexcs.QUIET = True

    # Do an intial setup of the logger.
    logger.initLogger(console=not plexcs.QUIET, log_dir=False,
                      verbose=plexcs.VERBOSE)

    if args.daemon:
        if sys.platform == 'win32':
            sys.stderr.write(
                "Daemonizing not supported under Windows, starting normally\n")
        else:
            plexcs.DAEMON = True
            plexcs.QUIET = True

    if args.pidfile:
        plexcs.PIDFILE = str(args.pidfile)

        # If the pidfile already exists, plexcs may still be running, so
        # exit
        if os.path.exists(plexcs.PIDFILE):
            raise SystemExit("PID file '%s' already exists. Exiting." %
                             plexcs.PIDFILE)

        # The pidfile is only useful in daemon mode, make sure we can write the
        # file properly
        if plexcs.DAEMON:
            plexcs.CREATEPID = True

            try:
                with open(plexcs.PIDFILE, 'w') as fp:
                    fp.write("pid\n")
            except IOError as e:
                raise SystemExit("Unable to write PID file: %s", e)
        else:
            logger.warn("Not running in daemon mode. PID file creation " \
                        "disabled.")

    # Determine which data directory and config file to use
    if args.datadir:
        plexcs.DATA_DIR = args.datadir
    else:
        plexcs.DATA_DIR = plexcs.PROG_DIR

    if args.config:
        config_file = args.config
    else:
        config_file = os.path.join(plexcs.DATA_DIR, 'config.ini')

    # Try to create the DATA_DIR if it doesn't exist
    if not os.path.exists(plexcs.DATA_DIR):
        try:
            os.makedirs(plexcs.DATA_DIR)
        except OSError:
            raise SystemExit(
                'Could not create data directory: ' + plexcs.DATA_DIR + '. Exiting....')

    # Make sure the DATA_DIR is writeable
    if not os.access(plexcs.DATA_DIR, os.W_OK):
        raise SystemExit(
            'Cannot write to the data directory: ' + plexcs.DATA_DIR + '. Exiting...')

    # Put the database in the DATA_DIR
    plexcs.DB_FILE = os.path.join(plexcs.DATA_DIR, 'plexcs.db')

    # Read config and start logging
    plexcs.initialize(config_file)

    if plexcs.DAEMON:
        plexcs.daemonize()

    # Force the http port if neccessary
    if args.port:
        http_port = args.port
        logger.info('Using forced web server port: %i', http_port)
    else:
        http_port = int(plexcs.CONFIG.HTTP_PORT)

    # Check if pyOpenSSL is installed. It is required for certificate generation
    # and for CherryPy.
    if plexcs.CONFIG.ENABLE_HTTPS:
        try:
            import OpenSSL
        except ImportError:
            logger.warn("The pyOpenSSL module is missing. Install this " \
                        "module to enable HTTPS. HTTPS will be disabled.")
            plexcs.CONFIG.ENABLE_HTTPS = False

    # Try to start the server. Will exit here is address is already in use.
    web_config = {
        'http_port': http_port,
        'http_host': plexcs.CONFIG.HTTP_HOST,
        'http_root': plexcs.CONFIG.HTTP_ROOT,
        'http_proxy': plexcs.CONFIG.HTTP_PROXY,
        'enable_https': plexcs.CONFIG.ENABLE_HTTPS,
        'https_cert': plexcs.CONFIG.HTTPS_CERT,
        'https_key': plexcs.CONFIG.HTTPS_KEY,
        'http_username': plexcs.CONFIG.HTTP_USERNAME,
        'http_password': plexcs.CONFIG.HTTP_PASSWORD,
    }
    webstart.initialize(web_config)

    # Start the background threads
    plexcs.start()

    # Open connection for websocket
    if plexcs.CONFIG.MONITORING_USE_WEBSOCKET:
        try:
            web_socket.start_thread()
        except:
            logger.warn(u"Websocket :: Unable to open connection.")
            # Fallback to polling
            plexcs.POLLING_FAILOVER = True
            plexcs.initialize_scheduler()

    # Open webbrowser
    if plexcs.CONFIG.LAUNCH_BROWSER and not args.nolaunch:
        plexcs.launch_browser(plexcs.CONFIG.HTTP_HOST, http_port,
                              plexcs.CONFIG.HTTP_ROOT)

    # Wait endlessy for a signal to happen
    while True:
        if not plexcs.SIGNAL:
            try:
                time.sleep(1)
            except KeyboardInterrupt:
                plexcs.SIGNAL = 'shutdown'
        else:
            logger.info('Received signal: %s', plexcs.SIGNAL)

            if plexcs.SIGNAL == 'shutdown':
                plexcs.shutdown()
            elif plexcs.SIGNAL == 'restart':
                plexcs.shutdown(restart=True)
            else:
                plexcs.shutdown(restart=True, update=True)

            plexcs.SIGNAL = None
Exemplo n.º 40
0
def initialize(options):

    # HTTPS stuff stolen from sickbeard
    enable_https = options['enable_https']
    https_cert = options['https_cert']
    https_key = options['https_key']

    if enable_https:
        # If either the HTTPS certificate or key do not exist, try to make
        # self-signed ones.
        if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)):
            if not create_https_certificates(https_cert, https_key):
                logger.warn("Unable to create certificate and key. Disabling " \
                    "HTTPS")
                enable_https = False

        if not (os.path.exists(https_cert) and os.path.exists(https_key)):
            logger.warn("Disabled HTTPS because of missing certificate and " \
                "key.")
            enable_https = False

    options_dict = {
        'server.socket_port': options['http_port'],
        'server.socket_host': options['http_host'],
        'server.thread_pool': 10,
        'tools.encode.on': True,
        'tools.encode.encoding': 'utf-8',
        'tools.decode.on': True,
        'log.screen': False,
        'engine.autoreload.on': False,
    }

    if enable_https:
        options_dict['server.ssl_certificate'] = https_cert
        options_dict['server.ssl_private_key'] = https_key
        protocol = "https"
    else:
        protocol = "http"

    logger.info("Starting Plex:CS web server on %s://%s:%d/", protocol,
        options['http_host'], options['http_port'])
    cherrypy.config.update(options_dict)

    conf = {
        '/': {
            'tools.staticdir.root': os.path.join(plexcs.PROG_DIR, 'data'),
            'tools.proxy.on': options['http_proxy']  # pay attention to X-Forwarded-Proto header
        },
        '/interfaces': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': "interfaces"
        },
        '/images': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': "images"
        },
        '/css': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': "css"
        },
        '/js': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': "js"
        },
        '/favicon.ico': {
            'tools.staticfile.on': True,
            'tools.staticfile.filename': os.path.join(os.path.abspath(
                os.curdir), "images" + os.sep + "favicon.ico")
        },
        '/cache': {
            'tools.staticdir.on': True,
            'tools.staticdir.dir': plexcs.CONFIG.CACHE_DIR
        }
    }

    if options['http_password']:
        logger.info("Web server authentication is enabled, username is '%s'", options['http_username'])

        conf['/'].update({
            'tools.auth_basic.on': True,
            'tools.auth_basic.realm': 'Plex:CS web server',
            'tools.auth_basic.checkpassword': cherrypy.lib.auth_basic.checkpassword_dict({
                options['http_username']: options['http_password']
            })
        })
        conf['/api'] = {'tools.auth_basic.on': False}

    # Prevent time-outs
    cherrypy.engine.timeout_monitor.unsubscribe()
    cherrypy.tree.mount(WebInterface(), str(options['http_root']), config=conf)

    try:
        cherrypy.process.servers.check_port(str(options['http_host']), options['http_port'])
        cherrypy.server.start()
    except IOError:
        sys.stderr.write('Failed to start on port: %i. Is something else running?\n' % (options['http_port']))
        sys.exit(1)

    cherrypy.server.wait()