JBOPS - Just a Bunch Of Plex Scripts

@blacktwin, @JonnyWong16 , @sander1 , @shopgirl284 or any other python dev, help?
Per my previous post, I’ve attempted to compose a ‘kill 3rd session’ python script heavily based on Blacktwin’s kill_more_than.py code.
I’ve mangled out every section that bases on IP, instead, looking for only what I think is user and session of the video.
I know Tautulli is set up correctly. The Email notifier emails me when I hit the 3 same user threshhold.
When I hit 3 sessions from one user (using me only in Tautulli conditions right now), I can see on my server a QUICK python window open up, but it closes so fast I can’t see any print statements or even if there were any.
Attached is my code.
Can you tell what I’ve done wrong?
Tips on how to figure out what I’ve done wrong?
Again, I don’t know python out-right, I just read most code well enough to tell what it’s doing.
Occasionally I can mangle and get stuff working.
Thanks in advance to anyone.
(File is a .py, renamed as .txt so forum wouldn’t reject)

@JamminR Good idea! Try this. The message is now an argument for Tautulli and the condition example to Time a user total stream count by killing the last one. Just add the condition to select that specific user.

This script is in a different branch. I’m going through all the existing scripts (especially killstreams) and updating them to lean more on the conditions. Was able to cut down on the number of scripts.

@blacktwin - Thanks. Re: kill_stream.py - getting error on script run. Not working
I’m on Win10Pro, Python 2.7.14 x64,
Plex network setting “Secure connection: Preferred” and network 192.168.1.x/255.255.255.0 allowed without auth.
Tautulli has “Enable HTTPS” with a LetsEncrypt cert.
Plex doesn’t use the custom cert domain, I let it do it’s own thing.

Tautulli Notifiers :: Script error:
Traceback (most recent call last):
    File "D:\plexcache\PlexrelatedFiles\Tautulli_scripts\Tautulli_KillSession.py", line 114, in <module>
        requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
    File "D:\PlexCache\Python\lib\site-packages\requests\api.py", line 112, in post
        return request('post', url, data=data, json=json, **kwargs)
    File "D:\PlexCache\Python\lib\site-packages\requests\api.py", line 58, in request
        return session.request(method=method, url=url, **kwargs)
    File "D:\PlexCache\Python\lib\site-packages\requests\sessions.py", line 508, in request
        resp = self.send(prep, **send_kwargs)
    File "D:\PlexCache\Python\lib\site-packages\requests\sessions.py", line 618, in send
        r = adapter.send(request, **kwargs)
    File "D:\PlexCache\Python\lib\site-packages\requests\adapters.py", line 506, in send
        raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8182): Max retries exceeded with url: /api/v2?cmd=terminate_session&message=%22Stream&apikey=********38&session_id=54 (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",),)) 

If it helps any for comparison, your “kill paused more than” works but with a warning

Tautulli Notifiers :: Script error:
    D:\PlexCache\Python\lib\site-packages\urllib3\connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
        InsecureRequestWarning)
    D:\PlexCache\Python\lib\site-packages\urllib3\connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
        InsecureRequestWarning)
    D:\PlexCache\Python\lib\site-packages\urllib3\connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
        InsecureRequestWarning)

@Blacktwin
Ok, it’s taken me about 5 hours of testing and hacking, but I’ve got it working. My first issue was SSL verification.
I force verify off. Not ideal, but will work for me for my local net.
Once I got that working and cancelling the stream, I realized that placing the cut off message in double, and even single, quotes, Tautulli was still passing individually, so “Session killed due to whatever” as cut message would always say “Session”

You may laugh it took me so long, but again, I can READ what most code does, that doesn’t mean I speak the language out-right.

As I don’t know Git well enough, or want to learn, to do pull request, I’ll let you decide whether or not my solutions to get my code working are necessary

Here they are - AS OF THIS POST - git lines
Line 105 -https://github.com/blacktwin/JBOPS/blob/fb7c16ee80ace4e298eb2e84c3b25a11f19547fd/killstream/kill_stream.py#L105
Current:
message = str(sys.argv[2])
Change I made so that it works (will show entire cut off message, technically, you wouldn’t even need quotes anymore (since they didn’t work to enclose from Tautllu in the first place)):
message = str(' '.join(sys.argv[2:]))

Line 112 – https://github.com/blacktwin/JBOPS/blob/fb7c16ee80ace4e298eb2e84c3b25a11f19547fd/killstream/kill_stream.py#L112
Current:
requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
Change I made (added 3 lines, inspired by your Pause More than code that turns verify false)

req = requests.Session()
req.verify = False
req.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)

Now, of course, with your expert skill, you are more than welcome to fix that last part in a more safe/secure way that doesn’t break the SSL lookup. I kept getting the error in my previous post, even if I set fallback URL to my external Tautulli SSL address that uses a LetsEncrypt cert.

Are you using the OVERRIDE or FALLBACK variables? If so, try leaving them blank.

@blacktwin - Originally, no, they’re left blank, and are blank now.
During my hours of testing, yes, I did try one, fallback as both http: //127.0.0.1:mytautulliport and https: //my.external:mytautulliport. Neither worked.
Even tried override as https: //my.external:mytautulliport

