From 3f56bedcc62e5b98d7c2c2f9402dcd976c2b14a7 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Mon, 28 Sep 2015 13:44:01 -0700 Subject: [PATCH 1/5] Change to notify stopped only if less than watched percent * Also fix paused notifications sending at the end of items (right before stopping) --- plexpy/activity_handler.py | 22 +++++++++--------- plexpy/activity_pinger.py | 47 ++++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 675eb891..391666e8 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -1,4 +1,4 @@ -# This file is part of PlexPy. +# This file is part of PlexPy. # # PlexPy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -178,6 +178,13 @@ class ActivityHandler(object): last_state = db_session['state'] last_key = str(db_session['rating_key']) + # Monitor if the stream has reached the watch percentage for notifications + # The only purpose of this is for notifications + progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) + if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering': + threading.Thread(target=notification_handler.notify, + kwargs=dict(stream_data=db_session, notify_action='watched')).start() + # Make sure the same item is being played if this_key == last_key: # Update the session state and viewOffset @@ -187,11 +194,11 @@ class ActivityHandler(object): view_offset=self.timeline['viewOffset']) # Start our state checks if this_state != last_state: - if this_state == 'paused': + if this_state == 'paused' and progress_percent < 99: self.on_pause() - elif last_state == 'paused' and this_state == 'playing': + elif last_state == 'paused' and this_state == 'playing' and progress_percent < 99: self.on_resume() - elif this_state == 'stopped': + elif this_state == 'stopped' and progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: self.on_stop() elif this_state == 'buffering': self.on_buffer() @@ -202,13 +209,6 @@ class ActivityHandler(object): self.on_stop(force_stop=True) self.on_start() - # Monitor if the stream has reached the watch percentage for notifications - # The only purpose of this is for notifications - progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) - if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering': - threading.Thread(target=notification_handler.notify, - kwargs=dict(stream_data=db_session, notify_action='watched')).start() - else: # We don't have this session in our table yet, start a new one. if this_state != 'buffering': diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py index 1b99cf05..18aed8de 100644 --- a/plexpy/activity_pinger.py +++ b/plexpy/activity_pinger.py @@ -1,4 +1,4 @@ -# This file is part of PlexPy. +# This file is part of PlexPy. # # PlexPy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -48,6 +48,12 @@ def check_active_sessions(ws_request=False): 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): + + if stream['view_offset'] and stream['duration']: + progress_percent = helpers.get_percent(stream['view_offset'], stream['duration']) + else: + progress_percent = None + # The user's session is still active for session in media_container: if session['session_key'] == str(stream['session_key']) and \ @@ -55,13 +61,13 @@ def check_active_sessions(ws_request=False): # 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': + if session['state'] == 'paused' and progress_percent < 99: # 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': + if session['state'] == 'playing' and stream['state'] == 'paused' and progress_percent < 99: # 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, @@ -126,13 +132,11 @@ def check_active_sessions(ws_request=False): # 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']) > plexpy.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() + if progress_percent > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and session['state'] != 'buffering': + # 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 @@ -141,18 +145,21 @@ def check_active_sessions(ws_request=False): 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']) > plexpy.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() + progress_percent = helpers.get_percent(stream['view_offset'], stream['duration']) + else: + progress_percent = None - # 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() + # Check if the user has reached the offset in the media we defined as the "watched" percent + if progress_percent > plexpy.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: + # 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) From fcbf82c6e0ae77d66bfa28285e6d31d7e441a6f6 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Mon, 28 Sep 2015 15:46:14 -0700 Subject: [PATCH 2/5] Don't group history in graphs modal table --- data/interfaces/default/history_table_modal.html | 1 + 1 file changed, 1 insertion(+) diff --git a/data/interfaces/default/history_table_modal.html b/data/interfaces/default/history_table_modal.html index 152589eb..58345fcf 100644 --- a/data/interfaces/default/history_table_modal.html +++ b/data/interfaces/default/history_table_modal.html @@ -38,6 +38,7 @@ type: "post", data: function ( d ) { return { 'json_data': JSON.stringify( d ), + 'grouping': false, 'start_date': '${data}' }; } From 138dd61e518aa89a4cbef94fca6f2c635cc28149 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Mon, 28 Sep 2015 15:53:26 -0700 Subject: [PATCH 3/5] Fix code to only affect notifications --- plexpy/activity_handler.py | 44 ++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 391666e8..33e72ceb 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -66,6 +66,8 @@ class ActivityHandler(object): def on_stop(self, force_stop=False): if self.is_valid_session(): + from plexpy import helpers + logger.debug(u"PlexPy ActivityHandler :: Session %s has stopped." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -83,8 +85,10 @@ class ActivityHandler(object): db_session = ap.get_session_by_key(session_key=self.get_session_key()) # Fire off notifications - threading.Thread(target=notification_handler.notify, - kwargs=dict(stream_data=db_session, notify_action='stop')).start() + progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) + if progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: + threading.Thread(target=notification_handler.notify, + kwargs=dict(stream_data=db_session, notify_action='stop')).start() # Write it to the history table monitor_proc = activity_processor.ActivityProcessor() @@ -95,6 +99,8 @@ class ActivityHandler(object): def on_pause(self): if self.is_valid_session(): + from plexpy import helpers + logger.debug(u"PlexPy ActivityHandler :: Session %s has been paused." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -110,11 +116,15 @@ class ActivityHandler(object): db_session = ap.get_session_by_key(session_key=self.get_session_key()) # Fire off notifications - threading.Thread(target=notification_handler.notify, - kwargs=dict(stream_data=db_session, notify_action='pause')).start() + progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) + if progress_percent < 99: + threading.Thread(target=notification_handler.notify, + kwargs=dict(stream_data=db_session, notify_action='pause')).start() def on_resume(self): if self.is_valid_session(): + from plexpy import helpers + logger.debug(u"PlexPy ActivityHandler :: Session %s has been resumed." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -130,8 +140,10 @@ class ActivityHandler(object): db_session = ap.get_session_by_key(session_key=self.get_session_key()) # Fire off notifications - threading.Thread(target=notification_handler.notify, - kwargs=dict(stream_data=db_session, notify_action='resume')).start() + progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) + if progress_percent < 99: + threading.Thread(target=notification_handler.notify, + kwargs=dict(stream_data=db_session, notify_action='resume')).start() def on_buffer(self): if self.is_valid_session(): @@ -178,13 +190,6 @@ class ActivityHandler(object): last_state = db_session['state'] last_key = str(db_session['rating_key']) - # Monitor if the stream has reached the watch percentage for notifications - # The only purpose of this is for notifications - progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) - if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering': - threading.Thread(target=notification_handler.notify, - kwargs=dict(stream_data=db_session, notify_action='watched')).start() - # Make sure the same item is being played if this_key == last_key: # Update the session state and viewOffset @@ -194,11 +199,11 @@ class ActivityHandler(object): view_offset=self.timeline['viewOffset']) # Start our state checks if this_state != last_state: - if this_state == 'paused' and progress_percent < 99: + if this_state == 'paused': self.on_pause() - elif last_state == 'paused' and this_state == 'playing' and progress_percent < 99: + elif last_state == 'paused' and this_state == 'playing': self.on_resume() - elif this_state == 'stopped' and progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: + elif this_state == 'stopped': self.on_stop() elif this_state == 'buffering': self.on_buffer() @@ -209,6 +214,13 @@ class ActivityHandler(object): self.on_stop(force_stop=True) self.on_start() + # Monitor if the stream has reached the watch percentage for notifications + # The only purpose of this is for notifications + progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) + if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and this_state != 'buffering': + threading.Thread(target=notification_handler.notify, + kwargs=dict(stream_data=db_session, notify_action='watched')).start() + else: # We don't have this session in our table yet, start a new one. if this_state != 'buffering': From d7aba4a01f770d584c8cbee081074bede0a9877d Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Mon, 28 Sep 2015 16:37:20 -0700 Subject: [PATCH 4/5] Add setting for consecutive notifications --- data/interfaces/default/settings.html | 6 ++++++ plexpy/activity_handler.py | 6 +++--- plexpy/activity_pinger.py | 12 +++++++----- plexpy/config.py | 1 + plexpy/webserve.py | 3 ++- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 000084b0..3adc935e 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -496,6 +496,12 @@ available_notification_agents = notifiers.available_notification_agents()

