From c03860ec70df5d35edb02c35b2dc3c4d0a1a2039 Mon Sep 17 00:00:00 2001 From: Landon Abney Date: Sat, 16 Jun 2018 23:36:27 -0700 Subject: [PATCH] Incorporate waiting to kill paused streams into kill_stream.py Incorporate the functionality of `wait_kill_paused_notify.py` into `kill_stream.py`. A new selector is added of `paused` enabling this mode, and adds two additional arguments to control how long to wait for paused streams, and how often to check a stream's status. The default values are to kill a stream after 20 minutes, checking every 30 seconds. The Tautulli API is used for all functionality, meaning the `plexapi` module is no longer required. --- killstream/kill_stream.py | 65 +++++++++++++- killstream/wait_kill_paused_notify.py | 119 -------------------------- 2 files changed, 64 insertions(+), 120 deletions(-) delete mode 100644 killstream/wait_kill_paused_notify.py diff --git a/killstream/kill_stream.py b/killstream/kill_stream.py index 310d353..22c92ae 100644 --- a/killstream/kill_stream.py +++ b/killstream/kill_stream.py @@ -32,6 +32,7 @@ Taultulli > Settings > Notification Agents > New Script > Script Arguments: Select: Playback Start, Playback Pause Arguments: --jbop SELECTOR --userId {user_id} --username {username} --sessionId {session_id} --notify notifierID + --interval 30 --limit 1200 --killMessage Your message here. No quotes. Save @@ -43,6 +44,8 @@ import requests import argparse import sys import os +from time import sleep +from datetime import datetime TAUTULLI_URL = '' TAUTULLI_APIKEY = '' @@ -64,7 +67,7 @@ if sess.verify is False: import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) -SELECTOR = ['stream', 'allStreams'] +SELECTOR = ['stream', 'allStreams', 'paused'] def send_notification(subject_text, body_text, notifier_id): @@ -183,6 +186,59 @@ def terminate_session(session_id, message, notifier=None, username=None): return None +def terminate_long_pause(session_id, message, limit, interval, notify=None): + """Kills the session if it is paused for longer than seconds. + + Parameters + ---------- + session_id : str + The session id of the session to monitor. + message : str + The message to use if the stream is terminated. + limit : int + The number of seconds the session is allowed to remain paused before it + is terminated. + interval : int + The amount of time to wait between checks of the session state. + notify : int + Tautulli Notification Agent ID to send a notification to on killing a + stream. + """ + start = datetime.now() + fudgeFactor = 100 # Keep checking this long after the defined limit + pausedTime = 0 + checkLimit = limit + interval + fudgeFactor + + while pausedTime < checkLimit: + sessions = get_activity() + foundSession = False + + for session in sessions: + if session['session_id'] == session_id: + foundSession = True + state = session['state'] + + if state == 'paused': + now = datetime.now() + diff = now - start + + if diff.total_seconds() >= limit: + terminate_session(session_id, message, notify) + sys.exit(0) + else: + sleep(interval) + elif state == 'playing' or state == 'buffering': + sys.stdout.write( + "Session '{}' has resumed, ".format(session_id) + + "stopping monitoring.") + sys.exit(0) + if not foundSession: + sys.stdout.write( + "Session '{}' is no longer active ".format(session_id) + + "on the server, stopping monitoring.") + sys.exit(0) + + if __name__ == "__main__": parser = argparse.ArgumentParser( description="Killing Plex streams from Tautulli.") @@ -197,6 +253,10 @@ if __name__ == "__main__": parser.add_argument('--notify', type=int, help='Notification Agent ID number to Agent to send ' + 'notification.') + parser.add_argument('--limit', type=int, default=(20 * 60), # 20 minutes + help='The time session is allowed to remain paused.') + parser.add_argument('--interval', type=int, default=30, + help='The seconds between paused session checks.') parser.add_argument('--killMessage', nargs='+', help='Message to send to user whose stream is killed.') @@ -213,3 +273,6 @@ if __name__ == "__main__": streams = get_user_session_ids(opts.userId) for session_id in streams: terminate_session(session_id, message, opts.notify, opts.username) + elif opts.jbop == 'paused': + terminate_long_pause(opts.sessionId, message, opts.limit, + opts.interval, opts.notify) diff --git a/killstream/wait_kill_paused_notify.py b/killstream/wait_kill_paused_notify.py deleted file mode 100644 index c0f6996..0000000 --- a/killstream/wait_kill_paused_notify.py +++ /dev/null @@ -1,119 +0,0 @@ -""" -Description: Kill paused sessions if paused for X amount of time. -Author: samwiseg00 -Requires: requests, plexapi - -Enabling Scripts in Tautulli: -Taultulli > Settings > Notification Agents > Add a Notification Agent > Script - -Configuration: -Taultulli > Settings > Notification Agents > New Script > Configuration: - - Script Name: wait_kill_notify.py - Set Script Timeout: 0 - Description: Killing long pauses - Save - -Triggers: -Taultulli > Settings > Notification Agents > New Script > Triggers: - - Check: Playback Pause - Save - -Conditions: -Taultulli > Settings > Notification Agents > New Script > Conditions: - - Set Conditions: Condition {1} | Username | is not | UsernameToExclude - Save - -Script Arguments: -Taultulli > Settings > Notification Agents > New Script > Script Arguments: - - Select: Playback Pause - Arguments: {session_key} {user} {title} TIMEOUT INTERVAL - - Save - Close - -Example: - {session_key} {user} {title} 1200 20 - This will tell the script to kill the stream after 20 minutes and check every 20 seconds - -""" - -import os -import sys -from time import sleep -from datetime import datetime -from plexapi.server import PlexServer -import requests - -PLEX_FALLBACK_URL = 'http://127.0.0.1:32400' -PLEX_FALLBACK_TOKEN = '' -PLEX_URL = os.getenv('PLEX_URL', PLEX_FALLBACK_URL) -PLEX_TOKEN = os.getenv('PLEX_TOKEN', PLEX_FALLBACK_TOKEN) - -PLEX_OVERRIDE_URL = '' -PLEX_OVERRIDE_TOKEN = '' - -if PLEX_OVERRIDE_URL: - PLEX_URL = PLEX_OVERRIDE_URL -if PLEX_OVERRIDE_TOKEN: - PLEX_TOKEN = PLEX_OVERRIDE_TOKEN - - -sess = requests.Session() -sess.verify = False -plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) - -sessionKey = sys.argv[1] -username = sys.argv[2] -streamTitle = sys.argv[3] -timeout = int(sys.argv[4]) -interval = int(sys.argv[5]) - -seconds = int(timeout) - -minutes, seconds = divmod(seconds, 60) -hours, minutes = divmod(minutes, 60) - -periods = [('hours', hours), ('minutes', minutes), ('seconds', seconds)] -time_string = ', '.join('{} {}'.format(value, name) - for name, value in periods - if value) -start = datetime.now() - -countdown = 0 -counter = timeout + interval + 100 - -while countdown < counter and countdown is not None: - - foundSession = False - - for session in plex.sessions(): - - if session.sessionKey == int(sessionKey): - foundSession = True - state = session.players[0].state - - if state == 'paused': - now = datetime.now() - diff = now - start - - if diff.total_seconds() >= timeout: - session.stop(reason="This stream has ended due to being paused for over {}.".format(time_string)) - print ("Killed {}'s {} paused stream of {}.".format(username, time_string, streamTitle)) - sys.exit(0) - - else: - sleep(interval) - counter = counter - interval - - elif state == 'playing' or state == 'buffering': - print ("{} resumed the stream of {} so we killed the script.".format(username, streamTitle)) - sys.exit(0) - - if not foundSession: - print ("Session key ({}) for user {} not found while playing {}. " - "The player may have gone to a paused then stopped state.".format(sessionKey, username, streamTitle)) - sys.exit(0)