From 3913e23804e9320b0a4ea13ff3a5ba84637a7448 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Wed, 14 Mar 2018 13:21:33 -0400 Subject: [PATCH 01/59] fix descriptions --- utility/plex_api_share.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/utility/plex_api_share.py b/utility/plex_api_share.py index 1a9324f..db56fce 100644 --- a/utility/plex_api_share.py +++ b/utility/plex_api_share.py @@ -10,13 +10,13 @@ optional arguments: --add Add additional libraries. --remove Remove existing libraries. --user [ ...] Space separated list of case sensitive names to process. Allowed names are: - (choices: All users names) + --allUsers Select all users. --libraries [ ...] Space separated list of case sensitive names to process. Allowed names are: (choices: All library names) (default: All Libraries) - + --allLibraries Select all libraries. --sync Allow user to sync content --camera Allow user to upload photos --channel Allow user to utilize installed channels @@ -48,7 +48,7 @@ Usage: plex_api_share.py --user USER --add --libraries Movies - Adds Movies library share to USER - plex_api_share.py --allUsers --remove --libraries Movies + plex_api_share.py --allUsers --remove --libraries Movies - Removes Movies library share from all Users plex_api_share.py --unshare --user USER @@ -72,6 +72,7 @@ from plexapi.server import PlexServer from time import sleep import argparse import requests +import json PLEX_URL = 'http://localhost:32400' PLEX_TOKEN = 'xxxx' @@ -96,6 +97,7 @@ def get_ratings_lst(section_id): content = requests.get("{}/library/sections/{}/contentRating".format(PLEX_URL, section_id), headers=headers, params=params) + # print(json.dumps(content.json(), indent=4, sort_keys=True)) ratings_keys = content.json()['MediaContainer']['Directory'] ratings_lst = [x['title'] for x in ratings_keys] return ratings_lst @@ -131,7 +133,7 @@ def share(user, sections, allowSync, camera, channels, filterMovies, filterTelev plex.myPlexAccount().updateFriend(user=user, server=plex, sections=sections, allowSync=allowSync, allowCameraUpload=camera, allowChannels=channels, filterMovies=filterMovies, filterTelevision=filterTelevision, filterMusic=filterMusic) - print('Shared libraries: {sections} with {user}.'.format(sections=list(set(sections)), user=user)) + print('Shared libraries: {sections} with {user}.'.format(sections=sections, user=user)) def unshare(user, sections): @@ -171,7 +173,7 @@ if __name__ == "__main__": help='Space separated list of case sensitive names to process. Allowed names are: \n' '(choices: %(choices)s') parser.add_argument('--allLibraries', default=False, action='store_true', - help='Select all users.') + help='Select all libraries.') parser.add_argument('--sync', default=False, action='store_true', help='Use to allow user to sync content.') parser.add_argument('--camera', default=False, action='store_true', From fc639507c8c6082703e8a4d5d9e1920d3c4c99e5 Mon Sep 17 00:00:00 2001 From: "Nicholas St. Germain" Date: Wed, 14 Mar 2018 12:44:50 -0500 Subject: [PATCH 02/59] Create purge_removed_plex_friends.py --- utility/purge_removed_plex_friends.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 utility/purge_removed_plex_friends.py diff --git a/utility/purge_removed_plex_friends.py b/utility/purge_removed_plex_friends.py new file mode 100644 index 0000000..4390b9a --- /dev/null +++ b/utility/purge_removed_plex_friends.py @@ -0,0 +1,27 @@ +import requests +from plexapi.myplex import MyPlexAccount + +TAUTULLI_BASE_URL = '192.168.1.100:8181' +TAUTULLI_API_KEY = 'asd8a9sd8789asd87f9aasdf' + +PLEX_USERNAME = 'someuser' +PLEX_PASSWORD = 'somepassword' + +# Do not edit past this line # +account = MyPlexAccount(PLEX_USERNAME, PLEX_PASSWORD) + +payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_user_names'} +tautulli_users = requests.get('http://{}/api/v2' + .format(TAUTULLI_BASE_URL), params=payload).json()['response']['data'] + +plex_friend_ids = [friend.id for friend in account.users()] +tautulli_user_ids = [user['user_id'] for user in tautulli_users] + +removed_user_ids = [user_id for user_id in tautulli_user_ids if user_id not in plex_friend_ids] + +if removed_user_ids: + payload['cmd'] = 'delete_user' + + for user_id in removed_user_ids: + payload['user_id'] = user_id + remove_user = requests.get('http://{}/api/v2'.format(TAUTULLI_BASE_URL), params=payload) From 735da3311d3573c2be66de68f610d4f1a1feda25 Mon Sep 17 00:00:00 2001 From: "Nicholas St. Germain" Date: Wed, 14 Mar 2018 13:07:56 -0500 Subject: [PATCH 03/59] added backup of db --- utility/purge_removed_plex_friends.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utility/purge_removed_plex_friends.py b/utility/purge_removed_plex_friends.py index 4390b9a..cfc4886 100644 --- a/utility/purge_removed_plex_friends.py +++ b/utility/purge_removed_plex_friends.py @@ -7,6 +7,9 @@ TAUTULLI_API_KEY = 'asd8a9sd8789asd87f9aasdf' PLEX_USERNAME = 'someuser' PLEX_PASSWORD = 'somepassword' +# Do you want to back up the database before deleting? +BACKUP_DB = True + # Do not edit past this line # account = MyPlexAccount(PLEX_USERNAME, PLEX_PASSWORD) @@ -19,6 +22,10 @@ tautulli_user_ids = [user['user_id'] for user in tautulli_users] removed_user_ids = [user_id for user_id in tautulli_user_ids if user_id not in plex_friend_ids] +if BACKUP_DB: + payload['cmd'] = 'backup_db' + backup = requests.get('http://{}/api/v2'.format(TAUTULLI_BASE_URL), params=payload) + if removed_user_ids: payload['cmd'] = 'delete_user' From 8ae872abc1f3092bbf3044fc04b0391ce678a681 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 16 Mar 2018 10:39:33 -0400 Subject: [PATCH 04/59] tautulli new branch for tautulli fixes. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2162161..e9d3116 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Most of these scripts utilize a combination of [PlexPy](https://github.com/JonnyWong16/plexpy), [python-plexapi](https://github.com/pkkid/python-plexapi), and [requests](http://docs.python-requests.org/en/master/user/install/#install). ## Notice: -These scripts have not been tested using Tautulli. The improvements in Tautulli may cause errors with scripts that pull data from Tautulli, especailly metadata. If you come into a problem please create an issue and reference the script that errors out with Tautulli. Once Tautulli is out of Beta I'll look into which scripts need updated and which can be removed. +Updating for Tautulli ## Scripts List [![Gist](https://img.shields.io/badge/gist-Blacktwin-green.svg)](https://gist.github.com/blacktwin) From c9de13dab84e460c218d4c8dce15ccf26fba5733 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 10:54:33 -0400 Subject: [PATCH 05/59] removing archive --- .idea/vcs.xml | 6 + archive/create_wait_kill_all.py | 187 ---------------------------- archive/create_wait_kill_trans.py | 172 -------------------------- archive/notify_geomail.py | 198 ------------------------------ 4 files changed, 6 insertions(+), 557 deletions(-) create mode 100644 .idea/vcs.xml delete mode 100644 archive/create_wait_kill_all.py delete mode 100644 archive/create_wait_kill_trans.py delete mode 100644 archive/notify_geomail.py diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/archive/create_wait_kill_all.py b/archive/create_wait_kill_all.py deleted file mode 100644 index c1a5a46..0000000 --- a/archive/create_wait_kill_all.py +++ /dev/null @@ -1,187 +0,0 @@ -''' -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{}://{}:{}/'.format(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 video in response['MediaContainer']['Video']: - if video['sessionKey'] == sys.argv[1]: - sess_id = video['Session']['id'] - user = video['User']['title'] - sess_key = video['sessionKey'] - title = (video['grandparentTitle'] + ' - ' if video['type'] == 'episode' else '') + video['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 diff --git a/archive/create_wait_kill_trans.py b/archive/create_wait_kill_trans.py deleted file mode 100644 index 75ac9a0..0000000 --- a/archive/create_wait_kill_trans.py +++ /dev/null @@ -1,172 +0,0 @@ -''' -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_trans.py - -PlexPy > Settings > Notifications > Script > Script Arguments: - {session_key} - - -create_wait_kill_trans.py creates a new file with the session_id (sub_script) as it's name. -PlexPy will timeout create_wait_kill_trans.py after 30 seconds (default) but sub_script.py will continue. -sub_script will check if the transcoding and 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 = 'xxxxx' - -TIMEOUT = 30 -INTERVAL = 10 - -REASON = 'Because....' -ignore_lst = ('test') - - -def fetch(path, t='GET'): - url = 'http{}://{}:{}/'.format(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} - - response = fetch('status/sessions') - - if response['MediaContainer']['Video']: - for video in response['MediaContainer']['Video']: - part = video['Media'][0]['Part'][0] - if video['sessionKey'] == sessionKey: - if xtime == ntime and video['Player']['state'] == 'paused' and part['decision'] == 'transcode': - 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 video['Player']['state'] in ('playing', 'buffering'): - sys.stdout.write("{user}'s stream of {title} is now {state}". - format(user=user, title=title, state=video['Player']['state'])) - return None - else: - return xtime - else: - return None - - -def find_sessionID(response): - - sessions = [] - for video in response['MediaContainer']['Video']: - part = video['Media'][0]['Part'][0] - if video['sessionKey'] == sys.argv[1] and video['Player']['state'] == 'paused' \ - and part['decision'] == 'transcode': - sess_id = video['Session']['id'] - user = video['User']['title'] - sess_key = video['sessionKey'] - title = (video['grandparentTitle'] + ' - ' if video['type'] == 'episode' else '') + video['title'] - title = unicodedata.normalize('NFKD', title).encode('ascii', 'ignore').translate(None,"'") - sessions.append((sess_id, user, title, sess_key)) - - else: - pass - - print(sessions) - 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 = 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 diff --git a/archive/notify_geomail.py b/archive/notify_geomail.py deleted file mode 100644 index 758b112..0000000 --- a/archive/notify_geomail.py +++ /dev/null @@ -1,198 +0,0 @@ -# 1. Install the requests module for python. -# pip install requests -# 2. Add script arguments in PlexPy. The following script arguments are available by default. More can be added below. -# -ip {ip_address} -us {user} -med {media_type} -tt {title} -pf {platform} -pl {player} -da {datestamp} -ti {timestamp} - -import argparse -import requests -import sys - - -## EDIT THESE SETTINGS ## - -PLEXPY_APIKEY = 'XXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -AGENT_ID = 10 # The notification agent ID for PlexPy -# 10 Email -# 15 Scripts -# 17 Browser - -# Replace LAN IP addresses that start with the LAN_SUBNET with a WAN IP address -# to retrieve geolocation data. Leave REPLACEMENT_WAN_IP blank for no replacement. -LAN_SUBNET = ('192.168') -REPLACEMENT_WAN_IP = '' - -# The notification subject and body -# - Use "{p.argument}" for script arguments -# - Use "{g.value}" for geolocation data -# - Use "{u.value}" for user data -SUBJECT_TEXT = "PlexPy Notification" -BODY_TEXT = """\ - - - -

Hi!
-
Poster unavailable {p.user} has watched {p.media_type}:{p.title}
-
On {p.platform}[{p.player}] in {g.city}, {g.country}.
-
At {p.timestamp} on {p.datestamp}.
-
IP address: {p.ip_address}
-
User email is: {u.email}
-
TEst area Data:{uip.data}
-

