Example #1
0
def on_connect():
    if plexpy.PLEX_SERVER_UP is None:
        plexpy.PLEX_SERVER_UP = True

    if not plexpy.PLEX_SERVER_UP:
        logger.info(u"Tautulli WebSocket :: The Plex Media Server is back up.")
        plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intup'})
        plexpy.PLEX_SERVER_UP = True

    plexpy.initialize_scheduler()
Example #2
0
    def configUpdate(self, **kwargs):
        # Handle the variable config options. Note - keys with False values aren't getting passed

        checked_configs = [
            "launch_browser", "enable_https", "api_enabled", "freeze_db", "growl_enabled",
            "prowl_enabled", "xbmc_enabled", "lms_enabled",
            "plex_enabled", "nma_enabled", "pushalot_enabled",
            "synoindex_enabled", "pushover_enabled", "pushbullet_enabled",
            "subsonic_enabled", "twitter_enabled", "osx_notify_enabled",
            "boxcar_enabled", "mpc_enabled", "email_enabled", "email_tls",
            "grouping_global_history", "grouping_user_history", "grouping_charts"
        ]
        for checked_config in checked_configs:
            if checked_config not in kwargs:
                # checked items should be zero or one. if they were not sent then the item was not checked
                kwargs[checked_config] = 0

        # Write Plex token to the config
        if (not plexpy.CONFIG.PMS_TOKEN or plexpy.CONFIG.PMS_TOKEN == '' \
                or kwargs['pms_username'] != plexpy.CONFIG.PMS_USERNAME) \
                and (kwargs['pms_username'] != '' or kwargs['pms_password'] != ''):

            plex_tv = plextv.PlexTV(kwargs['pms_username'], kwargs['pms_password'])
            token = plex_tv.get_token()

            if token:
                kwargs['pms_token'] = token
                logger.info('Plex.tv token sucessfully written to config.')
            else:
                logger.warn('Unable to write Plex.tv token to config.')

        # Clear Plex token if username or password set to blank
        if kwargs['pms_username'] == '' or kwargs['pms_password'] == '':
            kwargs['pms_token'] = ''

        # If passwords exists in config, do not overwrite when blank value received
        if kwargs['http_password'] == '    ' and plexpy.CONFIG.HTTP_PASSWORD != '':
            kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD
        if kwargs['pms_password'] == '    ' and plexpy.CONFIG.PMS_PASSWORD != '':
            kwargs['pms_password'] = plexpy.CONFIG.PMS_PASSWORD

        for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        # Reconfigure scheduler
        plexpy.initialize_scheduler()

        raise cherrypy.HTTPRedirect("config")
Example #3
0
def on_disconnect():
    if plexpy.PLEX_SERVER_UP is None:
        plexpy.PLEX_SERVER_UP = False

    if plexpy.PLEX_SERVER_UP:
        logger.info("Tautulli WebSocket :: Unable to get a response from the server, Plex server is down.")
        plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intdown'})
        plexpy.PLEX_SERVER_UP = False

    activity_processor.ActivityProcessor().set_temp_stopped()
    plexpy.initialize_scheduler()
Example #4
0
def on_connect():
    if plexpy.PLEX_SERVER_UP is None:
        plexpy.PLEX_SERVER_UP = True

    if not plexpy.PLEX_SERVER_UP:
        logger.info("Tautulli WebSocket :: The Plex Media Server is back up.")
        plexpy.NOTIFY_QUEUE.put({'notify_action': 'on_intup'})
        plexpy.PLEX_SERVER_UP = True

    plexpy.initialize_scheduler()
    if plexpy.CONFIG.WEBSOCKET_MONITOR_PING_PONG:
        send_ping()
Example #5
0
    def configUpdate(self, **kwargs):
        # Handle the variable config options. Note - keys with False values aren't getting passed

        checked_configs = [
            "launch_browser", "enable_https", "api_enabled", "freeze_db", "growl_enabled",
            "prowl_enabled", "xbmc_enabled", "check_github",
            "plex_enabled", "nma_enabled", "pushalot_enabled",
            "pushover_enabled", "pushbullet_enabled",
            "twitter_enabled", "osx_notify_enabled",
            "boxcar_enabled", "email_enabled", "email_tls",
            "grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif", "pms_ssl",
            "tv_notify_enable", "movie_notify_enable", "music_notify_enable",
            "tv_notify_on_start", "movie_notify_on_start", "music_notify_on_start",
            "tv_notify_on_stop", "movie_notify_on_stop", "music_notify_on_stop",
            "tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup",
            "ip_logging_enable", "video_logging_enable", "music_logging_enable", "pms_is_remote"
        ]
        for checked_config in checked_configs:
            if checked_config not in kwargs:
                # checked items should be zero or one. if they were not sent then the item was not checked
                kwargs[checked_config] = 0

        # If http password exists in config, do not overwrite when blank value received
        if 'http_password' in kwargs:
            if kwargs['http_password'] == '    ' and plexpy.CONFIG.HTTP_PASSWORD != '':
                kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD

        for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        # Reconfigure scheduler
        plexpy.initialize_scheduler()

        plextv.get_real_pms_url()

        # Refresh users table. Probably shouldn't do this on every config save, will improve this later.
        threading.Thread(target=plextv.refresh_users).start()

        raise cherrypy.HTTPRedirect("settings")
Example #6
0
def on_connect():
    if plexpy.PLEX_SERVER_UP is None:
        plexpy.PLEX_SERVER_UP = True

    if not plexpy.PLEX_SERVER_UP:
        logger.info("Tautulli WebSocket :: The Plex Media Server is back up.")
        plexpy.PLEX_SERVER_UP = True

        if activity_handler.ACTIVITY_SCHED.get_job('on_intdown'):
            logger.debug(
                "Tautulli WebSocket :: Cancelling scheduled Plex server down callback."
            )
            activity_handler.schedule_callback('on_intdown', remove_job=True)
        else:
            on_intup()

    plexpy.initialize_scheduler()
    if plexpy.CONFIG.WEBSOCKET_MONITOR_PING_PONG:
        send_ping()
