-
-
Save blacktwin/e1d199d98b258d6f2658dd9991c88ca0 to your computer and use it in GitHub Desktop.
''' | |
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 |
@christronyxyocum thanks! Updated with the correction.
No problem! Glad I finally figured it out. Thanks for all your work on this and for dealing with me for so long, haha.
Hello,
I just manually tested this script on a paused stream after it didn't trigger for my 5 minute timeout. I got the following error:
PlexPy Notifiers :: Script error:
Traceback (most recent call last):
File "/opt/plexpy/scripts/create_wait_kill_all.py", line 25, in
import requests
ImportError: No module named requests
Am I missing a prerequisite of some kind? This is the only plexpy script I've deployed.
@MostDefiantly you need to install requests
I've been getting this error. Any ideas?
PlexPy Notifiers :: Script error:
Traceback (most recent call last):
File "C:\Scripts\create_wait_kill_all.py", line 155, in
if find_sessionID(response):
File "C:\Scripts\create_wait_kill_all.py", line 128, in find_sessionID
if s['sessionKey'] == sys.argv[1]:
IndexError: list index out of range
@Foebik I've updated the script.
I tried it again, the first error is gone, but now Im seeing these.
I think the first one was me manually killing it, but can't be sure.
Aug 21, 2017 22:44:08.912 | ERROR | Session 10423758 terminated
Aug 21, 2017 22:41:28.512 | ERROR | Had trouble breaking 1503369683807
Aug 21, 2017 22:41:28.512 | ERROR | ERROR: Parsing request failed.
Aug 21, 2017 22:41:28.512 | ERROR | Error parsing HTTP request: PT8Iibcd92di0sZIVoygTBAMWnKEBX5OHJlHGZSYUDc6doDegpl8azCm%252bsdPSOiIBzaXt9cdORVRH4sJI6ruJupn%252f6jmw1%252fXZ4fjQqQSm0F8s%252bHylG8mOl%252bC5wjyBDCUKESyXXgGkaTqcUTdJw9cjJExYwdfOb7waLM%252bRud0Nldi7xcCy%252byZmT009jVWmjRGHtfE6u1ga6S6ZXMGsOqbmSYMtE03fV6tXs751hWvLeEj2e2%252fjGwUUkTj2RC%252btT2E6; snatched_view=list; soon_view=thumb; suggest_view=thumb; late_view=list; session_id=a967bb956592885a9067535986fd19926449be3a
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 |
---|
@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.
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} |
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!
@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.
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])
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
@royalchinook
thanks for that. i was just searching around, figuring there had to be a new one.
Yes, and it still doesn't do anything with the single quotes around the title that includes an apostrophe so it breaks the script. Here's a screenshot with it open in my editor so you can see it with syntax highlighting:
Imgur
Wrapping the title in double quotes seems to syntacically work, but I cannot figure out how to achieve that.
Edit: Ok, I believe I fixed it. Changed the double quotes to single quotes for lines 164 & 165, and then the single quotes around the three variables within those two lines to double quotes which results in the values of those three variables being wrapped in double quotes within the temp script.
Imgur
Result:
Imgur
No syntax error and I can see the temp script is still running in the background so I believe it is now working with titles that include an apostrophe.
Edit #2: It works.