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()
def check_recently_added(): with monitor_lock: # add delay to allow for metadata processing delay = plexcs.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY time_threshold = int(time.time()) - delay time_interval = plexcs.CONFIG.MONITORING_INTERVAL pms_connect = pmsconnect.PmsConnect() recently_added_list = pms_connect.get_recently_added_details(count='10') if recently_added_list: recently_added = recently_added_list['recently_added'] for item in recently_added: metadata = [] if 0 < time_threshold - int(item['added_at']) <= time_interval: if item['media_type'] == 'movie': metadata_list = pms_connect.get_metadata_details(item['rating_key']) if metadata_list: metadata = [metadata_list['metadata']] else: logger.error(u"Plex:CS Monitor :: Unable to retrieve metadata for rating_key %s" \ % str(item['rating_key'])) else: metadata_list = pms_connect.get_metadata_children_details(item['rating_key']) if metadata_list: metadata = metadata_list['metadata'] else: logger.error(u"Plex:CS Monitor :: Unable to retrieve children metadata for rating_key %s" \ % str(item['rating_key'])) if metadata: if not plexcs.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT: for item in metadata: if 0 < time_threshold - int(item['added_at']) <= time_interval: logger.debug(u"Plex:CS Monitor :: Library item %s has been added to Plex." % str(item['rating_key'])) # Fire off notifications threading.Thread(target=notification_handler.notify_timeline, kwargs=dict(timeline_data=item, notify_action='created')).start() else: item = max(metadata, key=lambda x:x['added_at']) if 0 < time_threshold - int(item['added_at']) <= time_interval: if item['media_type'] == 'episode' or item['media_type'] == 'track': metadata_list = pms_connect.get_metadata_details(item['grandparent_rating_key']) if metadata_list: item = metadata_list['metadata'] else: logger.error(u"Plex:CS Monitor :: Unable to retrieve grandparent metadata for grandparent_rating_key %s" \ % str(item['rating_key'])) logger.debug(u"Plex:CS Monitor :: Library item %s has been added to Plex." % str(item['rating_key'])) # Fire off notifications threading.Thread(target=notification_handler.notify_timeline, kwargs=dict(timeline_data=item, notify_action='created')).start()
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.')
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.')
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 build_notify_text(session=None, timeline=None, state=None): import re # Get the server name server_name = plexcs.CONFIG.PMS_NAME # Get the server uptime plex_tv = plextv.PlexTV() server_times = plex_tv.get_server_times() if server_times: updated_at = server_times[0]['updated_at'] server_uptime = helpers.human_duration( int(time.time() - helpers.cast_to_float(updated_at))) else: logger.error(u"Plex:CS Notifier :: Unable to retrieve server uptime.") server_uptime = 'N/A' # Get metadata feed for item if session: rating_key = session['rating_key'] elif timeline: rating_key = timeline['rating_key'] pms_connect = pmsconnect.PmsConnect() metadata_list = pms_connect.get_metadata_details(rating_key=rating_key) if metadata_list: metadata = metadata_list['metadata'] else: logger.error( u"Plex:CS Notifier :: Unable to retrieve metadata for rating_key %s" % str(rating_key)) return [] # Check for exclusion tags if metadata['media_type'] == 'movie': # Regex pattern to remove the text in the tags we don't want pattern = re.compile( '\n*<tv>[^>]+.</tv>\n*|\n*<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL) elif metadata['media_type'] == 'show' or metadata[ 'media_type'] == 'episode': # Regex pattern to remove the text in the tags we don't want pattern = re.compile( '\n*<movie>[^>]+.</movie>\n*|\n*?<music>[^>]+.</music>\n*', re.IGNORECASE | re.DOTALL) elif metadata['media_type'] == 'artist' or metadata[ 'media_type'] == 'track': # Regex pattern to remove the text in the tags we don't want pattern = re.compile( '\n*<tv>[^>]+.</tv>\n*|\n*<movie>[^>]+.</movie>\n*', re.IGNORECASE | re.DOTALL) else: pattern = None if metadata['media_type'] == 'movie' \ or metadata['media_type'] == 'show' or metadata['media_type'] == 'episode' \ or metadata['media_type'] == 'artist' or metadata['media_type'] == 'track' \ and pattern: # Remove the unwanted tags and strip any unmatch tags too. on_start_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT)) on_start_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_START_BODY_TEXT)) on_stop_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT)) on_stop_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_STOP_BODY_TEXT)) on_pause_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT)) on_pause_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT)) on_resume_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT)) on_resume_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT)) on_buffer_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT)) on_buffer_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT)) on_watched_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT)) on_watched_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT)) on_created_subject = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT)) on_created_body = strip_tag( re.sub(pattern, '', plexcs.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT)) else: on_start_subject = plexcs.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT on_start_body = plexcs.CONFIG.NOTIFY_ON_START_BODY_TEXT on_stop_subject = plexcs.CONFIG.NOTIFY_ON_STOP_SUBJECT_TEXT on_stop_body = plexcs.CONFIG.NOTIFY_ON_STOP_BODY_TEXT on_pause_subject = plexcs.CONFIG.NOTIFY_ON_PAUSE_SUBJECT_TEXT on_pause_body = plexcs.CONFIG.NOTIFY_ON_PAUSE_BODY_TEXT on_resume_subject = plexcs.CONFIG.NOTIFY_ON_RESUME_SUBJECT_TEXT on_resume_body = plexcs.CONFIG.NOTIFY_ON_RESUME_BODY_TEXT on_buffer_subject = plexcs.CONFIG.NOTIFY_ON_BUFFER_SUBJECT_TEXT on_buffer_body = plexcs.CONFIG.NOTIFY_ON_BUFFER_BODY_TEXT on_watched_subject = plexcs.CONFIG.NOTIFY_ON_WATCHED_SUBJECT_TEXT on_watched_body = plexcs.CONFIG.NOTIFY_ON_WATCHED_BODY_TEXT on_created_subject = plexcs.CONFIG.NOTIFY_ON_CREATED_SUBJECT_TEXT on_created_body = plexcs.CONFIG.NOTIFY_ON_CREATED_BODY_TEXT # Create a title if metadata['media_type'] == 'episode' or metadata['media_type'] == 'track': full_title = '%s - %s' % (metadata['grandparent_title'], metadata['title']) else: full_title = metadata['title'] duration = helpers.convert_milliseconds_to_minutes(metadata['duration']) # Default values video_decision = '' audio_decision = '' transcode_decision = '' stream_duration = 0 view_offset = 0 user = '' platform = '' player = '' ip_address = 'N/A' # Session values if session: # Generate a combined transcode decision value video_decision = session['video_decision'].title() audio_decision = session['audio_decision'].title() if session['video_decision'] == 'transcode' or session[ 'audio_decision'] == 'transcode': transcode_decision = 'Transcode' elif session['video_decision'] == 'copy' or session[ 'audio_decision'] == 'copy': transcode_decision = 'Direct Stream' else: transcode_decision = 'Direct Play' if state != 'play': if session['paused_counter']: stream_duration = int( (time.time() - helpers.cast_to_float(session['started']) - helpers.cast_to_float(session['paused_counter'])) / 60) else: stream_duration = int( (time.time() - helpers.cast_to_float(session['started'])) / 60) view_offset = helpers.convert_milliseconds_to_minutes( session['view_offset']) user = session['friendly_name'] platform = session['platform'] player = session['player'] ip_address = session['ip_address'] if session['ip_address'] else 'N/A' progress_percent = helpers.get_percent(view_offset, duration) available_params = { 'server_name': server_name, 'server_uptime': server_uptime, 'user': user, 'platform': platform, 'player': player, 'ip_address': ip_address, 'media_type': metadata['media_type'], 'title': full_title, 'show_name': metadata['grandparent_title'], 'episode_name': metadata['title'], 'artist_name': metadata['grandparent_title'], 'album_name': metadata['parent_title'], 'track_name': metadata['title'], 'season_num': metadata['parent_index'].zfill(1), 'season_num00': metadata['parent_index'].zfill(2), 'episode_num': metadata['index'].zfill(1), 'episode_num00': metadata['index'].zfill(2), 'video_decision': video_decision, 'audio_decision': audio_decision, 'transcode_decision': transcode_decision, 'year': metadata['year'], 'studio': metadata['studio'], 'content_rating': metadata['content_rating'], 'directors': ', '.join(metadata['directors']), 'writers': ', '.join(metadata['writers']), 'actors': ', '.join(metadata['actors']), 'genres': ', '.join(metadata['genres']), 'summary': metadata['summary'], 'tagline': metadata['tagline'], 'rating': metadata['rating'], 'duration': duration, 'stream_duration': stream_duration, 'remaining_duration': duration - view_offset, 'progress': view_offset, 'progress_percent': progress_percent } # Default subject text subject_text = 'Plex:CS (%s)' % server_name if state == 'play': # Default body text body_text = '%s (%s) is watching %s' % (session['friendly_name'], session['player'], full_title) if on_start_subject and on_start_body: try: subject_text = unicode(on_start_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_start_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'stop': # Default body text body_text = '%s (%s) has stopped %s' % (session['friendly_name'], session['player'], full_title) if on_stop_subject and on_stop_body: try: subject_text = unicode(on_stop_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_stop_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'pause': # Default body text body_text = '%s (%s) has paused %s' % (session['friendly_name'], session['player'], full_title) if on_pause_subject and on_pause_body: try: subject_text = unicode(on_pause_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_pause_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'resume': # Default body text body_text = '%s (%s) has resumed %s' % (session['friendly_name'], session['player'], full_title) if on_resume_subject and on_resume_body: try: subject_text = unicode(on_resume_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_resume_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'buffer': # Default body text body_text = '%s (%s) is buffering %s' % (session['friendly_name'], session['player'], full_title) if on_buffer_subject and on_buffer_body: try: subject_text = unicode(on_buffer_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_buffer_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'watched': # Default body text body_text = '%s (%s) has watched %s' % (session['friendly_name'], session['player'], full_title) if on_watched_subject and on_watched_body: try: subject_text = unicode(on_watched_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_watched_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] elif state == 'created': # Default body text body_text = '%s was recently added to Plex.' % full_title if on_created_subject and on_created_body: try: subject_text = unicode(on_created_subject).format( **available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification subject. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification subject. Using fallback." ) try: body_text = unicode(on_created_body).format(**available_params) except LookupError as e: logger.error( u"Plex:CS Notifier :: Unable to parse field %s in notification body. Using fallback." % e) except: logger.error( u"Plex:CS Notifier :: Unable to parse custom notification body. Using fallback." ) return [subject_text, body_text] else: return [subject_text, body_text] else: return None
def write_session_history(self, session=None, import_metadata=None, is_import=False, import_ignore_interval=0): from plexcs import users user_data = users.Users() user_details = user_data.get_user_friendly_name(user=session['user']) if session: logging_enabled = False if is_import: if str(session['stopped']).isdigit(): stopped = int(session['stopped']) else: stopped = int(time.time()) else: stopped = int(time.time()) if plexcs.CONFIG.MOVIE_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \ session['media_type'] == 'movie': logging_enabled = True elif plexcs.CONFIG.TV_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \ session['media_type'] == 'episode': logging_enabled = True elif plexcs.CONFIG.MUSIC_LOGGING_ENABLE and str(session['rating_key']).isdigit() and \ session['media_type'] == 'track': logging_enabled = True else: logger.debug( u"Plex:CS ActivityProcessor :: ratingKey %s not logged. Does not meet logging criteria. " u"Media type is '%s'" % (session['rating_key'], session['media_type'])) if str(session['paused_counter']).isdigit(): real_play_time = stopped - session['started'] - int( session['paused_counter']) else: real_play_time = stopped - session['started'] if plexcs.CONFIG.LOGGING_IGNORE_INTERVAL and not is_import: if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \ (real_play_time < int(plexcs.CONFIG.LOGGING_IGNORE_INTERVAL)): logging_enabled = False logger.debug( u"Plex:CS ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s " u"seconds, so we're not logging it." % (session['rating_key'], str(real_play_time), plexcs.CONFIG.LOGGING_IGNORE_INTERVAL)) if session['media_type'] == 'track' and not is_import: if real_play_time < 15 and session['duration'] >= 30: logging_enabled = False logger.debug( u"Plex:CS ActivityProcessor :: Play duration for ratingKey %s is %s secs, " u"looks like it was skipped so we're not logging it" % (session['rating_key'], str(real_play_time))) elif is_import and import_ignore_interval: if (session['media_type'] == 'movie' or session['media_type'] == 'episode') and \ (real_play_time < int(import_ignore_interval)): logging_enabled = False logger.debug( u"Plex:CS ActivityProcessor :: Play duration for ratingKey %s is %s secs which is less than %s " u"seconds, so we're not logging it." % (session['rating_key'], str(real_play_time), import_ignore_interval)) if not user_details['keep_history'] and not is_import: logging_enabled = False logger.debug( u"Plex:CS ActivityProcessor :: History logging for user '%s' is disabled." % session['user']) if logging_enabled: # logger.debug(u"Plex:CS ActivityProcessor :: Attempting to write to session_history table...") query = 'INSERT INTO session_history (started, stopped, rating_key, parent_rating_key, ' \ 'grandparent_rating_key, media_type, user_id, user, ip_address, paused_counter, player, ' \ 'platform, machine_id, view_offset) VALUES ' \ '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' args = [ session['started'], stopped, session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'], session['media_type'], session['user_id'], session['user'], session['ip_address'], session['paused_counter'], session['player'], session['platform'], session['machine_id'], session['view_offset'] ] # logger.debug(u"Plex:CS ActivityProcessor :: Writing session_history transaction...") self.db.action(query=query, args=args) # Check if we should group the session, select the last two rows from the user query = 'SELECT id, rating_key, user_id, reference_id FROM session_history \ WHERE user_id = ? ORDER BY id DESC LIMIT 2 ' args = [session['user_id']] result = self.db.select(query=query, args=args) new_session = { 'id': result[0]['id'], 'rating_key': result[0]['rating_key'], 'user_id': result[0]['user_id'], 'reference_id': result[0]['reference_id'] } if len(result) == 1: prev_session = None else: prev_session = { 'id': result[1]['id'], 'rating_key': result[1]['rating_key'], 'user_id': result[1]['user_id'], 'reference_id': result[1]['reference_id'] } query = 'UPDATE session_history SET reference_id = ? WHERE id = ? ' # If rating_key is the same in the previous session, then set the reference_id to the previous row, else set the reference_id to the new id if (prev_session is not None) and (prev_session['rating_key'] == new_session['rating_key']): args = [prev_session['reference_id'], new_session['id']] else: args = [new_session['id'], new_session['id']] self.db.action(query=query, args=args) # logger.debug(u"Plex:CS ActivityProcessor :: Successfully written history item, last id for session_history is %s" # % last_id) # Write the session_history_media_info table # logger.debug(u"Plex:CS ActivityProcessor :: Attempting to write to session_history_media_info table...") query = 'INSERT INTO session_history_media_info (id, rating_key, video_decision, audio_decision, ' \ 'duration, 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) VALUES ' \ '(last_insert_rowid(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' args = [ session['rating_key'], session['video_decision'], session['audio_decision'], session['duration'], session['width'], session['height'], session['container'], session['video_codec'], session['audio_codec'], session['bitrate'], session['video_resolution'], session['video_framerate'], session['aspect_ratio'], session['audio_channels'], session['transcode_protocol'], session['transcode_container'], session['transcode_video_codec'], session['transcode_audio_codec'], session['transcode_audio_channels'], session['transcode_width'], session['transcode_height'] ] # logger.debug(u"Plex:CS ActivityProcessor :: Writing session_history_media_info transaction...") self.db.action(query=query, args=args) if not is_import: logger.debug( u"Plex:CS ActivityProcessor :: Fetching metadata for item ratingKey %s" % session['rating_key']) pms_connect = pmsconnect.PmsConnect() result = pms_connect.get_metadata_details( rating_key=str(session['rating_key'])) metadata = result['metadata'] else: metadata = import_metadata # Write the session_history_metadata table directors = ";".join(metadata['directors']) writers = ";".join(metadata['writers']) actors = ";".join(metadata['actors']) genres = ";".join(metadata['genres']) # Build media item title if session['media_type'] == 'episode' or session[ 'media_type'] == 'track': full_title = '%s - %s' % (metadata['grandparent_title'], metadata['title']) elif session['media_type'] == 'movie': full_title = metadata['title'] else: full_title = metadata['title'] # logger.debug(u"Plex:CS ActivityProcessor :: Attempting to write to session_history_metadata table...") query = 'INSERT INTO session_history_metadata (id, rating_key, parent_rating_key, ' \ 'grandparent_rating_key, title, parent_title, grandparent_title, full_title, media_index, ' \ 'parent_media_index, thumb, parent_thumb, grandparent_thumb, art, media_type, year, ' \ 'originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, ' \ 'tagline, rating, duration, guid, directors, writers, actors, genres, studio) VALUES ' \ '(last_insert_rowid(), ' \ '?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' args = [ session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'], session['title'], session['parent_title'], session['grandparent_title'], full_title, metadata['index'], metadata['parent_index'], metadata['thumb'], metadata['parent_thumb'], metadata['grandparent_thumb'], metadata['art'], session['media_type'], metadata['year'], metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'], metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'], metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres, metadata['studio'] ] # logger.debug(u"Plex:CS ActivityProcessor :: Writing session_history_metadata transaction...") self.db.action(query=query, args=args)