Example #7
0
def on_disconnect():
    if plexpy.PLEX_SERVER_UP is None:
        plexpy.PLEX_SERVER_UP = False

    if plexpy.PLEX_SERVER_UP:
        logger.info(
            "Tautulli WebSocket :: Unable to get a response from the server, Plex server is down."
        )
        plexpy.PLEX_SERVER_UP = False

        logger.debug(
            "Tautulli WebSocket :: Scheduling Plex server down callback in %d seconds.",
            plexpy.CONFIG.NOTIFY_SERVER_CONNECTION_THRESHOLD)
        activity_handler.schedule_callback(
            'on_intdown',
            func=on_intdown,
            seconds=plexpy.CONFIG.NOTIFY_SERVER_CONNECTION_THRESHOLD)

    activity_processor.ActivityProcessor().set_temp_stopped()
    plexpy.initialize_scheduler()
Example #8
0
    def configUpdate(self, **kwargs):
        # Handle the variable config options. Note - keys with False values aren't getting passed

        checked_configs = [
            "launch_browser", "enable_https", "api_enabled", "freeze_db",
            "growl_enabled", "prowl_enabled", "xbmc_enabled", "plex_enabled",
            "nma_enabled", "pushalot_enabled", "pushover_enabled",
            "pushbullet_enabled", "twitter_enabled", "osx_notify_enabled",
            "boxcar_enabled", "email_enabled", "email_tls",
            "grouping_global_history", "grouping_user_history",
            "grouping_charts", "pms_use_bif"
        ]
        for checked_config in checked_configs:
            if checked_config not in kwargs:
                # checked items should be zero or one. if they were not sent then the item was not checked
                kwargs[checked_config] = 0

        # If http password exists in config, do not overwrite when blank value received
        if kwargs[
                'http_password'] == '    ' and plexpy.CONFIG.HTTP_PASSWORD != '':
            kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD

        for plain_config, use_config in [(x[4:], x) for x in kwargs
                                         if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        # Check if we have our users table
        plexwatch.check_db_tables()

        # Reconfigure scheduler
        plexpy.initialize_scheduler()

        raise cherrypy.HTTPRedirect("config")
Example #9
0
    def configUpdate(self, **kwargs):
        # Handle the variable config options. Note - keys with False values aren't getting passed

        checked_configs = [
            "launch_browser", "enable_https", "api_enabled", "freeze_db", "growl_enabled",
            "prowl_enabled", "xbmc_enabled",
            "plex_enabled", "nma_enabled", "pushalot_enabled",
            "pushover_enabled", "pushbullet_enabled",
            "twitter_enabled", "osx_notify_enabled",
            "boxcar_enabled", "email_enabled", "email_tls",
            "grouping_global_history", "grouping_user_history", "grouping_charts", "pms_use_bif"
        ]
        for checked_config in checked_configs:
            if checked_config not in kwargs:
                # checked items should be zero or one. if they were not sent then the item was not checked
                kwargs[checked_config] = 0

        # If http password exists in config, do not overwrite when blank value received
        if kwargs['http_password'] == '    ' and plexpy.CONFIG.HTTP_PASSWORD != '':
            kwargs['http_password'] = plexpy.CONFIG.HTTP_PASSWORD

        for plain_config, use_config in [(x[4:], x) for x in kwargs if x.startswith('use_')]:
            # the use prefix is fairly nice in the html, but does not match the actual config
            kwargs[plain_config] = kwargs[use_config]
            del kwargs[use_config]

        plexpy.CONFIG.process_kwargs(kwargs)

        # Write the config
        plexpy.CONFIG.write()

        # Check if we have our users table
        plexwatch.check_db_tables()

        # Reconfigure scheduler
        plexpy.initialize_scheduler()

        raise cherrypy.HTTPRedirect("config")
Example #10
0
def import_from_plexwatch(database=None, table_name=None, import_ignore_interval=0):

    try:
        connection = sqlite3.connect(database, timeout=20)
        connection.row_factory = sqlite3.Row
    except sqlite3.OperationalError:
        logger.error(u"PlexPy Importer :: Invalid filename.")
        return None
    except ValueError:
        logger.error(u"PlexPy Importer :: Invalid filename.")
        return None

    try:
        connection.execute('SELECT ratingKey from %s' % table_name)
    except sqlite3.OperationalError:
        logger.error(u"PlexPy Importer :: Database specified does not contain the required fields.")
        return None

    logger.debug(u"PlexPy Importer :: PlexWatch data import in progress...")

    logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.")
    plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
                        hours=0, minutes=0, seconds=0)
    plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
                        hours=0, minutes=0, seconds=0)
    plexpy.schedule_job(activity_pinger.check_server_response, 'Check for Plex remote access',
                        hours=0, minutes=0, seconds=0)

    ap = activity_processor.ActivityProcessor()
    user_data = users.Users()

    # Get the latest friends list so we can pull user id's
    try:
        plextv.refresh_users()
    except:
        logger.debug(u"PlexPy Importer :: Unable to refresh the users list. Aborting import.")
        return None

    query = 'SELECT time AS started, ' \
            'stopped, ' \
            'cast(ratingKey as text) AS rating_key, ' \
            'null AS user_id, ' \
            'user, ' \
            'ip_address, ' \
            'paused_counter, ' \
            'platform AS player, ' \
            'null AS platform, ' \
            'null as machine_id, ' \
            'parentRatingKey as parent_rating_key, ' \
            'grandparentRatingKey as grandparent_rating_key, ' \
            'null AS media_type, ' \
            'null AS view_offset, ' \
            'xml, ' \
            'rating as content_rating,' \
            'summary,' \
            'title AS full_title,' \
            '(case when orig_title_ep = "" then orig_title else ' \
            'orig_title_ep end) as title,' \
            '(case when orig_title_ep != "" then orig_title else ' \
            'null end) as grandparent_title ' \
            'FROM ' + table_name + ' ORDER BY id'

    result = connection.execute(query)

    for row in result:
        # Extract the xml from the Plexwatch db xml field.
        extracted_xml = extract_plexwatch_xml(row['xml'])

        # If we get back None from our xml extractor skip over the record and log error.
        if not extracted_xml:
            logger.error(u"PlexPy Importer :: Skipping record with ratingKey %s due to malformed xml."
                         % str(row['rating_key']))
            continue

        # Skip line if we don't have a ratingKey to work with
        if not row['rating_key']:
            logger.error(u"PlexPy Importer :: Skipping record due to null ratingKey.")
            continue

        # If the user_id no longer exists in the friends list, pull it from the xml.
        if user_data.get_user_id(user=row['user']):
            user_id = user_data.get_user_id(user=row['user'])
        else:
            user_id = extracted_xml['user_id']

        session_history = {'started': row['started'],
                           'stopped': row['stopped'],
                           'rating_key': row['rating_key'],
                           'title': row['title'],
                           'parent_title': extracted_xml['parent_title'],
                           'grandparent_title': row['grandparent_title'],
                           'user_id': user_id,
                           'user': row['user'],
                           'ip_address': row['ip_address'],
                           'paused_counter': row['paused_counter'],
                           'player': row['player'],
                           'platform': extracted_xml['platform'],
                           'machine_id': extracted_xml['machine_id'],
                           'parent_rating_key': row['parent_rating_key'],
                           'grandparent_rating_key': row['grandparent_rating_key'],
                           'media_type': extracted_xml['media_type'],
                           'view_offset': extracted_xml['view_offset'],
                           'video_decision': extracted_xml['video_decision'],
                           'audio_decision': extracted_xml['audio_decision'],
                           'duration': extracted_xml['duration'],
                           'width': extracted_xml['width'],
                           'height': extracted_xml['height'],
                           'container': extracted_xml['container'],
                           'video_codec': extracted_xml['video_codec'],
                           'audio_codec': extracted_xml['audio_codec'],
                           'bitrate': extracted_xml['bitrate'],
                           'video_resolution': extracted_xml['video_resolution'],
                           'video_framerate': extracted_xml['video_framerate'],
                           'aspect_ratio': extracted_xml['aspect_ratio'],
                           'audio_channels': extracted_xml['audio_channels'],
                           'transcode_protocol': extracted_xml['transcode_protocol'],
                           'transcode_container': extracted_xml['transcode_container'],
                           'transcode_video_codec': extracted_xml['transcode_video_codec'],
                           'transcode_audio_codec': extracted_xml['transcode_audio_codec'],
                           'transcode_audio_channels': extracted_xml['transcode_audio_channels'],
                           'transcode_width': extracted_xml['transcode_width'],
                           'transcode_height': extracted_xml['transcode_height']
                           }

        session_history_metadata = {'rating_key': helpers.latinToAscii(row['rating_key']),
                                    'parent_rating_key': row['parent_rating_key'],
                                    'grandparent_rating_key': row['grandparent_rating_key'],
                                    'title': row['title'],
                                    'parent_title': extracted_xml['parent_title'],
                                    'grandparent_title': row['grandparent_title'],
                                    'media_index': extracted_xml['media_index'],
                                    'parent_media_index': extracted_xml['parent_media_index'],
                                    'thumb': extracted_xml['thumb'],
                                    'parent_thumb': extracted_xml['parent_thumb'],
                                    'grandparent_thumb': extracted_xml['grandparent_thumb'],
                                    'art': extracted_xml['art'],
                                    'media_type': extracted_xml['media_type'],
                                    'year': extracted_xml['year'],
                                    'originally_available_at': extracted_xml['originally_available_at'],
                                    'added_at': extracted_xml['added_at'],
                                    'updated_at': extracted_xml['updated_at'],
                                    'last_viewed_at': extracted_xml['last_viewed_at'],
                                    'content_rating': row['content_rating'],
                                    'summary': row['summary'],
                                    'tagline': extracted_xml['tagline'],
                                    'rating': extracted_xml['rating'],
                                    'duration': extracted_xml['duration'],
                                    'guid': extracted_xml['guid'],
                                    'section_id': extracted_xml['section_id'],
                                    'directors': extracted_xml['directors'],
                                    'writers': extracted_xml['writers'],
                                    'actors': extracted_xml['actors'],
                                    'genres': extracted_xml['genres'],
                                    'studio': extracted_xml['studio'],
                                    'full_title': row['full_title']
                                    }

        # On older versions of PMS, "clip" items were still classified as "movie" and had bad ratingKey values
        # Just make sure that the ratingKey is indeed an integer
        if session_history_metadata['rating_key'].isdigit():
            ap.write_session_history(session=session_history,
                                     import_metadata=session_history_metadata,
                                     is_import=True,
                                     import_ignore_interval=import_ignore_interval)
        else:
            logger.debug(u"PlexPy Importer :: Item has bad rating_key: %s" % session_history_metadata['rating_key'])

    logger.debug(u"PlexPy Importer :: PlexWatch data import complete.")
    import_users()

    logger.debug(u"PlexPy Importer :: Re-enabling monitoring.")
    plexpy.initialize_scheduler()
