def set_user_profile_url(self, user=None, user_id=None, profile_url=None): if user_id: if profile_url.strip() == '': profile_url = None monitor_db = database.MonitorDatabase() control_value_dict = {"user_id": user_id} new_value_dict = {"custom_avatar_url": profile_url} try: monitor_db.upsert('users', new_value_dict, control_value_dict) except Exception as e: logger.debug(u"Uncaught exception %s" % e) if user: if profile_url.strip() == '': profile_url = None monitor_db = database.MonitorDatabase() control_value_dict = {"username": user} new_value_dict = {"custom_avatar_url": profile_url} try: monitor_db.upsert('users', new_value_dict, control_value_dict) except Exception as e: logger.debug(u"Uncaught exception %s" % e)
def get_user_friendly_name(self, user=None, user_id=None): if user_id: monitor_db = database.MonitorDatabase() query = 'select username, ' \ '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ 'do_notify, keep_history, custom_avatar_url as thumb ' \ 'FROM users WHERE user_id = ?' result = monitor_db.select(query, args=[user_id]) if result: user_detail = { 'user_id': user_id, 'user': result[0]['username'], 'friendly_name': result[0]['friendly_name'], 'thumb': result[0]['thumb'], 'do_notify': helpers.checked(result[0]['do_notify']), 'keep_history': helpers.checked(result[0]['keep_history']) } return user_detail else: user_detail = { 'user_id': user_id, 'user': '', 'friendly_name': '', 'do_notify': '', 'thumb': '', 'keep_history': '' } return user_detail elif user: monitor_db = database.MonitorDatabase() query = 'select user_id, ' \ '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ 'do_notify, keep_history, custom_avatar_url as thumb ' \ 'FROM users WHERE username = ?' result = monitor_db.select(query, args=[user]) if result: user_detail = { 'user_id': result[0]['user_id'], 'user': user, 'friendly_name': result[0]['friendly_name'], 'thumb': result[0]['thumb'], 'do_notify': helpers.checked(result[0]['do_notify']), 'keep_history': helpers.checked(result[0]['keep_history']) } return user_detail else: user_detail = { 'user_id': None, 'user': user, 'friendly_name': '', 'do_notify': '', 'thumb': '', 'keep_history': '' } return user_detail return None
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.")
def set_user_friendly_name(self, user=None, user_id=None, friendly_name=None, do_notify=0, keep_history=1): if user_id: if friendly_name.strip() == '': friendly_name = None monitor_db = database.MonitorDatabase() control_value_dict = {"user_id": user_id} new_value_dict = { "friendly_name": friendly_name, "do_notify": do_notify, "keep_history": keep_history } try: monitor_db.upsert('users', new_value_dict, control_value_dict) except Exception as e: logger.debug(u"Uncaught exception %s" % e) if user: if friendly_name.strip() == '': friendly_name = None monitor_db = database.MonitorDatabase() control_value_dict = {"username": user} new_value_dict = { "friendly_name": friendly_name, "do_notify": do_notify, "keep_history": keep_history } try: monitor_db.upsert('users', new_value_dict, control_value_dict) except Exception as e: logger.debug(u"Uncaught exception %s" % e)
def get_user_id(self, user=None): if user: try: monitor_db = database.MonitorDatabase() query = 'select user_id FROM users WHERE username = ?' result = monitor_db.select_single(query, args=[user]) if result: return result['user_id'] else: return None except: return None return None
def get_user_watch_time_stats(self, user=None, user_id=None): monitor_db = database.MonitorDatabase() time_queries = [1, 7, 30, 0] user_watch_time_stats = [] for days in time_queries: if days > 0: if user_id: query = 'SELECT (SUM(stopped - started) - ' \ 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ 'COUNT(id) AS total_plays ' \ 'FROM session_history ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \ 'AND user_id = ?' % days result = monitor_db.select(query, args=[user_id]) elif user: query = 'SELECT (SUM(stopped - started) - ' \ 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ 'COUNT(id) AS total_plays ' \ 'FROM session_history ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \ 'AND user = ?' % days result = monitor_db.select(query, args=[user]) else: query = 'SELECT (SUM(stopped - started) - ' \ 'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \ 'COUNT(id) AS total_plays ' \ 'FROM session_history ' \ 'WHERE user = ?' result = monitor_db.select(query, args=[user]) for item in result: if item['total_time']: total_time = item['total_time'] total_plays = item['total_plays'] else: total_time = 0 total_plays = 0 row = { 'query_days': days, 'total_time': total_time, 'total_plays': total_plays } user_watch_time_stats.append(row) return user_watch_time_stats
def import_users(): from plexcs import database logger.debug(u"Plex:CS Importer :: Importing PlexWatch Users...") monitor_db = database.MonitorDatabase() query = 'INSERT OR IGNORE INTO users (user_id, username) ' \ 'SELECT user_id, user ' \ 'FROM session_history WHERE user_id != 1 GROUP BY user_id' try: monitor_db.action(query) logger.debug(u"Plex:CS Importer :: Users imported.") except: logger.debug(u"Plex:CS Importer :: Failed to import users.")
def get_notify_state_timeline(timeline): monitor_db = database.MonitorDatabase() result = monitor_db.select( 'SELECT on_created, agent_id ' 'FROM notify_log ' 'WHERE rating_key = ? ' 'ORDER BY id DESC', args=[timeline['rating_key']]) notify_states = [] for item in result: notify_state = { 'on_created': item['on_created'], 'agent_id': item['agent_id'] } notify_states.append(notify_state) return notify_states
def set_notify_state(session, state, agent_info): if session and state and agent_info: monitor_db = database.MonitorDatabase() if state == 'play': values = {'on_play': int(time.time())} elif state == 'stop': values = {'on_stop': int(time.time())} elif state == 'pause': values = {'on_pause': int(time.time())} elif state == 'resume': values = {'on_resume': int(time.time())} elif state == 'buffer': values = {'on_buffer': int(time.time())} elif state == 'watched': values = {'on_watched': int(time.time())} elif state == 'created': values = {'on_created': int(time.time())} else: return if state == 'created': keys = { 'rating_key': session['rating_key'], 'agent_id': agent_info['id'], 'agent_name': agent_info['name'] } else: keys = { 'session_key': session['session_key'], 'rating_key': session['rating_key'], 'user_id': session['user_id'], 'user': session['user'], 'agent_id': agent_info['id'], 'agent_name': agent_info['name'] } monitor_db.upsert(table_name='notify_log', key_dict=keys, value_dict=values) else: logger.error('Plex:CS Notifier :: Unable to set notify state.')
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
def get_notify_state(session): monitor_db = database.MonitorDatabase() result = monitor_db.select( 'SELECT on_play, on_stop, on_pause, on_resume, on_buffer, on_watched, agent_id ' 'FROM notify_log ' 'WHERE session_key = ? ' 'AND rating_key = ? ' 'AND user = ? ' 'ORDER BY id DESC', args=[session['session_key'], session['rating_key'], session['user']]) notify_states = [] for item in result: notify_state = { 'on_play': item['on_play'], 'on_stop': item['on_stop'], 'on_pause': item['on_pause'], 'on_resume': item['on_resume'], 'on_buffer': item['on_buffer'], 'on_watched': item['on_watched'], 'agent_id': item['agent_id'] } notify_states.append(notify_state) return notify_states
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()
def __init__(self): self.db = database.MonitorDatabase()
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 }