Merge remote-tracking branch 'origin/master'
# Conflicts: # notify/notify_added_custom.py # notify/notify_on_added.py # utility/sync_watch_status.py
This commit is contained in:
commit
165e851cbc
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Description: Create and share playlists based on Most Popular TV/Movies from Tautulli
|
||||
and Aired this day in history.
|
||||
@ -106,14 +109,14 @@ import unicodedata
|
||||
from collections import Counter
|
||||
from plexapi.server import PlexServer, CONFIG
|
||||
|
||||
### EDIT SETTINGS ###
|
||||
# ### EDIT SETTINGS ###
|
||||
|
||||
PLEX_URL = ''
|
||||
PLEX_TOKEN = ''
|
||||
TAUTULLI_URL = ''
|
||||
TAUTULLI_APIKEY = ''
|
||||
|
||||
## CODE BELOW ##
|
||||
# ## CODE BELOW ##
|
||||
|
||||
if not PLEX_URL:
|
||||
PLEX_URL = CONFIG.data['auth'].get('server_baseurl')
|
||||
@ -150,6 +153,7 @@ playlist_lst = [x.title for x in plex.playlists()]
|
||||
today = datetime.datetime.now().date()
|
||||
weeknum = datetime.date(today.year, today.month, today.day).isocalendar()[1]
|
||||
|
||||
|
||||
def actions():
|
||||
"""
|
||||
add - create new playlist for admin or users
|
||||
@ -269,7 +273,7 @@ def sort_by_dates(video, date_type):
|
||||
return [[video.ratingKey] + [str(video.originallyAvailableAt)]]
|
||||
|
||||
# todo-me return object
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
# print(e)
|
||||
return
|
||||
|
||||
@ -327,8 +331,9 @@ def multi_filter_search(keyword_dict, library, search_eps=None):
|
||||
|
||||
return list(set(search_lst))
|
||||
|
||||
|
||||
def get_content(libraries, jbop, filters=None, search=None, limit=None):
|
||||
"""Get all movies or episodes from LIBRARY_NAME
|
||||
"""Get all movies or episodes from LIBRARY_NAME.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@ -342,6 +347,7 @@ def get_content(libraries, jbop, filters=None, search=None, limit=None):
|
||||
list
|
||||
Sorted list of Movie and episodes that
|
||||
aired on today's date.
|
||||
|
||||
"""
|
||||
child_lst = []
|
||||
filter_lst = []
|
||||
@ -525,7 +531,8 @@ def show_playlist(playlist_title, playlist_keys):
|
||||
title = unicodedata.normalize('NFKD', title).encode('ascii', 'ignore').translate(None, "'")
|
||||
playlist_list.append(title)
|
||||
|
||||
print(u"Contents of Playlist {title}:\n{playlist}".format(title=playlist_title,
|
||||
print(u"Contents of Playlist {title}:\n{playlist}".format(
|
||||
title=playlist_title,
|
||||
playlist=', '.join(playlist_list)))
|
||||
exit()
|
||||
|
||||
@ -552,13 +559,13 @@ def create_playlist(playlist_title, playlist_keys, server, user):
|
||||
playlist_list.append(episode)
|
||||
else:
|
||||
playlist_list.append(plex_obj)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
try:
|
||||
obj = plex.fetchItem(key)
|
||||
print("{} may not have permission to this title: {}".format(user, obj.title))
|
||||
# print("Error: {}".format(e))
|
||||
pass
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
print('Rating Key: {}, may have been deleted or moved.'.format(key))
|
||||
# print("Error: {}".format(e))
|
||||
|
||||
@ -595,7 +602,7 @@ def delete_playlist(playlist_dict, title):
|
||||
print("...Deleted Playlist: {playlist.title} for '{user}'."
|
||||
.format(playlist=playlist, user=user))
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
# print("Playlist not found on '{user}' account".format(user=user))
|
||||
pass
|
||||
|
||||
@ -672,8 +679,8 @@ def create_title(jbop, libraries, days, filters, search, limit):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description="Create, share, and clean Playlists for users.",
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Create, share, and clean Playlists for users.",
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
# todo-me use parser grouping instead of choices for action and jbop?
|
||||
parser.add_argument('--jbop', choices=selectors().keys(), metavar='',
|
||||
@ -778,7 +785,8 @@ if __name__ == "__main__":
|
||||
'all_playlists': all_playlists})
|
||||
|
||||
if opts.self or not users:
|
||||
playlist_dict['data'].append({'server': plex,
|
||||
playlist_dict['data'].append({
|
||||
'server': plex,
|
||||
'user': 'admin',
|
||||
'user_selected': selected_playlists,
|
||||
'all_playlists': playlist_lst})
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Author: Bailey Belvis (https://github.com/philosowaffle)
|
||||
#
|
||||
@ -13,11 +16,9 @@
|
||||
#
|
||||
# - 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
|
||||
@ -196,7 +197,7 @@ 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:
|
||||
if event not in events:
|
||||
logger.debug("Invalid action: " + event)
|
||||
exit()
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
'''
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
https://gist.github.com/blacktwin/4ccb79c7d01a95176b8e88bf4890cd2b
|
||||
'''
|
||||
"""
|
||||
|
||||
from plexapi.server import PlexServer
|
||||
import random
|
||||
import re
|
||||
|
||||
|
||||
baseurl = 'http://localhost:32400'
|
||||
token = 'xxxxx'
|
||||
plex = PlexServer(baseurl, token)
|
||||
@ -44,8 +47,11 @@ def sylco(word):
|
||||
if word[-2:] == "es" or word[-2:] == "ed":
|
||||
doubleAndtripple_1 = len(re.findall(r'[eaoui][eaoui]', word))
|
||||
if doubleAndtripple_1 > 1 or len(re.findall(r'[eaoui][^eaoui]', word)) > 1:
|
||||
if word[-3:] == "ted" or word[-3:] == "tes" or word[-3:] == "ses" or word[-3:] == "ied" or word[
|
||||
-3:] == "ies":
|
||||
if word[-3:] == "ted" or \
|
||||
word[-3:] == "tes" or \
|
||||
word[-3:] == "ses" or \
|
||||
word[-3:] == "ied" or \
|
||||
word[-3:] == "ies":
|
||||
pass
|
||||
else:
|
||||
disc += 1
|
||||
@ -140,7 +146,6 @@ def sylco(word):
|
||||
if word in exception_add:
|
||||
syls += 1
|
||||
|
||||
|
||||
# calculate the output
|
||||
return numVowels - disc + syls
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
'''
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
If server admin stream is experiencing buffering and there are concurrent transcode streams from
|
||||
another user, kill concurrent transcode stream that has the lowest percent complete. Message in
|
||||
kill stream will list why it was killed ('Server Admin's stream take priority and this user has X
|
||||
@ -11,7 +14,7 @@ Tautulli > Settings > Notification Agents > Scripts > Bell icon:
|
||||
Tautulli > Settings > Notification Agents > Scripts > Gear icon:
|
||||
Buffer Warnings: kill_else_if_buffering.py
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import requests
|
||||
from operator import itemgetter
|
||||
@ -19,7 +22,7 @@ import unicodedata
|
||||
from plexapi.server import PlexServer
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
PLEX_TOKEN = 'xxxx'
|
||||
PLEX_URL = 'http://localhost:32400'
|
||||
|
||||
@ -27,8 +30,8 @@ DEFAULT_REASON = 'Server Admin\'s stream takes priority and {user}(you) has {x}
|
||||
' {user}\'s stream of {video} is {time}% complete. Should be finished in {comp} minutes. ' \
|
||||
'Try again then.'
|
||||
|
||||
ADMIN_USER = ('Admin') # additional usernames can be added ('Admin', 'user2')
|
||||
##
|
||||
ADMIN_USER = ('Admin') # Additional usernames can be added ('Admin', 'user2')
|
||||
# ##
|
||||
|
||||
sess = requests.Session()
|
||||
sess.verify = False
|
||||
@ -71,7 +74,7 @@ def main():
|
||||
|
||||
# Remove users with only 1 stream. Targeting users with multiple concurrent streams
|
||||
filtered_dict = {key: value for key, value in user_dict.items()
|
||||
if len(value) is not 1}
|
||||
if len(value) != 1}
|
||||
|
||||
# Find who to kill and who will be finishing first.
|
||||
if filtered_dict:
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Description: Use conditions to kill a stream
|
||||
Author: Blacktwin, Arcanemagus, Samwiseg0, JonnyWong16, DirtyCajunRice
|
||||
@ -372,8 +375,8 @@ class Stream:
|
||||
if self.session_exists is False:
|
||||
sys.stdout.write(
|
||||
"Session '{}' from user '{}' is no longer active "
|
||||
.format(self.session_id, self.username)
|
||||
+ "on the server, stopping monitoring.\n")
|
||||
.format(self.session_id, self.username) +
|
||||
"on the server, stopping monitoring.\n")
|
||||
return False
|
||||
|
||||
now = datetime.now()
|
||||
@ -392,8 +395,8 @@ class Stream:
|
||||
elif self.state == 'playing' or self.state == 'buffering':
|
||||
sys.stdout.write(
|
||||
"Session '{}' from user '{}' has been resumed, "
|
||||
.format(self.session_id, self.username)
|
||||
+ "stopping monitoring.\n")
|
||||
.format(self.session_id, self.username) +
|
||||
"stopping monitoring.\n")
|
||||
return False
|
||||
|
||||
|
||||
@ -562,8 +565,8 @@ if __name__ == "__main__":
|
||||
parser.add_argument('--sessionId',
|
||||
help='The unique identifier for the stream.')
|
||||
parser.add_argument('--notify', type=int,
|
||||
help='Notification Agent ID number to Agent to send '
|
||||
+ 'notification.')
|
||||
help='Notification Agent ID number to Agent to ' +
|
||||
'send notification.')
|
||||
parser.add_argument('--limit', type=int, default=(20 * 60), # 20 minutes
|
||||
help='The time session is allowed to remain paused.')
|
||||
parser.add_argument('--interval', type=int, default=30,
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Description: Limiting Plex users by plays, watches, or total time from Tautulli.
|
||||
Author: Blacktwin, Arcanemagus
|
||||
@ -48,7 +51,7 @@ import argparse
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import os
|
||||
from plexapi.server import PlexServer, CONFIG
|
||||
from plexapi.server import PlexServer
|
||||
from time import time as ttime
|
||||
from time import sleep
|
||||
|
||||
@ -65,6 +68,7 @@ TAUTULLI_APIKEY = os.getenv('TAUTULLI_APIKEY', TAUTULLI_APIKEY)
|
||||
TAUTULLI_ENCODING = os.getenv('TAUTULLI_ENCODING', 'UTF-8')
|
||||
|
||||
# Using CONFIG file
|
||||
# from plexapi.server import CONFIG
|
||||
# PLEX_URL = CONFIG.data['auth'].get('server_baseurl', PLEX_URL)
|
||||
# PLEX_TOKEN = CONFIG.data['auth'].get('server_token', PLEX_TOKEN)
|
||||
# TAUTULLI_URL = CONFIG.data['auth'].get('tautulli_baseurl', TAUTULLI_URL)
|
||||
@ -346,16 +350,16 @@ if __name__ == "__main__":
|
||||
if not message:
|
||||
message = LIMIT_MESSAGE.format(delay=opts.delay)
|
||||
ep_watched = [data['watched_status'] for data in history['data']
|
||||
if data['grandparent_rating_key'] == opts.grandparent_rating_key
|
||||
and data['watched_status'] == 1]
|
||||
if data['grandparent_rating_key'] == opts.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 history['data']
|
||||
if data['grandparent_rating_key'] == opts.grandparent_rating_key
|
||||
and data['watched_status'] == 1]
|
||||
if data['grandparent_rating_key'] == opts.grandparent_rating_key and
|
||||
data['watched_status'] == 1]
|
||||
if not stopped_time:
|
||||
stopped_time = unix_time
|
||||
else:
|
||||
@ -366,9 +370,9 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
|
||||
if ep_watched >= total_limit:
|
||||
print("{}'s limit is {} and has watched {} episodes of this show today."
|
||||
.format(opts.username, total_limit, ep_watched))
|
||||
print("{}'s limit is {} and has watched {} episodes of this show today.".format(
|
||||
opts.username, total_limit, ep_watched))
|
||||
terminate_session(opts.sessionId, message, opts.notify, opts.username)
|
||||
else:
|
||||
print("{}'s limit is {} but has only watched {} episodes of this show today."
|
||||
.format(opts.username, total_limit, ep_watched))
|
||||
print("{}'s limit is {} but has only watched {} episodes of this show today.".format(
|
||||
opts.username, total_limit, ep_watched))
|
||||
|
@ -59,4 +59,3 @@ Arguments:
|
||||
```
|
||||
--jbop time --username {username} --sessionId {session_id} --duration {duration} --limit hours=2 --killMessage "You have met your limit of 3 days and 10 hours."
|
||||
```
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Use Tautulli draw a map connecting Server to Clients based on IP addresses.
|
||||
|
||||
@ -30,12 +33,12 @@ import json
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
import argparse
|
||||
import numpy as np
|
||||
# import numpy as np
|
||||
import time
|
||||
import webbrowser
|
||||
import re
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = '' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
@ -259,7 +262,7 @@ def draw_map(map_type, geo_dict, filename, headless, leg_choice):
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.basemap import Basemap
|
||||
|
||||
## Map stuff ##
|
||||
# ## Map stuff ##
|
||||
plt.figure(figsize=(16, 9), dpi=100, frameon=False)
|
||||
lon_r = 0
|
||||
lon_l = 0
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
# Maps
|
||||
|
||||
Maps are created with either Matplotlib/Basemap or as a geojson file on an anonymous gist.
|
||||
@ -57,6 +56,3 @@ Choose which map type you'd like by using the `-l` argument:
|
||||
- [ ] Find server's external IP, geolocation. Allow custom location to override
|
||||
- [ ] Add arg for tracert visualization from server to client
|
||||
- [ ] Animate tracert visualization? gif?
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Find what was added TFRAME ago and not watched and notify admin using Tautulli.
|
||||
|
||||
TAUTULLI_URL + delete_media_info_cache?section_id={section_id}
|
||||
@ -12,7 +14,7 @@ import time
|
||||
TFRAME = 1.577e+7 # ~ 6 months in seconds
|
||||
TODAY = time.time()
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
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.
|
||||
@ -162,10 +164,10 @@ for library in libraries:
|
||||
# Find movie 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 show in show_lst:
|
||||
try:
|
||||
@ -181,7 +183,7 @@ for show in show_lst:
|
||||
u" not been watched.<d/t> <dd>File location: {x.file}</dd> <br>".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 = """\
|
||||
|
368
notify/notify_added_custom.py
Normal file
368
notify/notify_added_custom.py
Normal file
@ -0,0 +1,368 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
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:
|
||||
notify_added_lastweek.py -t poster -d 1 -u all -i user1 user2 -s 250 100
|
||||
# email all users expect user1 & user2 what was added in the last day using posters that are 250x100
|
||||
|
||||
notify_added_lastweek.py -t poster -d 7 -u all
|
||||
# email all users what was added in the last 7 days(week) using posters that are default sized
|
||||
|
||||
notify_added_lastweek.py -t poster -d 7 -u all -s 1000 500
|
||||
# email all users what was added in the last 7 days(week) using posters that are 1000x500
|
||||
|
||||
notify_added_lastweek.py -t art -d 7 -u user1
|
||||
# email user1 & self what was added in the last 7 days(week) using artwork that is default sized
|
||||
|
||||
notify_added_lastweek.py -t art -d 7
|
||||
# email self what was added in the last 7 days(week) using artwork that is default sized
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.image import MIMEImage
|
||||
import email.utils
|
||||
import smtplib
|
||||
import urllib
|
||||
import cgi
|
||||
import uuid
|
||||
import argparse
|
||||
|
||||
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
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
|
||||
name = '' # Your name
|
||||
sender = '' # From email address
|
||||
to = [sender] # Whoever you want to email [sender, 'name@example.com']
|
||||
# Emails will be sent as BCC.
|
||||
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 = 'Tautulli Added Last {} day(s) Notification' # The email subject
|
||||
|
||||
# Default sizing for pictures
|
||||
# Poster
|
||||
poster_h = 205
|
||||
poster_w = 100
|
||||
# Artwork
|
||||
art_h = 100
|
||||
art_w = 205
|
||||
|
||||
# ## /EDIT THESE SETTINGS ##
|
||||
|
||||
|
||||
class METAINFO(object):
|
||||
def __init__(self, data=None):
|
||||
d = data or {}
|
||||
self.added_at = d['added_at']
|
||||
self.parent_rating_key = d['parent_rating_key']
|
||||
self.title = d['title']
|
||||
self.rating_key = d['rating_key']
|
||||
self.media_type = d['media_type']
|
||||
self.grandparent_title = d['grandparent_title']
|
||||
self.thumb = d['art']
|
||||
self.summary = d['summary']
|
||||
|
||||
|
||||
def get_recent(section_id, start, count):
|
||||
# Get the metadata for a media item. Count matters!
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'start': str(start),
|
||||
'count': str(count),
|
||||
'section_id': section_id,
|
||||
'cmd': 'get_recently_added'}
|
||||
|
||||
try:
|
||||
r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
||||
response = r.json()
|
||||
|
||||
if response['response']['result'] == 'success':
|
||||
res_data = response['response']['data']['recently_added']
|
||||
|
||||
return res_data
|
||||
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_recently_added' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
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}
|
||||
|
||||
try:
|
||||
r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
||||
response = r.json()
|
||||
if response['response']['result'] == 'success':
|
||||
res_data = response['response']['data']
|
||||
|
||||
return METAINFO(data=res_data)
|
||||
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_libraries_table():
|
||||
# Get the data on the Tautulli libraries table.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_libraries_table'}
|
||||
|
||||
try:
|
||||
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("Tautulli API 'get_libraries_table' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def update_library_media_info(section_id):
|
||||
# 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(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload)
|
||||
response = r.status_code
|
||||
if response != 200:
|
||||
print(r.content)
|
||||
|
||||
except Exception as 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': TAUTULLI_APIKEY,
|
||||
'cmd': 'pms_image_proxy',
|
||||
'img': thumb}
|
||||
|
||||
try:
|
||||
r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload, stream=True)
|
||||
return r.url
|
||||
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_users_tables' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_users():
|
||||
# Get the user list from Tautulli.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_users'}
|
||||
|
||||
try:
|
||||
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("Tautulli API 'get_user' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_rating_keys(TODAY, LASTDATE):
|
||||
|
||||
recent_lst = []
|
||||
# Get the rating_key for what was recently added
|
||||
count = 25
|
||||
for section_id in glt:
|
||||
start = 0
|
||||
|
||||
while True:
|
||||
# Assume all items will be returned in descending order of added_at
|
||||
recent_items = get_recent(section_id, start, count)
|
||||
|
||||
if all([recent_items]):
|
||||
start += count
|
||||
for item in recent_items:
|
||||
if LASTDATE <= int(item['added_at']) <= TODAY:
|
||||
recent_lst.append(item['rating_key'])
|
||||
continue
|
||||
elif not all([recent_items]):
|
||||
break
|
||||
|
||||
start += count
|
||||
if recent_lst:
|
||||
return recent_lst
|
||||
sys.stderr.write("Recently Added list: {0}.".format(recent_lst))
|
||||
exit()
|
||||
|
||||
|
||||
def build_html(rating_key, height, width, pic_type):
|
||||
|
||||
meta = get_metadata(str(rating_key))
|
||||
|
||||
added = time.ctime(float(meta.added_at))
|
||||
# Pull image url
|
||||
thumb_url = "{}.jpeg".format(get_pms_image_proxy(meta.thumb))
|
||||
if pic_type == 'poster':
|
||||
thumb_url = thumb_url.replace('%2Fart%', '%2Fposter%')
|
||||
image_name = "{}.jpg".format(str(rating_key))
|
||||
# Saving image in current path
|
||||
urllib.urlretrieve(thumb_url, image_name)
|
||||
image = dict(title=meta.rating_key, path=image_name, cid=str(uuid.uuid4()))
|
||||
if meta.grandparent_title == '' or meta.media_type == 'movie':
|
||||
# Movies
|
||||
notify = u"<dt>{x.title} ({x.rating_key}) was added {when}.</dt>" \
|
||||
u"</dt> <dd> <table> <tr> <th>" \
|
||||
'<img src="cid:{cid}" alt="{alt}" width="{width}" height="{height}"> </th>' \
|
||||
u" <th id=t11> {x.summary} </th> </tr> </table> </dd> <br>" \
|
||||
.format(
|
||||
x=meta, when=added, alt=cgi.escape(meta.rating_key),
|
||||
quote=True, width=width, height=height, **image)
|
||||
else:
|
||||
# Shows
|
||||
notify = u"<dt>{x.grandparent_title}: {x.title} ({x.rating_key}) was added {when}." \
|
||||
u"</dt> <dd> <table> <tr> <th>" \
|
||||
'<img src="cid:{cid}" alt="{alt}" width="{width}" height="{height}"> </th>' \
|
||||
u" <th id=t11> {x.summary} </th> </tr> </table> </dd> <br>" \
|
||||
.format(
|
||||
x=meta, when=added, alt=cgi.escape(meta.rating_key),
|
||||
quote=True, width=width, height=height, **image)
|
||||
|
||||
image_text = MIMEText(u'[image: {title}]'.format(**image), 'plain', 'utf-8')
|
||||
|
||||
return image_text, image, notify
|
||||
|
||||
|
||||
def send_email(msg_text_lst, notify_lst, image_lst, to, days):
|
||||
"""
|
||||
Using info found here: http://stackoverflow.com/a/20485764/7286812
|
||||
to accomplish emailing inline images
|
||||
"""
|
||||
msg_html = MIMEText("""\
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
th#t11 {{ padding: 6px; vertical-align: top; text-align: left; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hi!<br>
|
||||
<br>Below is the list of content added to Plex's {LIBRARY_NAMES} this week.<br>
|
||||
<dl>
|
||||
{notify_lst}
|
||||
</dl>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
""".format(
|
||||
notify_lst="\n".join(notify_lst).encode("utf-8"),
|
||||
LIBRARY_NAMES=" & ".join(LIBRARY_NAMES),
|
||||
quote=True), 'html', 'utf-8')
|
||||
|
||||
message = MIMEMultipart('related')
|
||||
message['Subject'] = email_subject.format(days)
|
||||
message['From'] = email.utils.formataddr((name, sender))
|
||||
message_alternative = MIMEMultipart('alternative')
|
||||
message.attach(message_alternative)
|
||||
|
||||
for msg_text in msg_text_lst:
|
||||
message_alternative.attach(msg_text)
|
||||
|
||||
message_alternative.attach(msg_html)
|
||||
|
||||
for img in image_lst:
|
||||
with open(img['path'], 'rb') as file:
|
||||
message_image_lst = [MIMEImage(file.read(), name=os.path.basename(img['path']))]
|
||||
|
||||
for msg in message_image_lst:
|
||||
message.attach(msg)
|
||||
msg.add_header('Content-ID', '<{}>'.format(img['cid']))
|
||||
|
||||
mailserver = smtplib.SMTP(email_server, email_port)
|
||||
mailserver.ehlo()
|
||||
mailserver.starttls()
|
||||
mailserver.ehlo()
|
||||
mailserver.login(email_username, email_password)
|
||||
mailserver.sendmail(sender, to, message.as_string())
|
||||
mailserver.quit()
|
||||
print('Email sent')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
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='*')
|
||||
parser.add_argument('-d', '--days', help='Time frame for which to check recently added to Plex.',
|
||||
required=True, type=int)
|
||||
parser.add_argument('-u', '--users', help='Which users from Plex will be emailed.',
|
||||
nargs='+', default='self', type=str)
|
||||
parser.add_argument('-i', '--ignore', help='Which users from Plex to ignore.',
|
||||
nargs='+', default='None', type=str)
|
||||
|
||||
opts = parser.parse_args()
|
||||
|
||||
TODAY = int(time.time())
|
||||
LASTDATE = int(TODAY - opts.days * 24 * 60 * 60)
|
||||
|
||||
# Image sizing based on type or custom size
|
||||
if opts.type == 'poster' and not opts.size:
|
||||
height = poster_h
|
||||
width = poster_w
|
||||
elif opts.size:
|
||||
height = opts.size[0]
|
||||
width = opts.size[1]
|
||||
else:
|
||||
height = art_h
|
||||
width = art_w
|
||||
|
||||
# Find the libraries from LIBRARY_NAMES
|
||||
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_users()
|
||||
if x['email'] is not None 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_users():
|
||||
for arg_users in opts.users:
|
||||
if arg_users in get_users['username']:
|
||||
to = to + [str(get_users['email'])]
|
||||
print('Sending email(s) to {}'.format(', '.join(to)))
|
||||
|
||||
# Gather rating_keys on recently added media.
|
||||
rating_keys_lst = get_rating_keys(TODAY, LASTDATE)
|
||||
|
||||
# Build html elements from rating_key
|
||||
image_lst = []
|
||||
msg_text_lst = []
|
||||
notify_lst = []
|
||||
|
||||
build_parts = [build_html(rating_key, height, width, opts.type) for rating_key in sorted(rating_keys_lst)]
|
||||
for parts in build_parts:
|
||||
msg_text_lst.append(parts[0])
|
||||
image_lst.append(parts[1])
|
||||
notify_lst.append(parts[2])
|
||||
|
||||
# Send email
|
||||
send_email(msg_text_lst, notify_lst, image_lst, to, opts.days)
|
||||
|
||||
# Delete images in current path
|
||||
for img in image_lst:
|
||||
os.remove(img['path'])
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Delay Notification Agent message for concurrent streams
|
||||
|
||||
@ -19,7 +22,7 @@ import sys
|
||||
import argparse
|
||||
from time import sleep
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = '' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
CONCURRENT_TOTAL = 2
|
||||
@ -44,7 +47,7 @@ BODY_TEXT = """\
|
||||
|
||||
|
||||
def get_activity():
|
||||
# Get the current activity on the PMS.
|
||||
"""Get the current activity on the PMS."""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_activity'}
|
||||
|
||||
@ -60,7 +63,7 @@ def get_activity():
|
||||
|
||||
|
||||
def send_notification(subject_text, body_text):
|
||||
# Format notification 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)
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Notify users of recently added episode to show that they have watched at least LIMIT times via email.
|
||||
Also notify users of new movies.
|
||||
@ -22,7 +25,7 @@ import smtplib
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'XXXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
@ -74,6 +77,7 @@ TV_BODY = """\
|
||||
|
||||
user_dict = {}
|
||||
|
||||
|
||||
class Users(object):
|
||||
def __init__(self, data=None):
|
||||
d = data or {}
|
||||
@ -108,6 +112,7 @@ def get_user(user_id):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_users():
|
||||
# Get the user list from Tautulli.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -134,8 +139,9 @@ def get_history(showkey):
|
||||
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')]
|
||||
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("Tautulli API 'get_history' request failed: {0}.".format(e))
|
||||
@ -188,7 +194,7 @@ def send_email(to, email_subject, body_html):
|
||||
mailserver.login(email_username, email_password)
|
||||
mailserver.sendmail(sender, to, message.as_string())
|
||||
mailserver.quit()
|
||||
print 'Email sent'
|
||||
print('Email sent')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Pulling together User IP information and Email.
|
||||
|
||||
@ -19,7 +21,7 @@ import argparse
|
||||
import requests
|
||||
import sys
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = '' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
NOTIFIER_ID = 12 # The notification notifier ID
|
||||
@ -69,7 +71,7 @@ class UserEmail(object):
|
||||
|
||||
|
||||
def get_user_ip_addresses(user_id='', ip_address=''):
|
||||
# Get the user IP list from Tautulli
|
||||
"""Get the user IP list from Tautulli."""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_user_ips',
|
||||
'user_id': user_id,
|
||||
@ -99,7 +101,7 @@ def get_user_ip_addresses(user_id='', ip_address=''):
|
||||
|
||||
|
||||
def get_geoip_info(ip_address=''):
|
||||
# Get the geo IP lookup from Tautulli
|
||||
"""Get the geo IP lookup from Tautulli."""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_geoip_lookup',
|
||||
'ip_address': ip_address}
|
||||
@ -123,7 +125,7 @@ def get_geoip_info(ip_address=''):
|
||||
|
||||
|
||||
def get_user_email(user_id=''):
|
||||
# Get the user email from Tautulli
|
||||
"""Get the user email from Tautulli."""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_user',
|
||||
'user_id': user_id}
|
||||
@ -147,7 +149,7 @@ def get_user_email(user_id=''):
|
||||
|
||||
|
||||
def send_notification(arguments=None, geodata=None, useremail=None):
|
||||
# Format notification text
|
||||
"""Format notification text."""
|
||||
try:
|
||||
subject = SUBJECT_TEXT.format(p=arguments, g=geodata, u=useremail)
|
||||
body = BODY_TEXT.format(p=arguments, g=geodata, u=useremail)
|
||||
|
103
notify/notify_on_added.py
Normal file
103
notify/notify_on_added.py
Normal file
@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Tautulli > Settings > Notification Agents > Scripts > Bell icon:
|
||||
[X] Notify on Recently Added
|
||||
Tautulli > Settings > Notification Agents > Scripts > Gear icon:
|
||||
Recently Added: notify_on_added.py
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
from email.mime.text import MIMEText
|
||||
import email.utils
|
||||
import smtplib
|
||||
import argparse
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
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('-med', '--show_type', action='store', default='',
|
||||
help='The type of media')
|
||||
parser.add_argument('-pos', '--poster', action='store', default='',
|
||||
help='The poster url')
|
||||
parser.add_argument('-tt', '--title', action='store', default='',
|
||||
help='The title of the TV show')
|
||||
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()
|
||||
|
||||
# Edit user@email.com and shows
|
||||
users = [{'email': 'user1@gmail.com',
|
||||
'shows': ('show1', 'show2')
|
||||
},
|
||||
{'email': 'user2@gmail.com',
|
||||
'shows': ('show1', 'show2', 'show3')
|
||||
},
|
||||
{'email': 'user3@gmail.com',
|
||||
'shows': ('show1', 'show2', 'show3', 'show4')
|
||||
}]
|
||||
|
||||
# Kill script now if show_name is not in lists
|
||||
too = list('Match' for u in users if p.show_name in u['shows'])
|
||||
if not too:
|
||||
print('Kill script now show_name is not in lists')
|
||||
exit()
|
||||
|
||||
# Join email addresses
|
||||
to = list([u['email'] for u in users if p.show_name in u['shows']])
|
||||
|
||||
# Email settings
|
||||
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)
|
||||
email_username = 'email' # Your email username
|
||||
email_password = 'password' # Your email password
|
||||
email_subject = 'New episode for ' + p.show_name + ' is available on ' + p.plex_server # The email subject
|
||||
|
||||
# Detailed body for tv shows
|
||||
show_html = """\
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p>Hi!<br>
|
||||
{p.show_name} S{p.season_num} - E{p.episode_num} -- {p.episode_name} -- was recently added to {p.library_name} on PLEX
|
||||
<br><br>
|
||||
<br> {p.summary} <br>
|
||||
<br><img src="{p.poster}" alt="Poster unavailable" height="150" width="102"><br>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
""".format(p=p)
|
||||
|
||||
# ### Do not edit below ###
|
||||
# Check to see whether it is a tv show
|
||||
if p.show_type.lower() == 'show' or p.show_type.lower() == 'episode':
|
||||
message = MIMEText(show_html, 'html')
|
||||
message['Subject'] = email_subject
|
||||
message['From'] = email.utils.formataddr((name, sender))
|
||||
|
||||
mailserver = smtplib.SMTP(email_server, email_port)
|
||||
mailserver.starttls()
|
||||
mailserver.ehlo()
|
||||
mailserver.login(email_username, email_password)
|
||||
mailserver.sendmail(sender, to, message.as_string())
|
||||
mailserver.quit()
|
||||
else:
|
||||
exit()
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Description: Notify only if recently aired/released
|
||||
Author: Blacktwin
|
||||
@ -64,6 +67,7 @@ aired_date = datetime.strptime(air_date, date_format)
|
||||
today = date.today()
|
||||
delta = today - aired_date.date()
|
||||
|
||||
|
||||
def notify_recently_added(rating_key, notifier_id):
|
||||
# Get the metadata for a media item.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -80,6 +84,7 @@ def notify_recently_added(rating_key, notifier_id):
|
||||
sys.stderr.write("Tautulli API 'notify_recently_added' request failed: {0}.".format(e))
|
||||
pass
|
||||
|
||||
|
||||
if delta.days < RECENT_DAYS:
|
||||
notify_recently_added(rating_key, NOTIFIER_ID)
|
||||
else:
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Notify users of recently added episode to show that they have watched at least LIMIT times via email.
|
||||
Block users with IGNORE_LST.
|
||||
@ -21,7 +24,7 @@ import smtplib
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'XXXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
@ -38,6 +41,7 @@ email_password = '' # Your email password
|
||||
|
||||
user_dict = {}
|
||||
|
||||
|
||||
class Users(object):
|
||||
def __init__(self, data=None):
|
||||
d = data or {}
|
||||
@ -74,7 +78,10 @@ def get_user(user_id):
|
||||
|
||||
|
||||
def get_history(showkey):
|
||||
# Get the user history from Tautulli. Length matters!
|
||||
"""Get the user history from Tautulli.
|
||||
|
||||
Length matters!
|
||||
"""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_history',
|
||||
'grandparent_rating_key': showkey,
|
||||
@ -84,8 +91,9 @@ def get_history(showkey):
|
||||
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')]
|
||||
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("Tautulli API 'get_history' request failed: {0}.".format(e))
|
||||
@ -199,4 +207,4 @@ if __name__ == '__main__':
|
||||
mailserver.login(email_username, email_password)
|
||||
mailserver.sendmail(sender, to, message.as_string())
|
||||
mailserver.quit()
|
||||
print 'Email sent'
|
||||
print('Email sent')
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Pulling together User IP information and Email.
|
||||
Enable the API under Settings > Access Control and remember your API key.
|
||||
@ -13,9 +16,9 @@ from email.mime.text import MIMEText
|
||||
import email.utils
|
||||
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}
|
||||
# ## -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 ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'xxxxxxxxxxx' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
@ -54,7 +57,10 @@ email_subject = "New IP has been detected using Plex."
|
||||
|
||||
IGNORE_LST = ['123456', '123456'] # User_id
|
||||
|
||||
##Geo Space##
|
||||
|
||||
# ##Geo Space##
|
||||
|
||||
|
||||
class GeoData(object):
|
||||
def __init__(self, data=None):
|
||||
data = data or {}
|
||||
@ -62,7 +68,10 @@ class GeoData(object):
|
||||
self.city = data.get('city', 'N/A')
|
||||
self.postal_code = data.get('postal_code', 'N/A')
|
||||
|
||||
##USER Space##
|
||||
|
||||
# ##USER Space##
|
||||
|
||||
|
||||
class UserEmail(object):
|
||||
def __init__(self, data=None):
|
||||
data = data or {}
|
||||
@ -70,7 +79,10 @@ class UserEmail(object):
|
||||
self.user_id = data.get('user_id', 'N/A')
|
||||
self.user_thumb = data.get('user_thumb', 'N/A')
|
||||
|
||||
##API Space##
|
||||
|
||||
# ##API Space##
|
||||
|
||||
|
||||
def get_user_ip_addresses(user_id='', ip_address=''):
|
||||
# Get the user IP list from Tautulli
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -99,6 +111,7 @@ def get_user_ip_addresses(user_id='', ip_address=''):
|
||||
except Exception as 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 Tautulli
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -146,6 +159,7 @@ def get_user_email(user_id=''):
|
||||
sys.stderr.write("Tautulli API 'get_user' request failed: {0}.".format(e))
|
||||
return UserEmail()
|
||||
|
||||
|
||||
def send_notification(arguments=None, geodata=None, useremail=None):
|
||||
# Format notification text
|
||||
try:
|
||||
@ -163,12 +177,12 @@ def send_notification(arguments=None, geodata=None, useremail=None):
|
||||
mailserver.login(email_username, email_password)
|
||||
mailserver.sendmail(sender, u.email, message.as_string())
|
||||
mailserver.quit()
|
||||
print 'Email sent'
|
||||
print('Email sent')
|
||||
except Exception as e:
|
||||
sys.stderr.write("Email Failure: {0}.".format(e))
|
||||
|
||||
def clr_sql(ip):
|
||||
|
||||
def clr_sql(ip):
|
||||
try:
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'sql',
|
||||
@ -179,6 +193,7 @@ def clr_sql(ip):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_sql' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Parse arguments from Tautulli
|
||||
parser = argparse.ArgumentParser()
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
1. Install the requests module for python.
|
||||
pip install requests
|
||||
@ -14,12 +17,12 @@ Tautulli > Settings > Notifications > Script > Script Arguments:
|
||||
https://gist.github.com/blacktwin/261c416dbed08291e6d12f6987d9bafa
|
||||
"""
|
||||
|
||||
from twitter import *
|
||||
from twitter import Twitter, OAuth
|
||||
import argparse
|
||||
import requests
|
||||
import os
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TOKEN = ''
|
||||
TOKEN_SECRET = ''
|
||||
CONSUMER_KEY = ''
|
||||
@ -80,12 +83,13 @@ if __name__ == '__main__':
|
||||
|
||||
p = parser.parse_args()
|
||||
|
||||
|
||||
if p.media_type == 'movie':
|
||||
BODY_TEXT = MOVIE_TEXT.format(media_type=p.media_type, title=p.title, duration=p.duration)
|
||||
elif p.media_type == 'episode':
|
||||
BODY_TEXT = TV_TEXT.format(media_type=p.media_type, show_name=p.show_name, title=p.title,
|
||||
season_num00=p.season_num, episode_num00=p.episode_num, duration=p.duration)
|
||||
BODY_TEXT = TV_TEXT.format(
|
||||
media_type=p.media_type, show_name=p.show_name, title=p.title,
|
||||
season_num00=p.season_num, episode_num00=p.episode_num,
|
||||
duration=p.duration)
|
||||
else:
|
||||
exit()
|
||||
|
||||
@ -101,7 +105,6 @@ if __name__ == '__main__':
|
||||
for chunk in request:
|
||||
image.write(chunk)
|
||||
|
||||
|
||||
t_upload = Twitter(domain='upload.twitter.com',
|
||||
auth=OAuth(TOKEN, TOKEN_SECRET, CONSUMER_KEY, CONSUMER_SECRET))
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Find when media was added between STARTFRAME and ENDFRAME to Plex through Tautulli.
|
||||
|
||||
@ -9,7 +12,6 @@ import requests
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
STARTFRAME = 1480550400 # 2016, Dec 1 in seconds
|
||||
ENDFRAME = 1488326400 # 2017, March 1 in seconds
|
||||
|
||||
@ -20,8 +22,7 @@ LASTMONTH = int(TODAY - 2629743) # 2629743 = 1 month in seconds
|
||||
# STARTFRAME = LASTMONTH
|
||||
# ENDFRAME = TODAY
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
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.
|
||||
@ -70,6 +71,7 @@ def get_new_rating_keys(rating_key, media_type):
|
||||
except Exception as 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!
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -88,6 +90,7 @@ def get_library_media_info(section_id):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_metadata(rating_key):
|
||||
# Get the metadata for a media item.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -106,6 +109,7 @@ def get_metadata(rating_key):
|
||||
except Exception as 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.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -122,6 +126,7 @@ def update_library_media_info(section_id):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'update_library_media_info' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_libraries_table():
|
||||
# Get the data on the Tautulli libraries table.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -181,13 +186,14 @@ for i in sorted(show_lst, reverse=True):
|
||||
# Shows
|
||||
print(u"{x.grandparent_title}: {x.title} ({x.rating_key}) was added {when}.".format(x=x, when=added))
|
||||
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
# Remove commented print below to investigate problems.
|
||||
# print("Metadata failed. Likely end of range: {e}").format(e=e)
|
||||
# Remove break if not finding files in range
|
||||
break
|
||||
|
||||
print("There were {amount} files added between {start}:{end}".format(amount=len(count_lst),
|
||||
print("There were {amount} files added between {start}:{end}".format(
|
||||
amount=len(count_lst),
|
||||
start=time.ctime(float(STARTFRAME)),
|
||||
end=time.ctime(float(ENDFRAME))))
|
||||
print("Total movies: {}".format(count_lst.count('movie')))
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 1. Install the requests module for python.
|
||||
# pip install requests
|
||||
# 2. Add script arguments in Tautulli.
|
||||
@ -10,7 +13,7 @@ import sys
|
||||
user = sys.argv[1]
|
||||
title = sys.argv[2]
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'XXXXXXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
NOTIFIER_ID = 10 # The notification notifier ID for Tautulli
|
||||
@ -35,7 +38,7 @@ class UserHIS(object):
|
||||
|
||||
|
||||
def get_history():
|
||||
# Get the user IP list from Tautulli
|
||||
"""Get the history from Tautulli."""
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'get_history',
|
||||
'user': user,
|
||||
|
@ -1,19 +1,23 @@
|
||||
'''
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Run script by itself. Will look for WARN code followed by /library/metadata/ str in Plex logs.
|
||||
This is find files that are corrupt or having playback issues.
|
||||
I corrupted a file to test.
|
||||
'''
|
||||
"""
|
||||
|
||||
import requests
|
||||
import sys
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'XXXXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
lib_met = []
|
||||
err_title = []
|
||||
|
||||
|
||||
class PlexLOG(object):
|
||||
def __init__(self, data=None):
|
||||
self.error_msg = []
|
||||
@ -43,6 +47,7 @@ def get_plex_log():
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_plex_log' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_history(key):
|
||||
# Get the user IP list from Tautulli
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -59,6 +64,7 @@ def get_history(key):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
p_log = get_plex_log()
|
||||
for co, msg in p_log.error_msg:
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import psutil
|
||||
import requests
|
||||
|
||||
@ -16,7 +19,8 @@ disk_check = [True for i in disk if drive in i.mountpoint]
|
||||
|
||||
if not disk_check:
|
||||
# Send the notification through Tautulli
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
payload = {
|
||||
'apikey': TAUTULLI_APIKEY,
|
||||
'cmd': 'notify',
|
||||
'subject': NOTIFY_SUBJECT,
|
||||
'body': NOTIFY_BODY}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Use Tautulli to print plays by library from 0, 1, 7, or 30 days ago. 0 = total
|
||||
|
||||
@ -26,14 +29,15 @@ import requests
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
|
||||
TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
OUTPUT = 'Library: {section}\nDays: {days}\nPlays: {plays}'
|
||||
|
||||
## CODE BELOW ##
|
||||
# ## CODE BELOW ##
|
||||
|
||||
|
||||
def get_library_names():
|
||||
# Get a list of new rating keys for the PMS of all of the item's parent/children.
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Use Tautulli to pull plays by library
|
||||
|
||||
@ -18,18 +21,17 @@ Usage:
|
||||
import requests
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
# import json
|
||||
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
|
||||
TAUTULLI_APIKEY = 'xxxxxx' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
|
||||
OUTPUT = '{section} - Plays: {plays}'
|
||||
|
||||
## CODE BELOW ##
|
||||
# ## CODE BELOW ##
|
||||
|
||||
|
||||
def get_libraries_table(sections=None):
|
||||
# Get a list of new rating keys for the PMS of all of the item's parent/children.
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
usage: plex_netflix_check.py [-h] [-l [...]] [-s ] [-t ]
|
||||
|
||||
@ -29,15 +32,14 @@ import argparse
|
||||
from xmljson import badgerfish as bf
|
||||
from lxml.html import fromstring
|
||||
from time import sleep
|
||||
import json
|
||||
from plexapi.server import PlexServer
|
||||
# pip install plexapi
|
||||
|
||||
|
||||
## Edit ##
|
||||
# ## Edit ##
|
||||
PLEX_URL = 'http://localhost:32400'
|
||||
PLEX_TOKEN = 'xxxx'
|
||||
## /Edit ##
|
||||
# ## /Edit ##
|
||||
|
||||
sess = requests.Session()
|
||||
sess.verify = False
|
||||
@ -108,7 +110,7 @@ def instantwatch_search(name, media_type, site, search_limit):
|
||||
pass
|
||||
|
||||
for data in results['span']:
|
||||
if data['@class'] == 'title' and search_limit is not 0:
|
||||
if data['@class'] == 'title' and search_limit != 0:
|
||||
if str(data['a']['$']).lower().startswith(name.lower()):
|
||||
if amazon_id:
|
||||
if data['a']['@data-title-id'] == amazon_id:
|
||||
@ -119,10 +121,10 @@ def instantwatch_search(name, media_type, site, search_limit):
|
||||
print('Page: {}{}'.format(NETFLIX_URL, data['a']['@data-title-id']))
|
||||
results_count += 1
|
||||
search_limit -= 1
|
||||
if search_limit is 0:
|
||||
if search_limit == 0:
|
||||
limit = True
|
||||
|
||||
elif data['@class'] == 'title' and search_limit is 0 and limit is False:
|
||||
elif data['@class'] == 'title' and search_limit == 0 and limit is False:
|
||||
if data['a']['$'].lower().startswith(name.lower()):
|
||||
if amazon_id:
|
||||
if data['a']['@data-title-id'] == amazon_id:
|
||||
@ -223,5 +225,6 @@ def main():
|
||||
else:
|
||||
plex_library_search(opts.library[0], opts.site, opts.episodes, opts.search_limit)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Description: Comparing content between two or more Plex servers.
|
||||
Creates .json file in script directory of server compared.
|
||||
@ -106,7 +108,7 @@ def get_meta(meta):
|
||||
|
||||
meta_dict = {'title': meta.title,
|
||||
'rating': meta.rating if
|
||||
meta.rating != None else 0.0,
|
||||
meta.rating is not None else 0.0,
|
||||
'genres': [x.tag for x in meta.genres],
|
||||
'server': [meta._server.friendlyName],
|
||||
'thumb': [thumb_url]
|
||||
@ -193,7 +195,8 @@ def org_diff(lst_dicts, media_type, main_server):
|
||||
# Sort item list by Plex rating
|
||||
# Duplicates will use originals rating
|
||||
meta_lst = sorted(meta_lst, key=lambda d: d['rating'], reverse=True)
|
||||
diff_dict[mtype] = {'combined': {'count': len(meta_lst),
|
||||
diff_dict[mtype] = {'combined': {
|
||||
'count': len(meta_lst),
|
||||
'list': meta_lst}}
|
||||
|
||||
print('...finding {}s missing from {}'.format(
|
||||
@ -205,12 +208,14 @@ def org_diff(lst_dicts, media_type, main_server):
|
||||
# Main Server name is absent in items server list
|
||||
elif main_server in item['server'] and len(item['server']) == 1:
|
||||
unique.append(item)
|
||||
diff_dict[mtype].update({'missing': {'count': len(missing),
|
||||
diff_dict[mtype].update({'missing': {
|
||||
'count': len(missing),
|
||||
'list': missing}})
|
||||
|
||||
print('...finding {}s unique to {}'.format(
|
||||
mtype, main_server))
|
||||
diff_dict[mtype].update({'unique': {'count': len(unique),
|
||||
diff_dict[mtype].update({'unique': {
|
||||
'count': len(unique),
|
||||
'list': unique}})
|
||||
|
||||
return diff_dict
|
||||
@ -272,7 +277,8 @@ if __name__ == "__main__":
|
||||
|
||||
main_dict = org_diff(combined_lst, opts.media_type, main_server.friendlyName)
|
||||
|
||||
filename = 'diff_{}_{}_servers.json'.format(opts.server[0],'_'.join(servers))
|
||||
filename = 'diff_{}_{}_servers.json'.format(
|
||||
opts.server[0], '_'.join(servers))
|
||||
|
||||
with open(filename, 'w') as fp:
|
||||
json.dump(main_dict, fp, indent=4, sort_keys=True)
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Use Tautulli to count how many plays per user occurred this week.
|
||||
Notify via Tautulli Notification
|
||||
@ -10,7 +13,7 @@ import time
|
||||
TODAY = int(time.time())
|
||||
LASTWEEK = int(TODAY - 7 * 24 * 60 * 60)
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'XXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
SUBJECT_TEXT = "Tautulli Weekly Plays Per User"
|
||||
@ -29,6 +32,7 @@ class UserHIS(object):
|
||||
self.full_title = d['full_title']
|
||||
self.date = d['date']
|
||||
|
||||
|
||||
def get_history():
|
||||
# Get the Tautulli history. Count matters!!!
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -46,6 +50,7 @@ def get_history():
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_history' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def send_notification(BODY_TEXT):
|
||||
# Format notification text
|
||||
try:
|
||||
@ -73,12 +78,14 @@ def send_notification(BODY_TEXT):
|
||||
sys.stderr.write("Tautulli API 'notify' request failed: {0}.".format(e))
|
||||
return None
|
||||
|
||||
|
||||
def add_to_dictlist(d, key, val):
|
||||
if key not in d:
|
||||
d[key] = [val]
|
||||
else:
|
||||
d[key].append(val)
|
||||
|
||||
|
||||
user_dict = {}
|
||||
notify_lst = []
|
||||
|
||||
@ -106,7 +113,9 @@ BODY_TEXT = """\
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
""".format(notify_lst="\n".join(notify_lst).encode("utf-8"),end=time.ctime(float(TODAY)),
|
||||
""".format(
|
||||
notify_lst="\n".join(notify_lst).encode("utf-8"),
|
||||
end=time.ctime(float(TODAY)),
|
||||
start=time.ctime(float(LASTWEEK)))
|
||||
|
||||
send_notification(BODY_TEXT)
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
import argparse
|
||||
from plexapi.myplex import MyPlexAccount
|
||||
@ -36,6 +38,7 @@ VERIFY_SSL = False
|
||||
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
|
||||
|
||||
class Connection:
|
||||
def __init__(self, url=None, apikey=None, verify_ssl=False):
|
||||
self.url = url
|
||||
@ -66,7 +69,7 @@ class Library(object):
|
||||
try:
|
||||
self.parent_count = d['parent_count']
|
||||
self.child_count = d['child_count']
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
# print(e)
|
||||
pass
|
||||
|
||||
@ -254,7 +257,6 @@ if __name__ == '__main__':
|
||||
|
||||
opts = parser.parse_args()
|
||||
|
||||
|
||||
sections_totals_dict = {}
|
||||
sections_dict = {}
|
||||
user_dict = {}
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Pull library and user statistics of last week.
|
||||
|
||||
@ -13,7 +16,7 @@ import requests
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import json
|
||||
# import json
|
||||
from operator import itemgetter
|
||||
import argparse
|
||||
|
||||
@ -72,6 +75,7 @@ BODY_TEXT = """\
|
||||
|
||||
# /EDIT THESE SETTINGS #
|
||||
|
||||
|
||||
def get_history(section_id, check_date):
|
||||
# Get the Tautulli history.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -290,5 +294,6 @@ def main():
|
||||
print('Sending message.')
|
||||
send_notification(BODY_TEXT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
11
setup.cfg
Normal file
11
setup.cfg
Normal file
@ -0,0 +1,11 @@
|
||||
; Contains configuration for various linters
|
||||
|
||||
; E501: Disable line length limits (for now)
|
||||
; W504: Require newlines after binary operators, use W503 for requiring the
|
||||
; operators on the next line
|
||||
|
||||
[flake8]
|
||||
ignore = E501,W504
|
||||
|
||||
[pylama]
|
||||
ignore = E501,W504
|
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
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:
|
||||
@ -13,14 +15,14 @@ optional arguments:
|
||||
(default: None)
|
||||
|
||||
List of IP addresses is cleared before adding new IPs
|
||||
'''
|
||||
"""
|
||||
|
||||
import requests
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
PLEX_TOKEN = 'xxxx'
|
||||
PLEX_URL = 'http://localhost:32400'
|
||||
TAUTULLI_APIKEY = 'xxxx' # Your Tautulli API key
|
||||
|
@ -1,3 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
From a list of TV shows, check if users in a list has watched shows episodes.
|
||||
If all users in list have watched an episode of listed show, then delete episode.
|
||||
@ -9,8 +12,7 @@ import requests
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
TAUTULLI_APIKEY = 'xxxxx' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
SHOW_LST = [123456, 123456, 123456, 123456] # Show rating keys.
|
||||
@ -109,7 +111,8 @@ for user in USER_LST:
|
||||
|
||||
for meta_dict in meta_lst:
|
||||
if set(USER_LST) == set(meta_dict['watched_by']):
|
||||
print("{} {} has been watched by {}".format(meta_dict['grandparent_title'].encode('UTF-8'),
|
||||
print("{} {} has been watched by {}".format(
|
||||
meta_dict['grandparent_title'].encode('UTF-8'),
|
||||
meta_dict['title'].encode('UTF-8'),
|
||||
" & ".join(USER_LST)))
|
||||
print("Removing {}".format(meta_dict['file']))
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Description: Enable or disable all users remote access to Tautulli
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Enable or disable all users remote access to Tautulli.
|
||||
|
||||
Author: DirtyCajunRice
|
||||
Requires: requests, python3.6+
|
||||
"""
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Find location of Plex metadata.
|
||||
|
||||
@ -18,7 +20,7 @@ import hashlib
|
||||
import argparse
|
||||
import requests
|
||||
|
||||
## Edit ##
|
||||
# ## Edit ##
|
||||
PLEX_URL = ''
|
||||
PLEX_TOKEN = ''
|
||||
PLEX_URL = CONFIG.data['auth'].get('server_baseurl', PLEX_URL)
|
||||
@ -28,7 +30,7 @@ PLEX_TOKEN = CONFIG.data['auth'].get('server_token', PLEX_TOKEN)
|
||||
PLEX_LOCAL_TV_PATH = os.path.join(os.getenv('LOCALAPPDATA'), 'Plex Media Server\Metadata\TV Shows')
|
||||
PLEX_LOCAL_MOVIE_PATH = os.path.join(os.getenv('LOCALAPPDATA'), 'Plex Media Server\Metadata\Movies')
|
||||
PLEX_LOCAL_ALBUM_PATH = os.path.join(os.getenv('LOCALAPPDATA'), 'Plex Media Server\Metadata\Albums')
|
||||
## /Edit ##
|
||||
# ## /Edit ##
|
||||
|
||||
sess = requests.Session()
|
||||
# Ignore verifying the SSL certificate
|
||||
@ -44,6 +46,7 @@ if sess.verify is False:
|
||||
|
||||
plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
|
||||
|
||||
|
||||
def hash_to_path(hash_str, path, title, media_type, artist=None):
|
||||
full_hash = hashlib.sha1(hash_str).hexdigest()
|
||||
hash_path = '{}\{}{}'.format(full_hash[0], full_hash[1::1], '.bundle')
|
||||
@ -54,12 +57,11 @@ def hash_to_path(hash_str, path, title, media_type, artist=None):
|
||||
output = "{} titled: {}\nPath: {}".format(media_type.title(), title, full_path)
|
||||
print(output)
|
||||
|
||||
|
||||
def get_plex_hash(search, mediatype=None):
|
||||
for searched in plex.search(search, mediatype=mediatype):
|
||||
# Remove special characters from name
|
||||
clean_title = re.sub('\W+',' ', searched.title)
|
||||
if searched.type == 'show':
|
||||
# Need to find guid.
|
||||
if searched.type == 'show':
|
||||
# Get tvdb_if from first episode
|
||||
db_id = searched.episodes()[0].guid
|
||||
# Find str to pop
|
||||
@ -89,6 +91,7 @@ def get_plex_hash(search, mediatype=None):
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Helping navigate Plex's locally stored data.")
|
||||
parser.add_argument('-s', '--search', required=True, help='Search Plex for title.')
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
|
||||
Find what was added TFRAME ago and not watched using Tautulli.
|
||||
|
||||
"""
|
||||
|
||||
import requests
|
||||
@ -13,7 +14,7 @@ TFRAME = 1.577e+7 # ~ 6 months in seconds
|
||||
TODAY = time.time()
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
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.
|
||||
@ -81,7 +82,7 @@ def get_metadata(rating_key):
|
||||
res_data = response['response']['data']
|
||||
return METAINFO(data=res_data)
|
||||
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
# sys.stderr.write("Tautulli API 'get_metadata' request failed: {0}.".format(e))
|
||||
pass
|
||||
|
||||
@ -103,6 +104,7 @@ def get_library_media_info(section_id):
|
||||
except Exception as e:
|
||||
sys.stderr.write("Tautulli API 'get_library_media_info' request failed: {0}.".format(e))
|
||||
|
||||
|
||||
def get_libraries_table():
|
||||
# Get the data on the Tautulli libraries table.
|
||||
payload = {'apikey': TAUTULLI_APIKEY,
|
||||
@ -118,6 +120,7 @@ def get_libraries_table():
|
||||
except Exception as 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()
|
||||
if del_file.startswith('y'):
|
||||
@ -127,6 +130,7 @@ def delete_files(tmp_lst):
|
||||
else:
|
||||
print('Ok. doing nothing.')
|
||||
|
||||
|
||||
show_lst = []
|
||||
path_lst = []
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Description: Get a list of "Serial Transcoders"
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Get a list of "Serial Transcoders".
|
||||
|
||||
Author: DirtyCajunRice
|
||||
Requires: requests, plexapi, python3.6+
|
||||
"""
|
||||
|
@ -1,8 +1,9 @@
|
||||
# -*- encoding: UTF-8 -*-
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
"""
|
||||
https://gist.github.com/blacktwin/f435aa0ccd498b0840d2407d599bf31d
|
||||
'''
|
||||
"""
|
||||
|
||||
import os
|
||||
import httplib2
|
||||
@ -12,16 +13,12 @@ from oauth2client.file import Storage
|
||||
from googleapiclient.discovery import build
|
||||
from oauth2client.client import OAuth2WebServerFlow
|
||||
|
||||
import time, shutil, sys
|
||||
|
||||
# Copy your credentials from the console
|
||||
# https://console.developers.google.com
|
||||
CLIENT_ID = ''
|
||||
CLIENT_SECRET = ''
|
||||
OUT_PATH = '' # Output Path
|
||||
|
||||
|
||||
|
||||
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
|
||||
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
|
||||
CREDS_FILE = os.path.join(os.path.dirname(__file__), 'credentials.json')
|
||||
@ -48,6 +45,7 @@ http = credentials.authorize(http)
|
||||
|
||||
drive_service = build('drive', 'v2', http=http)
|
||||
|
||||
|
||||
def list_files(service):
|
||||
page_token = None
|
||||
while True:
|
||||
@ -90,11 +88,11 @@ for item in list_files(drive_service):
|
||||
if 'mimeType' in item and 'image/jpeg' in item['mimeType'] or 'video/mp4' in item['mimeType']:
|
||||
download_url = item['downloadUrl']
|
||||
else:
|
||||
print 'ERROR getting %s' % item.get('title')
|
||||
print item
|
||||
print dir(item)
|
||||
print('ERROR getting %s' % item.get('title'))
|
||||
print(item)
|
||||
print(dir(item))
|
||||
if download_url:
|
||||
print "downloading %s" % item.get('title')
|
||||
print("downloading %s" % item.get('title'))
|
||||
resp, content = drive_service._http.request(download_url)
|
||||
if resp.status == 200:
|
||||
if os.path.isfile(outfile):
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Description: Removes Shows from On Deck
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Removes Shows from On Deck.
|
||||
|
||||
Author: Blacktwin
|
||||
Requires: requests, plexapi
|
||||
|
||||
@ -142,7 +144,8 @@ def get_on_deck(server, off_deck=None):
|
||||
watched_statuses['grandparent'] = grandparent
|
||||
watched_statuses['episodes'] = []
|
||||
for episode in grandparent.episodes():
|
||||
watched_statuses['episodes'].append({'object': episode,
|
||||
watched_statuses['episodes'].append({
|
||||
'object': episode,
|
||||
'viewCount': episode.viewCount})
|
||||
else:
|
||||
if item.type == 'episode':
|
||||
|
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Invite new users to share Plex libraries.
|
||||
|
||||
optional arguments:
|
||||
@ -33,7 +35,7 @@ Usage:
|
||||
plex_api_invite.py --libraries Movies --user USER --movieRatings G, PG-13
|
||||
- Share Movie library with USER but restrict them to only G and PG-13 titles.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from plexapi.server import PlexServer, CONFIG
|
||||
import argparse
|
||||
@ -71,11 +73,11 @@ def invite(user, sections, allowSync, camera, channels, filterMovies, filterTele
|
||||
allowCameraUpload=camera, allowChannels=channels, filterMovies=filterMovies,
|
||||
filterTelevision=filterTelevision, filterMusic=filterMusic)
|
||||
print('Invited {user} to share libraries: \n{sections}'.format(sections=sections, user=user))
|
||||
if allowSync == True:
|
||||
if allowSync is True:
|
||||
print('Sync: Enabled')
|
||||
if camera == True:
|
||||
if camera is True:
|
||||
print('Camera Upload: Enabled')
|
||||
if channels == True:
|
||||
if channels is True:
|
||||
print('Plugins: Enabled')
|
||||
if filterMovies:
|
||||
print('Movie Filters: {}'.format(filterMovies))
|
||||
|
@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Set as cron or task for times of allowing and not allowing user access to server.
|
||||
Unsharing will kill any current stream from user before unsharing.
|
||||
|
||||
@ -33,7 +35,7 @@ Usage:
|
||||
- Unshared all libraries with USER.
|
||||
- USER is still exists as a Friend or Home User
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import argparse
|
||||
|
@ -1,6 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Description: Pull Movie and TV Show poster images from Plex. Save to Movie and TV Show directories in scripts working directory.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Pull Movie and TV Show poster images from Plex.
|
||||
|
||||
Saves the poster images to Movie and TV Show directories in scripts working
|
||||
directory.
|
||||
|
||||
Author: Blacktwin
|
||||
Requires: plexapi
|
||||
|
||||
@ -15,7 +20,7 @@ import re
|
||||
import os
|
||||
import urllib
|
||||
|
||||
library_name = ['Movies','TV Shows'] # You library names
|
||||
library_name = ['Movies', 'TV Shows'] # Your library names
|
||||
|
||||
PLEX_URL = ''
|
||||
PLEX_TOKEN = ''
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Share or unshare libraries.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Share or unshare libraries.
|
||||
|
||||
optional arguments:
|
||||
-h, --help Show this help message and exit
|
||||
@ -96,7 +97,7 @@ Usage:
|
||||
plex_api_share.py --share -u USER --allLibraries --libraries Movies
|
||||
- Shared [all libraries but Movies] with USER.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from plexapi.server import PlexServer, CONFIG
|
||||
import time
|
||||
@ -137,13 +138,14 @@ movies_keys = [x.key for x in plex.library.sections() if x.type == 'movie']
|
||||
show_keys = [x.key for x in plex.library.sections() if x.type == 'show']
|
||||
|
||||
json_check = sorted([f for f in os.listdir('.') if os.path.isfile(f) and
|
||||
f.endswith(".json") and f.startswith(plex.friendlyName)],
|
||||
f.endswith(".json") and
|
||||
f.startswith(plex.friendlyName)],
|
||||
key=os.path.getmtime)
|
||||
|
||||
my_server_names = []
|
||||
# Find all owners server names. For owners with multiple servers.
|
||||
for res in plex.myPlexAccount().resources():
|
||||
if res.provides == 'server' and res.owned == True:
|
||||
if res.provides == 'server' and res.owned is True:
|
||||
my_server_names.append(res.name)
|
||||
|
||||
|
||||
@ -166,7 +168,7 @@ def filter_clean(filter_type):
|
||||
labels = v.replace('%20', ' ')
|
||||
labels = labels.split('%2C')
|
||||
clean[k] = labels
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
return clean
|
||||
|
||||
@ -193,7 +195,7 @@ def find_shares(user):
|
||||
if server.name == plex.friendlyName:
|
||||
sections = []
|
||||
for section in server.sections():
|
||||
if section.shared == True:
|
||||
if section.shared is True:
|
||||
sections.append(section.title)
|
||||
user_backup['sections'] = sections
|
||||
|
||||
@ -219,17 +221,17 @@ def share(user, sections, allowSync, camera, channels, filterMovies, filterTelev
|
||||
filterTelevision=filterTelevision, filterMusic=filterMusic)
|
||||
if sections:
|
||||
print('{user}\'s updated shared libraries: \n{sections}'.format(sections=sections, user=user))
|
||||
if allowSync == True:
|
||||
if allowSync is True:
|
||||
print('Sync: Enabled')
|
||||
if allowSync == False:
|
||||
if allowSync is False:
|
||||
print('Sync: Disabled')
|
||||
if camera == True:
|
||||
if camera is True:
|
||||
print('Camera Upload: Enabled')
|
||||
if camera == False:
|
||||
if camera is False:
|
||||
print('Camera Upload: Disabled')
|
||||
if channels == True:
|
||||
if channels is True:
|
||||
print('Plugins: Enabled')
|
||||
if channels == False:
|
||||
if channels is False:
|
||||
print('Plugins: Disabled')
|
||||
if filterMovies:
|
||||
print('Movie Filters: {}'.format(filterMovies))
|
||||
@ -241,7 +243,7 @@ def share(user, sections, allowSync, camera, channels, filterMovies, filterTelev
|
||||
print('Show Filters:')
|
||||
if filterMusic:
|
||||
print('Music Filters: {}'.format(filterMusic))
|
||||
if filterMusic == {} and filterMusic != None:
|
||||
if filterMusic == {} and filterMusic is not None:
|
||||
print('Music Filters:')
|
||||
|
||||
|
||||
@ -286,7 +288,7 @@ if __name__ == "__main__":
|
||||
help='Show all shares by library.')
|
||||
|
||||
# For Plex Pass members
|
||||
if plex.myPlexSubscription == True:
|
||||
if plex.myPlexSubscription is True:
|
||||
movie_ratings = []
|
||||
show_ratings = []
|
||||
for movie in movies_keys:
|
||||
@ -400,7 +402,6 @@ if __name__ == "__main__":
|
||||
for section, users in section_users.items():
|
||||
print("{} is shared to the following users:\n {}\n".format(section, ", ".join(users)))
|
||||
|
||||
|
||||
# Share, Unshare, Kill, Add, or Remove
|
||||
for user in users:
|
||||
user_shares = find_shares(user)
|
||||
|
@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Change show deletion settings by library.
|
||||
|
||||
|
@ -1,18 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Pull poster images from Imgur and places them inside Shows root folder.
|
||||
/path/to/show/Show.jpg
|
||||
|
||||
Skips download if showname.jpg exists or if show does not exist.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
import os
|
||||
|
||||
|
||||
## Edit ##
|
||||
# ## Edit ##
|
||||
|
||||
# Imgur info
|
||||
CLIENT_ID = 'xxxxx' # Tautulli Settings > Notifications > Imgur Client ID
|
||||
@ -21,7 +23,8 @@ ALBUM_ID = '7JeSw' # http://imgur.com/a/7JeSw <--- 7JeSw is the ablum_id
|
||||
# Local info
|
||||
SHOW_PATH = 'D:\\Shows\\'
|
||||
|
||||
## /Edit ##
|
||||
# ## /Edit ##
|
||||
|
||||
|
||||
class IMGURINFO(object):
|
||||
def __init__(self, data=None):
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Build playlist from popular tracks.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Build playlist from popular tracks.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -14,7 +15,7 @@ optional arguments:
|
||||
|
||||
* LIBRARY_EXCLUDE are excluded from libraries choice.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import requests
|
||||
|
@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Download theme songs from Plex TV Shows. Theme songs are mp3 and named by shows as displayed by Plex.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Download theme songs from Plex TV Shows.
|
||||
|
||||
Theme songs are mp3 and named by shows as displayed by Plex.
|
||||
Songs are saved in a 'Theme Songs' directory located in script's path.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from plexapi.server import PlexServer, CONFIG
|
||||
@ -13,14 +16,14 @@ import re
|
||||
import urllib
|
||||
import requests
|
||||
|
||||
## Edit ##
|
||||
# ## Edit ##
|
||||
PLEX_URL = ''
|
||||
PLEX_TOKEN = ''
|
||||
PLEX_URL = CONFIG.data['auth'].get('server_baseurl', PLEX_URL)
|
||||
PLEX_TOKEN = CONFIG.data['auth'].get('server_token', PLEX_TOKEN)
|
||||
|
||||
TV_LIBRARY = 'TV Shows' # Name of your TV Show library
|
||||
## /Edit ##
|
||||
# ## /Edit ##
|
||||
|
||||
sess = requests.Session()
|
||||
# Ignore verifying the SSL certificate
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""
|
||||
Delete all playlists from Plex using PlexAPI
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Delete all playlists from Plex.
|
||||
|
||||
|
||||
"""
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Description: Purge Tautulli users that no longer exist as a friend in Plex
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Purge Tautulli users that no longer exist as a friend in Plex.
|
||||
|
||||
Author: DirtyCajunRice
|
||||
Requires: requests, plexapi, python3.6+
|
||||
"""
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Refresh the next episode of show once current episode is watched.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Refresh the next episode of show once current episode is watched.
|
||||
|
||||
Check Tautulli's Watched Percent in Tautulli > Settings > General
|
||||
|
||||
1. Tautulli > Settings > Notification Agents > Scripts > Bell icon:
|
||||
@ -12,7 +14,7 @@ Check Tautulli's Watched Percent in Tautulli > Settings > General
|
||||
3. Tautulli > Settings > Notifications > Script > Script Arguments:
|
||||
{show_name} {episode_num00} {season_num00}
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import requests
|
||||
import sys
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Unshare or Remove users who have been inactive for X days. Prints out last seen for all users.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Unshare or Remove users who have been inactive for X days. Prints out last seen for all users.
|
||||
|
||||
Just run.
|
||||
|
||||
@ -114,6 +115,3 @@ for user in TAUTULLI_USERS:
|
||||
else:
|
||||
print('{}, and has reached their inactivity limit. Unsharing.'.format(OUTPUT))
|
||||
ACCOUNT.updateFriend(PLEX_USERS[UID], SERVER, removeSections=True)
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""
|
||||
Find Movies that have been watched by a list of users.
|
||||
If all users have watched movie than delete.
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Find and delete Movies that have been watched by a list of users.
|
||||
|
||||
Deletion is prompted
|
||||
"""
|
||||
@ -11,12 +12,13 @@ import os
|
||||
import shutil
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ##
|
||||
# ## EDIT THESE SETTINGS ##
|
||||
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
|
||||
|
||||
|
||||
class UserHIS(object):
|
||||
def __init__(self, data=None):
|
||||
d = data or {}
|
||||
@ -81,6 +83,7 @@ def delete_files(tmp_lst):
|
||||
else:
|
||||
print('Ok. doing nothing.')
|
||||
|
||||
|
||||
movie_dict = {}
|
||||
movie_lst = []
|
||||
delete_lst = []
|
||||
|
@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Share functions from https://gist.github.com/JonnyWong16/f8139216e2748cb367558070c1448636
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Share functions from https://gist.github.com/JonnyWong16/f8139216e2748cb367558070c1448636
|
||||
|
||||
Once user stream count hits LIMIT they are unshared from libraries expect for banned library.
|
||||
Once user stream count is below LIMIT and banned library video is watched their shares are restored.
|
||||
@ -30,7 +31,6 @@ Tautulli will continue displaying that user is watching after unshare is execute
|
||||
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.
|
||||
Name library and video whatever you want.
|
||||
|
||||
@ -59,7 +59,7 @@ import email.utils
|
||||
import smtplib
|
||||
|
||||
|
||||
## EDIT THESE SETTINGS ###
|
||||
# ## EDIT THESE SETTINGS ###
|
||||
|
||||
TAUTULLI_APIKEY = 'XXXXXX' # Your Tautulli API key
|
||||
TAUTULLI_URL = 'http://localhost:8181/' # Your Tautulli URL
|
||||
@ -107,7 +107,7 @@ email_username = '' # Your email username
|
||||
email_password = '' # Your email password
|
||||
|
||||
|
||||
## DO NOT EDIT BELOW ##
|
||||
# ## DO NOT EDIT BELOW ##
|
||||
|
||||
class Activity(object):
|
||||
def __init__(self, data=None):
|
||||
@ -216,7 +216,7 @@ def unshare(user_id):
|
||||
return
|
||||
|
||||
elif r.status_code == 400:
|
||||
print r.content
|
||||
print(r.content)
|
||||
return
|
||||
|
||||
elif r.status_code == 200:
|
||||
@ -256,6 +256,7 @@ def get_activity():
|
||||
except Exception as 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
|
||||
try:
|
||||
@ -297,7 +298,7 @@ if __name__ == "__main__":
|
||||
# Trigger for first and next violation
|
||||
unshare(user) # Remove libraries
|
||||
share(user, BAN) # Share banned library
|
||||
sys.stdout.write("Shared BAN_LIBRARY with user {0}".format(i))
|
||||
sys.stdout.write("Shared BAN_LIBRARY with user {0}".format(user))
|
||||
if type(history) is int:
|
||||
# Next violation, history of banned video.
|
||||
send_notification(mail_add, friendly, history, VIOLATION_LIMIT, FIRST_WARN)
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Description: Sync the watch status from one Plex or Tautulli user to other users across any owned server.
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Sync the watch status from one Plex or Tautulli user to other users across any owned server.
|
||||
|
||||
Author: Blacktwin
|
||||
Requires: requests, plexapi, argparse
|
||||
|
||||
@ -49,7 +51,7 @@ Taultulli > Settings > Notification Agents > New Script > Script Arguments:
|
||||
- Synced watch statuses from Tautulli {title from library} to {USER2 or USER3}'s account on selected servers.
|
||||
|
||||
sync_watch_status.py --userFrom USER1=Tautulli --userTo USER2=Server1 USER3=Server2 --ratingKey 1234
|
||||
- Synced watch status of rating key 1234 from USER1's Tautulli history to {USER2 or USER3}'s account
|
||||
- Synced watch statuse of rating key 1234 from USER1's Tautulli history to {USER2 or USER3}'s account
|
||||
on selected servers.
|
||||
**Rating key must be a movie or episode. Shows and Seasons not support.... yet.
|
||||
"""
|
||||
@ -158,7 +160,7 @@ class Tautulli:
|
||||
return
|
||||
|
||||
def get_watched_history(self, user=None, section_id=None, rating_key=None, start=None, length=None):
|
||||
"""Call Tautulli's get_history api endpoint"""
|
||||
"""Call Tautulli's get_history api endpoint."""
|
||||
payload = {"order_column": "full_title",
|
||||
"order_dir": "asc"}
|
||||
if user:
|
||||
@ -177,14 +179,12 @@ class Tautulli:
|
||||
return [d for d in history['data'] if d['watched_status'] == 1]
|
||||
|
||||
def get_metadata(self, rating_key):
|
||||
"""Call Tautulli's get_metadata api endpoint"""
|
||||
|
||||
"""Call Tautulli's get_metadata api endpoint."""
|
||||
payload = {"rating_key": rating_key}
|
||||
return self._call_api('get_metadata', payload)
|
||||
|
||||
def get_libraries(self):
|
||||
"""Call Tautulli's get_libraries api endpoint"""
|
||||
|
||||
"""Call Tautulli's get_libraries api endpoint."""
|
||||
payload = {}
|
||||
return self._call_api('get_libraries', payload)
|
||||
|
||||
@ -198,23 +198,27 @@ class Plex:
|
||||
self.server = PlexServer(baseurl=url, token=token, session=session)
|
||||
|
||||
def admin_servers(self):
|
||||
"""All owned servers
|
||||
"""Get all owned servers.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: dict
|
||||
|
||||
"""
|
||||
resources = {}
|
||||
for resource in self.account.resources():
|
||||
if 'server' in [resource.provides] and resource.owned == True:
|
||||
if 'server' in [resource.provides] and resource.owned is True:
|
||||
resources[resource.name] = resource
|
||||
|
||||
return resources
|
||||
|
||||
def all_users(self):
|
||||
"""All users
|
||||
"""Get all users.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: dict
|
||||
|
||||
"""
|
||||
users = {self.account.title: self.account}
|
||||
for user in self.account.users():
|
||||
@ -223,10 +227,12 @@ class Plex:
|
||||
return users
|
||||
|
||||
def all_sections(self):
|
||||
"""All sections from all owned servers
|
||||
"""Get all sections from all owned servers.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: dict
|
||||
|
||||
"""
|
||||
data = {}
|
||||
servers = self.admin_servers()
|
||||
@ -239,10 +245,12 @@ class Plex:
|
||||
return data
|
||||
|
||||
def users_access(self):
|
||||
"""Users access across all owned servers
|
||||
"""Get users access across all owned servers.
|
||||
|
||||
Returns
|
||||
-------
|
||||
data: dict
|
||||
|
||||
"""
|
||||
all_users = self.all_users().values()
|
||||
admin_servers = self.admin_servers()
|
||||
@ -257,7 +265,7 @@ class Plex:
|
||||
if admin_servers.get(server.name):
|
||||
access = {}
|
||||
sections = {section.title: section for section in server.sections()
|
||||
if section.shared == True}
|
||||
if section.shared is True}
|
||||
access['server'] = {server.name: admin_servers.get(server.name)}
|
||||
access['sections'] = sections
|
||||
servers += [access]
|
||||
@ -278,7 +286,8 @@ class Plex:
|
||||
|
||||
|
||||
def connect_to_server(server_obj, user_account):
|
||||
"""Find server url and connect using user token
|
||||
"""Find server url and connect using user token.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
server_obj: class
|
||||
@ -287,6 +296,7 @@ def connect_to_server(server_obj, user_account):
|
||||
Returns
|
||||
-------
|
||||
user_connection.server: class
|
||||
|
||||
"""
|
||||
server_name = server_obj.name
|
||||
user = user_account.title
|
||||
@ -308,6 +318,7 @@ def connect_to_server(server_obj, user_account):
|
||||
|
||||
def check_users_access(access, user, server_name, libraries=None):
|
||||
"""Check user's access to server. If allowed connect.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
access: dict
|
||||
@ -318,6 +329,7 @@ def check_users_access(access, user, server_name, libraries=None):
|
||||
Returns
|
||||
-------
|
||||
server_connection: class
|
||||
|
||||
"""
|
||||
try:
|
||||
_user = access.get(user)
|
||||
@ -349,7 +361,8 @@ def check_users_access(access, user, server_name, libraries=None):
|
||||
|
||||
|
||||
def sync_watch_status(watched, section, accountTo, userTo, same_server=False):
|
||||
"""
|
||||
"""Sync watched status between two users.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
watched: list
|
||||
@ -362,10 +375,12 @@ def sync_watch_status(watched, section, accountTo, userTo, same_server=False):
|
||||
User's server class of sync to user
|
||||
same_server: bool
|
||||
Are serverFrom and serverTo the same
|
||||
|
||||
"""
|
||||
print('Marking watched...')
|
||||
sectionTo = accountTo.library.section(section)
|
||||
for item in watched:
|
||||
print(item)
|
||||
try:
|
||||
if same_server:
|
||||
fetch_check = sectionTo.fetchItem(item.ratingKey)
|
||||
@ -484,9 +499,13 @@ if __name__ == '__main__':
|
||||
# Check library for watched items
|
||||
sectionFrom = watchedFrom.library.section(_library.title)
|
||||
if _library.type == 'show':
|
||||
watched_lst = sectionFrom.search(libtype='episode', unwatched=False)
|
||||
for show in sectionFrom.all():
|
||||
for episode in show.episodes():
|
||||
if episode.isWatched:
|
||||
watched_lst.append(episode)
|
||||
else:
|
||||
watched_lst = sectionFrom.search(unwatched=False)
|
||||
for item in sectionFrom.search(unwatched=False):
|
||||
watched_lst.append(item)
|
||||
|
||||
for user in plexTo:
|
||||
username, server = user
|
||||
@ -519,27 +538,5 @@ if __name__ == '__main__':
|
||||
username, server = user
|
||||
sync_watch_status([watched_item], watched_item.libraryName, server, username)
|
||||
|
||||
elif opts.ratingKey and serverFrom != "Tautulli":
|
||||
plexTo = []
|
||||
watched_item = []
|
||||
|
||||
if userFrom != "Tautulli":
|
||||
print("Request manually triggered to update watch status")
|
||||
watchedFrom = check_users_access(plex_access, userFrom, serverFrom)
|
||||
watched_item = watchedFrom.fetchItem(opts.ratingKey)
|
||||
if watched_item.type in ["episode", "movie"]:
|
||||
if not watched_item.isWatched:
|
||||
print("Rating Key {} was not reported as watched in Plex for user {}".format(opts.ratingKey,
|
||||
userFrom))
|
||||
exit()
|
||||
|
||||
for user, server_name in opts.userTo:
|
||||
# Check access and connect
|
||||
plexTo.append([user, check_users_access(plex_access, user, server_name, libraries)])
|
||||
|
||||
for user in plexTo:
|
||||
username, server = user
|
||||
sync_watch_status([watched_item], watched_item.libraryName, server, username)
|
||||
|
||||
else:
|
||||
print("You aren't using this script correctly... bye!")
|
@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Description: Sync Tautulli friendly names with Ombi aliases (Tautulli as master)
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Sync Tautulli friendly names with Ombi aliases (Tautulli as master).
|
||||
|
||||
Author: DirtyCajunRice
|
||||
Requires: requests, python3.6+
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user