Example #11
0
def import_from_plexwatch(database=None, table_name=None, import_ignore_interval=0):

    try:
        connection = sqlite3.connect(database, timeout=20)
        connection.row_factory = sqlite3.Row
    except sqlite3.OperationalError:
        logger.error("PlexPy Importer :: Invalid filename.")
        return None
    except ValueError:
        logger.error("PlexPy Importer :: Invalid filename.")
        return None

    try:
        connection.execute("SELECT ratingKey from %s" % table_name)
    except sqlite3.OperationalError:
        logger.error("PlexPy Importer :: Database specified does not contain the required fields.")
        return None

    logger.debug(u"PlexPy Importer :: PlexWatch data import in progress...")

    logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.")
    plexpy.schedule_job(
        activity_pinger.check_active_sessions, "Check for active sessions", hours=0, minutes=0, seconds=0
    )

    ap = activity_processor.ActivityProcessor()
    user_data = users.Users()

    # Get the latest friends list so we can pull user id's
    try:
        plextv.refresh_users()
    except:
        logger.debug(u"PlexPy Importer :: Unable to refresh the users list. Aborting import.")
        return None

    query = (
        "SELECT time AS started, "
        "stopped, "
        "cast(ratingKey as text) AS rating_key, "
        "null AS user_id, "
        "user, "
        "ip_address, "
        "paused_counter, "
        "platform AS player, "
        "null AS platform, "
        "null as machine_id, "
        "parentRatingKey as parent_rating_key, "
        "grandparentRatingKey as grandparent_rating_key, "
        "null AS media_type, "
        "null AS view_offset, "
        "xml, "
        "rating as content_rating,"
        "summary,"
        "title AS full_title,"
        '(case when orig_title_ep = "" then orig_title else '
        "orig_title_ep end) as title,"
        '(case when orig_title_ep != "" then orig_title else '
        "null end) as grandparent_title "
        "FROM " + table_name + " ORDER BY id"
    )

    result = connection.execute(query)

    for row in result:
        # Extract the xml from the Plexwatch db xml field.
        extracted_xml = extract_plexwatch_xml(row["xml"])

        # If we get back None from our xml extractor skip over the record and log error.
        if not extracted_xml:
            logger.error(
                u"PlexPy Importer :: Skipping line with ratingKey %s due to malformed xml." % str(row["rating_key"])
            )
            continue

        # If the user_id no longer exists in the friends list, pull it from the xml.
        if user_data.get_user_id(user=row["user"]):
            user_id = user_data.get_user_id(user=row["user"])
        else:
            user_id = extracted_xml["user_id"]

        session_history = {
            "started": row["started"],
            "stopped": row["stopped"],
            "rating_key": row["rating_key"],
            "title": row["title"],
            "parent_title": extracted_xml["parent_title"],
            "grandparent_title": row["grandparent_title"],
            "user_id": user_id,
            "user": row["user"],
            "ip_address": row["ip_address"],
            "paused_counter": row["paused_counter"],
            "player": row["player"],
            "platform": extracted_xml["platform"],
            "machine_id": extracted_xml["machine_id"],
            "parent_rating_key": row["parent_rating_key"],
            "grandparent_rating_key": row["grandparent_rating_key"],
            "media_type": extracted_xml["media_type"],
            "view_offset": extracted_xml["view_offset"],
            "video_decision": extracted_xml["video_decision"],
            "audio_decision": extracted_xml["audio_decision"],
            "duration": extracted_xml["duration"],
            "width": extracted_xml["width"],
            "height": extracted_xml["height"],
            "container": extracted_xml["container"],
            "video_codec": extracted_xml["video_codec"],
            "audio_codec": extracted_xml["audio_codec"],
            "bitrate": extracted_xml["bitrate"],
            "video_resolution": extracted_xml["video_resolution"],
            "video_framerate": extracted_xml["video_framerate"],
            "aspect_ratio": extracted_xml["aspect_ratio"],
            "audio_channels": extracted_xml["audio_channels"],
            "transcode_protocol": extracted_xml["transcode_protocol"],
            "transcode_container": extracted_xml["transcode_container"],
            "transcode_video_codec": extracted_xml["transcode_video_codec"],
            "transcode_audio_codec": extracted_xml["transcode_audio_codec"],
            "transcode_audio_channels": extracted_xml["transcode_audio_channels"],
            "transcode_width": extracted_xml["transcode_width"],
            "transcode_height": extracted_xml["transcode_height"],
        }

        session_history_metadata = {
            "rating_key": helpers.latinToAscii(row["rating_key"]),
            "parent_rating_key": row["parent_rating_key"],
            "grandparent_rating_key": row["grandparent_rating_key"],
            "title": row["title"],
            "parent_title": extracted_xml["parent_title"],
            "grandparent_title": row["grandparent_title"],
            "index": extracted_xml["media_index"],
            "parent_index": extracted_xml["parent_media_index"],
            "thumb": extracted_xml["thumb"],
            "parent_thumb": extracted_xml["parent_thumb"],
            "grandparent_thumb": extracted_xml["grandparent_thumb"],
            "art": extracted_xml["art"],
            "media_type": extracted_xml["media_type"],
            "year": extracted_xml["year"],
            "originally_available_at": extracted_xml["originally_available_at"],
            "added_at": extracted_xml["added_at"],
            "updated_at": extracted_xml["updated_at"],
            "last_viewed_at": extracted_xml["last_viewed_at"],
            "content_rating": row["content_rating"],
            "summary": row["summary"],
            "tagline": extracted_xml["tagline"],
            "rating": extracted_xml["rating"],
            "duration": extracted_xml["duration"],
            "guid": extracted_xml["guid"],
            "directors": extracted_xml["directors"],
            "writers": extracted_xml["writers"],
            "actors": extracted_xml["actors"],
            "genres": extracted_xml["genres"],
            "studio": extracted_xml["studio"],
            "full_title": row["full_title"],
        }

        # On older versions of PMS, "clip" items were still classified as "movie" and had bad ratingKey values
        # Just make sure that the ratingKey is indeed an integer
        if session_history_metadata["rating_key"].isdigit():
            ap.write_session_history(
                session=session_history,
                import_metadata=session_history_metadata,
                is_import=True,
                import_ignore_interval=import_ignore_interval,
            )
        else:
            logger.debug(u"PlexPy Importer :: Item has bad rating_key: %s" % session_history_metadata["rating_key"])

    logger.debug(u"PlexPy Importer :: PlexWatch data import complete.")
    import_users()

    logger.debug(u"PlexPy Importer :: Re-enabling monitoring.")
    plexpy.initialize_scheduler()