Set the progress percentage of when a watched notification should be triggered. Minimum 50, Maximum 95.

+
+ +

Disable to prevent consecutive notifications (i.e. both watched & stopped notifications).

+

Custom Notification Messages

diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 33e72ceb..19cbed7f 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -86,7 +86,7 @@ class ActivityHandler(object): # Fire off notifications progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) - if progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: + if plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: threading.Thread(target=notification_handler.notify, kwargs=dict(stream_data=db_session, notify_action='stop')).start() @@ -117,7 +117,7 @@ class ActivityHandler(object): # Fire off notifications progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) - if progress_percent < 99: + if plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99: threading.Thread(target=notification_handler.notify, kwargs=dict(stream_data=db_session, notify_action='pause')).start() @@ -141,7 +141,7 @@ class ActivityHandler(object): # Fire off notifications progress_percent = helpers.get_percent(self.timeline['viewOffset'], db_session['duration']) - if progress_percent < 99: + if plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99: threading.Thread(target=notification_handler.notify, kwargs=dict(stream_data=db_session, notify_action='resume')).start() diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py index 18aed8de..5955abe1 100644 --- a/plexpy/activity_pinger.py +++ b/plexpy/activity_pinger.py @@ -61,13 +61,15 @@ def check_active_sessions(ws_request=False): # 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' and progress_percent < 99: + if session['state'] == 'paused' \ + and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99): # 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' and progress_percent < 99: + if session['state'] == 'playing' and stream['state'] == 'paused' \ + and (plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < 99): # 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, @@ -132,7 +134,7 @@ def check_active_sessions(ws_request=False): # 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 progress_percent > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and session['state'] != 'buffering': + if progress_percent >= plexpy.CONFIG.NOTIFY_WATCHED_PERCENT and session['state'] != 'buffering': # 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, @@ -151,12 +153,12 @@ def check_active_sessions(ws_request=False): progress_percent = None # Check if the user has reached the offset in the media we defined as the "watched" percent - if progress_percent > plexpy.CONFIG.NOTIFY_WATCHED_PERCENT: + if plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent >= plexpy.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: + if plexpy.CONFIG.NOTIFY_CONSECUTIVE or progress_percent < plexpy.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='stop')).start() diff --git a/plexpy/config.py b/plexpy/config.py index c5e1c8a2..6810a693 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -122,6 +122,7 @@ _CONFIG_DEFINITIONS = { 'NMA_ON_RESUME': (int, 'NMA', 0), 'NMA_ON_BUFFER': (int, 'NMA', 0), 'NMA_ON_WATCHED': (int, 'NMA', 0), + 'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1), 'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85), 'NOTIFY_ON_START_SUBJECT_TEXT': (str, 'Monitoring', 'PlexPy ({server_name})'), 'NOTIFY_ON_START_BODY_TEXT': (str, 'Monitoring', '{user} ({player}) started playing {title}.'), diff --git a/plexpy/webserve.py b/plexpy/webserve.py index 46bb3dc2..3129e493 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -439,6 +439,7 @@ class WebInterface(object): "music_logging_enable": checked(plexpy.CONFIG.MUSIC_LOGGING_ENABLE), "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL, "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE), + "notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE), "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT, "notify_on_start_subject_text": plexpy.CONFIG.NOTIFY_ON_START_SUBJECT_TEXT, "notify_on_start_body_text": plexpy.CONFIG.NOTIFY_ON_START_BODY_TEXT, @@ -476,7 +477,7 @@ class WebInterface(object): "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", "home_stats_type", - "group_history_tables" + "group_history_tables", "notify_consecutive" ] for checked_config in checked_configs: if checked_config not in kwargs: From f3f031bd9c2e609cd5c18795504042e08163a6e2 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Tue, 29 Sep 2015 00:56:40 -0700 Subject: [PATCH 5/5] Remove inline imports --- plexpy/activity_handler.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plexpy/activity_handler.py b/plexpy/activity_handler.py index 19cbed7f..681705b8 100644 --- a/plexpy/activity_handler.py +++ b/plexpy/activity_handler.py @@ -16,7 +16,7 @@ import time import plexpy -from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler +from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers class ActivityHandler(object): @@ -66,8 +66,6 @@ class ActivityHandler(object): def on_stop(self, force_stop=False): if self.is_valid_session(): - from plexpy import helpers - logger.debug(u"PlexPy ActivityHandler :: Session %s has stopped." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -99,8 +97,6 @@ class ActivityHandler(object): def on_pause(self): if self.is_valid_session(): - from plexpy import helpers - logger.debug(u"PlexPy ActivityHandler :: Session %s has been paused." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -123,8 +119,6 @@ class ActivityHandler(object): def on_resume(self): if self.is_valid_session(): - from plexpy import helpers - logger.debug(u"PlexPy ActivityHandler :: Session %s has been resumed." % str(self.get_session_key())) # Set the session last_paused timestamp @@ -177,8 +171,6 @@ class ActivityHandler(object): # This function receives events from our websocket connection def process(self): if self.is_valid_session(): - from plexpy import helpers - ap = activity_processor.ActivityProcessor() db_session = ap.get_session_by_key(session_key=self.get_session_key())