- - -""" - -## CODE BELOW ## - -##Geo Space## -class GeoData(object): - def __init__(self, data=None): - data = data or {} - self.continent = data.get('continent', 'N/A') - self.country = data.get('country', 'N/A') - self.region = data.get('region', 'N/A') - self.city = data.get('city', 'N/A') - self.postal_code = data.get('postal_code', 'N/A') - self.timezone = data.get('timezone', 'N/A') - self.latitude = data.get('latitude', 'N/A') - self.longitude = data.get('longitude', 'N/A') - self.accuracy = data.get('accuracy', 'N/A') - -##USER Space## -class UserEmail(object): - def __init__(self, data=None): - data = data or {} - self.email = data.get('email', 'N/A') - self.user_id = data.get('user_id', 'N/A') - self.user_thumb = data.get('user_thumb', 'N/A') - -##API Space## -def get_geoip_info(ip_address=''): - # Get the geo IP lookup from PlexPy - payload = {'apikey': PLEXPY_APIKEY, - 'cmd': 'get_geoip_lookup', - 'ip_address': ip_address} - - try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) - response = r.json() - - if response['response']['result'] == 'success': - data = response['response']['data'] - if data.get('error'): - raise Exception(data['error']) - else: - sys.stdout.write("Successfully retrieved geolocation data.") - return GeoData(data=data) - else: - raise Exception(response['response']['message']) - except Exception as e: - sys.stderr.write("PlexPy API 'get_geoip_lookup' request failed: {0}.".format(e)) - return GeoData() - - -def get_user_email(user_id=''): - # Get the user email from PlexPy - payload = {'apikey': PLEXPY_APIKEY, - 'cmd': 'get_user', - 'user_id': user_id} - - try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) - response = r.json() - - if response['response']['result'] == 'success': - data = response['response']['data'] - if data.get('error'): - raise Exception(data['error']) - else: - sys.stdout.write("Successfully retrieved user email data.") - return UserEmail(data=data) - else: - raise Exception(response['response']['message']) - except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) - return UserEmail() - -def send_notification(arguments=None, geodata=None, useremail=None, user_address=None): - # Format notification text - try: - subject = SUBJECT_TEXT.format(p=arguments, g=geodata, u=useremail) - body = BODY_TEXT.format(p=arguments, g=geodata, u=useremail) - except LookupError as e: - sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) - return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, - 'cmd': 'notify', - 'agent_id': AGENT_ID, - 'subject': subject, - 'body': body} - - try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) - response = r.json() - - if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") - else: - raise Exception(response['response']['message']) - except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) - return None - -if __name__ == '__main__': - # Parse arguments from PlexPy - parser = argparse.ArgumentParser() - - parser.add_argument('-ip', '--ip_address', action='store', default='', - help='The IP address of the stream') - parser.add_argument('-us', '--user', action='store', default='', - help='Username of the person watching the stream') - parser.add_argument('-uid', '--user_id', action='store', default='', - help='User_ID of the person watching the stream') - parser.add_argument('-med', '--media_type', action='store', default='', - help='The media type of the stream') - parser.add_argument('-tt', '--title', action='store', default='', - help='The title of the media') - parser.add_argument('-pf', '--platform', action='store', default='', - help='The platform of the stream') - parser.add_argument('-pl', '--player', action='store', default='', - help='The player of the stream') - parser.add_argument('-da', '--datestamp', action='store', default='', - help='The date of the stream') - parser.add_argument('-ti', '--timestamp', action='store', default='', - help='The time of the stream') - parser.add_argument('-sn', '--show_name', action='store', default='', - help='The name of the TV show') - parser.add_argument('-ena', '--episode_name', action='store', default='', - help='The name of the episode') - parser.add_argument('-ssn', '--season_num', action='store', default='', - help='The season number of the TV show') - parser.add_argument('-enu', '--episode_num', action='store', default='', - help='The episode number of the TV show') - parser.add_argument('-srv', '--plex_server', action='store', default='', - help='The name of the Plex server') - parser.add_argument('-pos', '--poster', action='store', default='', - help='The poster url') - parser.add_argument('-sum', '--summary', action='store', default='', - help='The summary of the TV show') - parser.add_argument('-lbn', '--library_name', action='store', default='', - help='The name of the TV show') - - p = parser.parse_args() - - # Check to make sure there is an IP address before proceeding - if p.ip_address: - if p.ip_address.startswith(LAN_SUBNET) and REPLACEMENT_WAN_IP: - ip_address = REPLACEMENT_WAN_IP - else: - ip_address = p.ip_address - - g = get_geoip_info(ip_address=ip_address) - u = get_user_email(user_id=p.user_id) - send_notification(arguments=p, geodata=g, useremail=u) - - else: - sys.stdout.write("No IP address passed from PlexPy.") From 309167eda33f378b0b3f6ab5ccb5d7f630c33bee Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 16 Mar 2018 10:58:48 -0400 Subject: [PATCH 06/59] Delete vcs.xml --- .idea/vcs.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 5fd674226a7dea43b0d13921b8445f0949521309 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 11:25:01 -0400 Subject: [PATCH 07/59] updated metadata section for tautulli --- notify/notify_added_custom.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/notify/notify_added_custom.py b/notify/notify_added_custom.py index eef1f88..59ec308 100644 --- a/notify/notify_added_custom.py +++ b/notify/notify_added_custom.py @@ -35,8 +35,9 @@ import uuid import argparse + ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxx' # Your PlexPy API key +PLEXPY_APIKEY = '' # Your PlexPy API key PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL LIBRARY_NAMES = ['Movies', 'TV Shows'] # Name of libraries you want to check. @@ -70,7 +71,6 @@ class METAINFO(object): self.rating_key = d['rating_key'] self.media_type = d['media_type'] self.grandparent_title = d['grandparent_title'] - self.file_size = d['file_size'] self.thumb = d['art'] self.summary = d['summary'] @@ -107,7 +107,8 @@ def get_get_metadata(rating_key): r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] + return METAINFO(data=res_data) except Exception as e: @@ -286,7 +287,7 @@ def send_email(msg_text_lst, notify_lst, image_lst, to, days): mailserver.login(email_username, email_password) mailserver.sendmail(sender, to, message.as_string()) mailserver.quit() - print 'Email sent' + print('Email sent') if __name__ == '__main__': From da3427c33af4d00471e878beb1dde21ed8531aae Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 11:32:46 -0400 Subject: [PATCH 08/59] testing pycharm push --- notify/test.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 notify/test.txt diff --git a/notify/test.txt b/notify/test.txt new file mode 100644 index 0000000..07433b2 --- /dev/null +++ b/notify/test.txt @@ -0,0 +1 @@ +this is a test. \ No newline at end of file From 764d509126b212e772e65c6932c87221e7f40fce Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 16 Mar 2018 14:28:36 -0400 Subject: [PATCH 09/59] Delete test.txt --- notify/test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 notify/test.txt diff --git a/notify/test.txt b/notify/test.txt deleted file mode 100644 index 07433b2..0000000 --- a/notify/test.txt +++ /dev/null @@ -1 +0,0 @@ -this is a test. \ No newline at end of file From 6afbb692d3b9a127bc18a3426d0c816981d758c2 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 15:31:27 -0400 Subject: [PATCH 10/59] maps update. pep8 edits --- maps/ips_to_maps.py | 55 ++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/maps/ips_to_maps.py b/maps/ips_to_maps.py index 33ffe2a..7db6334 100644 --- a/maps/ips_to_maps.py +++ b/maps/ips_to_maps.py @@ -32,8 +32,6 @@ from collections import OrderedDict import argparse import numpy as np import time -from collections import Counter - import webbrowser ## EDIT THESE SETTINGS ## @@ -56,16 +54,16 @@ SERVER_CITY = '' SERVER_STATE = '' SERVER_PLATFORM = 'Server' -DEFAULT_COLOR = '#A96A1C' # Plex Orange? +DEFAULT_COLOR = '#A96A1C' # Plex Orange? -PLATFORM_COLORS = {'Android': '#a4c639', # Green - 'Roku':'#800080', # Purple - 'Chromecast':'#ffff00', # Yellow - 'Xbox One':'#ffffff', # White - 'Chrome':'#ff0000', # Red - 'Playstation 4':'#0000ff', # Blue - 'iOS':'#8b4513', # Poop brown - 'Samsung': '#0c4da2', # Blue +PLATFORM_COLORS = {'Android': '#a4c639', # Green + 'Roku': '#800080', # Purple + 'Chromecast': '#ffff00', # Yellow + 'Xbox One': '#ffffff', # White + 'Chrome': '#ff0000', # Red + 'Playstation 4': '#0000ff', # Blue + 'iOS': '#8b4513', # Poop brown + 'Samsung': '#0c4da2', # Blue 'Windows': DEFAULT_COLOR, 'Xbox 360 App': DEFAULT_COLOR} @@ -95,6 +93,7 @@ class UserIPs(object): self.play_count = d['play_count'] self.platform = d['platform'] + def get_get_users_tables(users='', length=''): # Get the users list from PlexPy @@ -124,6 +123,7 @@ def get_get_users_tables(users='', length=''): except Exception as e: sys.stderr.write("PlexPy API 'get_get_users_tables' request failed: {0}.".format(e)) + def get_get_users_ips(user_id, length): # Get the user IP list from PlexPy payload = {'apikey': PLEXPY_APIKEY, @@ -139,6 +139,7 @@ def get_get_users_ips(user_id, length): except Exception as e: sys.stderr.write("PlexPy API 'get_get_users_ips' request failed: {0}.".format(e)) + def get_geoip_info(ip_address=''): # Get the geo IP lookup from PlexPy payload = {'apikey': PLEXPY_APIKEY, @@ -161,6 +162,7 @@ def get_geoip_info(ip_address=''): sys.stderr.write("PlexPy API 'get_geoip_lookup' request failed: {0}.".format(e)) pass + def get_stream_type_by_top_10_platforms(): # Get the user IP list from PlexPy payload = {'apikey': PLEXPY_APIKEY, @@ -174,6 +176,7 @@ def get_stream_type_by_top_10_platforms(): except Exception as e: sys.stderr.write("PlexPy API 'get_stream_type_by_top_10_platforms' request failed: {0}.".format(e)) + def add_to_dictlist(d, key, val): if key not in d: d[key] = [val] @@ -183,6 +186,7 @@ def add_to_dictlist(d, key, val): if (val['region'], val['city']) == (x['region'], x['city']): x['location_count'] += 1 + def get_geo_dict(length, users): geo_dict = {SERVER_FRIENDLY: [{'lon': SERVER_LON, 'lat': SERVER_LAT, 'city': SERVER_CITY, 'region': SERVER_STATE, 'ip': REPLACEMENT_WAN_IP, 'play_count': 0, 'platform': SERVER_PLATFORM, @@ -201,8 +205,8 @@ def get_geo_dict(length, users): add_to_dictlist(geo_dict, a.friendly_name, {'lon': str(g.longitude), 'lat': str(g.latitude), 'city': str(g.city), 'region': str(g.region), - 'ip': ip, 'play_count': a.play_count, - 'platform':a.platform, 'location_count': city_cnt}) + 'ip': ip, 'play_count': a.play_count, + 'platform': a.platform, 'location_count': city_cnt}) except AttributeError: print('User: {} IP: {} caused error in geo_dict.'.format(a.friendly_name, a.ip_address)) pass @@ -211,6 +215,7 @@ def get_geo_dict(length, users): pass return geo_dict + def get_geojson_dict(user_locations): locs = [] for username, locations in user_locations.iteritems(): @@ -255,13 +260,14 @@ def get_geojson_dict(user_locations): "features": locs } + def draw_map(map_type, geo_dict, filename, headless): import matplotlib as mpl if headless: mpl.use("Agg") import matplotlib.pyplot as plt from mpl_toolkits.basemap import Basemap - + ## Map stuff ## plt.figure(figsize=(16, 9), dpi=100, frameon=False, tight_layout=True) lon_r = 0 @@ -324,7 +330,7 @@ def draw_map(map_type, geo_dict, filename, headless): # Keeping lines inside the Location. Plots outside Location will still be in legend if float(data['lon']) != float(SERVER_LON) and float(data['lat']) != float(SERVER_LAT) and \ - lon_l < float(data['lon']) < lon_r: + lon_l < float(data['lon']) < lon_r: # Drawing lines from Server location to client location if data['location_count'] > 1: lines = m.plot(x, y, marker=marker, color=color, markersize=0, @@ -332,16 +338,14 @@ def draw_map(map_type, geo_dict, filename, headless): # Adding dash sequence to 2nd, 3rd, etc lines from same city,state for line in lines: line.set_solid_capstyle('round') - dashes = [x * data['location_count'] for x in [5,8,5,8]] + dashes = [x * data['location_count'] for x in [5, 8, 5, 8]] line.set_dashes(dashes) else: - lines = m.plot(x, y, marker=marker, color=color, markersize=0, label=legend, alpha=.4, zorder=zord, - linewidth=2) - - client_plot = m.plot(px, py, marker=marker, color=color, markersize=markersize, - label=legend, alpha=alph, zorder=zord) + m.plot(x, y, marker=marker, color=color, markersize=0, label=legend, alpha=.4, zorder=zord, + linewidth=2) + m.plot(px, py, marker=marker, color=color, markersize=markersize, label=legend, alpha=alph, zorder=zord) handles, labels = plt.gca().get_legend_handles_labels() idx = labels.index('Location: {}, {}, User: {}\nPlatform: {}, IP: {}, Play Count: {}'. @@ -381,11 +385,11 @@ if __name__ == '__main__': json_check = sorted([f for f in os.listdir('.') if os.path.isfile(f) and f.endswith(".json")], key=os.path.getmtime) parser = argparse.ArgumentParser(description="Use PlexPy to draw map of user locations base on IP address.", formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('-l', '--location', default='NA', choices=['NA', 'EU','World', 'Geo'], metavar='', + parser.add_argument('-l', '--location', default='NA', choices=['NA', 'EU', 'World', 'Geo'], metavar='', help='Map location. choices: (%(choices)s) \n(default: %(default)s)') - parser.add_argument('-c', '--count', nargs='?', type=int , default=2, metavar='', + parser.add_argument('-c', '--count', nargs='?', type=int, default=2, metavar='', help='How many IPs to attempt to check. \n(default: %(default)s)') - parser.add_argument('-u', '--users', nargs='+', type=str ,default='all', choices=user_lst, metavar='', + parser.add_argument('-u', '--users', nargs='+', type=str, default='all', choices=user_lst, metavar='', help='Space separated list of case sensitive names to process. Allowed names are: \n' '%(choices)s \n(default: %(default)s)') parser.add_argument('-i', '--ignore', nargs='+', type=str, default=None, choices=user_lst, metavar='', @@ -410,7 +414,7 @@ if __name__ == '__main__': with open(''.join(opts.json)) as json_data: geo_json = json.load(json_data) else: - print(opts) + # print(opts) if opts.ignore and opts.users == 'all': users = [x for x in user_lst if x not in opts.ignore] else: @@ -445,4 +449,5 @@ if __name__ == '__main__': print(r.json()['html_url']) webbrowser.open(r.json()['html_url']) else: + print(geo_json) draw_map(opts.location, geo_json, filename, opts.headless) From f8b1cd6adfde072dd5eda29f2b7d32de752fe17a Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 15:37:09 -0400 Subject: [PATCH 11/59] readme for tautulli --- README.md | 76 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index e9d3116..bbd4d68 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4J6RPWZ9J9YML) [![PM](https://img.shields.io/badge/Reddit-Message-lightgrey.svg)](https://www.reddit.com/user/Blacktwin/) [![PM](https://img.shields.io/badge/Plex-Message-orange.svg)](https://forums.plex.tv/profile/discussions/Blacktwin) [![Issue](https://img.shields.io/badge/Submit-Issue-red.svg)](https://github.com/blacktwin/JBOPS/issues/new) -Most of these scripts utilize a combination of [PlexPy](https://github.com/JonnyWong16/plexpy), [python-plexapi](https://github.com/pkkid/python-plexapi), and [requests](http://docs.python-requests.org/en/master/user/install/#install). +Most of these scripts utilize a combination of [Tautulli](https://github.com/Tautulli/Tautulli), [python-plexapi](https://github.com/pkkid/python-plexapi), and [requests](http://docs.python-requests.org/en/master/user/install/#install). ## Notice: Updating for Tautulli @@ -28,7 +28,7 @@ Scripts pulled from my gist profile. Maps - Using PlexPy data, draw a map connecting Server to Clients based on IP addresses. + Using Tautulli data, draw a map connecting Server to Clients based on IP addresses. @@ -69,8 +69,8 @@ Scripts pulled from my gist profile. wait_kill_pause_notify_main - Receive session_key from PlexPy when paused. Use sub-script wait_kill_pause_notify_sub - to wait for X time then check if still paused. If so, kill. Toggle whether you'd like to be notified through a PlexPy notification agent. + Receive session_key from Tautulli when paused. Use sub-script wait_kill_pause_notify_sub + to wait for X time then check if still paused. If so, kill. Toggle whether you'd like to be notified through a Tautulli notification agent. @@ -97,7 +97,7 @@ Scripts pulled from my gist profile. ip_whitelist - Receive session_key and IP from PlexPy when playback starts. Use IP to check against whitelist. If not in whitelist use session_key to determine stream and kill. + Receive session_key and IP from Tautulli when playback starts. Use IP to check against whitelist. If not in whitelist use session_key to determine stream and kill. @@ -107,7 +107,7 @@ Scripts pulled from my gist profile. kill_else_if_buffering - Kill concurrent transcode streams of other users if Admin user is experiencing buffering warnings from PlexPy. + Kill concurrent transcode streams of other users if Admin user is experiencing buffering warnings from Tautulli. @@ -137,7 +137,7 @@ Scripts pulled from my gist profile. kill_trans_pause - Kill Plex paused video transcoding streams using PlexPy. + Kill Plex paused video transcoding streams using Tautulli. @@ -160,12 +160,12 @@ Scripts pulled from my gist profile. find_unwatched_notify - Find what was added TFRAME ago and not watched and notify admin using PlexPy. + Find what was added TFRAME ago and not watched and notify admin using Tautulli. notify_added_custom - Send an email with what was added to Plex in the past week using PlexPy. Email includes title (TV: Show Name: Episode Name; Movie: Movie Title), time added, image, and summary. + Send an email with what was added to Plex in the past week using Tautulli. Email includes title (TV: Show Name: Episode Name; Movie: Movie Title), time added, image, and summary. @@ -223,7 +223,7 @@ Scripts pulled from my gist profile. bypass_auth_name - Use PlexPy to pull last IP address from user and add to List of IP addresses and networks that are allowed without auth in Plex. + Use Tautulli to pull last IP address from user and add to List of IP addresses and networks that are allowed without auth in Plex. @@ -238,7 +238,7 @@ Scripts pulled from my gist profile. find_unwatched - Find what was added TFRAME ago and not watched using PlexPy. + Find what was added TFRAME ago and not watched using Tautulli. @@ -305,7 +305,7 @@ Scripts pulled from my gist profile. added_to_plex - Find when media was added between STARTFRAME and ENDFRAME to Plex through PlexPy. + Find when media was added between STARTFRAME and ENDFRAME to Plex through Tautulli. @@ -320,12 +320,12 @@ Scripts pulled from my gist profile. drive_check - Check if drive exists. If not then notify via PlexPy notifier agent. + Check if drive exists. If not then notify via Tautulli notifier agent. userplays_weekly_reporting - Use PlexPy to count how many plays per user occurred this week and send email via PlexPy. + Use Tautulli to count how many plays per user occurred this week and send email via Tautulli. @@ -334,25 +334,37 @@ Scripts pulled from my gist profile. ----
-Setting Up PlexPy for Custom Scripts +Setting Up Tautulli for Custom Scripts -#### Enabling Scripts in PlexPy: +#### Enabling Scripts in Tautulli: -Settings > Notification Agents > Click the Scripts gear +Taultulli > Settings > Notification Agents > Add a Notification Agent > Script -- [ ] Set scripts location to location of your scripts +Taultulli > Settings > Notification Agents > New Script > Configuration: +- [ ] Set scripts location to location of your script - [ ] Scroll down to option you want to use and select the script from the drop down menu +- [ ] Set desired Script Timeout value +- [ ] Optional - Add a description of the script for easy reference - [ ] Save -Settings > Notification Agents > Click the Bell next to Scripts +Taultulli > Settings > Notification Agents > New Script > Triggers: - [ ] Check desired trigger -- [ ] Close - -Settings > Notifications > Click Script - -- [ ] Enter in the Script Arguments - [ ] Save + +Taultulli > Settings > Notification Agents > New Script > Conditions: + +- [ ] Set desired conditions +- [ ] Save + +For more information on Tautulli conditions see [here](https://github.com/Tautulli/Tautulli-Wiki/wiki/Custom-Notification-Conditions) + +Taultulli > Settings > Notification Agents > New Script > Script Arguments: + +- [ ] Select desired trigger +- [ ] Input desired notification parameters (List of parameters will likely be found inside script) +- [ ] Save +- [ ] Close
@@ -364,21 +376,15 @@ Settings > Notifications > Click Script
Plex -- [ ] PLEX_HOST - Local IP to connect to Plex ('localhost', '192.168.0.x', '127.0.0.1', etc.) -- [ ] PLEX_PORT - Port number used by Plex (default: 32400) -- [ ] PLEX_SSL - http:// or https://? '' if http and 's' if https -- [ ] PLEX_TOKEN - [Plex](https://support.plex.tv/hc/en-us/articles/204059436-Finding-an-authentication-token-X-Plex-Token) or PlexPy Settings > Plex.tv Account > PMS Token +- [ ] PLEX_URL - Local/Remote IP to connect to Plex ('http://localhost:32400', 'https://x.x.x.x:32412', etc.) +- [ ] PLEX_TOKEN - [Plex](https://support.plex.tv/hc/en-us/articles/204059436-Finding-an-authentication-token-X-Plex-Token) or Tautulli Settings > Plex.tv Account > PMS Token
-PlexPy +Tautulli -- [ ] PLEXPY_URL - Local IP to connect to PlexPy ('localhost', '192.168.0.x', '127.0.0.1', etc.) -- [ ] PLEXPY_APIKEY - PlexPy Settings > Access Control > Enable API - API Key +- [ ] TAUTULLI_URL - Local/Remote IP to connect to Tautulli ('http://localhost:8181', 'https://x.x.x.x:8182', etc.) +- [ ] TAUTULLI_APIKEY - Tautulli Settings > Access Control > Enable API - API Key
- - - - From 76cef02e437fa7aa408aebd9c434f87028caf66c Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 15:39:34 -0400 Subject: [PATCH 12/59] readme formatting --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bbd4d68..f3954ec 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,8 @@ Scripts pulled from my gist profile. Taultulli > Settings > Notification Agents > Add a Notification Agent > Script +#### Configuration + Taultulli > Settings > Notification Agents > New Script > Configuration: - [ ] Set scripts location to location of your script - [ ] Scroll down to option you want to use and select the script from the drop down menu @@ -347,11 +349,13 @@ Taultulli > Settings > Notification Agents > New Script > Configuration: - [ ] Optional - Add a description of the script for easy reference - [ ] Save +#### Triggers Taultulli > Settings > Notification Agents > New Script > Triggers: - [ ] Check desired trigger - [ ] Save - + +#### Conditions Taultulli > Settings > Notification Agents > New Script > Conditions: - [ ] Set desired conditions @@ -359,6 +363,7 @@ Taultulli > Settings > Notification Agents > New Script > Conditions: For more information on Tautulli conditions see [here](https://github.com/Tautulli/Tautulli-Wiki/wiki/Custom-Notification-Conditions) +#### Script Arguments Taultulli > Settings > Notification Agents > New Script > Script Arguments: - [ ] Select desired trigger From 3266f88ed499f2e33f95890be695e3ba7c624dc7 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 16 Mar 2018 16:12:35 -0400 Subject: [PATCH 13/59] updated naming and pep8 --- notify/find_unwatched_notify.py | 145 ++++++++++++++++---------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/notify/find_unwatched_notify.py b/notify/find_unwatched_notify.py index cef3f99..9b107d7 100644 --- a/notify/find_unwatched_notify.py +++ b/notify/find_unwatched_notify.py @@ -1,6 +1,6 @@ """ -Find what was added TFRAME ago and not watched and notify admin using PlexPy. +Find what was added TFRAME ago and not watched and notify admin using Tautulli. """ @@ -8,16 +8,15 @@ import requests import sys import time -TFRAME = 1.577e+7 # ~ 6 months in seconds +TFRAME = 1.577e+7 # ~ 6 months in seconds TODAY = time.time() - ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -LIBRARY_NAMES = ['My Movies', 'My TV Shows'] # Name of libraries you want to check. -SUBJECT_TEXT = "PlexPy Notification" -AGENT_ID = 10 # The email notification agent ID for PlexPy +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8183/' # Your Tautulli URL +LIBRARY_NAMES = ['Movies', 'TV Shows'] # Name of libraries you want to check. +SUBJECT_TEXT = "Tautulli Notification" +NOTIFIER_ID = 12 # The email notification agent ID for Tautulli class LIBINFO(object): @@ -44,15 +43,15 @@ class METAINFO(object): self.file = d['file'] -def get_get_new_rating_keys(rating_key, media_type): +def get_new_rating_keys(rating_key, media_type): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_new_rating_keys', 'rating_key': rating_key, 'media_type': media_type} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] @@ -63,139 +62,143 @@ def get_get_new_rating_keys(rating_key, media_type): return episode_lst except Exception as e: - sys.stderr.write("PlexPy API 'get_new_rating_keys' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_new_rating_keys' request failed: {0}.".format(e)) -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] return METAINFO(data=res_data) except Exception as e: - # sys.stderr.write("PlexPy API 'get_get_metadata' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) pass -def get_get_library_media_info(section_id): - # Get the data on the PlexPy media info tables. - payload = {'apikey': PLEXPY_APIKEY, +def get_library_media_info(section_id): + # Get the data on the Tautulli media info tables. + payload = {'apikey': TAUTULLI_APIKEY, 'section_id': section_id, - 'cmd': 'get_library_media_info', - 'length': 10000} + 'cmd': 'get_library_media_info'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - - res_data = response['response']['data']['data'] + print(response) + res_data = response['response']['data'] return [LIBINFO(data=d) for d in res_data if d['play_count'] is None and (TODAY - int(d['added_at'])) > TFRAME] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e)) -def get_get_libraries_table(): - # Get the data on the PlexPy libraries table. - payload = {'apikey': PLEXPY_APIKEY, + +def get_libraries_table(): + # Get the data on the Tautulli libraries table. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries_table'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [d['section_id'] for d in res_data if d['section_name'] in LIBRARY_NAMES] except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries_table' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries_table' request failed: {0}.".format(e)) -def send_notification(BODY_TEXT): + +def send_notification(body_text): # Format notification text try: subject = SUBJECT_TEXT - body = BODY_TEXT + body = body_text except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None show_lst = [] notify_lst = [] -glt = [lib for lib in get_get_libraries_table()] +libraries = [lib for lib in get_libraries_table()] -for i in glt: +for library in libraries: + print(library, type(library)) try: - gglm = get_get_library_media_info(i) - for x in gglm: + library_media_info = get_library_media_info(library) + for lib in library_media_info: try: - if x.media_type in ['show', 'episode']: + if lib.media_type in ['show', 'episode']: # Need to find TV shows rating_key for episode. - show_lst += get_get_new_rating_keys(x.rating_key, x.media_type) + show_lst += get_new_rating_keys(lib.rating_key, lib.media_type) else: # Find movie rating_key. - show_lst += [int(x.rating_key)] + show_lst += [int(lib.rating_key)] except Exception as e: - print("Rating_key failed: {e}").format(e=e) + print "Rating_key failed: {e}".format(e=e) except Exception as e: - print("Library media info failed: {e}").format(e=e) + print "Library media info failed: {e}".format(e=e) -for i in show_lst: +for show in show_lst: try: - x = get_get_metadata(str(i)) - added = time.ctime(float(x.added_at)) - if x.grandparent_title == '' or x.media_type == 'movie': + meta = get_metadata(str(show)) + added = time.ctime(float(meta.added_at)) + if meta.grandparent_title == '' or meta.media_type == 'movie': # Movies notify_lst += [u"
{x.title} ({x.rating_key}) was added {when} and has not been" - u" watched.
File location: {x.file}

".format(x=x, when=added)] + u" watched.
File location: {x.file}

".format(x=meta, when=added)] else: # Shows notify_lst += [u"
{x.grandparent_title}: {x.title} ({x.rating_key}) was added {when} and has" - u" not been watched.
File location: {x.file}

".format(x=x, when=added)] + u" not been watched.
File location: {x.file}

".format(x=meta, when=added)] except Exception as e: - print("Metadata failed. Likely end of range: {e}").format(e=e) + print "Metadata failed. Likely end of range: {e}".format(e=e) +if notify_lst: + BODY_TEXT = """\ + + + +

Hi!
+
Below is the list of {LIBRARY_NAMES} that have not been watched.
+

+ {notify_lst} +
+

+ + + """.format(notify_lst="\n".join(notify_lst).encode("utf-8"), LIBRARY_NAMES=" & ".join(LIBRARY_NAMES)) -BODY_TEXT = """\ - - - -

Hi!
-
Below is the list of {LIBRARY_NAMES} that have not been watched.
-

- {notify_lst} -
-