Example #12
0
def run():
    from websocket import create_connection

    if plexpy.CONFIG.PMS_SSL and plexpy.CONFIG.PMS_URL[:5] == 'https':
        uri = plexpy.CONFIG.PMS_URL.replace('https://', 'wss://') + '/:/websockets/notifications'
        secure = ' secure'
    else:
        uri = 'ws://%s:%s/:/websockets/notifications' % (
            plexpy.CONFIG.PMS_IP,
            plexpy.CONFIG.PMS_PORT
        )
        secure = ''

    # Set authentication token (if one is available)
    if plexpy.CONFIG.PMS_TOKEN:
        header = ["X-Plex-Token: %s" % plexpy.CONFIG.PMS_TOKEN]
    else:
        header = []

    global ws_reconnect
    ws_reconnect = False
    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"PlexPy WebSocket :: Opening%s websocket, connection attempt %s." % (secure, str(reconnects + 1)))
            ws = create_connection(uri, header=header)
            reconnects = 0
            ws_connected = True
            logger.info(u"PlexPy WebSocket :: Ready")
        except IOError as e:
            logger.error(u"PlexPy 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, Exception):
            if reconnects <= 15:
                reconnects += 1

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

                logger.warn(u"PlexPy WebSocket :: Connection has closed, reconnection attempt %s." % reconnects)
                try:
                    ws = create_connection(uri, header=header)
                except IOError as e:
                    logger.info(u"PlexPy WebSocket :: %s." % e)

            else:
                ws.shutdown()
                ws_connected = False
                break

        # Check if we recieved a restart notification and close websocket connection cleanly
        if ws_reconnect:
            logger.info(u"PlexPy WebSocket :: Reconnecting websocket...")
            ws.shutdown()
            ws_connected = False
            start_thread()
    
    if not ws_connected and not ws_reconnect:
        logger.error(u"PlexPy WebSocket :: Connection unavailable, falling back to polling.")
        plexpy.POLLING_FAILOVER = True
        plexpy.initialize_scheduler()

    logger.debug(u"PlexPy WebSocket :: Leaving thread.")
Example #13
0
def import_from_plexwatch(database=None, table_name=None, import_ignore_interval=0):

    try:
        connection = sqlite3.connect(database, timeout=20)
    except sqlite3.OperationalError:
        logger.error('PlexPy Importer :: Invalid filename.')
        return None
    except ValueError:
        logger.error('PlexPy Importer :: Invalid filename.')
        return None

    try:
        connection.execute('SELECT ratingKey from %s' % table_name)
    except sqlite3.OperationalError:
        logger.error('PlexPy Importer :: Database specified does not contain the required fields.')
        return None

    logger.debug(u"PlexPy Importer :: PlexWatch data import in progress...")

    logger.debug(u"PlexPy Importer :: Disabling monitoring while import in progress.")
    plexpy.schedule_job(monitor.check_active_sessions, 'Check for active sessions', hours=0, minutes=0, seconds=0)

    monitor_processing = monitor.MonitorProcessing()
    data_factory = datafactory.DataFactory()

    # Get the latest friends list so we can pull user id's
    try:
        plextv.refresh_users()
    except:
        logger.debug(u"PlexPy Importer :: Unable to refresh the users list. Aborting import.")
        return None

    query = 'SELECT time AS started, ' \
            'stopped, ' \
            'ratingKey AS rating_key, ' \
            'null AS user_id, ' \
            'user, ' \
            'ip_address, ' \
            'paused_counter, ' \
            'platform AS player, ' \
            'null AS platform, ' \
            'null as machine_id, ' \
            'parentRatingKey as parent_rating_key, ' \
            'grandparentRatingKey as grandparent_rating_key, ' \
            'null AS media_type, ' \
            'null AS view_offset, ' \
            'xml, ' \
            'rating as content_rating,' \
            'summary,' \
            'title AS full_title,' \
            'orig_title AS title, ' \
            'orig_title_ep AS grandparent_title ' \
            'FROM ' + table_name + ' ORDER BY id'

    result = connection.execute(query)

    for row in result:
        # Extract the xml from the Plexwatch db xml field.
        extracted_xml = extract_plexwatch_xml(row[14])

        # If the user_id no longer exists in the friends list, pull it from the xml.
        if data_factory.get_user_id(user=row[4]):
            user_id = data_factory.get_user_id(user=row[4])
        else:
            user_id = extracted_xml['user_id']

        session_history = {'started': row[0],
                           'stopped': row[1],
                           'rating_key': row[2],
                           'title': extracted_xml['title'],
                           'parent_title': extracted_xml['parent_title'],
                           'grandparent_title': extracted_xml['grandparent_title'],
                           'user_id': user_id,
                           'user': row[4],
                           'ip_address': row[5],
                           'paused_counter': row[6],
                           'player': row[7],
                           'platform': extracted_xml['platform'],
                           'machine_id': extracted_xml['machine_id'],
                           'parent_rating_key': row[10],
                           'grandparent_rating_key': row[11],
                           'media_type': extracted_xml['media_type'],
                           'view_offset': extracted_xml['view_offset'],
                           'video_decision': extracted_xml['video_decision'],
                           'audio_decision': extracted_xml['audio_decision'],
                           'duration': extracted_xml['duration'],
                           'width': extracted_xml['width'],
                           'height': extracted_xml['height'],
                           'container': extracted_xml['container'],
                           'video_codec': extracted_xml['video_codec'],
                           'audio_codec': extracted_xml['audio_codec'],
                           'bitrate': extracted_xml['bitrate'],
                           'video_resolution': extracted_xml['video_resolution'],
                           'video_framerate': extracted_xml['video_framerate'],
                           'aspect_ratio': extracted_xml['aspect_ratio'],
                           'audio_channels': extracted_xml['audio_channels'],
                           'transcode_protocol': extracted_xml['transcode_protocol'],
                           'transcode_container': extracted_xml['transcode_container'],
                           'transcode_video_codec': extracted_xml['transcode_video_codec'],
                           'transcode_audio_codec': extracted_xml['transcode_audio_codec'],
                           'transcode_audio_channels': extracted_xml['transcode_audio_channels'],
                           'transcode_width': extracted_xml['transcode_width'],
                           'transcode_height': extracted_xml['transcode_height']
                           }

        session_history_metadata = {'rating_key': row[2],
                                    'parent_rating_key': row[10],
                                    'grandparent_rating_key': row[11],
                                    'title': extracted_xml['title'],
                                    'parent_title': extracted_xml['parent_title'],
                                    'grandparent_title': extracted_xml['grandparent_title'],
                                    'index': extracted_xml['media_index'],
                                    'parent_index': extracted_xml['parent_media_index'],
                                    'thumb': extracted_xml['thumb'],
                                    'parent_thumb': extracted_xml['parent_thumb'],
                                    'grandparent_thumb': extracted_xml['grandparent_thumb'],
                                    'art': extracted_xml['art'],
                                    'media_type': extracted_xml['media_type'],
                                    'year': extracted_xml['year'],
                                    'originally_available_at': extracted_xml['originally_available_at'],
                                    'added_at': extracted_xml['added_at'],
                                    'updated_at': extracted_xml['updated_at'],
                                    'last_viewed_at': extracted_xml['last_viewed_at'],
                                    'content_rating': row[15],
                                    'summary': row[16],
                                    'rating': extracted_xml['rating'],
                                    'duration': extracted_xml['duration'],
                                    'guid': extracted_xml['guid'],
                                    'directors': extracted_xml['directors'],
                                    'writers': extracted_xml['writers'],
                                    'actors': extracted_xml['actors'],
                                    'genres': extracted_xml['genres'],
                                    'studio': extracted_xml['studio'],
                                    'full_title': row[17]
                                    }

        # On older versions of PMS, "clip" items were still classified as "movie" and had bad ratingKey values
        # Just make sure that the ratingKey is indeed an integer
        if str(row[2]).isdigit():
            monitor_processing.write_session_history(session=session_history,
                                                     import_metadata=session_history_metadata,
                                                     is_import=True,
                                                     import_ignore_interval=import_ignore_interval)
        else:
            logger.debug(u"PlexPy Importer :: Item has bad rating_key: %s" % str(row[2]))

    logger.debug(u"PlexPy Importer :: PlexWatch data import complete.")

    logger.debug(u"PlexPy Importer :: Re-enabling monitoring.")
    plexpy.initialize_scheduler()
Example #14
0
def run():
    from websocket import create_connection

    if plexpy.CONFIG.PMS_SSL and plexpy.CONFIG.PMS_URL[:5] == 'https':
        uri = plexpy.CONFIG.PMS_URL.replace(
            'https://', 'wss://') + '/:/websockets/notifications'
        secure = ' secure'
    else:
        uri = 'ws://%s:%s/:/websockets/notifications' % (
            plexpy.CONFIG.PMS_IP, plexpy.CONFIG.PMS_PORT)
        secure = ''

    # Set authentication token (if one is available)
    if plexpy.CONFIG.PMS_TOKEN:
        header = ["X-Plex-Token: %s" % plexpy.CONFIG.PMS_TOKEN]
    else:
        header = []

    global ws_reconnect
    ws_reconnect = False
    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"PlexPy WebSocket :: Opening%s websocket, connection attempt %s."
                % (secure, str(reconnects + 1)))
            ws = create_connection(uri, header=header)
            reconnects = 0
            ws_connected = True
            logger.info(u"PlexPy WebSocket :: Ready")
        except IOError as e:
            logger.error(u"PlexPy 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, Exception):
            if reconnects <= 15:
                reconnects += 1

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

                logger.warn(
                    u"PlexPy WebSocket :: Connection has closed, reconnection attempt %s."
                    % reconnects)
                try:
                    ws = create_connection(uri, header=header)
                except IOError as e:
                    logger.info(u"PlexPy WebSocket :: %s." % e)

            else:
                ws.shutdown()
                ws_connected = False
                break

        # Check if we recieved a restart notification and close websocket connection cleanly
        if ws_reconnect:
            logger.info(u"PlexPy WebSocket :: Reconnecting websocket...")
            ws.shutdown()
            ws_connected = False
            start_thread()

    if not ws_connected and not ws_reconnect:
        logger.error(
            u"PlexPy WebSocket :: Connection unavailable, falling back to polling."
        )
        plexpy.POLLING_FAILOVER = True
        plexpy.initialize_scheduler()

    logger.debug(u"PlexPy WebSocket :: Leaving thread.")
Example #15
0
def main():
    """
    PlexPy application entry point. Parses arguments, setups encoding and
    initializes the application.
    """

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

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

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

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

    # for OSes that are poorly configured I'll just force UTF-8
    if not plexpy.SYS_ENCODING or plexpy.SYS_ENCODING in ('ANSI_X3.4-1968',
                                                          'US-ASCII', 'ASCII'):
        plexpy.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 PlexPy to run on a specified port')
    parser.add_argument('--dev',
                        action='store_true',
                        help='Start PlexPy in the development environment')
    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)')
    parser.add_argument(
        '--nofork',
        action='store_true',
        help='Start PlexPy as a service, do not fork when restarting')

    args = parser.parse_args()

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

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

    if args.dev:
        plexpy.DEV = True
        logger.debug(u"PlexPy is running in the dev environment.")

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

    if args.nofork:
        plexpy.NOFORK = True
        logger.info(
            "PlexPy is running as a service, it will not fork when restarted.")

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

        # If the pidfile already exists, plexpy may still be running, so
        # exit
        if os.path.exists(plexpy.PIDFILE):
            try:
                with open(plexpy.PIDFILE, 'r') as fp:
                    pid = int(fp.read())
                os.kill(pid, 0)
            except IOError as e:
                raise SystemExit("Unable to read PID file: %s", e)
            except OSError:
                logger.warn("PID file '%s' already exists, but PID %d is " \
                            "not running. Ignoring PID file." %
                            (plexpy.PIDFILE, pid))
            else:
                # The pidfile exists and points to a live PID. plexpy may
                # still be running, so exit.
                raise SystemExit("PID file '%s' already exists. Exiting." %
                                 plexpy.PIDFILE)

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

            try:
                with open(plexpy.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:
        plexpy.DATA_DIR = args.datadir
    else:
        plexpy.DATA_DIR = plexpy.PROG_DIR

    if args.config:
        config_file = args.config
    else:
        config_file = os.path.join(plexpy.DATA_DIR, config.FILENAME)

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

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

    # Put the database in the DATA_DIR
    plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, database.FILENAME)

    if plexpy.DAEMON:
        plexpy.daemonize()

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

    # Start the background threads
    plexpy.start()

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

    # 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(plexpy.CONFIG.HTTP_PORT)

    # Check if pyOpenSSL is installed. It is required for certificate generation
    # and for CherryPy.
    if plexpy.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.")
            plexpy.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': plexpy.CONFIG.HTTP_HOST,
        'http_root': plexpy.CONFIG.HTTP_ROOT,
        'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
        'http_proxy': plexpy.CONFIG.HTTP_PROXY,
        'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
        'https_cert': plexpy.CONFIG.HTTPS_CERT,
        'https_key': plexpy.CONFIG.HTTPS_KEY,
        'http_username': plexpy.CONFIG.HTTP_USERNAME,
        'http_password': plexpy.CONFIG.HTTP_PASSWORD,
        'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
    }
    webstart.initialize(web_config)

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

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

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

            plexpy.SIGNAL = None
