Skip to content

Instantly share code, notes, and snippets.

@blacktwin
Last active June 11, 2018 16:43
Show Gist options
  • Save blacktwin/e1d199d98b258d6f2658dd9991c88ca0 to your computer and use it in GitHub Desktop.
Save blacktwin/e1d199d98b258d6f2658dd9991c88ca0 to your computer and use it in GitHub Desktop.
Receive session_key from PlexPy when paused. Use session_id to create sub-script to wait for X time then check if still paused. If paused kill.
'''
fetch function from https://gist.github.com/Hellowlol/ee47b6534410b1880e19
PlexPy > Settings > Notification Agents > Scripts > Bell icon:
[X] Notify on pause
PlexPy > Settings > Notification Agents > Scripts > Gear icon:
Playback Pause: create_wait_kill_all.py
PlexPy > Settings > Notifications > Script > Script Arguments:
{session_key}
create_wait_kill_all.py creates a new file with the session_id (sub_script) as it's name.
PlexPy will timeout create_wait_kill_all.py after 30 seconds (default) but sub_script.py will continue.
sub_script will check if the stream's session_id is still pause or if playing as restarted.
If playback is restarted then sub_script will stop and delete itself.
If stream remains paused then it will be killed and sub_script will stop and delete itself.
Set TIMEOUT to max time before killing stream
Set INTERVAL to how often you want to check the stream status
'''
import os
import platform
import subprocess
import sys
from uuid import getnode
import unicodedata
import requests
## EDIT THESE SETTINGS ##
PLEX_HOST = ''
PLEX_PORT = 32400
PLEX_SSL = '' # s or ''
PLEX_TOKEN = ''
PLEXPY_APIKEY = 'xxxxxxx' # Your PlexPy API key
PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL
TIMEOUT = 120
INTERVAL = 20
REASON = 'Because....'
ignore_lst = ('test')
class Activity(object):
def __init__(self, data=None):
d = data or {}
self.video_decision = d['video_decision']
self.state = d['state']
self.session_key = d['session_key']
def get_get_activity():
# Get the user IP list from PlexPy
payload = {'apikey': PLEXPY_APIKEY,
'cmd': 'get_activity'}
try:
r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload)
response = r.json()
res_data = response['response']['data']['sessions']
return [Activity(data=d) for d in res_data]
except Exception as e:
sys.stderr.write("PlexPy API 'get_get_activity' request failed: {0}.".format(e))
def fetch(path, t='GET'):
url = 'http%s://%s:%s/' % (PLEX_SSL, PLEX_HOST, PLEX_PORT)
headers = {'X-Plex-Token': PLEX_TOKEN,
'Accept': 'application/json',
'X-Plex-Provides': 'controller',
'X-Plex-Platform': platform.uname()[0],
'X-Plex-Platform-Version': platform.uname()[2],
'X-Plex-Product': 'Plexpy script',
'X-Plex-Version': '0.9.5',
'X-Plex-Device': platform.platform(),
'X-Plex-Client-Identifier': str(hex(getnode()))
}
try:
if t == 'GET':
r = requests.get(url + path, headers=headers, verify=False)
elif t == 'POST':
r = requests.post(url + path, headers=headers, verify=False)
elif t == 'DELETE':
r = requests.delete(url + path, headers=headers, verify=False)
if r and len(r.content): # incase it dont return anything
return r.json()
else:
return r.content
except Exception as e:
print e
def kill_stream(sessionId, message, xtime, ntime, user, title, sessionKey):
headers = {'X-Plex-Token': PLEX_TOKEN}
params = {'sessionId': sessionId,
'reason': message}
activity = get_get_activity()
for a in activity:
if a.session_key == sessionKey:
if a.state == 'paused' and xtime == ntime:
sys.stdout.write("Killing {user}'s paused stream of {title}".format(user=user, title=title))
requests.get('http{}://{}:{}/status/sessions/terminate'.format(PLEX_SSL, PLEX_HOST, PLEX_PORT),
headers=headers, params=params)
return ntime
elif a.state in ('playing', 'buffering'):
sys.stdout.write("{user}'s stream of {title} is now {state}".format(user=user, title=title,
state=a.state))
return None
else:
return xtime
def find_sessionID(response):
sessions = []
for s in response['MediaContainer']['Video']:
if s['sessionKey'] == sys.argv[1]:
sess_id = s['Session']['id']
user = s['User']['title']
sess_key = s['sessionKey']
title = (s['grandparentTitle'] + ' - ' if s['type'] == 'episode' else '') + s['title']
title = unicodedata.normalize('NFKD', title).encode('ascii','ignore')
sessions.append((sess_id, user, title, sess_key))
else:
pass
for session in sessions:
if session[1] not in ignore_lst:
return session
else:
print("{}'s stream of {} is ignored.".format(session[1], session[2]))
return None
if __name__ == '__main__':
startupinfo = None
if os.name == 'nt':
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
response = fetch('status/sessions')
fileDir = fileDir = os.path.dirname(os.path.realpath(__file__))
try:
if find_sessionID(response):
stream_info = find_sessionID(response)
file_name = "{}.py".format(stream_info[0])
full_path = os.path.join(fileDir, file_name)
file = "from time import sleep\n" \
"import sys, os\n" \
"from {script} import kill_stream \n" \
"message = '{REASON}'\n" \
"sessionID = os.path.basename(sys.argv[0])[:-3]\n" \
"x = 0\n" \
"n = {ntime}\n" \
"try:\n" \
" while x < n and x is not None:\n" \
" sleep({xtime})\n" \
" x += kill_stream(sessionID, message, {xtime}, n, '{user}', '{title}', '{sess_key}')\n" \
" kill_stream(sessionID, message, {ntime}, n, '{user}', '{title}', '{sess_key}')\n" \
" os.remove(sys.argv[0])\n" \
"except TypeError as e:\n" \
" os.remove(sys.argv[0])".format(script=os.path.basename(__file__)[:-3],
ntime=TIMEOUT, xtime=INTERVAL, REASON=REASON,
user=stream_info[1], title=stream_info[2],
sess_key=stream_info[3])
with open(full_path, "w+") as output:
output.write(file)
subprocess.Popen([sys.executable, full_path], startupinfo=startupinfo)
exit(0)
except TypeError as e:
print(e)
pass
@PokeFoundry
Copy link