- - -""".format(notify_lst="\n".join(notify_lst).encode("utf-8"),LIBRARY_NAMES=" & ".join(LIBRARY_NAMES)) - -send_notification(BODY_TEXT) + print(BODY_TEXT) + send_notification(BODY_TEXT) +else: + exit() From 1a5607dbbd111787324ab38ea24c28a287df6b4b Mon Sep 17 00:00:00 2001 From: philosowaffle Date: Sun, 18 Mar 2018 11:31:06 -0500 Subject: [PATCH 14/59] Create plex_lifx_color_theme.pyw --- fun/plex_lifx_color_theme.pyw | 278 ++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 fun/plex_lifx_color_theme.pyw diff --git a/fun/plex_lifx_color_theme.pyw b/fun/plex_lifx_color_theme.pyw new file mode 100644 index 0000000..59cf5fa --- /dev/null +++ b/fun/plex_lifx_color_theme.pyw @@ -0,0 +1,278 @@ +# +# Author: Bailey Belvis (https://github.com/philosowaffle) +# +# Will dim your lifx lights and set them to a color theme matching the currently +# playing media. +# +# - Enable `Upload Posters to Imgur for Notifications` - required for lights to match the posters color scheme +# - Triggers - PlexLifx supports the following triggers, enable the ones you are interested in. +# - Notify on Playback Start +# - Notify on Playback Stop +# - Notify on Playback Resume +# - Notify on Playback Pause +# +# - Copy paste the following line to each of the Triggers you enabled (found on the Arguments tab): +# -a {action} -mt {media_type} -mi {machine_id} -rk {rating_key} -pu {poster_url} +# +import os +import sys +import logging +import hashlib +import shutil +import numpy +import argparse +import urllib + +from random import shuffle +from pifx import PIFX +from colorthief import ColorThief + +###################################### +# Configuration - EDIT THESE SETTINGS +###################################### + +LogFile = "plex_lifx.log" + +# List of Player Id's that should trigger this script. In order to identify your +# players UUID, enter a dummy id, enable the script, and start playing some media +# on the target device. In the Tautulli logs, search for `plex_lifx`, the UUID should +# appear there. +PlayerUUIDs = "" + +# LIFX API Key +APIKey = "" + +# Bulb Brightness when media is playing +Brightness = .25 + +# Transition duration +Duration = 3.0 + +# Number of colors to be used across your lights +NumColors = 5 + +# How closely the colors should match the media thumbnail, 10 is the highest +ColorQuality = 10 + +# Default theme to restore lights to on media pause/stop +DefaultPauseTheme = "Basic" + +# Default theme to set lights to when media poster fails +DefaultPlayTheme = "Blue" + +# Lights that should be controlled by this script, the order the lights are specified in will effect the order in which colors are applied. +# You can play around with different light orders until you find one that spreads the colors how you like. You can also configure more +# or fewer colors above (see 'NumColors') in order to increase or decrease the amount of color diversity across lights. +Lights = "Corner Lamp,Kitchen Lamp,Standing Lamp 1,Standing Lamp 3,Standing Lamp 2,Bedroom Lamp,Tall Corner Lamp,Titan Lamp" + +############################## +# Logging Setup +############################## + +logger = logging.getLogger('plex_lifx') +logger.setLevel(logging.DEBUG) + +# Formatter +formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s: %(message)s') + +# File Handler +file_handler = logging.FileHandler(LogFile) +file_handler.setLevel(logging.DEBUG) +file_handler.setFormatter(formatter) + +# Console Handler +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +console_handler.setFormatter(formatter) + +logger.addHandler(file_handler) +logger.addHandler(console_handler) + +logger.debug("Starting Plex+Lifx Script :)") + +############################## +# Plex Setup +############################## + +filtered_players = [] if PlayerUUIDs == "none" else PlayerUUIDs.split(',') + +logger.debug("Filtered Players: " + filtered_players.__str__()) + +events = [ + 'play', + 'pause', + 'resume', + 'stop' +] + +############################## +# LIFX Setup +############################## + +brightness = Brightness if Brightness else .35 +duration = Duration if Duration else 2.0 +num_colors = NumColors if NumColors else 4 +color_quality = ColorQuality if ColorQuality else 1 + +if not APIKey: + logger.error("Missing LIFX API Key") + exit(1) +else: + lifx_api_key = APIKey + logger.debug("LIFX API Key: " + lifx_api_key) + +pifx = PIFX(lifx_api_key) + +lights = [] +if Lights: + lights_use_name = True + lights = Lights.split(',') + + tmp = [] + for light in lights: + tmp.append(light.strip()) + lights = tmp +else: + lights_detail = pifx.list_lights() + for light in lights_detail: + lights.append(light['id']) + shuffle(lights) + +scenes_details = pifx.list_scenes() +scenes = dict() +for scene in scenes_details: + scenes[scene['name']] = scene['uuid'] + +logger.debug(scenes) +logger.debug(lights) + +default_pause_theme = DefaultPauseTheme +default_play_theme = DefaultPlayTheme + +default_pause_uuid = scenes[default_pause_theme] +default_play_uuid = scenes[default_play_theme] + +number_of_lights = len(lights) +if number_of_lights < num_colors: + num_colors = number_of_lights + +light_groups = numpy.array_split(numpy.array(lights), num_colors) + +logger.debug("Number of Lights: " + color_quality.__str__()) +logger.debug("Number of Colors: " + num_colors.__str__()) +logger.debug("Color Quality: " + color_quality.__str__()) + +############################## +# Arg Parser +############################## +p = argparse.ArgumentParser() +p.add_argument('-a', '--action', action='store', default='', + help='The action that triggered the script.') +p.add_argument('-mt', '--media_type', action='store', default='', + help='The media type of the media being played.') +p.add_argument('-mi', '--machine_id', action='store', default='', + help='The machine id of where the media is playing.') +p.add_argument('-rk', '--rating_key', action='store', default='', + help='The unique identifier for the media.') +p.add_argument('-pu', '--poster_url', action='store', default='', + help='The poster url for the media playing.') + +parser = p.parse_args() + +############################## +# Script Begin +############################## + +event = parser.action +media_type = parser.media_type +player_uuid = parser.machine_id +media_guid = parser.rating_key +poster_url = parser.poster_url + +logger.debug("Event: " + event) +logger.debug("Media Type: " + media_type) +logger.debug("Player UUI: " + player_uuid) +logger.debug("Media Guid: " + media_guid) +logger.debug("Poster Url: " + poster_url) + +# Only perform action for event play/pause/resume/stop for TV and Movies +if not event in events: + logger.debug("Invalid action: " + event) + exit() + +if (media_type != "movie") and (media_type != "episode"): + logger.debug("Media type was not movie or episode, ignoring.") + exit() + +# If we configured only specific players to be able to play with the lights +if filtered_players: + try: + if player_uuid not in filtered_players: + logger.info(player_uuid + " player is not able to play with the lights") + exit() + except Exception as e: + logger.error("Failed to check uuid - " + e.__str__()) + +# Setup Thumbnail directory paths +upload_folder = os.getcwd() + '\\tmp' +thumb_folder = os.path.join(upload_folder, media_guid) +thumb_path = os.path.join(thumb_folder, "thumb.jpg") + +if event == 'stop': + if os.path.exists(thumb_folder): + logger.debug("Removing Directory: " + thumb_folder) + shutil.rmtree(thumb_folder) + + pifx.activate_scene(default_pause_uuid) + exit() + +if event == 'pause': + pifx.activate_scene(default_pause_uuid) + exit() + +if event == 'play' or event == "resume": + + # If the file already exists then we don't need to re-upload the image + if not os.path.exists(thumb_folder): + try: + logger.debug("Making Directory: " + thumb_folder) + os.makedirs(thumb_folder) + urllib.urlretrieve(poster_url, thumb_path) + except Exception as e: + logger.error(e) + logger.info("No file found in request") + pifx.activate_scene(default_play_uuid) + exit() + + # Determine Color Palette for Lights + color_thief = ColorThief(thumb_path) + palette = color_thief.get_palette(color_count=num_colors, quality=color_quality) + logger.debug("Color Palette: " + palette.__str__()) + + # Set Color Palette + pifx.set_state(selector='all', power="off") + for index in range(len(light_groups)): + try: + color = palette[index] + light_group = light_groups[index] + + logger.debug(light_group) + logger.debug(color) + + color_rgb = ', '.join(str(c) for c in color) + color_rgb = "rgb:" + color_rgb + color_rgb = color_rgb.replace(" ", "") + + for light_id in light_group: + if lights_use_name: + selector = "label:" + light_id + else: + selector = light_id + + logger.debug("Setting light: " + selector + " to color: " + color_rgb) + pifx.set_state(selector=selector, power="on", color=color_rgb, brightness=brightness, duration=duration) + + except Exception as e: + logger.error(e) + +exit() From 38fba84fd72a46aa9d72142756111f51af426687 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Sun, 18 Mar 2018 20:14:25 -0400 Subject: [PATCH 15/59] If else Added else --- killstream/kill_trans_exp_audio.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/killstream/kill_trans_exp_audio.py b/killstream/kill_trans_exp_audio.py index b252725..4b2bce6 100644 --- a/killstream/kill_trans_exp_audio.py +++ b/killstream/kill_trans_exp_audio.py @@ -40,17 +40,18 @@ def kill_session(): if user in USER_IGNORE or media_type == 'track': print('Ignoring {}\'s {} stream.'.format(user, media_type)) pass - try: - trans_dec = session.transcodeSessions[0].videoDecision - if trans_dec == 'transcode': - platform = session.players[0].platform - MESSAGE = DEVICES.get(platform, DEFAULT_REASON) - # print(MESSAGE) - print('Killing {user}\'s stream for transcoding video on {plat}.'.format(user=user, plat=platform)) - session.stop(reason=MESSAGE) - except IndexError: - # print('{} not transcoding.'.format(user)) - pass + else: + try: + trans_dec = session.transcodeSessions[0].videoDecision + if trans_dec == 'transcode': + platform = session.players[0].platform + MESSAGE = DEVICES.get(platform, DEFAULT_REASON) + # print(MESSAGE) + print('Killing {user}\'s stream for transcoding video on {plat}.'.format(user=user, plat=platform)) + session.stop(reason=MESSAGE) + except IndexError: + # print('{} not transcoding.'.format(user)) + pass if __name__ == '__main__': From dd9eb94956813c8c2933376afd66b381c57c2d85 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Mon, 19 Mar 2018 09:18:33 -0400 Subject: [PATCH 16/59] play_lst check check if play_lst returned anything, if not then just exit. --- fun/aired_today_playlist.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fun/aired_today_playlist.py b/fun/aired_today_playlist.py index b97935b..79c18bf 100644 --- a/fun/aired_today_playlist.py +++ b/fun/aired_today_playlist.py @@ -66,7 +66,11 @@ def find_air_dates(content_lst): play_lst = [x[0] for x in aired_lst] return play_lst + remove_old() play_lst = find_air_dates(get_all_content(LIBRARY_NAMES)) # Create Playlist -plex.createPlaylist(TODAY_PLAY_TITLE, play_lst) +if play_lst: + plex.createPlaylist(TODAY_PLAY_TITLE, play_lst) +else: + print('Found nothing aired on this day in history.') From 4a139f930e63c6908fed1be51d00b02b467a0ac8 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 11:19:22 -0400 Subject: [PATCH 17/59] updated metadata section for tautulli --- notify/find_unwatched_notify.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/notify/find_unwatched_notify.py b/notify/find_unwatched_notify.py index 9b107d7..a5ce082 100644 --- a/notify/find_unwatched_notify.py +++ b/notify/find_unwatched_notify.py @@ -2,6 +2,7 @@ Find what was added TFRAME ago and not watched and notify admin using Tautulli. +TAUTULLI_URL + delete_media_info_cache?section_id={section_id} """ import requests @@ -39,8 +40,10 @@ class METAINFO(object): self.rating_key = d['rating_key'] self.media_type = d['media_type'] self.grandparent_title = d['grandparent_title'] - self.file_size = d['file_size'] - self.file = d['file'] + media_info = d['media_info'][0] + parts = media_info['parts'][0] + self.file_size = parts['file_size'] + self.file = parts['file'] def get_new_rating_keys(rating_key, media_type): @@ -69,8 +72,7 @@ def get_metadata(rating_key): # Get the metadata for a media item. payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, - 'cmd': 'get_metadata', - 'media_info': True} + 'cmd': 'get_metadata'} try: r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) @@ -92,8 +94,7 @@ def get_library_media_info(section_id): try: r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - print(response) - res_data = response['response']['data'] + res_data = response['response']['data']['data'] return [LIBINFO(data=d) for d in res_data if d['play_count'] is None and (TODAY - int(d['added_at'])) > TFRAME] except Exception as e: @@ -150,7 +151,6 @@ notify_lst = [] libraries = [lib for lib in get_libraries_table()] for library in libraries: - print(library, type(library)) try: library_media_info = get_library_media_info(library) for lib in library_media_info: @@ -201,4 +201,5 @@ if notify_lst: print(BODY_TEXT) send_notification(BODY_TEXT) else: + print('Nothing to report.') exit() From 5e31da304e741ccae694cfa991ded57a259cf055 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 11:35:48 -0400 Subject: [PATCH 18/59] updated for tautulli and pep8 --- notify/notify_delay.py | 21 +++++++++-------- notify/notify_newip.py | 53 +++++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/notify/notify_delay.py b/notify/notify_delay.py index db3c665..bd0eaf0 100644 --- a/notify/notify_delay.py +++ b/notify/notify_delay.py @@ -1,4 +1,4 @@ -''' +""" Delay Notification Agent message for concurrent streams Arguments passed from PlexPy @@ -12,22 +12,21 @@ PlexPy > Settings > Notification Agents > Scripts > Gear icon: User Concurrent Streams: notify_delay.py PlexPy Settings > Notification Agents > Scripts (Gear) > Script Timeout: 0 to disable or set to > 180 -''' +""" import requests import sys import argparse from time import sleep - ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +PLEXPY_APIKEY = '' # Your PlexPy API key +PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL CONCURRENT_TOTAL = 2 TIMEOUT = 180 INTERVAL = 20 -AGENT_ID = 10 # Notification agent ID for PlexPy +NOTIFIER_ID = 10 # Notification notifier ID for PlexPy # Find Notification agent ID here: # https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify @@ -59,11 +58,12 @@ def get_get_activity(): sys.stderr.write("PlexPy API 'get_activity' request failed: {0}.".format(e)) pass -def send_notification(SUBJECT_TEXT, BODY_TEXT): + +def send_notification(subject_text, body_text): # Format notification text try: - subject = SUBJECT_TEXT.format(p=p, total=cc_total) - body = BODY_TEXT.format(p=p, total=cc_total, time=TIMEOUT/60) + subject = subject_text.format(p=p, total=cc_total) + body = body_text.format(p=p, total=cc_total, time=TIMEOUT / 60) except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) @@ -71,7 +71,7 @@ def send_notification(SUBJECT_TEXT, BODY_TEXT): # Send the notification through PlexPy payload = {'apikey': PLEXPY_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} @@ -87,6 +87,7 @@ def send_notification(SUBJECT_TEXT, BODY_TEXT): sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) return None + if __name__ == '__main__': parser = argparse.ArgumentParser() diff --git a/notify/notify_newip.py b/notify/notify_newip.py index d3f624d..8307ade 100644 --- a/notify/notify_newip.py +++ b/notify/notify_newip.py @@ -1,4 +1,3 @@ - """ Pulling together User IP information and Email. @@ -9,7 +8,9 @@ PlexPy > Settings > Notification Agents > Scripts > Gear icon: Playback Start: notify_newip.py Arguments passed from PlexPy --sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -ip {ip_address} -us {user} -uid {user_id} -pf {platform} -pl {player} -da {datestamp} -ti {timestamp} +-sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} +-pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -ip {ip_address} -us {user} -uid {user_id} +-pf {platform} -pl {player} -da {datestamp} -ti {timestamp} """ @@ -17,11 +18,10 @@ import argparse import requests import sys - ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXX' # Your PlexPy API key +PLEXPY_APIKEY = '' # Your PlexPy API key PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -NOTIFICATION_ID = 10 # The notification agent ID for PlexPy 10 = Email +NOTIFIER_ID = 12 # The notification notifier ID # Replace LAN IP addresses that start with the LAN_SUBNET with a WAN IP address # to retrieve geolocation data. Leave REPLACEMENT_WAN_IP blank for no replacement. @@ -38,16 +38,19 @@ BODY_TEXT = """\

Hi!
-
Poster unavailable {p.user} has watched {p.media_type}:{p.title} from a new IP address: {p.ip_address}
-
On {p.platform}[{p.player}] in {g.city}, {g.country} {g.postal_code} at {p.timestamp} on {p.datestamp}
+
Poster unavailable + {p.user} has watched {p.media_type}:{p.title} from a new IP address: {p.ip_address}
+
On {p.platform}[{p.player}] in + {g.city}, {g.country} {g.postal_code} + at {p.timestamp} on {p.datestamp}



User email is: {u.email}

""" - -##Geo Space## + + class GeoData(object): def __init__(self, data=None): data = data or {} @@ -55,27 +58,22 @@ class GeoData(object): self.city = data.get('city', 'N/A') self.postal_code = data.get('postal_code', 'N/A') -##USER Space## + class UserEmail(object): def __init__(self, data=None): data = data or {} self.email = data.get('email', 'N/A') self.user_id = data.get('user_id', 'N/A') self.user_thumb = data.get('user_thumb', 'N/A') - -##IP Space## -class UserIPs(object): - def __init__(self, data=None): - data = data or {} - -##API Space## + + def get_user_ip_addresses(user_id='', ip_address=''): # Get the user IP list from PlexPy payload = {'apikey': PLEXPY_APIKEY, 'cmd': 'get_user_ips', 'user_id': user_id, 'search': ip_address} - + try: r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() @@ -88,7 +86,7 @@ def get_user_ip_addresses(user_id='', ip_address=''): sys.stdout.write("Successfully retrieved UserIPs data.") if response['response']['data']['recordsFiltered'] == 0: sys.stdout.write("IP has no history.") - return UserIPs(data=data) + return data else: sys.stdout.write("IP has history, killing script.") exit() @@ -96,7 +94,8 @@ def get_user_ip_addresses(user_id='', ip_address=''): raise Exception(response['response']['message']) except Exception as e: sys.stderr.write("PlexPy API 'get_user_ip_addresses' request failed: {0}.".format(e)) - return UserIPs() + return + def get_geoip_info(ip_address=''): # Get the geo IP lookup from PlexPy @@ -145,6 +144,7 @@ def get_user_email(user_id=''): sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) return UserEmail() + def send_notification(arguments=None, geodata=None, useremail=None): # Format notification text try: @@ -156,14 +156,14 @@ def send_notification(arguments=None, geodata=None, useremail=None): # Send the notification through PlexPy payload = {'apikey': PLEXPY_APIKEY, 'cmd': 'notify', - 'agent_id': NOTIFICATION_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - + if response['response']['result'] == 'success': sys.stdout.write("Successfully sent PlexPy notification.") else: @@ -172,6 +172,7 @@ def send_notification(arguments=None, geodata=None, useremail=None): sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) return None + if __name__ == '__main__': # Parse arguments from PlexPy parser = argparse.ArgumentParser() @@ -181,7 +182,7 @@ if __name__ == '__main__': parser.add_argument('-us', '--user', action='store', default='', help='Username of the person watching the stream') parser.add_argument('-uid', '--user_id', action='store', default='', - help='User_ID of the person watching the stream') + help='User_ID of the person watching the stream') parser.add_argument('-med', '--media_type', action='store', default='', help='The media type of the stream') parser.add_argument('-tt', '--title', action='store', default='', @@ -210,7 +211,7 @@ if __name__ == '__main__': help='The summary of the TV show') parser.add_argument('-lbn', '--library_name', action='store', default='', help='The name of the TV show') - + p = parser.parse_args() # Check to make sure there is an IP address before proceeding @@ -219,11 +220,11 @@ if __name__ == '__main__': ip_address = REPLACEMENT_WAN_IP else: ip_address = p.ip_address - + g = get_geoip_info(ip_address=ip_address) u = get_user_email(user_id=p.user_id) get_user_ip_addresses(user_id=p.user_id, ip_address=p.ip_address) send_notification(arguments=p, geodata=g, useremail=u) - + else: sys.stdout.write("No IP address passed from PlexPy.") From f546732f433f5d33aa1572e75be92c996090232f Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 12:33:44 -0400 Subject: [PATCH 19/59] PlexPy names to tautulli. Agent_id to notifier_id. --- killstream/ip_whitelist.py | 24 ++++---- killstream/kill_all_more_than.py | 10 ++-- killstream/kill_device.py | 6 +- killstream/kill_else_if_buffering.py | 4 +- killstream/kill_more_than.py | 10 ++-- killstream/kill_outsider_stream.py | 6 +- killstream/kill_session_bitrate.py | 4 +- killstream/kill_time.py | 22 +++---- killstream/kill_trans_exp_audio.py | 6 +- killstream/kill_trans_library.py | 12 ++-- killstream/kill_trans_pause.py | 6 +- killstream/kill_trans_pause_notify.py | 20 +++---- killstream/kill_trans_quality.py | 10 ++-- killstream/play_limit.py | 22 +++---- killstream/time_limit.py | 22 +++---- killstream/wait_kill_pause_notify_main.py | 24 ++++---- killstream/wait_kill_pause_notify_sub.py | 2 +- killstream/watch_limit.py | 22 +++---- maps/ips_to_maps.py | 54 ++++++++--------- notify/notify_added_custom.py | 70 +++++++++++------------ notify/notify_delay.py | 36 ++++++------ notify/notify_fav_tv_all_movie.py | 48 ++++++++-------- notify/notify_newip.py | 50 ++++++++-------- notify/notify_on_added.py | 8 +-- notify/notify_user_favorites.py | 36 ++++++------ notify/notify_user_newip.py | 40 ++++++------- notify/twitter_notify.py | 6 +- reporting/added_to_plex.py | 58 +++++++++---------- reporting/check_play.py | 34 +++++------ reporting/check_plex_log.py | 28 ++++----- reporting/drive_check.py | 18 +++--- reporting/library_play_days.py | 20 +++---- reporting/plays_by_library.py | 14 ++--- reporting/userplays_weekly_reporting.py | 36 ++++++------ reporting/weekly_stats_reporting.py | 58 +++++++++---------- utility/bypass_auth_name.py | 38 ++++++------ utility/delete_watched_TV.py | 26 ++++----- utility/find_unwatched.py | 50 ++++++++-------- utility/plex_imgur_dl.py | 2 +- utility/refresh_next_episode.py | 8 +-- utility/remove_inactive_users.py | 12 ++-- utility/remove_watched_movies.py | 26 ++++----- utility/stream_limiter_ban_email.py | 56 +++++++++--------- 43 files changed, 532 insertions(+), 532 deletions(-) diff --git a/killstream/ip_whitelist.py b/killstream/ip_whitelist.py index 80e25fc..a660bac 100644 --- a/killstream/ip_whitelist.py +++ b/killstream/ip_whitelist.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- ''' -Receive session_key and IP from PlexPy when playback starts. +Receive session_key and IP from Tautulli when playback starts. Use IP to check against whitelist. If not in whitelist use session_key to determine stream and kill. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: ip_whitelist.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: {session_key} {ip_address} ''' @@ -24,15 +24,15 @@ from plexapi.server import PlexServer PLEX_TOKEN = 'xxxxxx' PLEX_URL = 'http://localhost:32400' -PLEXPY_APIKEY = 'xxxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL IP_WHITELIST = ['10.10.0.12'] # List IP addresses. IGNORE_LST = ('') # List usernames that should be ignored. REASON = 'IP Address: {} was not found in whitelist.' -AGENT_ID = 14 # Notification agent ID for PlexPy +AGENT_ID = 14 # Notification agent ID for Tautulli # Find Notification agent ID here: # https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify @@ -56,23 +56,23 @@ def send_notification(subject_text, body_text): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'agent_id': AGENT_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None diff --git a/killstream/kill_all_more_than.py b/killstream/kill_all_more_than.py index 437743b..2f6c5e6 100644 --- a/killstream/kill_all_more_than.py +++ b/killstream/kill_all_more_than.py @@ -2,16 +2,16 @@ If user has 2* or more concurrent streams kill all user's streams -*PlexPy > Settings > Notification> User Concurrent Stream Threshold - The number of concurrent streams by a single user for PlexPy to trigger a notification. Minimum 2. +*Tautulli > Settings > Notification> User Concurrent Stream Threshold + The number of concurrent streams by a single user for Tautulli to trigger a notification. Minimum 2. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on user concurrent streams -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback User Concurrent Streams: kill_more_than.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {user} """ diff --git a/killstream/kill_device.py b/killstream/kill_device.py index 45f19c8..fdc7045 100644 --- a/killstream/kill_device.py +++ b/killstream/kill_device.py @@ -1,10 +1,10 @@ """ Kill Plex streams based on device. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_device.py """ @@ -18,7 +18,7 @@ PLEX_URL = 'http://localhost:32400' DEFAULT_REASON = 'This stream has ended due to your device type.' -# Find platforms that have history in PlexPy in Play count by platform and stream type Graph +# Find platforms that have history in Tautulli in Play count by platform and stream type Graph DEVICES = {'Android': 'Andriod message', 'Chrome': 'Chrome message', 'Plex Media Player': 'PMP message', diff --git a/killstream/kill_else_if_buffering.py b/killstream/kill_else_if_buffering.py index f1a0d33..db9198d 100644 --- a/killstream/kill_else_if_buffering.py +++ b/killstream/kill_else_if_buffering.py @@ -5,10 +5,10 @@ kill stream will list why it was killed ('Server Admin's stream take priority an concurrent streams'). Message will also include an approximation of when the other concurrent stream will finish, stream that is closest to finish will be used. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on buffer warning -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Buffer Warnings: kill_else_if_buffering.py ''' diff --git a/killstream/kill_more_than.py b/killstream/kill_more_than.py index d07456d..b59141d 100644 --- a/killstream/kill_more_than.py +++ b/killstream/kill_more_than.py @@ -1,15 +1,15 @@ """ If user has 2* or more concurrent streams and the IP of the 2nd stream differs from 1st kill 2nd. If 2nd stream IP is the same as 1st stream don't kill. -*PlexPy > Settings > Notification> User Concurrent Stream Threshold - The number of concurrent streams by a single user for PlexPy to trigger a notification. Minimum 2. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +*Tautulli > Settings > Notification> User Concurrent Stream Threshold + The number of concurrent streams by a single user for Tautulli to trigger a notification. Minimum 2. +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on user concurrent streams -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback User Concurrent Streams: kill_more_than.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} {ip_address} {session_key} """ diff --git a/killstream/kill_outsider_stream.py b/killstream/kill_outsider_stream.py index d13abc6..75b3fce 100644 --- a/killstream/kill_outsider_stream.py +++ b/killstream/kill_outsider_stream.py @@ -1,13 +1,13 @@ """ Kill stream of user if they are accessing Plex from outside network -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_outsider_stream.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} """ diff --git a/killstream/kill_session_bitrate.py b/killstream/kill_session_bitrate.py index 7e68345..2c63777 100644 --- a/killstream/kill_session_bitrate.py +++ b/killstream/kill_session_bitrate.py @@ -1,10 +1,10 @@ """ Kill stream if bitrate is > BITRATE_LIMIT -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_session_bitrate.py """ diff --git a/killstream/kill_time.py b/killstream/kill_time.py index 04cf975..886e925 100644 --- a/killstream/kill_time.py +++ b/killstream/kill_time.py @@ -2,13 +2,13 @@ Limit number of plays of TV Show episodes during time of day. Idea is to reduce continuous plays while sleeping. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_time.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} {media_type} {grandparent_rating_key} """ @@ -20,8 +20,8 @@ from time import time as ttime from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxx' PLEX_URL = 'http://localhost:32400' @@ -51,16 +51,16 @@ sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) -def get_get_history(username): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(username): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': username, 'start_date': TODAY, 'order_column': 'date'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] @@ -70,7 +70,7 @@ def get_get_history(username): return [ep_watched, stopped_time[0]] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def kill_session(user): @@ -85,7 +85,7 @@ def kill_session(user): if media_type is not 'episode': exit() -watched_count, last_stop = get_get_history(username) +watched_count, last_stop = get_history(username) if abs(last_stop - unix_time) > 20: exit() diff --git a/killstream/kill_trans_exp_audio.py b/killstream/kill_trans_exp_audio.py index b252725..757f4cb 100644 --- a/killstream/kill_trans_exp_audio.py +++ b/killstream/kill_trans_exp_audio.py @@ -1,10 +1,10 @@ """ Kill Plex video transcoding streams only. All audio streams are left alone. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_trans_exp_audio.py """ @@ -19,7 +19,7 @@ PLEX_URL = 'http://localhost:32400' DEFAULT_REASON = 'This stream has ended due to requiring video transcoding. ' \ 'Please raise your Remote Quality to Original to play this content.' -# Find platforms that have history in PlexPy in Play count by platform and stream type Graph +# Find platforms that have history in Tautulli in Play count by platform and stream type Graph DEVICES = {'Android': 'Andriod message', 'Chrome': 'Chrome message', 'Plex Media Player': 'PMP message', diff --git a/killstream/kill_trans_library.py b/killstream/kill_trans_library.py index 9f659a4..cac7cd6 100644 --- a/killstream/kill_trans_library.py +++ b/killstream/kill_trans_library.py @@ -1,13 +1,13 @@ """ Kill Plex transcoding streams from specific libraries -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_trans_library.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: {section_id} {session_key} """ @@ -24,7 +24,7 @@ TARGET_LIBRARIES = ['1', '2'] # Library IDs DEFAULT_REASON = 'Stream terminated due to video transcoding of {} content. ' \ 'Please set your device to use "Original" quality.'.format(', '.join(TARGET_LIBRARIES)) -# Find platforms that have history in PlexPy in Play count by platform and stream type Graph +# Find platforms that have history in Tautulli in Play count by platform and stream type Graph DEVICES = {'Android': 'Andriod message', 'Chrome': 'Chrome message', 'Plex Media Player': 'PMP message', @@ -32,7 +32,7 @@ DEVICES = {'Android': 'Andriod message', USER_IGNORE = ('') # ('Username','User2') -PLEXPY_LOG = 'Killing {user}\'s stream of {title} due to video transcoding content from section {section}.' +TAUTULLI_LOG = 'Killing {user}\'s stream of {title} due to video transcoding content from section {section}.' ## sess = requests.Session() @@ -57,5 +57,5 @@ if __name__ == '__main__': trans_dec = session.transcodeSessions[0].videoDecision if trans_dec == 'transcode': reason = DEVICES.get(session.players[0].platform, DEFAULT_REASON) - print(PLEXPY_LOG.format(user=username, title=title, section=section_id)) + print(TAUTULLI_LOG.format(user=username, title=title, section=section_id)) session.stop(reason=reason) diff --git a/killstream/kill_trans_pause.py b/killstream/kill_trans_pause.py index 1959fad..4a7de6b 100644 --- a/killstream/kill_trans_pause.py +++ b/killstream/kill_trans_pause.py @@ -1,13 +1,13 @@ """ Kill Plex paused video transcoding streams. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback pause -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Pause: kill_trans_pause.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: {session_key} """ diff --git a/killstream/kill_trans_pause_notify.py b/killstream/kill_trans_pause_notify.py index 9e74476..b7f6d6a 100644 --- a/killstream/kill_trans_pause_notify.py +++ b/killstream/kill_trans_pause_notify.py @@ -2,10 +2,10 @@ Kill Plex paused video transcoding streams and receive notification. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback pause -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Pause: kill_trans_pause.py """ @@ -18,8 +18,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## PLEX_URL = 'http://localhost:32400' PLEX_TOKEN = 'xxxxx' -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL KILL_MESSAGE = 'This stream has ended due to being paused and transcoding.' @@ -28,7 +28,7 @@ USER_IGNORE = ('') # ('Username','User2') SUBJECT_TEXT = "Killed Paused Transcoded Stream." BODY_TEXT = "Killed {user}'s paused transcoded stream of {title}." -AGENT_ID = 14 # Notification agent ID for PlexPy +AGENT_ID = 14 # Notification agent ID for Tautulli # Find Notification agent ID here: # https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify @@ -39,23 +39,23 @@ sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) def send_notification(subject_text, body_text): - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'agent_id': AGENT_ID, 'subject': subject_text, 'body': body_text} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None diff --git a/killstream/kill_trans_quality.py b/killstream/kill_trans_quality.py index 02b8d0c..411ab36 100644 --- a/killstream/kill_trans_quality.py +++ b/killstream/kill_trans_quality.py @@ -1,13 +1,13 @@ """ Kill Plex transcoding streams only. Checks original quality. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: kill_trans_quality.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: {rating_key} """ @@ -31,7 +31,7 @@ DEVICES = {'Android': 'Andriod message', USER_IGNORE = ('') # ('Username','User2') -PLEXPY_LOG = 'Killing {user}\'s stream of {title} due to video transcoding of {original} content' +TAUTULLI_LOG = 'Killing {user}\'s stream of {title} due to video transcoding of {original} content' ## sess = requests.Session() @@ -54,5 +54,5 @@ if __name__ == '__main__': trans_dec = session.transcodeSessions[0].videoDecision if sess_rating == str(rating_key) and orig_quality in TARGET_QUALITY and trans_dec == 'transcode': reason = DEVICES.get(session.players[0].platform, DEFAULT_REASON) - print(PLEXPY_LOG.format(user=username, title=title,original=orig_quality)) + print(TAUTULLI_LOG.format(user=username, title=title,original=orig_quality)) session.stop(reason=reason) diff --git a/killstream/play_limit.py b/killstream/play_limit.py index 8b7cc2f..be81cf5 100644 --- a/killstream/play_limit.py +++ b/killstream/play_limit.py @@ -1,13 +1,13 @@ """ Kill streams if user has played too much Plex Today. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: play_limit.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} {section_id} """ @@ -19,8 +19,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxxx' PLEX_URL = 'http://localhost:32400' @@ -48,23 +48,23 @@ sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) -def get_get_history(username, section_id): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(username, section_id): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': username, 'section_id': section_id, 'start_date': TODAY} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['recordsFiltered'] return res_data except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def kill_session(user): @@ -82,6 +82,6 @@ for items in PLAY_LIMIT[username]: section_id = items['section_id'] limit = items['limit'] -if get_get_history(username, section_id) > limit: +if get_history(username, section_id) > limit: print('User has reached play limit for today.') kill_session(username) diff --git a/killstream/time_limit.py b/killstream/time_limit.py index f45ca08..5994ecc 100644 --- a/killstream/time_limit.py +++ b/killstream/time_limit.py @@ -1,13 +1,13 @@ """ Kill streams if user has exceeded time limit on Plex server. Choose to unshare or remove user. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: time_limit.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} """ @@ -18,8 +18,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxx' PLEX_URL = 'http://localhost:32400' @@ -41,21 +41,21 @@ plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) sections_lst = [x.title for x in plex.library.sections()] -def get_get_history(username): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(username): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': username} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return sum([data['duration'] for data in res_data]) except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def unshare(user, libraries): @@ -88,7 +88,7 @@ if TIME_LIMIT[username]['m']: total_time += TIME_LIMIT[username]['m'] * 60 -if get_get_history(username) > total_time: +if get_history(username) > total_time: print('User has reached time limit.') kill_session(username) if TIME_LIMIT[username]['remove']: diff --git a/killstream/wait_kill_pause_notify_main.py b/killstream/wait_kill_pause_notify_main.py index af8ecd0..44e8a78 100644 --- a/killstream/wait_kill_pause_notify_main.py +++ b/killstream/wait_kill_pause_notify_main.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- ''' -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on pause -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Pause: wait_kill_pause_notify_main.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: {session_key} wait_kill_pause_notify_main.py & wait_kill_pause_notify_sub.py should be in the same directory. wait_kill_pause_notify_main.py executes sub_script wait_kill_pause_notify_sub.py. -PlexPy will timeout wait_kill_pause_notify_main.py after 30 seconds (default) +Tautulli will timeout wait_kill_pause_notify_main.py after 30 seconds (default) but wait_kill_pause_notify_sub.py will continue. wait_kill_pause_notify_sub will check if the stream's session_id is still paused or if playing as restarted. @@ -31,8 +31,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## PLEX_TOKEN = '' PLEX_URL = 'http://localhost:32400' -PLEXPY_APIKEY = '' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL TIMEOUT = '120' INTERVAL = '20' @@ -44,7 +44,7 @@ USER_IGNORE = ('') # ('Username','User2') SUBJECT_TEXT = "Killed Paused Transcoded Stream." BODY_TEXT = "Killed {user}'s paused transcoded stream of {title}." -AGENT_ID = 10 # Notification agent ID for PlexPy +AGENT_ID = 10 # Notification agent ID for Tautulli # Find Notification agent ID here: # https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify # AGENT = '' to disable notification @@ -60,23 +60,23 @@ sessionKey = sys.argv[1] def send_notification(subject_text, body_text): - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'agent_id': AGENT_ID, 'subject': subject_text, 'body': body_text} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None diff --git a/killstream/wait_kill_pause_notify_sub.py b/killstream/wait_kill_pause_notify_sub.py index 3d845a9..12dc1ec 100644 --- a/killstream/wait_kill_pause_notify_sub.py +++ b/killstream/wait_kill_pause_notify_sub.py @@ -4,7 +4,7 @@ wait_kill_pause_notify_main.py & wait_kill_pause_notify_sub.py should be in the same directory. wait_kill_pause_notify_main.py executes sub_script wait_kill_pause_notify_sub.py. -PlexPy will timeout wait_kill_pause_notify_main.py after 30 seconds (default) +Tautulli will timeout wait_kill_pause_notify_main.py after 30 seconds (default) but wait_kill_pause_notify_sub.py will continue. wait_kill_pause_notify_sub will check if the stream's session_id is still paused or if playing as restarted. diff --git a/killstream/watch_limit.py b/killstream/watch_limit.py index 480d419..a5b3450 100644 --- a/killstream/watch_limit.py +++ b/killstream/watch_limit.py @@ -1,13 +1,13 @@ """ Kill streams if user has watched too much Plex Today. -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: watch_limit.py -PlexPy > Settings > Notifications > Script > Script Arguments +Tautulli > Settings > Notifications > Script > Script Arguments {username} """ @@ -19,8 +19,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxxx' PLEX_URL = 'http://localhost:32400' @@ -41,22 +41,22 @@ sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) -def get_get_history(username): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(username): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': username, 'start_date': TODAY} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return sum([data['watched_status'] for data in res_data]) except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def kill_session(user): @@ -69,6 +69,6 @@ def kill_session(user): session.stop(reason=MESSAGE) -if get_get_history(username) > WATCH_LIMIT[username]: +if get_history(username) > WATCH_LIMIT[username]: print('User has reached watch limit for today.') kill_session(username) diff --git a/maps/ips_to_maps.py b/maps/ips_to_maps.py index 7db6334..b334456 100644 --- a/maps/ips_to_maps.py +++ b/maps/ips_to_maps.py @@ -1,5 +1,5 @@ """ -Use PlexPy draw a map connecting Server to Clients based on IP addresses. +Use Tautulli draw a map connecting Server to Clients based on IP addresses. optional arguments: -h, --help show this help message and exit @@ -35,8 +35,8 @@ import time import webbrowser ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = '' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL # Replace LAN IP addresses that start with the LAN_SUBNET with a WAN IP address # to retrieve geolocation data. Leave REPLACEMENT_WAN_IP blank for no replacement. @@ -94,19 +94,19 @@ class UserIPs(object): self.platform = d['platform'] -def get_get_users_tables(users='', length=''): - # Get the users list from PlexPy +def get_users_tables(users='', length=''): + # Get the users list from Tautulli if length: - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_users_table', 'length': length} else: - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_users_table'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] if not length and not users: @@ -121,33 +121,33 @@ def get_get_users_tables(users='', length=''): return [d['user_id'] for user in users for d in res_data if user == d['friendly_name']] except Exception as e: - sys.stderr.write("PlexPy API 'get_get_users_tables' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_users_tables' request failed: {0}.".format(e)) -def get_get_users_ips(user_id, length): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_users_ips(user_id, length): + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user_ips', 'user_id': user_id, 'length': length} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [UserIPs(data=d) for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_get_users_ips' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_users_ips' request failed: {0}.".format(e)) def get_geoip_info(ip_address=''): - # Get the geo IP lookup from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the geo IP lookup from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_geoip_lookup', 'ip_address': ip_address} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': data = response['response']['data'] @@ -159,22 +159,22 @@ def get_geoip_info(ip_address=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_geoip_lookup' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_geoip_lookup' request failed: {0}.".format(e)) pass def get_stream_type_by_top_10_platforms(): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_stream_type_by_top_10_platforms'} # length is number of returns, default is 25 try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['categories'] return res_data except Exception as e: - sys.stderr.write("PlexPy API 'get_stream_type_by_top_10_platforms' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_stream_type_by_top_10_platforms' request failed: {0}.".format(e)) def add_to_dictlist(d, key, val): @@ -192,8 +192,8 @@ def get_geo_dict(length, users): 'ip': REPLACEMENT_WAN_IP, 'play_count': 0, 'platform': SERVER_PLATFORM, 'location_count': 0}]} - for i in get_get_users_tables(users): - user_ip = get_get_users_ips(user_id=i, length=length) + for i in get_users_tables(users): + user_ip = get_users_ips(user_id=i, length=length) city_cnt = 0 for a in user_ip: try: @@ -380,10 +380,10 @@ def draw_map(map_type, geo_dict, filename, headless): if __name__ == '__main__': timestr = time.strftime("%Y%m%d-%H%M%S") - user_count = get_get_users_tables() - user_lst = sorted(get_get_users_tables('friendly_name', user_count)) + user_count = get_users_tables() + user_lst = sorted(get_users_tables('friendly_name', user_count)) json_check = sorted([f for f in os.listdir('.') if os.path.isfile(f) and f.endswith(".json")], key=os.path.getmtime) - parser = argparse.ArgumentParser(description="Use PlexPy to draw map of user locations base on IP address.", + parser = argparse.ArgumentParser(description="Use Tautulli to draw map of user locations base on IP address.", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-l', '--location', default='NA', choices=['NA', 'EU', 'World', 'Geo'], metavar='', help='Map location. choices: (%(choices)s) \n(default: %(default)s)') diff --git a/notify/notify_added_custom.py b/notify/notify_added_custom.py index 59ec308..59c7dbf 100644 --- a/notify/notify_added_custom.py +++ b/notify/notify_added_custom.py @@ -1,5 +1,5 @@ """ -Send an email with what was added to Plex in the past week using PlexPy. +Send an email with what was added to Plex in the past week using Tautulli. Email includes title (TV: Show Name: Episode Name; Movie: Movie Title), time added, image, and summary. Uses: @@ -37,8 +37,8 @@ import argparse ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = '' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL LIBRARY_NAMES = ['Movies', 'TV Shows'] # Name of libraries you want to check. # Email settings @@ -50,7 +50,7 @@ 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 -email_subject = 'PlexPy Added Last {} day(s) Notification' #The email subject +email_subject = 'Tautulli Added Last {} day(s) Notification' #The email subject # Default sizing for pictures # Poster @@ -75,16 +75,16 @@ class METAINFO(object): self.summary = d['summary'] -def get_get_recent(section_id, start, count): +def get_recent(section_id, start, count): # Get the metadata for a media item. Count matters! - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'start': str(start), 'count': str(count), 'section_id': section_id, 'cmd': 'get_recently_added'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -93,18 +93,18 @@ def get_get_recent(section_id, start, count): return res_data except Exception as e: - sys.stderr.write("PlexPy API 'get_recently_added' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_recently_added' request failed: {0}.".format(e)) -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': res_data = response['response']['data'] @@ -112,68 +112,68 @@ def get_get_metadata(rating_key): return METAINFO(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_metadata' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) -def get_get_libraries_table(): - # Get the data on the PlexPy libraries table. - payload = {'apikey': PLEXPY_APIKEY, +def get_libraries_table(): + # Get the data on the Tautulli libraries table. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries_table'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [d['section_id'] for d in res_data if d['section_name'] in LIBRARY_NAMES] except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries_table' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries_table' request failed: {0}.".format(e)) def update_library_media_info(section_id): - # Get the data on the PlexPy media info tables. - payload = {'apikey': PLEXPY_APIKEY, + # Get the data on the Tautulli media info tables. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_library_media_info', 'section_id': section_id, 'refresh': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.status_code if response != 200: print(r.content) except Exception as e: - sys.stderr.write("PlexPy API 'update_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'update_library_media_info' request failed: {0}.".format(e)) def get_pms_image_proxy(thumb): # Gets an image from the PMS and saves it to the image cache directory. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'pms_image_proxy', 'img': thumb} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload, stream=True) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload, stream=True) return r.url except Exception as e: - sys.stderr.write("PlexPy API 'get_get_users_tables' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_users_tables' request failed: {0}.".format(e)) -def get_get_users(): - # Get the user list from PlexPy. - payload = {'apikey': PLEXPY_APIKEY, +def get_users(): + # Get the user list from Tautulli. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_users'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] return [d for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) def get_rating_keys(TODAY, LASTDATE): @@ -186,7 +186,7 @@ def get_rating_keys(TODAY, LASTDATE): while True: # Assume all items will be returned in descending order of added_at - recent_items = get_get_recent(section_id, start, count) + recent_items = get_recent(section_id, start, count) if all([recent_items]): start += count @@ -206,7 +206,7 @@ def get_rating_keys(TODAY, LASTDATE): def build_html(rating_key, height, width, pic_type): - meta = get_get_metadata(str(rating_key)) + meta = get_metadata(str(rating_key)) added = time.ctime(float(meta.added_at)) # Pull image url @@ -292,7 +292,7 @@ def send_email(msg_text_lst, notify_lst, image_lst, to, days): if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Send an email with what was added to Plex in the past week using PlexPy.") + parser = argparse.ArgumentParser(description="Send an email with what was added to Plex in the past week using Tautulli.") parser.add_argument('-t', '--type', help='Metadata picture type from Plex.', required= True, choices=['art', 'poster']) parser.add_argument('-s', '--size', help='Metadata picture size from Plex {Height Width}.', nargs='*') @@ -321,17 +321,17 @@ if __name__ == '__main__': width = art_w # Find the libraries from LIBRARY_NAMES - glt = [lib for lib in get_get_libraries_table()] + glt = [lib for lib in get_libraries_table()] # Update media info for libraries. [update_library_media_info(i) for i in glt] # Gather all users email addresses if opts.users == ['all']: - [to.append(x['email']) for x in get_get_users() if x['email'] != '' and x['email'] not in to + [to.append(x['email']) for x in get_users() if x['email'] != '' and x['email'] not in to and x['username'] not in opts.ignore] elif opts.users != ['all'] and opts.users != 'self': - for get_users in get_get_users(): + for get_users in get_users(): for arg_users in opts.users: if arg_users in get_users['username']: to = to + [str(get_users['email'])] diff --git a/notify/notify_delay.py b/notify/notify_delay.py index bd0eaf0..642dbed 100644 --- a/notify/notify_delay.py +++ b/notify/notify_delay.py @@ -1,17 +1,17 @@ """ Delay Notification Agent message for concurrent streams -Arguments passed from PlexPy +Arguments passed from Tautulli -u {user} -srv {server_name} You can add more arguments if you want more details in the email body -Adding to PlexPy -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Adding to Tautulli +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on concurrent streams -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: User Concurrent Streams: notify_delay.py -PlexPy Settings > Notification Agents > Scripts (Gear) > Script Timeout: 0 to disable or set to > 180 +Tautulli Settings > Notification Agents > Scripts (Gear) > Script Timeout: 0 to disable or set to > 180 """ import requests @@ -20,13 +20,13 @@ import argparse from time import sleep ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = '' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL CONCURRENT_TOTAL = 2 TIMEOUT = 180 INTERVAL = 20 -NOTIFIER_ID = 10 # Notification notifier ID for PlexPy +NOTIFIER_ID = 10 # Notification notifier ID for Tautulli # Find Notification agent ID here: # https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify @@ -43,19 +43,19 @@ BODY_TEXT = """\ """ -def get_get_activity(): +def get_activity(): # Get the current activity on the PMS. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_activity'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['sessions'] return [d['user'] for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_activity' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_activity' request failed: {0}.".format(e)) pass @@ -68,23 +68,23 @@ def send_notification(subject_text, body_text): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None @@ -102,7 +102,7 @@ if __name__ == '__main__': while x < TIMEOUT and x is not None: # check if user still has concurrent streams print('Checking concurrent stream count.') - cc_total = get_get_activity().count(p.user) + cc_total = get_activity().count(p.user) if cc_total >= CONCURRENT_TOTAL: print('{p.user} still has {total} concurrent streams.'.format(p=p, total=cc_total)) sleep(INTERVAL) diff --git a/notify/notify_fav_tv_all_movie.py b/notify/notify_fav_tv_all_movie.py index 67ef9a3..296b96b 100644 --- a/notify/notify_fav_tv_all_movie.py +++ b/notify/notify_fav_tv_all_movie.py @@ -3,15 +3,15 @@ Notify users of recently added episode to show that they have watched at least L Also notify users of new movies. Block users with IGNORE_LST. -Arguments passed from PlexPy +Arguments passed from Tautulli -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -grk {grandparent_rating_key} You can add more arguments if you want more details in the email body -Adding to PlexPy -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Adding to Tautulli +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on recently added -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Recently Added: notify_fav_tv_all_movie.py """ @@ -23,8 +23,8 @@ import sys import argparse ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL IGNORE_LST = ['123456', '123456'] # User_ids LIMIT = 3 @@ -93,52 +93,52 @@ class UserHIS(object): self.show_key = d['grandparent_rating_key'] -def get_get_user(user_id): - # Get the user list from PlexPy. - payload = {'apikey': PLEXPY_APIKEY, +def get_user(user_id): + # Get the user list from Tautulli. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user', 'user_id': int(user_id)} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] return Users(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) -def get_get_users(): - # Get the user list from PlexPy. - payload = {'apikey': PLEXPY_APIKEY, +def get_users(): + # Get the user list from Tautulli. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_users'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] return res_data except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) -def get_get_history(showkey): - # Get the user history from PlexPy. Length matters! - payload = {'apikey': PLEXPY_APIKEY, +def get_history(showkey): + # Get the user history from Tautulli. Length matters! + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'grandparent_rating_key': showkey, 'length': 10000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [UserHIS(data=d) for d in res_data if d['watched_status'] == 1 and d['media_type'].lower() in ('episode', 'show')] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def add_to_dictlist(d, key, val): @@ -149,7 +149,7 @@ def add_to_dictlist(d, key, val): def get_email(show): - history = get_get_history(show) + history = get_history(show) [add_to_dictlist(user_dict, h.user_id, h.show_key) for h in history] # {user_id1: [grand_key, grand_key], user_id2: [grand_key]} @@ -166,7 +166,7 @@ def get_email(show): for i in user_lst: try: if user_dict[i][show] > LIMIT: - g = get_get_user(i) + g = get_user(i) if g.user_id not in IGNORE_LST: sys.stdout.write("Sending {g.user_id} email for %s.".format(g=g) % show) email_lst += [g.email] @@ -237,7 +237,7 @@ if __name__ == '__main__': if p.media_type == 'movie': email_subject = MOVIE_SUBJECT.format(p=p) - to = filter(None, [x['email'] for x in get_get_users() if x['user_id'] not in IGNORE_LST]) + to = filter(None, [x['email'] for x in get_users() if x['user_id'] not in IGNORE_LST]) body_html = MOVIE_BODY.format(p=p) send_email(to, email_subject, body_html) diff --git a/notify/notify_newip.py b/notify/notify_newip.py index 8307ade..7171272 100644 --- a/notify/notify_newip.py +++ b/notify/notify_newip.py @@ -1,13 +1,13 @@ """ Pulling together User IP information and Email. -Adding to PlexPy -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Adding to Tautulli +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Start: notify_newip.py -Arguments passed from PlexPy +Arguments passed from Tautulli -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -ip {ip_address} -us {user} -uid {user_id} -pf {platform} -pl {player} -da {datestamp} -ti {timestamp} @@ -19,8 +19,8 @@ import requests import sys ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = '' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = '' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL NOTIFIER_ID = 12 # The notification notifier ID # Replace LAN IP addresses that start with the LAN_SUBNET with a WAN IP address @@ -68,14 +68,14 @@ class UserEmail(object): def get_user_ip_addresses(user_id='', ip_address=''): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user_ips', 'user_id': user_id, 'search': ip_address} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -93,18 +93,18 @@ def get_user_ip_addresses(user_id='', ip_address=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_user_ip_addresses' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user_ip_addresses' request failed: {0}.".format(e)) return def get_geoip_info(ip_address=''): - # Get the geo IP lookup from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the geo IP lookup from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_geoip_lookup', 'ip_address': ip_address} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -117,18 +117,18 @@ def get_geoip_info(ip_address=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_geoip_lookup' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_geoip_lookup' request failed: {0}.".format(e)) return GeoData() def get_user_email(user_id=''): - # Get the user email from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the user email from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user', 'user_id': user_id} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -141,7 +141,7 @@ def get_user_email(user_id=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) return UserEmail() @@ -153,28 +153,28 @@ def send_notification(arguments=None, geodata=None, useremail=None): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None if __name__ == '__main__': - # Parse arguments from PlexPy + # Parse arguments from Tautulli parser = argparse.ArgumentParser() parser.add_argument('-ip', '--ip_address', action='store', default='', @@ -227,4 +227,4 @@ if __name__ == '__main__': send_notification(arguments=p, geodata=g, useremail=u) else: - sys.stdout.write("No IP address passed from PlexPy.") + sys.stdout.write("No IP address passed from Tautulli.") diff --git a/notify/notify_on_added.py b/notify/notify_on_added.py index 160b154..56fce65 100644 --- a/notify/notify_on_added.py +++ b/notify/notify_on_added.py @@ -1,10 +1,10 @@ """ -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on Recently Added -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Recently Added: notify_on_added.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} You can add more arguments if you want more details in the email body @@ -61,7 +61,7 @@ if not too: to = list([u['email'] for u in users if p.show_name in u['shows']]) # Email settings -name = 'PlexPy' # Your name +name = 'Tautulli' # Your name sender = 'sender' # From email address email_server = 'smtp.gmail.com' # Email server (Gmail: smtp.gmail.com) email_port = 587 # Email port (Gmail: 587) diff --git a/notify/notify_user_favorites.py b/notify/notify_user_favorites.py index ea94155..c7a558c 100644 --- a/notify/notify_user_favorites.py +++ b/notify/notify_user_favorites.py @@ -2,15 +2,15 @@ Notify users of recently added episode to show that they have watched at least LIMIT times via email. Block users with IGNORE_LST. -Arguments passed from PlexPy +Arguments passed from Tautulli -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -grk {grandparent_rating_key} You can add more arguments if you want more details in the email body -Adding to PlexPy -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Adding to Tautulli +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on recently added -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Recently Added: notify_user_favorite.py """ @@ -22,8 +22,8 @@ import sys import argparse ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL IGNORE_LST = [123456, 123456] # User_ids LIMIT = 3 @@ -57,38 +57,38 @@ class UserHIS(object): self.show_key = d['grandparent_rating_key'] -def get_get_user(user_id): - # Get the user list from PlexPy. - payload = {'apikey': PLEXPY_APIKEY, +def get_user(user_id): + # Get the user list from Tautulli. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user', 'user_id': int(user_id)} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] return Users(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) -def get_get_history(showkey): - # Get the user history from PlexPy. Length matters! - payload = {'apikey': PLEXPY_APIKEY, +def get_history(showkey): + # Get the user history from Tautulli. Length matters! + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'grandparent_rating_key': showkey, 'length': 10000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [UserHIS(data=d) for d in res_data if d['watched_status'] == 1 and d['media_type'].lower() in ('episode', 'show')] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def add_to_dictlist(d, key, val): @@ -99,7 +99,7 @@ def add_to_dictlist(d, key, val): def get_email(show): - history = get_get_history(show) + history = get_history(show) [add_to_dictlist(user_dict, h.user_id, h.show_key) for h in history] # {user_id1: [grand_key, grand_key], user_id2: [grand_key]} @@ -116,7 +116,7 @@ def get_email(show): for i in user_lst: try: if user_dict[i][show] >= LIMIT: - g = get_get_user(i) + g = get_user(i) if g.user_id not in IGNORE_LST: sys.stdout.write("Sending {g.user_id} email for %s.".format(g=g) % show) email_lst += [g.email] diff --git a/notify/notify_user_newip.py b/notify/notify_user_newip.py index b6cec3b..d11677b 100644 --- a/notify/notify_user_newip.py +++ b/notify/notify_user_newip.py @@ -1,9 +1,9 @@ """ Pulling together User IP information and Email. Enable the API under Settings > Access Control and remember your API key. -Shutdown PlexPy and open your config.ini file in a text editor. +Shutdown Tautulli and open your config.ini file in a text editor. Set api_sql = 1 in the config file. -Restart PlexPy. +Restart Tautulli. Place in Playback Start """ import argparse @@ -16,8 +16,8 @@ import smtplib ## -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -srv {server_name} -med {media_type} -pos {poster_url} -tt {title} -sum {summary} -lbn {library_name} -ip {ip_address} -us {user} -uid {user_id} -pf {platform} -pl {player} -da {datestamp} -ti {timestamp} ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxxxxxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxxxxxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL # Replace LAN IP addresses that start with the LAN_SUBNET with a WAN IP address # to retrieve geolocation data. Leave REPLACEMENT_WAN_IP blank for no replacement. @@ -72,14 +72,14 @@ class UserEmail(object): ##API Space## def get_user_ip_addresses(user_id='', ip_address=''): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user_ips', 'user_id': user_id, 'search': ip_address} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -97,16 +97,16 @@ def get_user_ip_addresses(user_id='', ip_address=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_user_ip_addresses' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user_ip_addresses' request failed: {0}.".format(e)) def get_geoip_info(ip_address=''): - # Get the geo IP lookup from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the geo IP lookup from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_geoip_lookup', 'ip_address': ip_address} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -119,18 +119,18 @@ def get_geoip_info(ip_address=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_geoip_lookup' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_geoip_lookup' request failed: {0}.".format(e)) return GeoData() def get_user_email(user_id=''): - # Get the user email from PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Get the user email from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user', 'user_id': user_id} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': @@ -143,7 +143,7 @@ def get_user_email(user_id=''): else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'get_user' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e)) return UserEmail() def send_notification(arguments=None, geodata=None, useremail=None): @@ -170,17 +170,17 @@ def send_notification(arguments=None, geodata=None, useremail=None): def clr_sql(ip): try: - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'sql', 'query': 'DELETE FROM session_history WHERE ip_address = "' + ip + '";'} - requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) except Exception as e: - sys.stderr.write("PlexPy API 'get_sql' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_sql' request failed: {0}.".format(e)) if __name__ == '__main__': - # Parse arguments from PlexPy + # Parse arguments from Tautulli parser = argparse.ArgumentParser() parser.add_argument('-ip', '--ip_address', action='store', default='', diff --git a/notify/twitter_notify.py b/notify/twitter_notify.py index 0cd2662..ec7ba3a 100644 --- a/notify/twitter_notify.py +++ b/notify/twitter_notify.py @@ -3,11 +3,11 @@ pip install requests pip install twitter -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on Recently Added -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > Settings > Notification Agents > Scripts > Gear icon: Playback Recently Added: twitter_notify.py -PlexPy > Settings > Notifications > Script > Script Arguments: +Tautulli > Settings > Notifications > Script > Script Arguments: -sn {show_name} -ena {episode_name} -ssn {season_num00} -enu {episode_num00} -dur {duration} -srv {server_name} -med {media_type} -tt {title} -purl {plex_url} -post {poster_url} diff --git a/reporting/added_to_plex.py b/reporting/added_to_plex.py index a272fd6..e17a599 100644 --- a/reporting/added_to_plex.py +++ b/reporting/added_to_plex.py @@ -1,5 +1,5 @@ """ -Find when media was added between STARTFRAME and ENDFRAME to Plex through PlexPy. +Find when media was added between STARTFRAME and ENDFRAME to Plex through Tautulli. Some Exceptions have been commented out to supress what is printed. Uncomment Exceptions if you run into problem and need to investigate. @@ -22,8 +22,8 @@ LASTMONTH = int(TODAY - 2629743) # 2629743 = 1 month in seconds ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL LIBRARY_NAMES = ['TV Shows', 'Movies'] # Names of your libraries you want to check. @@ -49,15 +49,15 @@ class METAINFO(object): self.file_size = d['file_size'] -def get_get_new_rating_keys(rating_key, media_type): +def get_new_rating_keys(rating_key, media_type): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_new_rating_keys', 'rating_key': rating_key, 'media_type': media_type} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] @@ -68,35 +68,35 @@ def get_get_new_rating_keys(rating_key, media_type): return episode_lst except Exception as e: - #sys.stderr.write("PlexPy API 'get_get_new_rating_keys' request failed: {0}.".format(e)) + #sys.stderr.write("Tautulli API 'get_new_rating_keys' request failed: {0}.".format(e)) -def get_get_library_media_info(section_id): - # Get the data on the PlexPy media info tables. Length matters! - payload = {'apikey': PLEXPY_APIKEY, +def get_library_media_info(section_id): + # Get the data on the Tautulli media info tables. Length matters! + payload = {'apikey': TAUTULLI_APIKEY, 'section_id': section_id, 'order_dir ': 'asc', 'cmd': 'get_library_media_info', 'length': 10000000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [LIBINFO(data=d) for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e)) -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['metadata'] @@ -104,57 +104,57 @@ def get_get_metadata(rating_key): return METAINFO(data=res_data) except Exception as e: - # sys.stderr.write("PlexPy API 'get_get_metadata' request failed: {0}.".format(e)) + # sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) def update_library_media_info(section_id): - # Get the data on the PlexPy media info tables. - payload = {'apikey': PLEXPY_APIKEY, + # Get the data on the Tautulli media info tables. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_library_media_info', 'section_id': section_id, 'refresh': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.status_code if response != 200: print(r.content) except Exception as e: - sys.stderr.write("PlexPy API 'update_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'update_library_media_info' request failed: {0}.".format(e)) -def get_get_libraries_table(): - # Get the data on the PlexPy libraries table. - payload = {'apikey': PLEXPY_APIKEY, +def get_libraries_table(): + # Get the data on the Tautulli libraries table. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries_table'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [d['section_id'] for d in res_data if d['section_name'] in LIBRARY_NAMES] except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries_table' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries_table' request failed: {0}.".format(e)) show_lst = [] count_lst = [] size_lst = [] -glt = [lib for lib in get_get_libraries_table()] +glt = [lib for lib in get_libraries_table()] # Updating media info for libraries. [update_library_media_info(i) for i in glt] for i in glt: try: - gglm = get_get_library_media_info(i) + gglm = get_library_media_info(i) for x in gglm: try: if x.media_type in ['show', 'episode']: # Need to find TV shows rating_key for episode. - show_lst += get_get_new_rating_keys(x.rating_key, x.media_type) + show_lst += get_new_rating_keys(x.rating_key, x.media_type) else: # Find movie rating_key. show_lst += [int(x.rating_key)] @@ -170,7 +170,7 @@ for i in glt: for i in sorted(show_lst, reverse=True): try: - x = get_get_metadata(str(i)) + x = get_metadata(str(i)) added = time.ctime(float(x.added_at)) count_lst += [x.media_type] size_lst += [int(x.file_size)] diff --git a/reporting/check_play.py b/reporting/check_play.py index f9bab6c..75d4ce5 100644 --- a/reporting/check_play.py +++ b/reporting/check_play.py @@ -1,6 +1,6 @@ # 1. Install the requests module for python. # pip install requests -# 2. Add script arguments in PlexPy. +# 2. Add script arguments in Tautulli. # {user} {title} # Add to Playback Resume @@ -11,11 +11,11 @@ user = sys.argv[1] title = sys.argv[2] ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -AGENT_ID = 10 # The notification agent ID for PlexPy +TAUTULLI_APIKEY = 'XXXXXXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL +NOTIFIER_ID = 10 # The notification notifier ID for Tautulli -SUBJECT_TEXT = "PlexPy Notification" +SUBJECT_TEXT = "Tautulli Notification" BODY_TEXT = """\ @@ -33,22 +33,22 @@ class UserHIS(object): data = data or {} self.watched = [d['watched_status'] for d in data] -def get_get_history(): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_history(): + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': user, 'search': title} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['data']['recordsFiltered'] > 2: res_data = response['response']['data']['data'] return UserHIS(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def send_notification(): @@ -59,28 +59,28 @@ def send_notification(): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None if __name__ == '__main__': - hisy = get_get_history() + hisy = get_history() if sum(hisy.watched) == 0: sys.stdout.write(user + ' has attempted to watch ' + title + ' more than 3 times unsuccessfully.') diff --git a/reporting/check_plex_log.py b/reporting/check_plex_log.py index f407c24..b40594f 100644 --- a/reporting/check_plex_log.py +++ b/reporting/check_plex_log.py @@ -8,8 +8,8 @@ import requests import sys ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL lib_met = [] err_title = [] @@ -28,43 +28,43 @@ class UserHIS(object): self.title = [d['full_title'] for d in data] -def get_get_plex_log(): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_plex_log(): + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_plex_log'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return PlexLOG(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_get_plex_log' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_plex_log' request failed: {0}.".format(e)) -def get_get_history(key): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_history(key): + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'rating_key': key} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return UserHIS(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) if __name__ == '__main__': - p_log = get_get_plex_log() + p_log = get_plex_log() for co, msg in p_log.error_msg: lib_met += [(msg.split('/library/metadata/'))[1].split(r'\n')[0]] for i in lib_met: - his = get_get_history(int(i)) + his = get_history(int(i)) err_title += [x.encode('UTF8') for x in his.title] err_title = ''.join((set(err_title))) print(err_title + ' is having playback issues') diff --git a/reporting/drive_check.py b/reporting/drive_check.py index 0362e07..b788c60 100644 --- a/reporting/drive_check.py +++ b/reporting/drive_check.py @@ -6,23 +6,23 @@ drive = 'F:' disk = psutil.disk_partitions() -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL -PLEXPY_APIKEY = 'xxxxxx' # Enter your PlexPy API Key -AGENT_LST = [10, 11] # The PlexPy notifier agent id found here: https://github.com/drzoidberg33/plexpy/blob/master/plexpy/notifiers.py#L43 -NOTIFY_SUBJECT = 'PlexPy' # The notification subject +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL +TAUTULLI_APIKEY = 'xxxxxx' # Enter your Tautulli API Key +NOTIFIER_LST = [10, 11] # The Tautulli notifier notifier id found here: https://github.com/drzoidberg33/plexpy/blob/master/plexpy/notifiers.py#L43 +NOTIFY_SUBJECT = 'Tautulli' # The notification subject NOTIFY_BODY = 'The Plex disk {0} was not found'.format(drive) # The notification body disk_check = [True for i in disk if drive in i.mountpoint] if not disk_check: - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', 'subject': NOTIFY_SUBJECT, 'body': NOTIFY_BODY} - for agent in AGENT_LST: - payload['agent_id'] = agent - requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + for notifier in NOTIFIER_LST: + payload['notifier_id'] = notifier + requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) else: pass diff --git a/reporting/library_play_days.py b/reporting/library_play_days.py index c4ef737..263b63b 100644 --- a/reporting/library_play_days.py +++ b/reporting/library_play_days.py @@ -1,5 +1,5 @@ """ -Use PlexPy to print plays by library from 0, 1, 7, or 30 days ago. 0 = total +Use Tautulli to print plays by library from 0, 1, 7, or 30 days ago. 0 = total optional arguments: -h, --help show this help message and exit @@ -28,8 +28,8 @@ import argparse ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL OUTPUT = 'Library: {section}\nDays: {days}\nPlays: {plays}' @@ -37,11 +37,11 @@ OUTPUT = 'Library: {section}\nDays: {days}\nPlays: {plays}' def get_library_names(): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_library_names'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response, indent=4, sort_keys=True)) @@ -49,17 +49,17 @@ def get_library_names(): return [d for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_names' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_names' request failed: {0}.".format(e)) def get_library_watch_time_stats(section_id): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_library_watch_time_stats', 'section_id': section_id} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response, indent=4, sort_keys=True)) @@ -67,7 +67,7 @@ def get_library_watch_time_stats(section_id): return [d for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_watch_time_stats' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_watch_time_stats' request failed: {0}.".format(e)) def main(): @@ -75,7 +75,7 @@ def main(): lib_lst = [section['section_name'] for section in get_library_names()] days_lst = [0, 1, 7, 30] - parser = argparse.ArgumentParser(description="Use PlexPy to pull plays by library", + parser = argparse.ArgumentParser(description="Use Tautulli to pull plays by library", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-l', '--libraries', nargs='+', type=str, default=lib_lst, choices=lib_lst, metavar='', help='Space separated list of case sensitive names to process. Allowed names are: \n' diff --git a/reporting/plays_by_library.py b/reporting/plays_by_library.py index 2ac56ed..332882b 100644 --- a/reporting/plays_by_library.py +++ b/reporting/plays_by_library.py @@ -1,5 +1,5 @@ """ -Use PlexPy to pull plays by library +Use Tautulli to pull plays by library optional arguments: -h, --help show this help message and exit @@ -24,8 +24,8 @@ import json ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL OUTPUT = '{section} - Plays: {plays}' @@ -33,12 +33,12 @@ OUTPUT = '{section} - Plays: {plays}' def get_libraries_table(sections=None): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries_table', 'order_column': 'plays'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response, indent=4, sort_keys=True)) @@ -49,14 +49,14 @@ def get_libraries_table(sections=None): return [d for d in res_data if d['section_name']] except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries_table' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries_table' request failed: {0}.".format(e)) def main(): lib_lst = [section['section_name'] for section in get_libraries_table()] - parser = argparse.ArgumentParser(description="Use PlexPy to pull plays by library", + parser = argparse.ArgumentParser(description="Use Tautulli to pull plays by library", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-l', '--libraries', nargs='+', type=str, choices=lib_lst, metavar='', help='Space separated list of case sensitive names to process. Allowed names are: \n' diff --git a/reporting/userplays_weekly_reporting.py b/reporting/userplays_weekly_reporting.py index 09964a4..f3717f7 100644 --- a/reporting/userplays_weekly_reporting.py +++ b/reporting/userplays_weekly_reporting.py @@ -1,6 +1,6 @@ """ -Use PlexPy to count how many plays per user occurred this week. -Notify via PlexPy Notification +Use Tautulli to count how many plays per user occurred this week. +Notify via Tautulli Notification """ import requests @@ -11,10 +11,10 @@ TODAY = int(time.time()) LASTWEEK = int(TODAY - 7 * 24 * 60 * 60) ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -SUBJECT_TEXT = "PlexPy Weekly Plays Per User" -AGENT_ID = 10 # The email notification agent ID for PlexPy +TAUTULLI_APIKEY = 'XXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL +SUBJECT_TEXT = "Tautulli Weekly Plays Per User" +NOTIFIER_ID = 10 # The email notification notifier ID for Tautulli class UserHIS(object): @@ -29,14 +29,14 @@ class UserHIS(object): self.full_title = d['full_title'] self.date = d['date'] -def get_get_history(): - # Get the PlexPy history. Count matters!!! - payload = {'apikey': PLEXPY_APIKEY, +def get_history(): + # Get the Tautulli history. Count matters!!! + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'length': 100000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] @@ -44,7 +44,7 @@ def get_get_history(): LASTWEEK < d['date'] < TODAY] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def send_notification(BODY_TEXT): # Format notification text @@ -54,23 +54,23 @@ def send_notification(BODY_TEXT): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None def add_to_dictlist(d, key, val): @@ -82,7 +82,7 @@ def add_to_dictlist(d, key, val): user_dict ={} notify_lst = [] -[add_to_dictlist(user_dict, h.user, h.media) for h in get_get_history()] +[add_to_dictlist(user_dict, h.user, h.media) for h in get_history()] # Get count of media_type play in time frame for key, value in user_dict.items(): user_dict[key] = {x: value.count(x) for x in value} diff --git a/reporting/weekly_stats_reporting.py b/reporting/weekly_stats_reporting.py index adb7470..b8cec44 100644 --- a/reporting/weekly_stats_reporting.py +++ b/reporting/weekly_stats_reporting.py @@ -5,7 +5,7 @@ Library stats can display total items in Shows, Seasons, Episodes, Artists, Albu User stats display username and hour, minutes, and seconds of view time -PlexPy Settings > Extra Settings > Check - Calculate Total File Sizes [experimental] ...... wait +Tautulli Settings > Extra Settings > Check - Calculate Total File Sizes [experimental] ...... wait """ @@ -19,12 +19,12 @@ import argparse # EDIT THESE SETTINGS # -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL -SUBJECT_TEXT = "PlexPy Weekly Server, Library, and User Statistics" +TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL +SUBJECT_TEXT = "Tautulli Weekly Server, Library, and User Statistics" -# Notification agent ID: https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify -AGENT_ID = 10 # The email notification agent ID for PlexPy +# Notification notifier ID: https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify +NOTIFIER_ID = 10 # The email notification notifier ID for Tautulli # Remove library element you do not want shown. Logging before exclusion. # SHOW_STAT = 'Shows: {0}, Episodes: {2}' @@ -72,15 +72,15 @@ BODY_TEXT = """\ # /EDIT THESE SETTINGS # -def get_get_history(section_id, check_date): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(section_id, check_date): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'section_id': section_id, 'start_date': check_date} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response['response']['data'], indent=4, sort_keys=True)) res_data = response['response']['data'] @@ -90,40 +90,40 @@ def get_get_history(section_id, check_date): pass except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) -def get_get_libraries(): +def get_libraries(): # Get a list of all libraries on your server. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response['response']['data'], indent=4, sort_keys=True)) res_data = response['response']['data'] return res_data except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries' request failed: {0}.".format(e)) -def get_get_library_media_info(section_id): +def get_library_media_info(section_id): # Get a list of all libraries on your server. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_library_media_info', 'section_id': section_id} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() # print(json.dumps(response['response']['data'], indent=4, sort_keys=True)) res_data = response['response']['data'] return res_data['total_file_size'] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e)) def send_notification(body_text): @@ -134,23 +134,23 @@ def send_notification(body_text): except LookupError as e: sys.stderr.write("Unable to substitute '{0}' in the notification subject or body".format(e)) return None - # Send the notification through PlexPy - payload = {'apikey': PLEXPY_APIKEY, + # Send the notification through Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} try: - r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() if response['response']['result'] == 'success': - sys.stdout.write("Successfully sent PlexPy notification.") + sys.stdout.write("Successfully sent Tautulli notification.") else: raise Exception(response['response']['message']) except Exception as e: - sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e)) return None @@ -194,9 +194,9 @@ def get_server_stats(date_ranges): user_durations_lst =[] print('Checking library stats.') - for sections in get_get_libraries(): + for sections in get_libraries(): - lib_size = get_get_library_media_info(sections['section_id']) + lib_size = get_library_media_info(sections['section_id']) total_size += lib_size sections_id_lst += [sections['section_id']] @@ -221,7 +221,7 @@ def get_server_stats(date_ranges): for check_date in date_ranges: for section_id in sections_id_lst: # print(check_date, section_id) - history = get_get_history(section_id, check_date) + history = get_history(section_id, check_date) if history: # print(json.dumps(history, indent=4, sort_keys=True)) for data in history: @@ -254,7 +254,7 @@ def main(): global BODY_TEXT - parser = argparse.ArgumentParser(description="Use PlexPy to pull library and user statistics for date range.", + parser = argparse.ArgumentParser(description="Use Tautulli to pull library and user statistics for date range.", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-d', '--days', default=7, metavar='', type=int, help='Enter in number of days to go back. \n(default: %(default)s)') diff --git a/utility/bypass_auth_name.py b/utility/bypass_auth_name.py index 66a6454..21539d7 100644 --- a/utility/bypass_auth_name.py +++ b/utility/bypass_auth_name.py @@ -1,5 +1,5 @@ ''' -Use PlexPy to pull last IP address from user and add to List of IP addresses and networks that are allowed without auth in Plex. +Use Tautulli to pull last IP address from user and add to List of IP addresses and networks that are allowed without auth in Plex. optional arguments: -h, --help show this help message and exit @@ -22,35 +22,35 @@ import sys ## EDIT THESE SETTINGS ## PLEX_TOKEN = 'xxxx' PLEX_URL = 'http://localhost:32400' -PLEXPY_APIKEY = 'xxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL -def get_get_history(user_id): - # Get the user history from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_history(user_id): + # Get the user history from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user_id': user_id, 'length': 1} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [d['ip_address'] for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) -def get_get_user_names(username): - # Get the user names from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_user_names(username): + # Get the user names from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user_names'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] if username: @@ -59,7 +59,7 @@ def get_get_user_names(username): return [d['friendly_name'] for d in res_data] except Exception as e: - sys.stderr.write("PlexPy API 'get_get_user_names' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_user_names' request failed: {0}.".format(e)) def add_auth_bypass(net_str): @@ -70,8 +70,8 @@ def add_auth_bypass(net_str): if __name__ == '__main__': - user_lst = get_get_user_names('') - parser = argparse.ArgumentParser(description="Use PlexPy to pull last IP address from user and add to List of " + user_lst = get_user_names('') + parser = argparse.ArgumentParser(description="Use Tautulli to pull last IP address from user and add to List of " "IP addresses and networks that are allowed without auth in Plex.", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-u', '--users', nargs='+', type=str, choices=user_lst, metavar='', @@ -89,16 +89,16 @@ if __name__ == '__main__': elif opts.clear and len(opts.users) == 1: print('Clearing List of IP addresses and networks that are allowed without auth in Plex.') add_auth_bypass('') - user_id = get_get_user_names(opts.users) - user_ip = get_get_history(user_id) + user_id = get_user_names(opts.users) + user_ip = get_history(user_id) print('Adding {} to List of IP addresses and networks that are allowed without auth in Plex.' .format(''.join(user_ip))) add_auth_bypass(user_ip) elif opts.clear and len(opts.users) > 1: print('Clearing List of IP addresses and networks that are allowed without auth in Plex.') add_auth_bypass('') - userid_lst = [get_get_user_names(user_names) for user_names in opts.users] - userip_lst = [get_get_history(user_id) for user_id in userid_lst] + userid_lst = [get_user_names(user_names) for user_names in opts.users] + userip_lst = [get_history(user_id) for user_id in userid_lst] flat_list = [item for sublist in userip_lst for item in sublist] print('Adding {} to List of IP addresses and networks that are allowed without auth in Plex.' .format(', '.join(flat_list))) diff --git a/utility/delete_watched_TV.py b/utility/delete_watched_TV.py index c0cee95..a46ce15 100644 --- a/utility/delete_watched_TV.py +++ b/utility/delete_watched_TV.py @@ -9,8 +9,8 @@ import os ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL SHOW_LST = [123456, 123456, 123456, 123456] # Show rating keys. USER_LST = ['Sam', 'Jakie', 'Blacktwin'] # Name of users @@ -30,28 +30,28 @@ class METAINFO(object): self.grandparent_title = d['grandparent_title'] -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['metadata'] return METAINFO(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_get_metadata' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) pass -def get_get_history(user, show, start, length): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(user, show, start, length): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': user, 'grandparent_rating_key': show, @@ -59,14 +59,14 @@ def get_get_history(user, show, start, length): 'length': length} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [UserHIS(data=d) for d in res_data if d['watched_status'] == 1] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) meta_dict = {} @@ -79,13 +79,13 @@ for user in USER_LST: start = 0 while True: # Getting all watched history for listed users and shows - history = get_get_history(user, show, start, count) + history = get_history(user, show, start, count) try: if all([history]): start += count for h in history: # Getting metadata of what was watched - meta = get_get_metadata(h.rating_key) + meta = get_metadata(h.rating_key) if not any(d['title'] == meta.title for d in meta_lst): meta_dict = { 'title': meta.title, diff --git a/utility/find_unwatched.py b/utility/find_unwatched.py index 96f53ed..dd9f181 100644 --- a/utility/find_unwatched.py +++ b/utility/find_unwatched.py @@ -1,6 +1,6 @@ """ -Find what was added TFRAME ago and not watched using PlexPy. +Find what was added TFRAME ago and not watched using Tautulli. """ @@ -13,8 +13,8 @@ TODAY = time.time() ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'XXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL LIBRARY_NAMES = ['My TV Shows', 'My Movies'] # Name of libraries you want to check. @@ -42,15 +42,15 @@ class METAINFO(object): self.file = d['file'] -def get_get_new_rating_keys(rating_key, media_type): +def get_new_rating_keys(rating_key, media_type): # Get a list of new rating keys for the PMS of all of the item's parent/children. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_new_rating_keys', 'rating_key': rating_key, 'media_type': media_type} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] @@ -61,59 +61,59 @@ def get_get_new_rating_keys(rating_key, media_type): return episode_lst except Exception as e: - sys.stderr.write("PlexPy API 'get_new_rating_keys' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_new_rating_keys' request failed: {0}.".format(e)) -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['metadata'] return METAINFO(data=res_data) except Exception as e: - # sys.stderr.write("PlexPy API 'get_get_metadata' request failed: {0}.".format(e)) + # sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) pass -def get_get_library_media_info(section_id): - # Get the data on the PlexPy media info tables. - payload = {'apikey': PLEXPY_APIKEY, +def get_library_media_info(section_id): + # Get the data on the Tautulli media info tables. + payload = {'apikey': TAUTULLI_APIKEY, 'section_id': section_id, 'cmd': 'get_library_media_info', 'length': 10000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [LIBINFO(data=d) for d in res_data if d['play_count'] is None and (TODAY - int(d['added_at'])) > TFRAME] except Exception as e: - sys.stderr.write("PlexPy API 'get_library_media_info' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e)) -def get_get_libraries_table(): - # Get the data on the PlexPy libraries table. - payload = {'apikey': PLEXPY_APIKEY, +def get_libraries_table(): + # Get the data on the Tautulli libraries table. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_libraries_table'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [d['section_id'] for d in res_data if d['section_name'] in LIBRARY_NAMES] except Exception as e: - sys.stderr.write("PlexPy API 'get_libraries_table' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_libraries_table' request failed: {0}.".format(e)) def delete_files(tmp_lst): del_file = raw_input('Delete all unwatched files? (yes/no)').lower() @@ -127,16 +127,16 @@ def delete_files(tmp_lst): show_lst = [] path_lst = [] -glt = [lib for lib in get_get_libraries_table()] +glt = [lib for lib in get_libraries_table()] for i in glt: try: - gglm = get_get_library_media_info(i) + gglm = get_library_media_info(i) for x in gglm: try: if x.media_type in ['show', 'episode']: # Need to find TV shows rating_key for episode. - show_lst += get_get_new_rating_keys(x.rating_key, x.media_type) + show_lst += get_new_rating_keys(x.rating_key, x.media_type) else: # Find movie rating_key. show_lst += [int(x.rating_key)] @@ -149,7 +149,7 @@ for i in glt: # Remove reverse sort if you want the oldest keys first. for i in sorted(show_lst, reverse=True): try: - x = get_get_metadata(str(i)) + x = get_metadata(str(i)) added = time.ctime(float(x.added_at)) if x.grandparent_title == '' or x.media_type == 'movie': # Movies diff --git a/utility/plex_imgur_dl.py b/utility/plex_imgur_dl.py index f97b5ef..42d3921 100644 --- a/utility/plex_imgur_dl.py +++ b/utility/plex_imgur_dl.py @@ -14,7 +14,7 @@ import os ## Edit ## # Imgur info -CLIENT_ID = 'xxxxx' # PlexPy Settings > Notifications > Imgur Client ID +CLIENT_ID = 'xxxxx' # Tautulli Settings > Notifications > Imgur Client ID ALBUM_ID = '7JeSw' # http://imgur.com/a/7JeSw <--- 7JeSw is the ablum_id # Local info diff --git a/utility/refresh_next_episode.py b/utility/refresh_next_episode.py index 84014cc..68df018 100644 --- a/utility/refresh_next_episode.py +++ b/utility/refresh_next_episode.py @@ -1,14 +1,14 @@ ''' Refresh the next episode of show once current episode is watched. -Check PlexPy's Watched Percent in PlexPy > Settings > General +Check Tautulli's Watched Percent in Tautulli > Settings > General -1. PlexPy > Settings > Notification Agents > Scripts > Bell icon: +1. Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on watched -2. PlexPy > Settings > Notification Agents > Scripts > Gear icon: +2. Tautulli > Settings > Notification Agents > Scripts > Gear icon: Enter the "Script folder" where you save the script. Watched: refresh_next_episode.py Save -3. PlexPy > Settings > Notifications > Script > Script Arguments: +3. Tautulli > Settings > Notifications > Script > Script Arguments: {show_name} {episode_num00} {season_num00} ''' diff --git a/utility/remove_inactive_users.py b/utility/remove_inactive_users.py index 0739dca..af132bc 100644 --- a/utility/remove_inactive_users.py +++ b/utility/remove_inactive_users.py @@ -13,8 +13,8 @@ from plexapi.server import PlexServer ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8182/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxx' PLEX_URL = 'http://localhost:32400' @@ -34,21 +34,21 @@ today = time.mktime(datetime.datetime.today().timetuple()) def get_users_table(): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_users_table', 'order_column': 'last_seen', 'order_dir': 'asc'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [data for data in res_data if data['last_seen']] except Exception as e: - print("PlexPy API 'get_history' request failed: {0}.".format(e)) + print("Tautulli API 'get_history' request failed: {0}.".format(e)) def unshare(user): diff --git a/utility/remove_watched_movies.py b/utility/remove_watched_movies.py index d7576f0..4e80b33 100644 --- a/utility/remove_watched_movies.py +++ b/utility/remove_watched_movies.py @@ -12,8 +12,8 @@ import shutil ## EDIT THESE SETTINGS ## -PLEXPY_APIKEY = 'xxxxxxxx' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'xxxxxxxx' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL LIBRARY_NAMES = ['My Movies'] # Whatever your movie libraries are called. USER_LST = ['Joe', 'Alex'] # Name of users @@ -30,15 +30,15 @@ class METAINFO(object): self.file = d['file'] -def get_get_metadata(rating_key): +def get_metadata(rating_key): # Get the metadata for a media item. - payload = {'apikey': PLEXPY_APIKEY, + payload = {'apikey': TAUTULLI_APIKEY, 'rating_key': rating_key, 'cmd': 'get_metadata', 'media_info': True} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['metadata'] @@ -46,13 +46,13 @@ def get_get_metadata(rating_key): return METAINFO(data=res_data) except Exception as e: - sys.stderr.write("PlexPy API 'get_get_metadata' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) pass -def get_get_history(user, start, length): - # Get the PlexPy history. - payload = {'apikey': PLEXPY_APIKEY, +def get_history(user, start, length): + # Get the Tautulli history. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': user, 'media_type': 'movie', @@ -60,14 +60,14 @@ def get_get_history(user, start, length): 'length': length} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data']['data'] return [UserHIS(data=d) for d in res_data if d['watched_status'] == 1] except Exception as e: - sys.stderr.write("PlexPy API 'get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def delete_files(tmp_lst): @@ -88,13 +88,13 @@ for user in USER_LST: start = 0 while True: # Getting all watched history for listed users - history = get_get_history(user, start, count) + history = get_history(user, start, count) try: if all([history]): start += count for h in history: # Getting metadata of what was watched - movies = get_get_metadata(h.rating_key) + movies = get_metadata(h.rating_key) if not any(d['title'] == movies.title for d in movie_lst): movie_dict = { 'title': movies.title, diff --git a/utility/stream_limiter_ban_email.py b/utility/stream_limiter_ban_email.py index eb997a5..f425844 100644 --- a/utility/stream_limiter_ban_email.py +++ b/utility/stream_limiter_ban_email.py @@ -12,22 +12,22 @@ Caveats: 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. + Tautulli will not have that record. -Adding to PlexPy +Adding to Tautulli -PlexPy > Settings > Notification Agents > Scripts > Bell icon: +Tautulli > Settings > Notification Agents > Scripts > Bell icon: [X] Notify on playback start [X] Notify on watched -PlexPy > Settings > Notification Agents > Scripts > Gear icon: +Tautulli > 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. +If used in Tautulli: +Tautulli will continue displaying that user is watching after unshare is executed in ACTIVITY. +Tautulli will update after ~5 minutes and no longer display user's stream in ACTIVITY. +Tautulli will think that user has stopped. Create new library with one video. @@ -60,8 +60,8 @@ import smtplib ## EDIT THESE SETTINGS ### -PLEXPY_APIKEY = 'XXXXXX' # Your PlexPy API key -PLEXPY_URL = 'http://localhost:8181/' # Your PlexPy URL +TAUTULLI_APIKEY = 'XXXXXX' # Your Tautulli API key +TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL PLEX_TOKEN = "" SERVER_ID = "XXXXX" # Example: https://i.imgur.com/EjaMTUk.png @@ -127,33 +127,33 @@ class Users(object): 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, +def get_user(user_id): + # Get the user list from Tautulli. + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user', 'user_id': int(user_id)} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_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)) + sys.stderr.write("Tautulli API '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, +def get_history(user_id, bankey): + # Get the user history from Tautulli. Length matters! + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'rating_key': bankey, 'user_id': user_id, 'length': 10000} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() rec_filtered = response['response']['data']['recordsFiltered'] # grow this out how you will @@ -163,7 +163,7 @@ def get_get_history(user_id, bankey): return 'ban' except Exception as e: - sys.stderr.write("PlexPy API 'get_get_history' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) def share(user_id, ban): @@ -236,19 +236,19 @@ def unshare(user_id): return -def get_get_activity(): - # Get the user IP list from PlexPy - payload = {'apikey': PLEXPY_APIKEY, +def get_activity(): + # Get the user IP list from Tautulli + payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_activity'} try: - r = requests.get(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload) + r = requests.get(TAUTULLI_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)) + sys.stderr.write("Tautulli API '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 @@ -274,7 +274,7 @@ def send_notification(to=None, friendly=None, val_cnt=None, val_tot=None, mess=N if __name__ == "__main__": - activity = get_get_activity() + activity = get_activity() act_lst = [a.user_id for a in activity] user_lst = [key for key, value in USER_LIBRARIES.iteritems()] @@ -283,8 +283,8 @@ if __name__ == "__main__": UNBAN = 0 for i in user_lst: - history = get_get_history(i, BAN_RATING) - mail_add, friendly = get_get_user(i) + history = get_history(i, BAN_RATING) + mail_add, friendly = get_user(i) try: if act_lst.count(i) >= LIMIT: From d409764f35ec737d14fe7461063e4963654a8f2e Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 13:42:37 -0400 Subject: [PATCH 20/59] script header template --- scriptHeaderTemplate.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scriptHeaderTemplate.txt diff --git a/scriptHeaderTemplate.txt b/scriptHeaderTemplate.txt new file mode 100644 index 0000000..8c2bdf9 --- /dev/null +++ b/scriptHeaderTemplate.txt @@ -0,0 +1,34 @@ +""" +Enabling Scripts in Tautulli: +Taultulli > Settings > Notification Agents > Add a Notification Agent > Script + +Configuration: +Taultulli > Settings > Notification Agents > New Script > Configuration: + + Script Name: {script_name} + Set Script Timeout: {timeout} + Description: {description} + Save + +Triggers: +Taultulli > Settings > Notification Agents > New Script > Triggers: + + Check: {trigger} + Save + +Conditions: +Taultulli > Settings > Notification Agents > New Script > Conditions: + + Set Conditions: {conditions} + Save + +Script Arguments: +Taultulli > Settings > Notification Agents > New Script > Script Arguments: + + Select: {trigger} + Arguments: {arguments} + + Save + Close + +""" \ No newline at end of file From de75b09bea403f9d6bddba805583a97c3fb48f91 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 14:06:42 -0400 Subject: [PATCH 21/59] add purge_removed_plex_friends, scriptHeaderTemplate update. --- reporting/check_play.py | 1 + scriptHeaderTemplate.txt | 6 +++- utility/purge_removed_plex_friends.py | 40 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 utility/purge_removed_plex_friends.py diff --git a/reporting/check_play.py b/reporting/check_play.py index 75d4ce5..ed841b2 100644 --- a/reporting/check_play.py +++ b/reporting/check_play.py @@ -33,6 +33,7 @@ class UserHIS(object): data = data or {} self.watched = [d['watched_status'] for d in data] + def get_history(): # Get the user IP list from Tautulli payload = {'apikey': TAUTULLI_APIKEY, diff --git a/scriptHeaderTemplate.txt b/scriptHeaderTemplate.txt index 8c2bdf9..924beac 100644 --- a/scriptHeaderTemplate.txt +++ b/scriptHeaderTemplate.txt @@ -1,4 +1,8 @@ """ +Description: {description} +Author: {author} +Requires: {requirements} + Enabling Scripts in Tautulli: Taultulli > Settings > Notification Agents > Add a Notification Agent > Script @@ -7,7 +11,7 @@ Taultulli > Settings > Notification Agents > New Script > Configuration: Script Name: {script_name} Set Script Timeout: {timeout} - Description: {description} + Description: {Tautulli_description} Save Triggers: diff --git a/utility/purge_removed_plex_friends.py b/utility/purge_removed_plex_friends.py new file mode 100644 index 0000000..bca2b6a --- /dev/null +++ b/utility/purge_removed_plex_friends.py @@ -0,0 +1,40 @@ +""" +Description: Purge Tautulli users that no longer exist as a friend in Plex +Author: DirtyCajunRice +Requires: requests, plexapi +""" + +import requests +from plexapi.myplex import MyPlexAccount + +TAUTULLI_BASE_URL = '' +TAUTULLI_API_KEY = '' + +PLEX_USERNAME = '' +PLEX_PASSWORD = '' + +# Do you want to back up the database before deleting? +BACKUP_DB = True + +# Do not edit past this line # +account = MyPlexAccount(PLEX_USERNAME, PLEX_PASSWORD) + +payload = {'apikey': TAUTULLI_API_KEY, 'cmd': 'get_user_names'} +tautulli_users = requests.get('http://{}/api/v2' + .format(TAUTULLI_BASE_URL), params=payload).json()['response']['data'] + +plex_friend_ids = [friend.id for friend in account.users()] +tautulli_user_ids = [user['user_id'] for user in tautulli_users] + +removed_user_ids = [user_id for user_id in tautulli_user_ids if user_id not in plex_friend_ids] + +if BACKUP_DB: + payload['cmd'] = 'backup_db' + backup = requests.get('http://{}/api/v2'.format(TAUTULLI_BASE_URL), params=payload) + +if removed_user_ids: + payload['cmd'] = 'delete_user' + + for user_id in removed_user_ids: + payload['user_id'] = user_id + remove_user = requests.get('http://{}/api/v2'.format(TAUTULLI_BASE_URL), params=payload) \ No newline at end of file From 1ab221f673f0a6b0e8e5d13ee1371aaaa1a7ba55 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 19 Mar 2018 14:40:57 -0400 Subject: [PATCH 22/59] Add wait_kill_pause_notify from samwiseg00 --- killstream/wait_kill_paused_notify.py | 119 ++++++++++++++++++++++++++ scriptHeaderTemplate.txt | 2 + 2 files changed, 121 insertions(+) create mode 100644 killstream/wait_kill_paused_notify.py diff --git a/killstream/wait_kill_paused_notify.py b/killstream/wait_kill_paused_notify.py new file mode 100644 index 0000000..c0f6996 --- /dev/null +++ b/killstream/wait_kill_paused_notify.py @@ -0,0 +1,119 @@ +""" +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) diff --git a/scriptHeaderTemplate.txt b/scriptHeaderTemplate.txt index 924beac..893128b 100644 --- a/scriptHeaderTemplate.txt +++ b/scriptHeaderTemplate.txt @@ -35,4 +35,6 @@ Taultulli > Settings > Notification Agents > New Script > Script Arguments: Save Close + Example: + """ \ No newline at end of file From 5c392cfbe847e6dc3fb98e448be79ac5d02a9132 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 23 Mar 2018 09:29:50 -0400 Subject: [PATCH 23/59] updated metadata section for tautulli --- scriptHeaderTemplate.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scriptHeaderTemplate.txt b/scriptHeaderTemplate.txt index 893128b..d5c3b0b 100644 --- a/scriptHeaderTemplate.txt +++ b/scriptHeaderTemplate.txt @@ -23,7 +23,7 @@ Taultulli > Settings > Notification Agents > New Script > Triggers: Conditions: Taultulli > Settings > Notification Agents > New Script > Conditions: - Set Conditions: {conditions} + Set Conditions: [{condition} | {operator} | {value} ] Save Script Arguments: From 53c7fcf0d6ff320d60206f41c8bb5cdf5a2a6b38 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 23 Mar 2018 10:19:49 -0400 Subject: [PATCH 24/59] updated notifierID and notifierID location for tautulli --- killstream/ip_whitelist.py | 6 +++--- killstream/kill_trans_pause_notify.py | 6 +++--- killstream/wait_kill_pause_notify_main.py | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/killstream/ip_whitelist.py b/killstream/ip_whitelist.py index a660bac..14a354b 100644 --- a/killstream/ip_whitelist.py +++ b/killstream/ip_whitelist.py @@ -32,9 +32,9 @@ IGNORE_LST = ('') # List usernames that should be ignored. REASON = 'IP Address: {} was not found in whitelist.' -AGENT_ID = 14 # Notification agent ID for Tautulli +NOTIFIER_ID = 14 # Notification agent ID for Tautulli # Find Notification agent ID here: -# https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify +# Tautulli Settings -> NOTIFICATION AGENTS -> :bell: Agent (NotifierID - {Description) SUBJECT_TEXT = "IP Whitelist Violation" BODY_TEXT = "Killed {user}'s stream of {title}. IP: {ip} not in whitelist" @@ -59,7 +59,7 @@ def send_notification(subject_text, body_text): # Send the notification through Tautulli payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject, 'body': body} diff --git a/killstream/kill_trans_pause_notify.py b/killstream/kill_trans_pause_notify.py index b7f6d6a..825b008 100644 --- a/killstream/kill_trans_pause_notify.py +++ b/killstream/kill_trans_pause_notify.py @@ -28,9 +28,9 @@ USER_IGNORE = ('') # ('Username','User2') SUBJECT_TEXT = "Killed Paused Transcoded Stream." BODY_TEXT = "Killed {user}'s paused transcoded stream of {title}." -AGENT_ID = 14 # Notification agent ID for Tautulli +NOTIFIER_ID = 14 # Notification agent ID for Tautulli # Find Notification agent ID here: -# https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify +# Tautulli Settings -> NOTIFICATION AGENTS -> :bell: Agent (NotifierID - {Description) ##/EDIT THESE SETTINGS ## @@ -42,7 +42,7 @@ def send_notification(subject_text, body_text): # Send the notification through Tautulli payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject_text, 'body': body_text} diff --git a/killstream/wait_kill_pause_notify_main.py b/killstream/wait_kill_pause_notify_main.py index 44e8a78..cda861d 100644 --- a/killstream/wait_kill_pause_notify_main.py +++ b/killstream/wait_kill_pause_notify_main.py @@ -44,10 +44,10 @@ USER_IGNORE = ('') # ('Username','User2') SUBJECT_TEXT = "Killed Paused Transcoded Stream." BODY_TEXT = "Killed {user}'s paused transcoded stream of {title}." -AGENT_ID = 10 # Notification agent ID for Tautulli +NOTIFIER_ID = 10 # Notification agent ID for Tautulli # Find Notification agent ID here: -# https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify -# AGENT = '' to disable notification +# Tautulli Settings -> NOTIFICATION AGENTS -> :bell: Agent (NotifierID - {Description) +# NOTIFIER_ID = '' to disable notification sub_script = 'wait_kill_pause_notify_sub.py' ##/EDIT THESE SETTINGS ## @@ -63,7 +63,7 @@ def send_notification(subject_text, body_text): # Send the notification through Tautulli payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'notify', - 'agent_id': AGENT_ID, + 'notifier_id': NOTIFIER_ID, 'subject': subject_text, 'body': body_text} @@ -92,7 +92,7 @@ def kill_stream(session, xtime, ntime): title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title if state == 'paused' and xtime == ntime: - if AGENT_ID: + if NOTIFIER_ID: send_notification(SUBJECT_TEXT, BODY_TEXT.format(user=username, title=title)) session.stop(reason=KILL_MESSAGE) return ntime From 43834725925f6b7db52b595d3ad64740738e208e Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 23 Mar 2018 10:31:43 -0400 Subject: [PATCH 25/59] push to master --- .idea/inspectionProfiles/Project_Default.xml | 12 ++++++++++++ .idea/vcs.xml | 6 ++++++ 2 files changed, 18 insertions(+) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..920d523 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From a2d82989394e44a5c519526e9e6c3c1c6635ad41 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 23 Mar 2018 10:34:05 -0400 Subject: [PATCH 26/59] updated --- maps/ips_to_maps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/maps/ips_to_maps.py b/maps/ips_to_maps.py index b334456..92bafdf 100644 --- a/maps/ips_to_maps.py +++ b/maps/ips_to_maps.py @@ -128,8 +128,7 @@ def get_users_ips(user_id, length): # Get the user IP list from Tautulli payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_user_ips', - 'user_id': user_id, - 'length': length} + 'user_id': user_id} try: r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) From eeb40719eeda352ebc9e5147765f09cce7c46f2a Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 23 Mar 2018 10:37:36 -0400 Subject: [PATCH 27/59] Delete Project_Default.xml --- .idea/inspectionProfiles/Project_Default.xml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .idea/inspectionProfiles/Project_Default.xml diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 920d523..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file From 2cacb18823c839f9dc6ab8a8a5c7804b83c996d2 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 23 Mar 2018 10:37:44 -0400 Subject: [PATCH 28/59] Delete vcs.xml --- .idea/vcs.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 62a74bed7cfc4364840ac5a512a42dc40c5239f5 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 23 Mar 2018 10:38:54 -0400 Subject: [PATCH 29/59] update Notice --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f3954ec..3e1a059 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Most of these scripts utilize a combination of [Tautulli](https://github.com/Tautulli/Tautulli), [python-plexapi](https://github.com/pkkid/python-plexapi), and [requests](http://docs.python-requests.org/en/master/user/install/#install). ## Notice: -Updating for Tautulli +Updated for Tautulli. Use plexpy-branch if you are still using PlexPy. ## Scripts List [![Gist](https://img.shields.io/badge/gist-Blacktwin-green.svg)](https://gist.github.com/blacktwin) From e13968c9c92074db934f3b3cb3057fc4d26b1aaf Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 23 Mar 2018 10:50:36 -0400 Subject: [PATCH 30/59] updated --- scriptHeaderTemplate.txt | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 scriptHeaderTemplate.txt diff --git a/scriptHeaderTemplate.txt b/scriptHeaderTemplate.txt new file mode 100644 index 0000000..d5c3b0b --- /dev/null +++ b/scriptHeaderTemplate.txt @@ -0,0 +1,40 @@ +""" +Description: {description} +Author: {author} +Requires: {requirements} + +Enabling Scripts in Tautulli: +Taultulli > Settings > Notification Agents > Add a Notification Agent > Script + +Configuration: +Taultulli > Settings > Notification Agents > New Script > Configuration: + + Script Name: {script_name} + Set Script Timeout: {timeout} + Description: {Tautulli_description} + Save + +Triggers: +Taultulli > Settings > Notification Agents > New Script > Triggers: + + Check: {trigger} + Save + +Conditions: +Taultulli > Settings > Notification Agents > New Script > Conditions: + + Set Conditions: [{condition} | {operator} | {value} ] + Save + +Script Arguments: +Taultulli > Settings > Notification Agents > New Script > Script Arguments: + + Select: {trigger} + Arguments: {arguments} + + Save + Close + + Example: + +""" \ No newline at end of file From bd026a201779e796706baef9f390642e11f6b4b2 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Fri, 23 Mar 2018 13:13:22 -0400 Subject: [PATCH 31/59] Update plex_netflix_check.py --- reporting/plex_netflix_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reporting/plex_netflix_check.py b/reporting/plex_netflix_check.py index 99f3514..a793a1f 100644 --- a/reporting/plex_netflix_check.py +++ b/reporting/plex_netflix_check.py @@ -1,5 +1,5 @@ """ -usage: stream_check_instantwatcher.py [-h] [-l [...]] [-s ] [-t ] +usage: plex_netflix_check.py [-h] [-l [...]] [-s ] [-t ] Use instantwatcher.com to find if Plex items are on Netflix, Amazon, or both. From e6e44cb8a15b02efe564b6cb483cf0db5ee3eaeb Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Sat, 24 Mar 2018 13:53:57 -0400 Subject: [PATCH 32/59] metadata fix --- utility/delete_watched_TV.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utility/delete_watched_TV.py b/utility/delete_watched_TV.py index a46ce15..e855310 100644 --- a/utility/delete_watched_TV.py +++ b/utility/delete_watched_TV.py @@ -41,7 +41,7 @@ def get_metadata(rating_key): r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] return METAINFO(data=res_data) except Exception as e: From 1795700c66ebbdd487bf9afe42c93d5f50e1eac3 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Sat, 24 Mar 2018 13:58:47 -0400 Subject: [PATCH 33/59] updated metadata --- reporting/added_to_plex.py | 2 +- utility/find_unwatched.py | 2 +- utility/remove_watched_movies.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reporting/added_to_plex.py b/reporting/added_to_plex.py index e17a599..8977345 100644 --- a/reporting/added_to_plex.py +++ b/reporting/added_to_plex.py @@ -99,7 +99,7 @@ def get_metadata(rating_key): r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] if STARTFRAME <= int(res_data['added_at']) <= ENDFRAME: return METAINFO(data=res_data) diff --git a/utility/find_unwatched.py b/utility/find_unwatched.py index dd9f181..ecf8dca 100644 --- a/utility/find_unwatched.py +++ b/utility/find_unwatched.py @@ -75,7 +75,7 @@ def get_metadata(rating_key): r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] return METAINFO(data=res_data) except Exception as e: diff --git a/utility/remove_watched_movies.py b/utility/remove_watched_movies.py index 4e80b33..660a5f4 100644 --- a/utility/remove_watched_movies.py +++ b/utility/remove_watched_movies.py @@ -41,7 +41,7 @@ def get_metadata(rating_key): r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() - res_data = response['response']['data']['metadata'] + res_data = response['response']['data'] if res_data['library_name'] in LIBRARY_NAMES: return METAINFO(data=res_data) From fd0f4b749d7a18e3ec617ef933084adb2d42c1e3 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Sat, 24 Mar 2018 14:04:47 -0400 Subject: [PATCH 34/59] updated metadata --- reporting/added_to_plex.py | 4 ++-- utility/delete_watched_TV.py | 5 ++++- utility/find_unwatched.py | 6 ++++-- utility/remove_watched_movies.py | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/reporting/added_to_plex.py b/reporting/added_to_plex.py index 8977345..339023b 100644 --- a/reporting/added_to_plex.py +++ b/reporting/added_to_plex.py @@ -68,7 +68,7 @@ def get_new_rating_keys(rating_key, media_type): return episode_lst except Exception as e: - #sys.stderr.write("Tautulli API 'get_new_rating_keys' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_new_rating_keys' request failed: {0}.".format(e)) def get_library_media_info(section_id): # Get the data on the Tautulli media info tables. Length matters! @@ -104,7 +104,7 @@ def get_metadata(rating_key): return METAINFO(data=res_data) except Exception as e: - # sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) + sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e)) def update_library_media_info(section_id): # Get the data on the Tautulli media info tables. diff --git a/utility/delete_watched_TV.py b/utility/delete_watched_TV.py index e855310..1329fd7 100644 --- a/utility/delete_watched_TV.py +++ b/utility/delete_watched_TV.py @@ -25,7 +25,10 @@ class METAINFO(object): def __init__(self, data=None): d = data or {} self.title = d['title'] - self.file = d['file'] + media_info = d['media_info'][0] + parts = media_info['parts'][0] + self.file_size = parts['file_size'] + self.file = parts['file'] self.media_type = d['media_type'] self.grandparent_title = d['grandparent_title'] diff --git a/utility/find_unwatched.py b/utility/find_unwatched.py index ecf8dca..c22d605 100644 --- a/utility/find_unwatched.py +++ b/utility/find_unwatched.py @@ -38,8 +38,10 @@ class METAINFO(object): self.rating_key = d['rating_key'] self.media_type = d['media_type'] self.grandparent_title = d['grandparent_title'] - self.file_size = d['file_size'] - self.file = d['file'] + media_info = d['media_info'][0] + parts = media_info['parts'][0] + self.file_size = parts['file_size'] + self.file = parts['file'] def get_new_rating_keys(rating_key, media_type): diff --git a/utility/remove_watched_movies.py b/utility/remove_watched_movies.py index 660a5f4..ca9a142 100644 --- a/utility/remove_watched_movies.py +++ b/utility/remove_watched_movies.py @@ -27,7 +27,9 @@ class METAINFO(object): def __init__(self, data=None): d = data or {} self.title = d['title'] - self.file = d['file'] + media_info = d['media_info'][0] + parts = media_info['parts'][0] + self.file = parts['file'] def get_metadata(rating_key): From 98b4f1e6d1ee542d019e93b66265836a0bd5d71e Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Sat, 24 Mar 2018 15:01:38 -0400 Subject: [PATCH 35/59] fix if, add int --- killstream/kill_more_than.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/killstream/kill_more_than.py b/killstream/kill_more_than.py index b59141d..379f720 100644 --- a/killstream/kill_more_than.py +++ b/killstream/kill_more_than.py @@ -28,7 +28,7 @@ ignore_lst = ('') # 2nd stream information is passed USERNAME = sys.argv[1] ADDRESS = sys.argv[2] -SESSION_KEY = sys.argv[3] +SESSION_KEY = int(sys.argv[3]) if USERNAME in ignore_lst: print(u"{} ignored.".format(USERNAME)) @@ -48,16 +48,15 @@ def kill_session(user, ip_address, session_key): if username == user and address == ip_address: user_sessions.append((session)) - if len(user_sessions) > 1: + if len(user_sessions) == 1: for session in user_sessions: if session_key == session.sessionKey: title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title - print(u"Killing {}'s second stream of {} for {}".format(username, title, MESSAGE)) + print(u"Killing {}'s second stream of {} for {}".format(user, title, MESSAGE)) session.stop(reason=MESSAGE) else: - for session in user_sessions: - username = session.usernames[0] - print(u"Not killing {}'s second stream. Same IP".format(username)) + print(u"Not killing {}'s second stream. Same IP".format(user)) kill_session(USERNAME, ADDRESS, SESSION_KEY) + From 41b01d8267a74c8f0f376f2f82a7488f0f3640c2 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 26 Mar 2018 09:17:07 -0400 Subject: [PATCH 36/59] updated sessions check. Trailers and pre-rolls not included in plexapi sessions(). --- killstream/kill_more_than.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/killstream/kill_more_than.py b/killstream/kill_more_than.py index 379f720..07d7487 100644 --- a/killstream/kill_more_than.py +++ b/killstream/kill_more_than.py @@ -41,21 +41,25 @@ plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) def kill_session(user, ip_address, session_key): user_sessions = [] + userip_sessions = [] for session in plex.sessions(): username = session.usernames[0] address = session.players[0].address - if username == user and address == ip_address: + if username == user: user_sessions.append((session)) + if username == user and address == ip_address: + userip_sessions.append((session)) - if len(user_sessions) == 1: + + if len(user_sessions) >= 2 > len(userip_sessions): for session in user_sessions: if session_key == session.sessionKey: title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title print(u"Killing {}'s second stream of {} for {}".format(user, title, MESSAGE)) session.stop(reason=MESSAGE) else: - print(u"Not killing {}'s second stream. Same IP".format(user)) + print(u"Not killing {}'s second stream. Same IP.".format(user)) kill_session(USERNAME, ADDRESS, SESSION_KEY) From cd4bfde57ca3a4c81a1f28ebece25162fd85aab6 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 27 Mar 2018 08:36:07 -0400 Subject: [PATCH 37/59] adding else print out for logs. --- killstream/ip_whitelist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/killstream/ip_whitelist.py b/killstream/ip_whitelist.py index 14a354b..23ba7ac 100644 --- a/killstream/ip_whitelist.py +++ b/killstream/ip_whitelist.py @@ -85,3 +85,7 @@ if ip_address not in IP_WHITELIST: user=username, title=title, ip=ip_address)) session.stop(reason=REASON.format(ip_address)) send_notification(SUBJECT_TEXT, BODY_TEXT.format(user=username, ip=ip_address, title=title)) + else: + print('User: {} is ignored from this script.'.format(username)) +else: + print('IP: {} is in whitelist, ignoring.'.format(ip_address)) \ No newline at end of file From 79308f204b3c91c3b2175cb1609e95bd636e6917 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 27 Mar 2018 08:36:52 -0400 Subject: [PATCH 38/59] stdout instead of print. --- killstream/ip_whitelist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/killstream/ip_whitelist.py b/killstream/ip_whitelist.py index 23ba7ac..1cca440 100644 --- a/killstream/ip_whitelist.py +++ b/killstream/ip_whitelist.py @@ -86,6 +86,6 @@ if ip_address not in IP_WHITELIST: session.stop(reason=REASON.format(ip_address)) send_notification(SUBJECT_TEXT, BODY_TEXT.format(user=username, ip=ip_address, title=title)) else: - print('User: {} is ignored from this script.'.format(username)) + sys.stdout.write('User: {} is ignored from this script.'.format(username)) else: - print('IP: {} is in whitelist, ignoring.'.format(ip_address)) \ No newline at end of file + sys.stdout.write('IP: {} is in whitelist, ignoring.'.format(ip_address)) \ No newline at end of file From 5823476f627a35da9e1b831028c68e347ae98cc3 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Sun, 8 Apr 2018 21:37:34 -0400 Subject: [PATCH 39/59] make str make search and data str in case of int. --- reporting/plex_netflix_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reporting/plex_netflix_check.py b/reporting/plex_netflix_check.py index a793a1f..eacb613 100644 --- a/reporting/plex_netflix_check.py +++ b/reporting/plex_netflix_check.py @@ -109,7 +109,7 @@ def instantwatch_search(name, media_type, site, search_limit): for data in results['span']: if data['@class'] == 'title' and search_limit is not 0: - if data['a']['$'].lower().startswith(name.lower()): + if str(data['a']['$']).lower().startswith(name.lower()): if amazon_id: if data['a']['@data-title-id'] == amazon_id: print('Match found on Amazon for {}'.format(data['a']['$'])) @@ -196,7 +196,7 @@ def main(): parser.add_argument('-l', '--library', metavar='', choices=sections_lst, nargs='+', help='Space separated list of case sensitive names to process. Allowed names are:\n' '(choices: %(choices)s)') - parser.add_argument('-s', '--search', metavar='', nargs='?', + parser.add_argument('-s', '--search', metavar='', nargs='?', type=str, help='Search any name.') parser.add_argument('-m', '--media_type', metavar='', choices=['movie', 'show'], nargs='?', help='Refine search for name by using media type.\n' From 88ac4746f01d8af2343a39faa4b95497789a7f14 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 10 Apr 2018 12:55:03 -0400 Subject: [PATCH 40/59] Create sync_watch_status.py --- utility/sync_watch_status.py | 154 +++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 utility/sync_watch_status.py diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py new file mode 100644 index 0000000..9c3e3dc --- /dev/null +++ b/utility/sync_watch_status.py @@ -0,0 +1,154 @@ +""" +Description: Sync the watch status from one user to another. Either by user or user/libraries +Author: Blacktwin +Requires: requests, plexapi, argparse + +Enabling Scripts in Tautulli: +Taultulli > Settings > Notification Agents > Add a Notification Agent > Script + +Configuration: +Taultulli > Settings > Notification Agents > New Script > Configuration: + + Script Name: sync_watch_status.py + Set Script Timeout: default + Description: Sync watch status + Save + +Triggers: +Taultulli > Settings > Notification Agents > New Script > Triggers: + + Check: Notify on Watched + Save + +Conditions: +Taultulli > Settings > Notification Agents > New Script > Conditions: + + Set Conditions: [{username} | {is} | {user_to_sync_from} ] + Save + +Script Arguments: +Taultulli > Settings > Notification Agents > New Script > Script Arguments: + + Select: Notify on Watched + Arguments: --ratingKey {rating_key} --userTo "Username2" "Username3" --userFrom {username} + + Save + Close + + Example: + Set in Tautulli in script notification agent or run manually + + plex_api_share.py --userFrom USER1 --userTo USER2 --libraries Movies + - Synced watch status of {title from library} to {USER2}'s account. + + plex_api_share.py --userFrom USER1 --userTo USER2 USER3 --allLibraries + - Synced watch status of {title from library} to {USER2 or USER3}'s account. + + Excluding; + --libraries becomes excluded if --allLibraries is set + sync_watch_status.py --userFrom USER1 --userTo USER2 --allLibraries --libraries Movies + - Shared [all libraries but Movies] with USER. + +""" +import requests +import argparse +import os +from plexapi.server import PlexServer + + +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) + +sections_lst = [x.title for x in plex.library.sections()] +user_lst = [x.title for x in plex.myPlexAccount().users()] + +def get_account(user): + # Access Plex User's Account + userAccount = plex.myPlexAccount().user(user) + token = userAccount.get_token(plex.machineIdentifier) + server = PlexServer(PLEX_URL, token) + return server + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description="Sync watch status from one user to others.", + formatter_class=argparse.RawTextHelpFormatter) + requiredNamed = parser.add_argument_group('required named arguments') + parser.add_argument('--libraries', nargs='*', choices=sections_lst, metavar='library', + help='Space separated list of case sensitive names to process. Allowed names are: \n' + '(choices: %(choices)s)') + parser.add_argument('--allLibraries', action='store_true', + help='Select all libraries.') + parser.add_argument('--ratingKey', nargs=1, + help='Rating key of item whose watch status is to be synced.') + requiredNamed.add_argument('--userFrom', nargs=None, choices=user_lst, metavar='username', required=True, + help='Space separated list of case sensitive names to process. Allowed names are: \n' + '(choices: %(choices)s)') + requiredNamed.add_argument('--userTo', nargs='*', choices=user_lst, metavar='usernames', required=True, + help='Space separated list of case sensitive names to process. Allowed names are: \n' + '(choices: %(choices)s)') + + + opts = parser.parse_args() + # print(opts) + + # Create Sync-From user account + plexFrom = get_account(opts.userFrom) + + # Defining libraries + libraries = '' + if opts.allLibraries and not opts.libraries: + libraries = sections_lst + elif not opts.allLibraries and opts.libraries: + libraries = opts.libraries + elif opts.allLibraries and opts.libraries: + # If allLibraries is used then any libraries listed will be excluded + for library in opts.libraries: + sections_lst.remove(library) + libraries = sections_lst + + for user in opts.userTo: + # Create Sync-To user account + plexTo = get_account(user) + if libraries: + for library in libraries: + try: + print('Checking library: {}'.format(library)) + section = plexFrom.library.section(library) + for item in section.search(unwatched=False): + title = item.title.encode('utf-8') + plexTo.fetchItem(item.key).markWatched() + print('Synced watch status of {} to {}\'s account.'.format(title, user)) + except Exception as e: + if str(e).startswith('Unknown'): + print('Library ({}) does not have a watch status.'.format(library)) + elif str(e).startswith('Invalid'): + print('Library ({}) not shared to user: {}.'.format(library, opts.userFrom)) + elif str(e).startswith('(404)'): + print('Library ({}) not shared to user: {}.'.format(library, user)) + else: + print(e) + pass + elif opts.ratingKey: + item = plexTo.fetchItem(opts.ratingKey) + title = item.title.encode('utf-8') + print('Syncing watch status of {} to {}\'s account.'.format(title, user)) + item.markWatched() + else: + print('No libraries or rating key provided.') \ No newline at end of file From 9e3b4443fffc0720780b7a0f4c86d65c9214c9de Mon Sep 17 00:00:00 2001 From: blacktwin Date: Tue, 10 Apr 2018 12:57:01 -0400 Subject: [PATCH 41/59] Delete vcs.xml --- .idea/vcs.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 9dd7c2969a27c88477a4b926648195e47bd69913 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Tue, 10 Apr 2018 12:57:27 -0400 Subject: [PATCH 42/59] Delete requirements.txt --- requirements.txt | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d257d94..0000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -#--------------------------------------------------------- -# Potential requirements. -# pip install -r requirements.txt -#--------------------------------------------------------- -requests -plexapi -configparser -matplotlib -numpy -basemap \ No newline at end of file From 960add24bde2e030b7cd009879037eb8603f1900 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Tue, 10 Apr 2018 12:57:37 -0400 Subject: [PATCH 43/59] Delete config.ini --- config.ini | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 config.ini diff --git a/config.ini b/config.ini deleted file mode 100644 index 00c8195..0000000 --- a/config.ini +++ /dev/null @@ -1,9 +0,0 @@ -# Specify your details to this file and most of the scripts will get it. - -[plex] -url = http://localhost:32400 -token = xxxx - -[tautulli] -url = http://localhost:8181/ -api = xxxx From 54e1e4a8faff2541fff8aacbef21c434478cd3cd Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 10 Apr 2018 13:34:28 -0400 Subject: [PATCH 44/59] update getting url and token variables --- config.ini | 9 --------- requirements.txt | 10 ---------- utility/sync_watch_status.py | 13 ++++--------- 3 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 config.ini delete mode 100644 requirements.txt diff --git a/config.ini b/config.ini deleted file mode 100644 index 00c8195..0000000 --- a/config.ini +++ /dev/null @@ -1,9 +0,0 @@ -# Specify your details to this file and most of the scripts will get it. - -[plex] -url = http://localhost:32400 -token = xxxx - -[tautulli] -url = http://localhost:8181/ -api = xxxx diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d257d94..0000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -#--------------------------------------------------------- -# Potential requirements. -# pip install -r requirements.txt -#--------------------------------------------------------- -requests -plexapi -configparser -matplotlib -numpy -basemap \ No newline at end of file diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 9c3e3dc..108d65a 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -57,17 +57,12 @@ from plexapi.server import PlexServer 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 = '' +PLEX_URL = PLEX_OVERRIDE_URL or os.getenv('PLEX_URL', PLEX_FALLBACK_URL) -if PLEX_OVERRIDE_URL: - PLEX_URL = PLEX_OVERRIDE_URL -if PLEX_OVERRIDE_TOKEN: - PLEX_TOKEN = PLEX_OVERRIDE_TOKEN +PLEX_FALLBACK_TOKEN = '' +PLEX_OVERRIDE_TOKEN = '' +PLEX_TOKEN = PLEX_OVERRIDE_URL or os.getenv('PLEX_TOKEN', PLEX_FALLBACK_TOKEN) sess = requests.Session() From 97a0a43f90e247326ad5adaecc531f5361664fe4 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 10 Apr 2018 13:36:49 -0400 Subject: [PATCH 45/59] token correction --- utility/sync_watch_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 108d65a..0f61680 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -62,7 +62,7 @@ PLEX_URL = PLEX_OVERRIDE_URL or os.getenv('PLEX_URL', PLEX_FALLBACK_URL) PLEX_FALLBACK_TOKEN = '' PLEX_OVERRIDE_TOKEN = '' -PLEX_TOKEN = PLEX_OVERRIDE_URL or os.getenv('PLEX_TOKEN', PLEX_FALLBACK_TOKEN) +PLEX_TOKEN = PLEX_OVERRIDE_TOKEN or os.getenv('PLEX_TOKEN', PLEX_FALLBACK_TOKEN) sess = requests.Session() From 59add7f1679304afbbb3eb92e7eca7163f3c4b3a Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 10 Apr 2018 13:46:56 -0400 Subject: [PATCH 46/59] env and override correction --- utility/sync_watch_status.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 0f61680..9f8316d 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -55,14 +55,11 @@ import argparse import os from plexapi.server import PlexServer - -PLEX_FALLBACK_URL = 'http://127.0.0.1:32400' PLEX_OVERRIDE_URL = '' -PLEX_URL = PLEX_OVERRIDE_URL or os.getenv('PLEX_URL', PLEX_FALLBACK_URL) +PLEX_URL = PLEX_OVERRIDE_URL or os.getenv('PLEX_URL') -PLEX_FALLBACK_TOKEN = '' PLEX_OVERRIDE_TOKEN = '' -PLEX_TOKEN = PLEX_OVERRIDE_TOKEN or os.getenv('PLEX_TOKEN', PLEX_FALLBACK_TOKEN) +PLEX_TOKEN = PLEX_OVERRIDE_TOKEN or os.getenv('PLEX_TOKEN') sess = requests.Session() From 90738fc0767b359cb87631e1188b409ce28b577a Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 12 Apr 2018 15:39:19 -0400 Subject: [PATCH 47/59] added support for admin account --- utility/sync_watch_status.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 9f8316d..8e37b1f 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -68,12 +68,17 @@ plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) sections_lst = [x.title for x in plex.library.sections()] user_lst = [x.title for x in plex.myPlexAccount().users()] +# Adding admin account name to list +user_lst.append(plex.myPlexAccount().title) def get_account(user): - # Access Plex User's Account - userAccount = plex.myPlexAccount().user(user) - token = userAccount.get_token(plex.machineIdentifier) - server = PlexServer(PLEX_URL, token) + if user == plex.myPlexAccount().title: + server = plex + else: + # Access Plex User's Account + userAccount = plex.myPlexAccount().user(user) + token = userAccount.get_token(plex.machineIdentifier) + server = PlexServer(PLEX_URL, token) return server @@ -143,4 +148,4 @@ if __name__ == '__main__': print('Syncing watch status of {} to {}\'s account.'.format(title, user)) item.markWatched() else: - print('No libraries or rating key provided.') \ No newline at end of file + print('No libraries or rating key provided.') From 56c8289553a0307e5ecc1669a8ea0c8a8483bd16 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Thu, 12 Apr 2018 21:06:13 -0400 Subject: [PATCH 48/59] clarity --- killstream/readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/killstream/readme.md b/killstream/readme.md index 2dbb556..6c71099 100644 --- a/killstream/readme.md +++ b/killstream/readme.md @@ -1 +1,3 @@ +### README + Killing streams is a Plex Pass only feature. So these scripts will only work for Plex Pass users. From 0cedfb00084ff522a22c721143751861dea1f74e Mon Sep 17 00:00:00 2001 From: blacktwin Date: Thu, 12 Apr 2018 21:08:29 -0400 Subject: [PATCH 49/59] killstream clarity --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3e1a059..e4cc829 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Scripts pulled from my gist profile. Kill stream +Killing streams is a Plex Pass only feature. So these scripts will only work for Plex Pass users. From 5881857b411750cbef8e55bae1c15eee155fb148 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 12 Apr 2018 21:58:53 -0400 Subject: [PATCH 50/59] shows were being marked as watched if an episode was watched. check each episode. --- utility/sync_watch_status.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 8e37b1f..a820732 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -101,7 +101,6 @@ if __name__ == '__main__': help='Space separated list of case sensitive names to process. Allowed names are: \n' '(choices: %(choices)s)') - opts = parser.parse_args() # print(opts) @@ -120,18 +119,33 @@ if __name__ == '__main__': sections_lst.remove(library) libraries = sections_lst + # Go through list of users for user in opts.userTo: # Create Sync-To user account plexTo = get_account(user) if libraries: + # Go through Libraries for library in libraries: try: print('Checking library: {}'.format(library)) - section = plexFrom.library.section(library) - for item in section.search(unwatched=False): + # Check library for watched items + section = plexFrom.library.section(library).search(unwatched=False) + for item in section: title = item.title.encode('utf-8') - plexTo.fetchItem(item.key).markWatched() - print('Synced watch status of {} to {}\'s account.'.format(title, user)) + # Check movie media type + if item.type == 'movie': + plexTo.fetchItem(item.key).markWatched() + print('Synced watch status of {} to {}\'s account.'.format(title, user)) + # Check show media type + elif item.type == 'show': + # If one episode is watched, series is flagged as watched + for child in item.episodes(): + # Check each episode + if child.isWatched: + ep_title = child.title.encode('utf-8') + plexTo.fetchItem(item.key).markWatched() + print('Synced watch status of {} - {} to {}\'s account.' + .format(title, ep_title, user)) except Exception as e: if str(e).startswith('Unknown'): print('Library ({}) does not have a watch status.'.format(library)) @@ -142,6 +156,7 @@ if __name__ == '__main__': else: print(e) pass + # Check rating key from Tautulli elif opts.ratingKey: item = plexTo.fetchItem(opts.ratingKey) title = item.title.encode('utf-8') From 42bc38b9633358f11538893324739ae5bfd0dfe5 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Fri, 13 Apr 2018 13:25:48 -0400 Subject: [PATCH 51/59] marking series watched instead of episode. child not item. --- utility/sync_watch_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index a820732..afae1fe 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -143,7 +143,7 @@ if __name__ == '__main__': # Check each episode if child.isWatched: ep_title = child.title.encode('utf-8') - plexTo.fetchItem(item.key).markWatched() + plexTo.fetchItem(child.key).markWatched() print('Synced watch status of {} - {} to {}\'s account.' .format(title, ep_title, user)) except Exception as e: From 318180e373b572fdcbe4a620a632d144c5dc1747 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 1 May 2018 08:09:32 -0400 Subject: [PATCH 52/59] != instead of 'is not' --- killstream/kill_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/killstream/kill_time.py b/killstream/kill_time.py index 886e925..fdf56bc 100644 --- a/killstream/kill_time.py +++ b/killstream/kill_time.py @@ -82,7 +82,7 @@ def kill_session(user): session.stop(reason=MESSAGE) -if media_type is not 'episode': +if media_type != 'episode': exit() watched_count, last_stop = get_history(username) From 12a6dd17784544224514dceb1eaf444b0a427a9d Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Tue, 1 May 2018 10:52:31 -0400 Subject: [PATCH 53/59] grk to int, stopped_time bracket correction. --- killstream/kill_time.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/killstream/kill_time.py b/killstream/kill_time.py index fdf56bc..82eb2df 100644 --- a/killstream/kill_time.py +++ b/killstream/kill_time.py @@ -38,7 +38,7 @@ END_TIME = time(06,00) # 06:00 username = str(sys.argv[1]) media_type = str(sys.argv[2]) -grandparent_rating_key = str(sys.argv[3]) +grandparent_rating_key = int(sys.argv[3]) TODAY = datetime.today().strftime('%Y-%m-%d') @@ -66,7 +66,7 @@ def get_history(username): res_data = response['response']['data']['data'] ep_watched = sum([data['watched_status'] for data in res_data if data['grandparent_rating_key'] == grandparent_rating_key and data['watched_status'] == 1]) - stopped_time = [data['stopped'] for data in res_data if data['watched_status' == 1]] + stopped_time = [data['stopped'] for data in res_data if data['watched_status'] == 1] return [ep_watched, stopped_time[0]] except Exception as e: From 51a86268a6bb82da41b95291b49bd7a4149cdd2c Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 3 May 2018 10:13:24 -0400 Subject: [PATCH 54/59] remove config.ini mentions --- killstream/kill_plex_streams.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/killstream/kill_plex_streams.py b/killstream/kill_plex_streams.py index 2f8342e..13a58a1 100644 --- a/killstream/kill_plex_streams.py +++ b/killstream/kill_plex_streams.py @@ -9,22 +9,12 @@ from plexapi.server import PlexServer import configparser # EDIT THESE SETTINGS # -PLEX_URL = '' # leave blank if using config.ini. Overrides config -PLEX_TOKEN = '' # leave blank if using config.ini. Overrides config +PLEX_URL = '' +PLEX_TOKEN = '' MESSAGE = 'Because....' # /EDIT THESE SETTINGS # -config = configparser.ConfigParser() -try: - config.read('../config.ini') - if not PLEX_URL: - PLEX_URL = config.get('plex', 'url') - if not PLEX_TOKEN: - PLEX_TOKEN = config.get('plex', 'token') -except configparser.NoSectionError: - sys.exit('Error: No config and missing var values.') - sess = requests.Session() sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) From 669530e45a6f3e34bad53d0b5842ff373cf69396 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 3 May 2018 12:26:05 -0400 Subject: [PATCH 55/59] more messaging, moved episode check to conditions, correction for 0 watched history for the day. --- killstream/kill_time.py | 92 ++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/killstream/kill_time.py b/killstream/kill_time.py index 82eb2df..ab2a909 100644 --- a/killstream/kill_time.py +++ b/killstream/kill_time.py @@ -1,15 +1,43 @@ """ -Limit number of plays of TV Show episodes during time of day. -Idea is to reduce continuous plays while sleeping. +Description: Limit number of plays of TV Show episodes during time of day. + Idea is to reduce continuous plays while sleeping. +Author: Blacktwin +Requires: requests, plexapi -Tautulli > Settings > Notification Agents > Scripts > Bell icon: - [X] Notify on playback start +Enabling Scripts in Tautulli: +Taultulli > Settings > Notification Agents > Add a Notification Agent > Script -Tautulli > Settings > Notification Agents > Scripts > Gear icon: - Playback Start: kill_time.py +Configuration: +Taultulli > Settings > Notification Agents > New Script > Configuration: + + Script Name: kill_time.py + Set Script Timeout: default + Description: {Tautulli_description} + Save + +Triggers: +Taultulli > Settings > Notification Agents > New Script > Triggers: + + Check: Playback Start + Save + +Conditions: +Taultulli > Settings > Notification Agents > New Script > Conditions: + + Set Conditions: [{Media Type} | {is} | {episode} ] + Save + +Script Arguments: +Taultulli > Settings > Notification Agents > New Script > Script Arguments: + + Select: Playback Start + Arguments: {username} {grandparent_rating_key} + + Save + Close + + Example: -Tautulli > Settings > Notifications > Script > Script Arguments - {username} {media_type} {grandparent_rating_key} """ @@ -26,19 +54,20 @@ TAUTULLI_URL = 'http://localhost:8182/' # Your Tautulli URL PLEX_TOKEN = 'xxxx' PLEX_URL = 'http://localhost:32400' +TIME_DELAY = 60 + WATCH_LIMIT = {'user1': 2, 'user2': 3, 'user3': 4} -MESSAGE = 'Are you still watching or are you asleep? If not please wait and try again.' +MESSAGE = 'Are you still watching or are you asleep? If not please wait ~{} seconds and try again.'.format(TIME_DELAY) START_TIME = time(22,00) # 22:00 END_TIME = time(06,00) # 06:00 ##/EDIT THESE SETTINGS ## username = str(sys.argv[1]) -media_type = str(sys.argv[2]) -grandparent_rating_key = int(sys.argv[3]) +grandparent_rating_key = int(sys.argv[2]) TODAY = datetime.today().strftime('%Y-%m-%d') @@ -51,8 +80,8 @@ sess.verify = False plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) -def get_history(username): - # Get the Tautulli history. +def get_get_history(username): + # Get the PlexPy history. payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', 'user': username, @@ -64,10 +93,22 @@ def get_history(username): response = r.json() res_data = response['response']['data']['data'] - ep_watched = sum([data['watched_status'] for data in res_data - if data['grandparent_rating_key'] == grandparent_rating_key and data['watched_status'] == 1]) - stopped_time = [data['stopped'] for data in res_data if data['watched_status'] == 1] - return [ep_watched, stopped_time[0]] + + ep_watched = [data['watched_status'] for data in res_data + if data['grandparent_rating_key'] == grandparent_rating_key and data['watched_status'] == 1] + if not ep_watched: + ep_watched = 0 + else: + ep_watched = sum(ep_watched) + + stopped_time = [data['stopped'] for data in res_data + if data['grandparent_rating_key'] == grandparent_rating_key and data['watched_status'] == 1] + if not stopped_time: + stopped_time = unix_time + else: + stopped_time = stopped_time[0] + + return ep_watched, stopped_time except Exception as e: sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e)) @@ -76,21 +117,24 @@ def get_history(username): def kill_session(user): for session in plex.sessions(): # Check for users stream - if session.usernames[0] is user: + if session.usernames[0] == user: title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title print('{user} is watching {title} and they might be asleep.'.format(user=user, title=title)) session.stop(reason=MESSAGE) -if media_type != 'episode': - exit() +watched_count, last_stop = get_get_history(username) -watched_count, last_stop = get_history(username) - -if abs(last_stop - unix_time) > 20: +if abs(last_stop - unix_time) > TIME_DELAY: + print('{} is awake!'.format(username)) exit() if watched_count > WATCH_LIMIT[username]: + print('Checking time range for {}.'.format(username)) if START_TIME <= now_time or now_time <= END_TIME: - print('User may be asleep.') kill_session(username) + else: + print('{} outside of time range.'.format(username)) +else: + print('{} limit is {} but has only watched {} episodes of this show today.'.format( + username, WATCH_LIMIT[username], watched_count)) \ No newline at end of file From 6d26b19efda9604b46f842def8c1ed05aff91099 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 17 May 2018 09:55:53 -0400 Subject: [PATCH 56/59] Fixes for matplotlib==2.2.2: fillcontinents > drawlsmask remove Latitude and Longitude grid for World map Other: Added choice for including/excluding a legend Changed arg -l location to -m map --- maps/ips_to_maps.py | 71 +++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/maps/ips_to_maps.py b/maps/ips_to_maps.py index 92bafdf..81d19e9 100644 --- a/maps/ips_to_maps.py +++ b/maps/ips_to_maps.py @@ -260,7 +260,7 @@ def get_geojson_dict(user_locations): } -def draw_map(map_type, geo_dict, filename, headless): +def draw_map(map_type, geo_dict, filename, headless, leg_choice): import matplotlib as mpl if headless: mpl.use("Agg") @@ -268,7 +268,7 @@ def draw_map(map_type, geo_dict, filename, headless): from mpl_toolkits.basemap import Basemap ## Map stuff ## - plt.figure(figsize=(16, 9), dpi=100, frameon=False, tight_layout=True) + plt.figure(figsize=(16, 9), dpi=100, frameon=False) lon_r = 0 lon_l = 0 @@ -285,8 +285,8 @@ def draw_map(map_type, geo_dict, filename, headless): elif map_type == 'World': m = Basemap(projection='robin', lat_0=0, lon_0=-100, resolution='l', area_thresh=100000.0) - m.drawmeridians(np.arange(0, 360, 30)) - m.drawparallels(np.arange(-90, 90, 30)) + # m.drawmeridians(np.arange(0, 360, 30)) + # m.drawparallels(np.arange(-90, 90, 30)) lon_r = 180 lon_l = -180.0 @@ -296,7 +296,7 @@ def draw_map(map_type, geo_dict, filename, headless): m.drawcoastlines() m.drawstates() m.drawcountries() - m.fillcontinents(color='#3C3C3C', lake_color='#1F1F1F') + m.drawlsmask(land_color='#3C3C3C', ocean_color='#1F1F1F') for key, values in geo_dict.items(): # add Accuracy as plot/marker size, change play count to del_s value. @@ -346,25 +346,27 @@ def draw_map(map_type, geo_dict, filename, headless): m.plot(px, py, marker=marker, color=color, markersize=markersize, label=legend, alpha=alph, zorder=zord) - handles, labels = plt.gca().get_legend_handles_labels() - idx = labels.index('Location: {}, {}, User: {}\nPlatform: {}, IP: {}, Play Count: {}'. - format(SERVER_CITY, SERVER_STATE, SERVER_FRIENDLY, SERVER_PLATFORM, REPLACEMENT_WAN_IP, 0)) - labels = labels[idx:] + labels[:idx] - handles = handles[idx:] + handles[:idx] - by_label = OrderedDict(zip(labels, handles)) + if leg_choice: + handles, labels = plt.gca().get_legend_handles_labels() + idx = labels.index('Location: {}, {}, User: {}\nPlatform: {}, IP: {}, Play Count: {}'. + format(SERVER_CITY, SERVER_STATE, SERVER_FRIENDLY, SERVER_PLATFORM, REPLACEMENT_WAN_IP, + 0)) + labels = labels[idx:] + labels[:idx] + handles = handles[idx:] + handles[:idx] + by_label = OrderedDict(zip(labels, handles)) - leg = plt.legend(by_label.values(), by_label.keys(), fancybox=True, fontsize='x-small', - numpoints=1, title="Legend", labelspacing=1., borderpad=1.5, handletextpad=2.) - if leg: - lleng = len(leg.legendHandles) - for i in range(1, lleng): - leg.legendHandles[i]._legmarker.set_markersize(10) - leg.legendHandles[i]._legmarker.set_alpha(1) - leg.get_title().set_color('#7B777C') - leg.draggable() - leg.get_frame().set_facecolor('#2C2C2C') - for text in leg.get_texts(): - plt.setp(text, color='#A5A5A7') + leg = plt.legend(by_label.values(), by_label.keys(), fancybox=True, fontsize='x-small', + numpoints=1, title="Legend", labelspacing=1., borderpad=1.5, handletextpad=2.) + if leg: + lleng = len(leg.legendHandles) + for i in range(1, lleng): + leg.legendHandles[i]._legmarker.set_markersize(10) + leg.legendHandles[i]._legmarker.set_alpha(1) + leg.get_title().set_color('#7B777C') + leg.draggable() + leg.get_frame().set_facecolor('#2C2C2C') + for text in leg.get_texts(): + plt.setp(text, color='#A5A5A7') plt.title(title_string) if filename: @@ -375,16 +377,16 @@ def draw_map(map_type, geo_dict, filename, headless): mng.window.state('zoomed') plt.show() - if __name__ == '__main__': timestr = time.strftime("%Y%m%d-%H%M%S") user_count = get_users_tables() user_lst = sorted(get_users_tables('friendly_name', user_count)) - json_check = sorted([f for f in os.listdir('.') if os.path.isfile(f) and f.endswith(".json")], key=os.path.getmtime) - parser = argparse.ArgumentParser(description="Use Tautulli to draw map of user locations base on IP address.", + json_check = sorted([f for f in os.listdir('.') if os.path.isfile(f) and f.endswith(".json")], + key=os.path.getmtime) + parser = argparse.ArgumentParser(description="Use PlexPy to draw map of user locations base on IP address.", formatter_class=argparse.RawTextHelpFormatter) - parser.add_argument('-l', '--location', default='NA', choices=['NA', 'EU', 'World', 'Geo'], metavar='', + parser.add_argument('-m', '--map', default='NA', choices=['NA', 'EU', 'World', 'Geo'], metavar='', help='Map location. choices: (%(choices)s) \n(default: %(default)s)') parser.add_argument('-c', '--count', nargs='?', type=int, default=2, metavar='', help='How many IPs to attempt to check. \n(default: %(default)s)') @@ -401,7 +403,12 @@ if __name__ == '__main__': help='Filename of map. None will not save. \n(default: %(default)s)') parser.add_argument('-j', '--json', nargs='?', type=str, choices=json_check, metavar='', help='Filename of json file to use. \n(choices: %(choices)s)') - parser.add_argument('--headless', help='Run headless.', action='store_true') + + parser.add_argument('--headless', action='store_true', help='Run headless.') + + parser.add_argument('--legend', dest='legend', action='store_true', help='Toggle on legend.') + parser.add_argument('--no_legend', dest='legend', action='store_false', help='Toggle off legend.') + parser.set_defaults(legend=True) opts = parser.parse_args() if opts.json: @@ -428,7 +435,7 @@ if __name__ == '__main__': with open(json_file, 'w') as fp: json.dump(geo_json, fp, indent=4, sort_keys=True) - if opts.location == 'Geo': + if opts.map == 'Geo': geojson = get_geojson_dict(geo_json) print("\n") @@ -446,7 +453,7 @@ if __name__ == '__main__': }) print(r.json()['html_url']) - webbrowser.open(r.json()['html_url']) + if not opts.headless: + webbrowser.open(r.json()['html_url']) else: - print(geo_json) - draw_map(opts.location, geo_json, filename, opts.headless) + draw_map(opts.map, geo_json, filename, opts.headless, opts.legend) From e660b8e69ad7de3138d2d35708943fc25ba2b239 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 17 May 2018 09:56:49 -0400 Subject: [PATCH 57/59] get_history's grandparent_rating_key to int() --- notify/notify_fav_tv_all_movie.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify/notify_fav_tv_all_movie.py b/notify/notify_fav_tv_all_movie.py index 296b96b..f5854e0 100644 --- a/notify/notify_fav_tv_all_movie.py +++ b/notify/notify_fav_tv_all_movie.py @@ -127,7 +127,7 @@ def get_history(showkey): # Get the user history from Tautulli. Length matters! payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_history', - 'grandparent_rating_key': showkey, + 'grandparent_rating_key': int(showkey), 'length': 10000} try: From 81d7051e550b946a7c9d2b7039fc73f35f17bfc0 Mon Sep 17 00:00:00 2001 From: blacktwin Date: Tue, 22 May 2018 11:03:29 -0400 Subject: [PATCH 58/59] added discord button discord button links to Tautulli's discord server. Buttons calls out Scripts section. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e4cc829..d3a4166 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # JBOPS - Just a Bunch Of Plex Scripts -[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4J6RPWZ9J9YML) [![PM](https://img.shields.io/badge/Reddit-Message-lightgrey.svg)](https://www.reddit.com/user/Blacktwin/) [![PM](https://img.shields.io/badge/Plex-Message-orange.svg)](https://forums.plex.tv/profile/discussions/Blacktwin) [![Issue](https://img.shields.io/badge/Submit-Issue-red.svg)](https://github.com/blacktwin/JBOPS/issues/new) +[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4J6RPWZ9J9YML) [![PM](https://img.shields.io/badge/Discord-Scripts-lightgrey.svg?colorB=7289da)](https://discord.gg/tQcWEUp) [![PM](https://img.shields.io/badge/Reddit-Message-lightgrey.svg)](https://www.reddit.com/user/Blacktwin/) [![PM](https://img.shields.io/badge/Plex-Message-orange.svg)](https://forums.plex.tv/profile/discussions/Blacktwin) [![Issue](https://img.shields.io/badge/Submit-Issue-red.svg)](https://github.com/blacktwin/JBOPS/issues/new) Most of these scripts utilize a combination of [Tautulli](https://github.com/Tautulli/Tautulli), [python-plexapi](https://github.com/pkkid/python-plexapi), and [requests](http://docs.python-requests.org/en/master/user/install/#install). From 6210e361f36ba070800e1b1b8bca6881ae3e3c84 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Thu, 24 May 2018 13:29:38 -0400 Subject: [PATCH 59/59] cleaning special characters from region, city, and friendly names. removed unused get_platform function --- maps/ips_to_maps.py | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/maps/ips_to_maps.py b/maps/ips_to_maps.py index 81d19e9..3365edb 100644 --- a/maps/ips_to_maps.py +++ b/maps/ips_to_maps.py @@ -33,6 +33,7 @@ import argparse import numpy as np import time import webbrowser +import re ## EDIT THESE SETTINGS ## TAUTULLI_APIKEY = '' # Your Tautulli API key @@ -71,13 +72,18 @@ PLATFORM_COLORS = {'Android': '#a4c639', # Green title_string = "Location of Plex users based on ISP data" +def clean_up_text(title): + cleaned = re.sub('\W+', ' ', title) + return cleaned + + class GeoData(object): def __init__(self, data=None): data = data or {} self.continent = data.get('continent', 'N/A') self.country = data.get('country', 'N/A') - self.region = data.get('region', 'N/A') - self.city = data.get('city', 'N/A') + self.region = clean_up_text(data.get('region', 'N/A')) + self.city = clean_up_text(data.get('city', 'N/A')) self.postal_code = data.get('postal_code', 'N/A') self.timezone = data.get('timezone', 'N/A') self.latitude = data.get('latitude', 'N/A') @@ -89,7 +95,7 @@ class UserIPs(object): def __init__(self, data=None): d = data or {} self.ip_address = d['ip_address'] - self.friendly_name = d['friendly_name'] + self.friendly_name = clean_up_text(d['friendly_name']) self.play_count = d['play_count'] self.platform = d['platform'] @@ -162,20 +168,6 @@ def get_geoip_info(ip_address=''): pass -def get_stream_type_by_top_10_platforms(): - # Get the user IP list from Tautulli - payload = {'apikey': TAUTULLI_APIKEY, - 'cmd': 'get_stream_type_by_top_10_platforms'} # length is number of returns, default is 25 - - try: - r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) - response = r.json() - res_data = response['response']['data']['categories'] - return res_data - except Exception as e: - sys.stderr.write("Tautulli API 'get_stream_type_by_top_10_platforms' request failed: {0}.".format(e)) - - def add_to_dictlist(d, key, val): if key not in d: d[key] = [val] @@ -377,6 +369,7 @@ def draw_map(map_type, geo_dict, filename, headless, leg_choice): mng.window.state('zoomed') plt.show() + if __name__ == '__main__': timestr = time.strftime("%Y%m%d-%H%M%S") @@ -396,9 +389,6 @@ if __name__ == '__main__': parser.add_argument('-i', '--ignore', nargs='+', type=str, default=None, choices=user_lst, metavar='', help='Space separated list of case sensitive names to process. Allowed names are: \n' '%(choices)s \n(default: %(default)s)') - parser.add_argument('-p', '--platform', nargs='+', type=str, default='all', - choices=get_stream_type_by_top_10_platforms(), metavar='', - help='Search by platform. Allowed platforms are:\n%(choices)s \n(default: %(default)s)') parser.add_argument('-f', '--filename', nargs='+', type=str, default='Map_{}'.format(timestr), metavar='', help='Filename of map. None will not save. \n(default: %(default)s)') parser.add_argument('-j', '--json', nargs='?', type=str, choices=json_check, metavar='',
Gist File