Example #16
0
                    time.sleep(5)

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

            else:
                ws_connected = False
                break

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

    logger.debug(u'PlexPy WebSocket :: Leaving thread.')


def receive(ws):
    frame = ws.recv_frame()

    if not frame:
        raise websocket.WebSocketException("Not a valid frame %s" % frame)
    elif frame.opcode in opcode_data:
        return frame.opcode, frame.data
    elif frame.opcode == websocket.ABNF.OPCODE_CLOSE:
        ws.send_close()
        return frame.opcode, None
    elif frame.opcode == websocket.ABNF.OPCODE_PING:
Example #17
0
def main():
    """
    PlexPy application entry point. Parses arguments, setups encoding and
    initializes the application.
    """

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

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

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

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

    # for OSes that are poorly configured I'll just force UTF-8
    if not plexpy.SYS_ENCODING or plexpy.SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
        plexpy.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 PlexPy to run on a specified port')
    parser.add_argument(
        '--dev', action='store_true', help='Start PlexPy in the development environment')
    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:
        plexpy.VERBOSE = True
    if args.quiet:
        plexpy.QUIET = True

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

    if args.dev:
        plexpy.DEV = True
        logger.debug(u"PlexPy is running in the dev environment.")

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

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

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

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

            try:
                with open(plexpy.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:
        plexpy.DATA_DIR = args.datadir
    else:
        plexpy.DATA_DIR = plexpy.PROG_DIR

    if args.config:
        config_file = args.config
    else:
        config_file = os.path.join(plexpy.DATA_DIR, config.FILENAME)

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

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

    # Put the database in the DATA_DIR
    plexpy.DB_FILE = os.path.join(plexpy.DATA_DIR, database.FILENAME)

    if plexpy.DAEMON:
        plexpy.daemonize()

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

    # Start the background threads
    plexpy.start()

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

    # 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(plexpy.CONFIG.HTTP_PORT)

    # Check if pyOpenSSL is installed. It is required for certificate generation
    # and for CherryPy.
    if plexpy.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.")
            plexpy.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': plexpy.CONFIG.HTTP_HOST,
        'http_root': plexpy.CONFIG.HTTP_ROOT,
        'http_environment': plexpy.CONFIG.HTTP_ENVIRONMENT,
        'http_proxy': plexpy.CONFIG.HTTP_PROXY,
        'enable_https': plexpy.CONFIG.ENABLE_HTTPS,
        'https_cert': plexpy.CONFIG.HTTPS_CERT,
        'https_key': plexpy.CONFIG.HTTPS_KEY,
        'http_username': plexpy.CONFIG.HTTP_USERNAME,
        'http_password': plexpy.CONFIG.HTTP_PASSWORD,
        'http_basic_auth': plexpy.CONFIG.HTTP_BASIC_AUTH
    }
    webstart.initialize(web_config)

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

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

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

            plexpy.SIGNAL = None
Example #18
0
def import_from_plexwatch(database=None,
                          table_name=None,
                          import_ignore_interval=0):

    try:
        connection = sqlite3.connect(database, timeout=20)
        connection.row_factory = sqlite3.Row
    except sqlite3.OperationalError:
        logger.error(u"PlexPy Importer :: Invalid filename.")
        return None
    except ValueError:
        logger.error(u"PlexPy Importer :: Invalid filename.")
        return None

    try:
        connection.execute('SELECT ratingKey from %s' % table_name)
    except sqlite3.OperationalError:
        logger.error(
            u"PlexPy Importer :: Database specified does not contain the required fields."
        )
        return None

    logger.debug(u"PlexPy Importer :: PlexWatch data import in progress...")

    logger.debug(
        u"PlexPy Importer :: Disabling monitoring while import in progress.")
    plexpy.schedule_job(activity_pinger.check_active_sessions,
                        'Check for active sessions',
                        hours=0,
                        minutes=0,
                        seconds=0)
    plexpy.schedule_job(activity_pinger.check_recently_added,
                        'Check for recently added items',
                        hours=0,
                        minutes=0,
                        seconds=0)
    plexpy.schedule_job(activity_pinger.check_server_response,
                        'Check for server response',
                        hours=0,
                        minutes=0,
                        seconds=0)

    ap = activity_processor.ActivityProcessor()
    user_data = users.Users()

    # Get the latest friends list so we can pull user id's
    try:
        plextv.refresh_users()
    except:
        logger.debug(
            u"PlexPy Importer :: Unable to refresh the users list. Aborting import."
        )
        return None

    query = 'SELECT time AS started, ' \
            'stopped, ' \
            'cast(ratingKey as text) AS rating_key, ' \
            'null AS user_id, ' \
            'user, ' \
            'ip_address, ' \
            'paused_counter, ' \
            'platform AS player, ' \
            'null AS platform, ' \
            'null as machine_id, ' \
            'parentRatingKey as parent_rating_key, ' \
            'grandparentRatingKey as grandparent_rating_key, ' \
            'null AS media_type, ' \
            'null AS view_offset, ' \
            'xml, ' \
            'rating as content_rating,' \
            'summary,' \
            'title AS full_title,' \
            '(case when orig_title_ep = "" then orig_title else ' \
            'orig_title_ep end) as title,' \
            '(case when orig_title_ep != "" then orig_title else ' \
            'null end) as grandparent_title ' \
            'FROM ' + table_name + ' ORDER BY id'

    result = connection.execute(query)

    for row in result:
        # Extract the xml from the Plexwatch db xml field.
        extracted_xml = extract_plexwatch_xml(row['xml'])

        # If we get back None from our xml extractor skip over the record and log error.
        if not extracted_xml:
            logger.error(
                u"PlexPy Importer :: Skipping record with ratingKey %s due to malformed xml."
                % str(row['rating_key']))
            continue

        # Skip line if we don't have a ratingKey to work with
        if not row['rating_key']:
            logger.error(
                u"PlexPy Importer :: Skipping record due to null ratingRey.")
            continue

        # If the user_id no longer exists in the friends list, pull it from the xml.
        if user_data.get_user_id(user=row['user']):
            user_id = user_data.get_user_id(user=row['user'])
        else:
            user_id = extracted_xml['user_id']

        session_history = {
            'started': row['started'],
            'stopped': row['stopped'],
            'rating_key': row['rating_key'],
            'title': row['title'],
            'parent_title': extracted_xml['parent_title'],
            'grandparent_title': row['grandparent_title'],
            'user_id': user_id,
            'user': row['user'],
            'ip_address': row['ip_address'],
            'paused_counter': row['paused_counter'],
            'player': row['player'],
            'platform': extracted_xml['platform'],
            'machine_id': extracted_xml['machine_id'],
            'parent_rating_key': row['parent_rating_key'],
            'grandparent_rating_key': row['grandparent_rating_key'],
            'media_type': extracted_xml['media_type'],
            'view_offset': extracted_xml['view_offset'],
            'video_decision': extracted_xml['video_decision'],
            'audio_decision': extracted_xml['audio_decision'],
            'duration': extracted_xml['duration'],
            'width': extracted_xml['width'],
            'height': extracted_xml['height'],
            'container': extracted_xml['container'],
            'video_codec': extracted_xml['video_codec'],
            'audio_codec': extracted_xml['audio_codec'],
            'bitrate': extracted_xml['bitrate'],
            'video_resolution': extracted_xml['video_resolution'],
            'video_framerate': extracted_xml['video_framerate'],
            'aspect_ratio': extracted_xml['aspect_ratio'],
            'audio_channels': extracted_xml['audio_channels'],
            'transcode_protocol': extracted_xml['transcode_protocol'],
            'transcode_container': extracted_xml['transcode_container'],
            'transcode_video_codec': extracted_xml['transcode_video_codec'],
            'transcode_audio_codec': extracted_xml['transcode_audio_codec'],
            'transcode_audio_channels':
            extracted_xml['transcode_audio_channels'],
            'transcode_width': extracted_xml['transcode_width'],
            'transcode_height': extracted_xml['transcode_height']
        }

        session_history_metadata = {
            'rating_key': helpers.latinToAscii(row['rating_key']),
            'parent_rating_key': row['parent_rating_key'],
            'grandparent_rating_key': row['grandparent_rating_key'],
            'title': row['title'],
            'parent_title': extracted_xml['parent_title'],
            'grandparent_title': row['grandparent_title'],
            'media_index': extracted_xml['media_index'],
            'parent_media_index': extracted_xml['parent_media_index'],
            'thumb': extracted_xml['thumb'],
            'parent_thumb': extracted_xml['parent_thumb'],
            'grandparent_thumb': extracted_xml['grandparent_thumb'],
            'art': extracted_xml['art'],
            'media_type': extracted_xml['media_type'],
            'year': extracted_xml['year'],
            'originally_available_at':
            extracted_xml['originally_available_at'],
            'added_at': extracted_xml['added_at'],
            'updated_at': extracted_xml['updated_at'],
            'last_viewed_at': extracted_xml['last_viewed_at'],
            'content_rating': row['content_rating'],
            'summary': row['summary'],
            'tagline': extracted_xml['tagline'],
            'rating': extracted_xml['rating'],
            'duration': extracted_xml['duration'],
            'guid': extracted_xml['guid'],
            'section_id': extracted_xml['section_id'],
            'directors': extracted_xml['directors'],
            'writers': extracted_xml['writers'],
            'actors': extracted_xml['actors'],
            'genres': extracted_xml['genres'],
            'studio': extracted_xml['studio'],
            'full_title': row['full_title']
        }

        # On older versions of PMS, "clip" items were still classified as "movie" and had bad ratingKey values
        # Just make sure that the ratingKey is indeed an integer
        if session_history_metadata['rating_key'].isdigit():
            ap.write_session_history(
                session=session_history,
                import_metadata=session_history_metadata,
                is_import=True,
                import_ignore_interval=import_ignore_interval)
        else:
            logger.debug(u"PlexPy Importer :: Item has bad rating_key: %s" %
                         session_history_metadata['rating_key'])

    logger.debug(u"PlexPy Importer :: PlexWatch data import complete.")
    import_users()

    logger.debug(u"PlexPy Importer :: Re-enabling monitoring.")
    plexpy.initialize_scheduler()