Suggestion - In kill_stream.py examples

   Limit User stream count, kill last stream:
      Set Trigger: Playback Start
      Set Conditions: [ {User Streams} | {is greater than} | {3} ]

Why not use…

   Limit User stream count, kill last stream:
      Set Trigger: User Concurrent Streams 
      Set Conditions: [none]

This prevents the script running every single play (slightly less resource usage/time for all scripts), and follows general settings from elsewhere (threshhold, and if checked, the different ip rule checkbox)

Technically the script won’t be running every Playback start because the condition will prevent it if not True. Using the Concurrent Streams means it’s limited/restricted to that global setting. So by using the User Streams on Playback Start and adding a specific User(s), you’ve have a clearer defined rule for that one User or group of Users. At least that was my thought process.

Also, something else I found in regards to the SSL issues.

try:

sess = requests.Session()
# Ignore verifying the SSL certificate
sess.verify = False # '/path/to/certfile'
# If verify is set to a path to a directory,
# the directory must have been processed using the c_rehash utility supplied with OpenSSL.

If you’re able to, you can add the direct path to your certfile to verify.

@Blacktwin said:
Technically the script won’t be running every Playback start because the condition will prevent it if not True.
At least that was my thought process.

Ugh. You’re absolutely right. I keep forgetting how powerful the condition system is and the rules it allows for.
Still getting used to Tautulli doing much of the preliminary work for me.

Also, something else I found in regards to the SSL issues.

try:

sess = requests.Session()
# Ignore verifying the SSL certificate
sess.verify = False # '/path/to/certfile'
# If verify is set to a path to a directory,
# the directory must have been processed using the c_rehash utility supplied with OpenSSL.

If you’re able to, you can add the direct path to your certfile to verify.

Trying the following code…

req = requests.Session()
req.verify = 'D:\\plexcache\\PlexrelatedFiles\\MyCert.crt'
req.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)

Fails with

Tautulli Notifiers :: Script error:
    Traceback (most recent call last):
        File "D:\plexcache\PlexrelatedFiles\Tautulli_scripts	inkering\Tautulli_KillSession_SSLTest.py", line 117, in <module>
            req.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
        File "D:\PlexCache\Python\lib\site-packages\requests\sessions.py", line 555, in post
            return self.request('POST', url, data=data, json=json, **kwargs)
        File "D:\PlexCache\Python\lib\site-packages\requests\sessions.py", line 508, in request
            resp = self.send(prep, **send_kwargs)
        File "D:\PlexCache\Python\lib\site-packages\requests\sessions.py", line 618, in send
            r = adapter.send(request, **kwargs)
        File "D:\PlexCache\Python\lib\site-packages\requests\adapters.py", line 506, in send
            raise SSLError(e, request=request)
    requests.exceptions.SSLError: HTTPSConnectionPool(host='jamminr.punked.us', port=8182): Max retries exceeded with url: /api/v2?cmd=terminate_session&message=YOU+SHALL+NOT+PAUSE%21%21%2C+er+PASS&apikey=********38&session_id=2e1eo0ylzlcu04k1nu7zqu5s (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",),)) 

I originally had all URL configs blank, then realized localhost didn’t have a cert, so set URL_FALLBACK to my external access that the cert works for. Same error though.

I wasn’t sure what it means by I must run c_rehash though - I don’t have OpenSSL installed, to my knowledge, unless it’s hidden in Python libraries somewhere.
(And in case you start wondering, yes, from the server, using a browser, I can access my public name as address, and as I’d expect, browsers give me error when attempting to access Tautulli using localhost as address due to Tautulli’s configured cert.

Hey @Blacktwin ,

Props on the ip_whitelist.py script! I’ve been looking for something like this FOREVER. I love it! Anyways, I was wondering if it would be possible for a tweak to the script.

For example, whenever playback is attempted and their IP is not in the whitelist…

I’d like to have an e-mail sent to me with the user, media name they were trying to watch, and the denied IP address if possible. This would be helpful for people that are computer illiterate and don’t understand how/where to get their public IP to provide to me to add them to the whitelist.

@bcanilao - Though it would be a bit of work if you have lots of IPs whitelisted, you could set a Tautulli Email agent to do the same with no code modification to whitelist required.
(Technically, with Tautulli, you wouldn’t even need whitelist.py, could just use kill_session with below triggers/basic setup of IPs in conditions instead of within the py script)

Email:
Basically, set email agent up, trigger “on play”. Conditions tab = for each ip, {ip_address} “is not” “1.2.3.4”, condition logic {1} or {2} or {3} or … number of IPs you have white listed in condition.
Then Send email message text
{user} / {username} attempted to play {title} and the IP {ip_address} wasn’t in Conditions list.

It would be a bit of extra work modifying the whitelist script manually, then going into Tautulli settings for that particular email agent and adding ip line and condition too, but, if you instead converted your whitelist agent to kill_session, then added ip is not X in conditions for it following the email above, you could use tautulli for all edits/additions.

Thanks @JamminR ! I got it all setup, tested, and working. I didn’t even think of using the conditions section (i’ve had Plex for years but just found out about Tautulli) that just opened a whole new world of possibilities for me!

@bcanilao - Welcome. Plexpy’s agent’s weren’t as configurable - but, as Blacktwin mentioned to me a few posts ago, he’s in agreement that most of the scripts for killing sessions aren’t even really required any longer now that Tautulli includes the conditions and condition order. They can now do much of the monitoring to see if scripts even need to be run (check the kill_session.py comments, several examples there showing it can replace a few of the old Plexpy ones.)

@JamminR said:
@bcanilao - Though it would be a bit of work if you have lots of IPs whitelisted, you could set a Tautulli Email agent to do the same with no code modification to whitelist required.
(Technically, with Tautulli, you wouldn’t even need whitelist.py, could just use kill_session with below triggers/basic setup of IPs in conditions instead of within the py script)

Email:
Basically, set email agent up, trigger “on play”. Conditions tab = for each ip, {ip_address} “is not” “1.2.3.4”, condition logic {1} or {2} or {3} or … number of IPs you have white listed in condition.
Then Send email message text
{user} / {username} attempted to play {title} and the IP {ip_address} wasn’t in Conditions list.

It would be a bit of extra work modifying the whitelist script manually, then going into Tautulli settings for that particular email agent and adding ip line and condition too, but, if you instead converted your whitelist agent to kill_session, then added ip is not X in conditions for it following the email above, you could use tautulli for all edits/additions.

You can do it with a single condition: IP Address is not 1.2.3.4 OR 1.2.3.5 OR 1.2.3.6 OR 1.2.3.7
Condition Logic can be left blank as well.

@JonnyWong16 said:
You can do it with a single condition: IP Address is not 1.2.3.4 OR 1.2.3.5 OR 1.2.3.6 OR 1.2.3.7
Condition Logic can be left blank as well.

Good to know, thanks.
Also, you should add a stream_paused_time to Tautulli.
As a condition, if stream_paused is 1800 - script action (I’d run kill session)
Using Blacktwin’s script for now, but not having to run external unless counter reached condition would be great.

@JamminR said:
Good to know, thanks.
Also, you should add a stream_paused_time to Tautulli.
As a condition, if stream_paused is 1800 - script action (I’d run kill session)
Using Blacktwin’s script for now, but not having to run external unless counter reached condition would be great.

I think the issue with that would be forcing Tautulli to start polling every time someone pauses a stream instead of only reacting to the change in actions. Using the scripts offloads that need for polling from Tautulli and allows for only running when requested (conditions).

@Blacktwin said:
I think the issue with that would be forcing Tautulli to start polling every time someone pauses a stream instead of only reacting to the change in actions.

True, but, I’m under the impression the Home page of Tautulli is doing some polling already when paused.
There’s likely no definitive rule, other than perhaps in @JonnyWong16 's master design plan, that says “Tautulli will never create it’s own triggers”. I imagine a new global setting (pause threshhold), and a new trigger (stream_pause_timedout).
Big imagination, I know. :smiley:

I asked in the general forum about being able to download posters and artwork (see this post - http://forums.plex.tv/discussion/323262/download-posters-artwork) and it was suggested that one of the JBOPS scripts would possibly do it. Although I have python installed for using Tautulli, I am not very literate. So, I have a few questions…

I go to the link and can see the script but how do I download it (do I just copy the text to a notepad and name it xxxxx.py?
Once I download the script, how do I run it?
Does it need to be in a particular folder?
Will it go through all of my libraries or does it have to be run from each individually?
Does it download the posters and the fanart?
Does the script name the files correctly so that Plex uses that artwork as its default?
Movies and TV Shows? Music?

I know, a lot of questions! I had been using the little app created by @“MovieFan.Plex” but it stopped working a while ago and he hasn’t had time to fix it. I am more than willing to donate to get a script that does all of the things that I need, I just can’t do it myself.

Thanks for any help you can provide!

@JamminR @Blacktwin,
The below diff will suppress the ‘Unverified HTTPS request is being made’ warnings from wait_kill_paused_notify.py.

49a50,53
> from requests.packages.urllib3.exceptions import InsecureRequestWarning
> 
> requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
> 
66d69
< sess.verify = False

(edited to replace attachment with code block)

@rbeatse

I go to the link and can see the script but how do I download it (do I just copy the text to a notepad and name it xxxxx.py?
copy and paste is fine.

Once I download the script, how do I run it?
once all the variables are set you should be able to double click it or from cmd python path/to/script.py
Does it need to be in a particular folder?
no
Will it go through all of my libraries or does it have to be run from each individually?
It will go through all the libraries that are list in library_name
Does it download the posters and the fanart?
Will download the current poster for that series/movie
Does the script name the files correctly so that Plex uses that artwork as its default?
Movies and TV Shows? Music?
No, it will name the image files as the title name of that series/movie in the same directory as the script.

script.py
\movie
   |---deadpool (year).png
	v shows
   |---friends (year).png

Not sure what you’re trying to do. You want to pull the posters then put them in the folders of the titles? Why? Plex has the poster already set. You are making a copy of the poster into the title directory.

The script can be changed to meet your needs though.