Skip to content

Instantly share code, notes, and snippets.

@blacktwin
Last active October 27, 2020 21:33
Show Gist options
  • Save blacktwin/2f619e62d99edcec27f680998379664c to your computer and use it in GitHub Desktop.
Save blacktwin/2f619e62d99edcec27f680998379664c to your computer and use it in GitHub Desktop.
This is indented to restrict a user to the LIMIT amount of concurrent streams. User will be warned, punished, and banned completely if violations continue.
"""
Share functions from https://gist.github.com/JonnyWong16/f8139216e2748cb367558070c1448636
Once user stream count hits LIMIT they are unshared from libraries expect for banned library.
Once user stream count is below LIMIT and banned library video is watched their shares are restored.
Once violation count have been reached ban permanently.
Caveats:
Unsharing doesn't pause the stream.
After unsharing user can pause but not skip ahead or skip back.
Going from unshared to shared while playing still kills the connection to the server.
Effected user will need to refresh browser/app or restart app to reconnect.
User watch record stop when unshare is executed.
If user finishes a movie/show while unshared they will not have that record.
PlexPy will not have that record.
Adding to PlexPy
PlexPy > Settings > Notification Agents > Scripts > Bell icon:
[X] Notify on playback start
[X] Notify on watched
PlexPy > Settings > Notification Agents > Scripts > Gear icon:
Playback Start: stream_limiter_ban_email.py
Playback Watched: stream_limiter_ban_email.py
If used in PlexPy:
PlexPy will continue displaying that user is watching after unshare is executed in ACTIVITY.
PlexPy will update after ~5 minutes and no longer display user's stream in ACTIVITY.
PlexPy will think that user has stopped.
Create new library with one video.
Name library and video whatever you want.
My suggestion:
Video: https://www.youtube.com/watch?v=dQw4w9WgXcQ
Video Summary: "You have been banned for one reason or another. Please watch this video to become un-banned.
The entire video must be watched in order to trigger un-ban."
Change Video Poster and Background.
Entire video needs to be watched to trigger normal sharing.
This is indented to restrict a user to the LIMIT amount of concurrent streams. User will be warned, punished, and
banned completely if violations continue.
Clear user history for banned video to remove violation counts and run manually to share again.
Concurrent stream count is the trigger. Trigger can be anything you want.
"""
import requests
import sys
from xml.dom import minidom
from email.mime.text import MIMEText
import email.utils
import smtplib
## EDIT THESE SETTINGS ###
PLEXPY_APIKEY = 'XXXXXX' # Your PlexPy API key
PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL
PLEX_TOKEN = "<token>"
SERVER_ID = "XXXXX" # Example: https://i.imgur.com/EjaMTUk.png
# Get the User IDs and Library IDs from
# https://plex.tv/api/servers/SERVER_ID/shared_servers
# Example: https://i.imgur.com/yt26Uni.png
# Enter the User IDs and Library IDs in this format below:
# {UserID1: [LibraryID1, LibraryID2],
# UserID2: [LibraryID1, LibraryID2]}
USER_LIBRARIES = {123456: [123456, 123456, 123456, 123456, 123456, 123456]}
BAN_LIBRARY = {123456: [123456]} # {UserID1: [LibraryID1], UserID2: [LibraryID1]}
BAN_RATING = 123456 # Banned rating_key to check if it's been watched.
LIMIT = 3
VIOLATION_LIMIT = 3
FIRST_WARN = 'Please watch this video to become un-banned. The whole video must be watched in order to trigger un-ban.'
FINAL_WARN = '3 Strikes you are out! You are permanently banned from my Plex.'
SUBJECT_TEXT = "Plex rules have been violated."
BODY_TEXT = """\
<html>
<head></head>
<body>
<p>Hello,<br>
<br>{f}, you have been banned from my Plex and unshared from most of my libraries.
This is your violation {v} of {vt}.<br>
<br>{m}<br>
<br><br>
</p>
</body>
</html>
"""
# Email settings
name = '' # Your name
sender = '' # From email address
email_server = 'smtp.gmail.com' # Email server (Gmail: smtp.gmail.com)
email_port = 587 # Email port (Gmail: 587)
email_username = '' # Your email username
email_password = '' # Your email password
## DO NOT EDIT BELOW ##
class Activity(object):
def __init__(self, data=None):
d = data or {}
self.rating_key = d['rating_key']
self.title = d['full_title']
self.user = d['user']
self.user_id = d['user_id']
self.video_decision = d['video_decision']
self.transcode_decision = d['transcode_decision']
self.transcode_key = d['transcode_key']
self.state = d['state']
class Users(object):
def __init__(self, data=None):
d = data or {}
self.email = d['email']
self.user_id = d['user_id']
self.friendly_name = d['friendly_name']
def get_get_user(user_id):
# Get the user list from PlexPy.
payload = {'apikey': PLEXPY_APIKEY,
'cmd': 'get_user',
'user_id': int(user_id)}
try:
r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload)
response = r.json()
email = response['response']['data']['email']
friend_name = response['response']['data']['friendly_name']
return [email, friend_name]
except Exception as e:
sys.stderr.write("PlexPy API 'get_get_user' request failed: {0}.".format(e))
def get_get_history(user_id, bankey):
# Get the user history from PlexPy. Length matters!
payload = {'apikey': PLEXPY_APIKEY,
'cmd': 'get_history',
'rating_key': bankey,
'user_id': user_id,
'length': 10000}
try:
r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload)
response = r.json()
rec_filtered = response['response']['data']['recordsFiltered']
# grow this out how you will
if rec_filtered < VIOLATION_LIMIT:
return rec_filtered
elif rec_filtered >= VIOLATION_LIMIT:
return 'ban'
except Exception as e:
sys.stderr.write("PlexPy API 'get_get_history' request failed: {0}.".format(e))
def share(user_id, ban):
headers = {"X-Plex-Token": PLEX_TOKEN,
"Accept": "application/json"}
url = "https://plex.tv/api/servers/" + SERVER_ID + "/shared_servers"
if ban == 1:
library_ids = BAN_LIBRARY[user_id]
else:
library_ids = USER_LIBRARIES[user_id]
payload = {"server_id": SERVER_ID,
"shared_server": {"library_section_ids": library_ids,
"invited_id": user_id}
}
r = requests.post(url, headers=headers, json=payload)
if r.status_code == 401:
print("Invalid Plex token")
return
elif r.status_code == 400:
print(r.content)
return r.status_code
elif r.status_code == 200:
print("Shared libraries with user %s" % str(user_id))
return
return
def unshare(user_id):
headers = {"X-Plex-Token": PLEX_TOKEN,
"Accept": "application/json"}
url = "https://plex.tv/api/servers/" + SERVER_ID + "/shared_servers"
r = requests.get(url, headers=headers)
if r.status_code == 401:
print("Invalid Plex token")
return
elif r.status_code == 400:
print r.content
return
elif r.status_code == 200:
response_xml = minidom.parseString(r.content)
MediaContainer = response_xml.getElementsByTagName("MediaContainer")[0]
SharedServer = MediaContainer.getElementsByTagName("SharedServer")
shared_servers = {int(s.getAttribute("userID")): int(s.getAttribute("id"))
for s in SharedServer}
server_id = shared_servers.get(user_id)
if server_id:
url = "https://plex.tv/api/servers/" + SERVER_ID + "/shared_servers/" + str(server_id)
r = requests.delete(url, headers=headers)
if r.status_code == 200:
print("Unshared libraries with user %s" % str(user_id))
else:
print("No libraries shared with user %s" % str(user_id))
return
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 send_notification(to=None, friendly=None, val_cnt=None, val_tot=None, mess=None):
# Format notification text
try:
email_subject = SUBJECT_TEXT
body = BODY_TEXT.format(f=friendly, v=val_cnt, vt=val_tot, m=mess)
message = MIMEText(body, 'html')
message['Subject'] = email_subject
message['From'] = email.utils.formataddr((name, sender))
mailserver = smtplib.SMTP(email_server, email_port)
mailserver.ehlo()
mailserver.starttls()
mailserver.ehlo()
mailserver.login(email_username, email_password)
mailserver.sendmail(sender, to, message.as_string())
mailserver.quit()
print('Email sent')
except Exception as e:
sys.stderr.write("Email Failure: {0}.".format(e))
if __name__ == "__main__":
activity = get_get_activity()
act_lst = [a.user_id for a in activity]
user_lst = [key for key, value in USER_LIBRARIES.iteritems()]
BAN = 1
UNBAN = 0
for i in user_lst:
history = get_get_history(i, BAN_RATING)
mail_add, friendly = get_get_user(i)
try:
if act_lst.count(i) >= LIMIT:
# Trigger for first and next violation
unshare(i) # Remove libraries
share(i, BAN) # Share banned library
sys.stdout.write("Shared BAN_LIBRARY with user {0}".format(i))
if type(history) is int:
# Next violation, history of banned video.
send_notification(mail_add, friendly, history, VIOLATION_LIMIT, FIRST_WARN)
else:
# First violation, no history of banned video.
send_notification(mail_add, friendly, 1, VIOLATION_LIMIT, FIRST_WARN)
# email address, friendly name, violation number, violation limit, message
elif type(history) is int:
# Trigger to share
if share(i, UNBAN) == 400:
exit() # User has history of watching banned video but libraries are already restored.
else:
unshare(i) # Remove banned library
share(i, UNBAN) # Restore libraries
elif history == 'ban':
# Trigger for ban
unshare(i)
send_notification(mail_add, friendly, VIOLATION_LIMIT, VIOLATION_LIMIT, FINAL_WARN)
# email address, friendly name, violation number, violation limit, message
sys.stdout.write("User {0} has been banned".format(i))
except Exception as e:
sys.stderr.write("Share_unshare failed: {0}.".format(e))
@GilbN
Copy link

GilbN commented Aug 1, 2017

I keep getting

PlexPy Users :: Unable to retrieve user XXXXXXX from database. Returning 'Local' user.
--

What am I doing wrong?

@blacktwin
Copy link
Author

@GilbN Who is the user? Is it admin? Plex accounts are free, create a test account?

@Tangogr
Copy link

Tangogr commented Apr 15, 2020

Is it possible to configure this script to ALL users?
And ALL libraries?
Can I use some sort of wildcard?
BAN_LIBRARY = {ALL_USERS: [ALL_LIBRARIES_]} # {UserID1: [LibraryID1], UserID2: [LibraryID1]}
Thks!

Edit: moved to request.

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