Example #19
0
def update_section_ids():
    from plexpy import pmsconnect, activity_pinger
    import threading

    plexpy.CONFIG.UPDATE_SECTION_IDS = -1

    logger.info(u"PlexPy Libraries :: Updating section_id's in database.")

    logger.debug(u"PlexPy Libraries :: Disabling monitoring while update in progress.")
    plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
                        hours=0, minutes=0, seconds=0)
    plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
                        hours=0, minutes=0, seconds=0)
    plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
                        hours=0, minutes=0, seconds=0)

    monitor_db = database.MonitorDatabase()

    try:
        query = 'SELECT id, rating_key FROM session_history_metadata WHERE section_id IS NULL'
        result = monitor_db.select(query=query)
    except Exception as e:
        logger.warn(u"PlexPy Libraries :: Unable to execute database query for update_section_ids: %s." % e)

        logger.warn(u"PlexPy Libraries :: Unable to update section_id's in database.")
        plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 1)
        plexpy.CONFIG.write()

        logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.")
        plexpy.initialize_scheduler()
        return None

    # Add thread filter to the logger
    logger.debug(u"PlexPy Libraries :: Disabling logging in the current thread while update in progress.")
    thread_filter = logger.NoThreadFilter(threading.current_thread().name)
    for handler in logger.logger.handlers:
        handler.addFilter(thread_filter)

    pms_connect = pmsconnect.PmsConnect()

    error_keys = set()
    for item in result:
        id = item['id']
        rating_key = item['rating_key']
        metadata = pms_connect.get_metadata_details(rating_key=rating_key)

        if metadata:
            metadata = metadata['metadata']
            section_keys = {'id': id}
            section_values = {'section_id': metadata['section_id']}
            monitor_db.upsert('session_history_metadata', key_dict=section_keys, value_dict=section_values)
        else:
            error_keys.add(rating_key)

    # Remove thread filter from the logger
    for handler in logger.logger.handlers:
        handler.removeFilter(thread_filter)
    logger.debug(u"PlexPy Libraries :: Re-enabling logging in the current thread.")

    if error_keys:
        logger.info(u"PlexPy Libraries :: Updated all section_id's in database except for rating_keys: %s." %
                     ', '.join(str(key) for key in error_keys))
    else:
        logger.info(u"PlexPy Libraries :: Updated all section_id's in database.")

    plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 0)
    plexpy.CONFIG.write()

    logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.")
    plexpy.initialize_scheduler()

    return True