Hello,

I'm getting a different error then what's above.

  PlexPy Notifiers :: Script error:     Traceback (most recent call last):         File "/scripts/kill_trans_pause.py", line 135, in             if find_sessionID(response):         File "/scripts/kill_trans_pause.py", line 104, in find_sessionID             if video['sessionKey'] == sys.argv[1] and video['Player']['state'] == 'paused' \     IndexError: list index out of range

@blacktwin
Copy link
Author

@ajhong2 @Foebik I've updated this script to match the most recent updates found in the JBOPS repo. Please use the repo to report any errors found.

@derailius
Copy link

I am getting:

2017-09-07 14:14:38 INFO PlexPy Notifiers :: Script notification sent.
2017-09-07 14:14:38 DEBUG PlexPy Notifiers :: Script returned:     string indices must be integers, not str
2017-09-07 14:14:37 DEBUG PlexPy Notifiers :: Executing script in a new thread.
2017-09-07 14:14:37 DEBUG PlexPy Notifiers :: Full script is: ['python', u'/opt/plexpy/plexpy-custom-scripts/create_wait_kill_all.py', '{session_key}']
2017-09-07 14:14:37 DEBUG PlexPy Notifiers :: Trying to run notify script, action: test, arguments: {session_key}

@blurb2m
Copy link

blurb2m commented Sep 10, 2017

My initial testing is working flawlessly on my unRAID server with Plex and Plexpy running in their own dockers.
Slack is currently setup to report concurrent streams from the same user and when users are streaming from a new device.
I was wondering if there was a way to have it send a message to my slack notification client upon killing a stream. This would be awesome.
Thanks for your great project!

@blacktwin
Copy link
Author

blacktwin commented Sep 13, 2017

@derailius You need to actually pass an argument for the script to work. Using the Test Script button will not work unless you know the session_key and enter it into the argument field in the Test Script section.

@blurb2m Yes you add that functionality. Check out this notify script for using PlexPy's send_notification call. Let me know if you need any help.

All please use the JBOPS repo for issues as gists don't ping me when you create a comment.

@cnewcome
Copy link

cnewcome commented Dec 7, 2017

I had a file that had a title with an apostrophe in it, so I had to modify the title on the output to file portion:

           file = "from time import sleep\n" \
                   "import sys, os\n" \
                   "from {script} import kill_stream \n" \
                   "message = '{REASON}'\n" \
                   "sessionID =  os.path.basename(sys.argv[0])[:-3]\n" \
                   "x = 0\n" \
                   "n = {ntime}\n" \
                   "try:\n" \
                   "    while x < n and x is not None:\n" \
                   "        sleep({xtime})\n" \
                   "        x += kill_stream(sessionID, message, {xtime}, n, '{user}', '''{title}''', '{sess_key}')\n" \
                   "    kill_stream(sessionID, message, {ntime}, n, '{user}', '''{title}''', '{sess_key}')\n" \
                   "    os.remove(sys.argv[0])\n" \
                   "except TypeError as e:\n" \
                   "    os.remove(sys.argv[0])".format(script=os.path.basename(__file__)[:-3],
                                                       ntime=TIMEOUT, xtime=INTERVAL, REASON=REASON,
                                                       user=stream_info[1], title=stream_info[2],
                                                       sess_key=stream_info[3])

@royalchinook
Copy link

I was stuck trying to get this script to work for awhile but since I'm using Tautulli I needed the newer scripts from https://github.com/blacktwin/JBOPS/tree/master/killstream

@BombshellBill
Copy link

@royalchinook
thanks for that. i was just searching around, figuring there had to be a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment