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
[](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!
-
{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 @@
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4J6RPWZ9J9YML) [](https://www.reddit.com/user/Blacktwin/) [](https://forums.plex.tv/profile/discussions/Blacktwin) [](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!
-
{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}
+
+ {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}