Merge pull request #76 from Arcanemagus/kill_stream-paused
Incorporate waiting to kill paused streams into kill_stream.py
This commit is contained in:
commit
203a3b0bb9
@ -1,23 +1,25 @@
|
|||||||
"""
|
"""
|
||||||
Description: Use conditions to kill a stream
|
Description: Use conditions to kill a stream
|
||||||
Author: Blacktwin
|
Author: Blacktwin, Arcanemagus, samwiseg00
|
||||||
Requires: requests
|
Requires: requests
|
||||||
|
|
||||||
Enabling Scripts in Tautulli:
|
Adding the script to Tautulli:
|
||||||
Taultulli > Settings > Notification Agents > Add a Notification Agent > Script
|
Taultulli > Settings > Notification Agents > Add a new notification agent >
|
||||||
|
Script
|
||||||
|
|
||||||
Configuration:
|
Configuration:
|
||||||
Taultulli > Settings > Notification Agents > New Script > Configuration:
|
Taultulli > Settings > Notification Agents > New Script > Configuration:
|
||||||
|
|
||||||
Script Name: kill_stream
|
Script Folder: /path/to/your/scripts
|
||||||
Set Script Timeout: {timeout}
|
Script File: ./kill_stream.py (Should be selectable in a dropdown list)
|
||||||
|
Script Timeout: {timeout}
|
||||||
Description: Kill stream(s)
|
Description: Kill stream(s)
|
||||||
Save
|
Save
|
||||||
|
|
||||||
Triggers:
|
Triggers:
|
||||||
Taultulli > Settings > Notification Agents > New Script > Triggers:
|
Taultulli > Settings > Notification Agents > New Script > Triggers:
|
||||||
|
|
||||||
Check: {trigger}
|
Check: Playback Start and/or Playback Pause
|
||||||
Save
|
Save
|
||||||
|
|
||||||
Conditions:
|
Conditions:
|
||||||
@ -31,17 +33,20 @@ Taultulli > Settings > Notification Agents > New Script > Script Arguments:
|
|||||||
|
|
||||||
Select: Playback Start, Playback Pause
|
Select: Playback Start, Playback Pause
|
||||||
Arguments: --jbop SELECTOR --userId {user_id} --username {username}
|
Arguments: --jbop SELECTOR --userId {user_id} --username {username}
|
||||||
--sessionId {session_id} --killMessage Your message here. No quotes. --notify notifierID
|
--sessionId {session_id} --notify notifierID
|
||||||
|
--interval 30 --limit 1200
|
||||||
|
--killMessage Your message here. No quotes.
|
||||||
|
|
||||||
Save
|
Save
|
||||||
Close
|
Close
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
from time import sleep
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
TAUTULLI_URL = ''
|
TAUTULLI_URL = ''
|
||||||
TAUTULLI_APIKEY = ''
|
TAUTULLI_APIKEY = ''
|
||||||
@ -49,19 +54,35 @@ TAUTULLI_URL = os.getenv('TAUTULLI_URL', TAUTULLI_URL)
|
|||||||
TAUTULLI_APIKEY = os.getenv('TAUTULLI_APIKEY', TAUTULLI_APIKEY)
|
TAUTULLI_APIKEY = os.getenv('TAUTULLI_APIKEY', TAUTULLI_APIKEY)
|
||||||
|
|
||||||
SUBJECT_TEXT = "Tautulli has killed a stream."
|
SUBJECT_TEXT = "Tautulli has killed a stream."
|
||||||
BODY_TEXT = "Killed {user}'s stream. Reason: {message}."
|
BODY_TEXT = "Killed session ID '{id}'. Reason: {message}"
|
||||||
|
BODY_TEXT_USER = "Killed {user}'s stream. Reason: {message}."
|
||||||
|
|
||||||
sess = requests.Session()
|
sess = requests.Session()
|
||||||
# Ignore verifying the SSL certificate
|
# Ignore verifying the SSL certificate
|
||||||
sess.verify = False # '/path/to/certfile'
|
sess.verify = False # '/path/to/certfile'
|
||||||
# If verify is set to a path to a directory,
|
# If verify is set to a path to a directory,
|
||||||
# the directory must have been processed using the c_rehash utility supplied with OpenSSL.
|
# the directory must have been processed using the c_rehash utility supplied
|
||||||
|
# with OpenSSL.
|
||||||
|
if sess.verify is False:
|
||||||
|
# Disable the warning that the request is insecure, we know that...
|
||||||
|
import urllib3
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
SELECTOR = ['stream', 'allStreams']
|
SELECTOR = ['stream', 'allStreams', 'paused']
|
||||||
|
|
||||||
|
|
||||||
def send_notification(subject_text, body_text, notifier_id):
|
def send_notification(subject_text, body_text, notifier_id):
|
||||||
# Send the notification through Tautulli
|
"""Send a notification through Tautulli
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
subject_text : str
|
||||||
|
The text to use for the subject line of the message.
|
||||||
|
body_text : str
|
||||||
|
The text to use for the body of the notification.
|
||||||
|
notifier_id : int
|
||||||
|
Tautulli Notification Agent ID to send the notification to.
|
||||||
|
"""
|
||||||
payload = {'apikey': TAUTULLI_APIKEY,
|
payload = {'apikey': TAUTULLI_APIKEY,
|
||||||
'cmd': 'notify',
|
'cmd': 'notify',
|
||||||
'notifier_id': notifier_id,
|
'notifier_id': notifier_id,
|
||||||
@ -69,7 +90,7 @@ def send_notification(subject_text, body_text, notifier_id):
|
|||||||
'body': body_text}
|
'body': body_text}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
r = sess.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
||||||
response = r.json()
|
response = r.json()
|
||||||
|
|
||||||
if response['response']['result'] == 'success':
|
if response['response']['result'] == 'success':
|
||||||
@ -77,30 +98,69 @@ def send_notification(subject_text, body_text, notifier_id):
|
|||||||
else:
|
else:
|
||||||
raise Exception(response['response']['message'])
|
raise Exception(response['response']['message'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e))
|
sys.stderr.write(
|
||||||
|
"Tautulli API 'notify' request failed: {0}.".format(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_activity(user_id):
|
def get_activity():
|
||||||
# Get the current activity on the PMS.
|
"""Get the current activity on the PMS.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
The current active sessions on the Plex server.
|
||||||
|
"""
|
||||||
payload = {'apikey': TAUTULLI_APIKEY,
|
payload = {'apikey': TAUTULLI_APIKEY,
|
||||||
'cmd': 'get_activity'}
|
'cmd': 'get_activity'}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
req = sess.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
||||||
response = req.json()
|
response = req.json()
|
||||||
|
|
||||||
res_data = response['response']['data']['sessions']
|
res_data = response['response']['data']['sessions']
|
||||||
user_streams = [d['session_id'] for d in res_data if d['user_id'] == user_id]
|
return res_data
|
||||||
return user_streams
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write("Tautulli API 'get_activity' request failed: {0}.".format(e))
|
sys.stderr.write(
|
||||||
|
"Tautulli API 'get_activity' request failed: {0}.".format(e))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def terminate_session(session_id, message):
|
def get_user_session_ids(user_id):
|
||||||
# Stop a streaming session.
|
"""Get current session IDs for a specific user.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
user_id : int
|
||||||
|
The ID of the user to grab sessions for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
The active session IDs for the specific user ID.
|
||||||
|
|
||||||
|
"""
|
||||||
|
sessions = get_activity()
|
||||||
|
user_streams = [s['session_id']
|
||||||
|
for s in sessions if s['user_id'] == user_id]
|
||||||
|
return user_streams
|
||||||
|
|
||||||
|
|
||||||
|
def terminate_session(session_id, message, notifier=None, username=None):
|
||||||
|
"""Stop a streaming session.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
session_id : str
|
||||||
|
The session ID of the stream to terminate.
|
||||||
|
message : str
|
||||||
|
The message to display to the user when terminating a stream.
|
||||||
|
notifier : int
|
||||||
|
Notification agent ID to send a message to (the default is None).
|
||||||
|
username : str
|
||||||
|
The username for the terminated session (the default is None).
|
||||||
|
"""
|
||||||
payload = {'apikey': TAUTULLI_APIKEY,
|
payload = {'apikey': TAUTULLI_APIKEY,
|
||||||
'cmd': 'terminate_session',
|
'cmd': 'terminate_session',
|
||||||
'session_id': session_id,
|
'session_id': session_id,
|
||||||
@ -111,16 +171,79 @@ def terminate_session(session_id, message):
|
|||||||
response = req.json()
|
response = req.json()
|
||||||
|
|
||||||
if response['response']['result'] == 'success':
|
if response['response']['result'] == 'success':
|
||||||
sys.stdout.write("Successfully killed Plex session: {0}.".format(session_id))
|
sys.stdout.write(
|
||||||
|
"Successfully killed Plex session: {0}.".format(session_id))
|
||||||
|
if notifier:
|
||||||
|
if username:
|
||||||
|
body = BODY_TEXT_USER.format(user=username,
|
||||||
|
message=message)
|
||||||
|
else:
|
||||||
|
body = BODY_TEXT.format(id=session_id, message=message)
|
||||||
|
send_notification(SUBJECT_TEXT, body, notifier)
|
||||||
else:
|
else:
|
||||||
raise Exception(response['response']['message'])
|
raise Exception(response['response']['message'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write("Tautulli API 'terminate_session' request failed: {0}.".format(e))
|
sys.stderr.write(
|
||||||
|
"Tautulli API 'terminate_session' request failed: {0}.".format(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def terminate_long_pause(session_id, message, limit, interval, notify=None):
|
||||||
|
"""Kills the session if it is paused for longer than <limit> 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()
|
||||||
|
checked_time = 0
|
||||||
|
# Continue checking 2 intervals past the allowed limit in order to
|
||||||
|
# account for system variances.
|
||||||
|
check_limit = limit + (interval * 2)
|
||||||
|
|
||||||
|
while checked_time < check_limit:
|
||||||
|
sessions = get_activity()
|
||||||
|
found_session = False
|
||||||
|
|
||||||
|
for session in sessions:
|
||||||
|
if session['session_id'] == session_id:
|
||||||
|
found_session = True
|
||||||
|
state = session['state']
|
||||||
|
now = datetime.now()
|
||||||
|
checked_time = (now - start).total_seconds()
|
||||||
|
|
||||||
|
if state == 'paused':
|
||||||
|
if checked_time >= limit:
|
||||||
|
terminate_session(session_id, message, notify)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sleep(interval)
|
||||||
|
elif state == 'playing' or state == 'buffering':
|
||||||
|
sys.stdout.write(
|
||||||
|
"Session '{}' has resumed, ".format(session_id) +
|
||||||
|
"stopping monitoring.")
|
||||||
|
return
|
||||||
|
if not found_session:
|
||||||
|
sys.stdout.write(
|
||||||
|
"Session '{}' is no longer active ".format(session_id) +
|
||||||
|
"on the server, stopping monitoring.")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Killing Plex streams from Tautulli.")
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Killing Plex streams from Tautulli.")
|
||||||
parser.add_argument('--jbop', required=True, choices=SELECTOR,
|
parser.add_argument('--jbop', required=True, choices=SELECTOR,
|
||||||
help='Kill selector.\nChoices: (%(choices)s)')
|
help='Kill selector.\nChoices: (%(choices)s)')
|
||||||
parser.add_argument('--userId', type=int,
|
parser.add_argument('--userId', type=int,
|
||||||
@ -129,10 +252,15 @@ if __name__ == "__main__":
|
|||||||
help='The username of the person streaming.')
|
help='The username of the person streaming.')
|
||||||
parser.add_argument('--sessionId', required=True,
|
parser.add_argument('--sessionId', required=True,
|
||||||
help='The unique identifier for the stream.')
|
help='The unique identifier for the stream.')
|
||||||
|
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='+',
|
parser.add_argument('--killMessage', nargs='+',
|
||||||
help='Message to send to user whose stream is killed.')
|
help='Message to send to user whose stream is killed.')
|
||||||
parser.add_argument('--notify', type=int,
|
|
||||||
help='Notification Agent ID number to Agent to send notification.')
|
|
||||||
|
|
||||||
opts = parser.parse_args()
|
opts = parser.parse_args()
|
||||||
|
|
||||||
@ -142,12 +270,11 @@ if __name__ == "__main__":
|
|||||||
message = ''
|
message = ''
|
||||||
|
|
||||||
if opts.jbop == 'stream':
|
if opts.jbop == 'stream':
|
||||||
terminate_session(opts.sessionId, message)
|
terminate_session(opts.sessionId, message, opts.notify, opts.username)
|
||||||
elif opts.jbop == 'allStreams':
|
elif opts.jbop == 'allStreams':
|
||||||
streams = get_activity(opts.userId)
|
streams = get_user_session_ids(opts.userId)
|
||||||
for session_id in streams:
|
for session_id in streams:
|
||||||
terminate_session(session_id, message)
|
terminate_session(session_id, message, opts.notify, opts.username)
|
||||||
|
elif opts.jbop == 'paused':
|
||||||
if opts.notify:
|
terminate_long_pause(opts.sessionId, message, opts.limit,
|
||||||
BODY_TEXT = BODY_TEXT.format(user=opts.username, message=message)
|
opts.interval, opts.notify)
|
||||||
send_notification(SUBJECT_TEXT, BODY_TEXT, opts.notify)
|
|
||||||
|
@ -1,81 +1,166 @@
|
|||||||
## README
|
# README
|
||||||
|
|
||||||
Killing streams is a Plex Pass only feature. So these scripts will only work for Plex Pass users.
|
Killing streams is a Plex Pass only feature. So these scripts will **only** work for Plex Pass users.
|
||||||
|
|
||||||
|
## `kill_stream.py` examples:
|
||||||
|
|
||||||
|
### Kill transcodes
|
||||||
|
|
||||||
### Kill_stream.py examples:
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
|
||||||
#### Arguments examples:
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage Transcoding streams are not allowed.
|
||||||
|
```
|
||||||
|
|
||||||
Kill the one offending stream with a custom message and send notification to notfication agent ID 1
|
### Kill non-local streams paused for a long time
|
||||||
|
|
||||||
--jbop stream --userId {user_id} --username {username} --sessionId {session_id} --killMessage You did something wrong. --notify 1
|
|
||||||
|
|
||||||
Kill all the offending users streams with a custom message and send notification to notfication agent ID 1
|
_The default values will kill anything paused for over 20 minutes, checking every 30 seconds._
|
||||||
|
|
||||||
--jbop allStreams --userId {user_id} --username {username} --sessionId {session_id} --killMessage You did something wrong. --notify 1
|
|
||||||
|
|
||||||
Kill the one offending stream with default message
|
Script Timeout: 0 _**Important!**_
|
||||||
|
Triggers: Playback Paused
|
||||||
|
Conditions: \[ `Stream Local` | `is not` | `1` \]
|
||||||
|
|
||||||
--jbop stream --userId {user_id} --username {username} --sessionId {session_id}
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop paused --sessionId {session_id} --killMessage Your stream was paused for over 20 minutes and has been automatically stopped for you.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill streams paused for a custom time
|
||||||
|
|
||||||
#### Condition Examples:
|
_This is an example of customizing the paused stream monitoring to check every 15 seconds, and kill any stream paused for over 5 minutes._
|
||||||
|
|
||||||
Kill transcodes:
|
Script Timeout: 0 _**Important!**_
|
||||||
|
Triggers: Playback Paused
|
||||||
|
|
||||||
Set Trigger: Playback Start
|
Arguments:
|
||||||
Set Conditions: [ {Transcode Decision} | {is} | {transcode} ]
|
```
|
||||||
|
--jbop paused --interval 15 --limit 300 --sessionId {session_id} --killMessage Your stream was paused for over 5 minutes and has been automatically stopped for you.
|
||||||
|
```
|
||||||
|
|
||||||
Kill paused transcodes:
|
### Kill paused transcodes
|
||||||
|
|
||||||
Set Trigger: Playback Paused
|
|
||||||
Set Conditions: [ {Transcode Decision} | {is} | {transcode} ]
|
|
||||||
|
|
||||||
Limit User stream count, kill last stream:
|
Triggers: Playback Paused
|
||||||
|
Conditions: \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
Set Trigger: Playback Start
|
|
||||||
Set Conditions: [ {User Streams} | {is greater than} | {3} ]
|
|
||||||
|
|
||||||
IP Whitelist:
|
Arguments:
|
||||||
|
```
|
||||||
Set Trigger: Playback Start
|
--jbop stream --username {username} --sessionId {session_id} --killMessage Paused streams are automatically stopped.
|
||||||
Set Conditions: [ {IP Address} | {is not} | {192.168.0.100 or 192.168.0.101} ]
|
```
|
||||||
|
|
||||||
Kill by platform:
|
### Limit User stream count, kill last stream
|
||||||
|
|
||||||
Set Trigger: Playback Start
|
|
||||||
Set Conditions: [ {Platform} | {is} | {Roku or Android} ]
|
|
||||||
|
|
||||||
Kill transcode by library:
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `User Streams` | `is greater than` | `3` \]
|
||||||
Set Trigger: Playback Start
|
|
||||||
Set Conditions: [ {Transcode Decision} | {is} | {transcode} ]
|
|
||||||
[ {Library Name} | {is} | {4K Movies} ]
|
|
||||||
|
|
||||||
Kill transcode by original resolution:
|
Arguments:
|
||||||
|
```
|
||||||
Set Trigger: Playback Start
|
--jbop stream --username {username} --sessionId {session_id} --killMessage You are only allowed 3 streams.
|
||||||
Set Conditions: [ {Transcode Decision} | {is} | {transcode} ]
|
```
|
||||||
[ {Video Resolution} | {is} | {1080 or 720}]
|
|
||||||
|
|
||||||
Kill transcode by bitrate:
|
### IP Whitelist
|
||||||
|
|
||||||
Set Trigger: Playback Start
|
|
||||||
Set Conditions: [ {Transcode Decision} | {is} | {transcode} ]
|
|
||||||
[ {Bitrate} | {is greater than} | {4000} ]
|
|
||||||
|
|
||||||
Kill by hours of the day:
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `IP Address` | `is not` | `192.168.0.100 or 192.168.0.101` \]
|
||||||
Set Trigger: Playback Start
|
|
||||||
Set Conditions: [ {Timestamp} | {begins with} | {09 or 10} ]
|
|
||||||
# Killing any streams from 9am to 11am
|
|
||||||
|
|
||||||
Kill non local streams:
|
Arguments:
|
||||||
|
```
|
||||||
Set Trigger: Playback Start
|
--jbop stream --username {username} --sessionId {session_id} --killMessage {ip_address} is not allowed to access {server_name}.
|
||||||
Set Conditions: [ {Stream location} | {is} | {wan} ]
|
```
|
||||||
or
|
|
||||||
Set Conditions: [ {Stream location} | {is not} | {lan} ]
|
|
||||||
|
|
||||||
|
### Kill by platform
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Platform` | `is` | `Roku or Android` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage {platform} is not allowed on {server_name}.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill transcode by library
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions:
|
||||||
|
* \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
* \[ `Library Name` | `is` | `4K Movies` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage Transcoding streams are not allowed from the 4K Movies library.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill transcode by original resolution
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions:
|
||||||
|
* \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
* \[ `Video Resolution` | `is` | `1080 or 720`\]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage Transcoding streams are not allowed for {stream_video_resolution}p streams.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill transcode by bitrate
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions:
|
||||||
|
* \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
* \[ `Bitrate` | `is greater than` | `4000` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage Transcoding streams are not allowed from over 4 Mbps (Yours: {stream_bitrate}).
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill by hours of the day
|
||||||
|
|
||||||
|
_Kills any streams during 9 AM to 10 AM._
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Timestamp` | `begins with` | `09 or 10` \]
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage {server_name} is unavailable between 9 and 10 AM.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill non local streams
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Stream Local` | `is not` | `1` \]
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --killMessage {server_name} only allows local streams.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill transcodes and send a notification to agent 1
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id} --notify 1 --killMessage Transcoding streams are not allowed.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill transcodes using the default message
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Transcode Decision` | `is` | `transcode` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop stream --username {username} --sessionId {session_id}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kill all of a user's streams with notification
|
||||||
|
|
||||||
|
Triggers: Playback Start
|
||||||
|
Conditions: \[ `Username` | `is` | `Bob` \]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
```
|
||||||
|
--jbop allStreams --userId {user_id} --notify 1 --killMessage Hey Bob, we need to talk!
|
||||||
|
```
|
||||||
|
@ -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)
|
|
Loading…
x
Reference in New Issue
Block a user