JBOPS/utility/plex_api_share.py

457 lines
19 KiB
Python
Raw Normal View History

2018-07-25 16:33:10 +00:00
#!/usr/bin/env python
2017-09-09 02:50:56 +00:00
'''
Share or unshare libraries.
optional arguments:
-h, --help Show this help message and exit
--share To share libraries to user.
2018-08-04 07:05:29 +00:00
--shared Display user's share settings.
--unshare To unshare all libraries from user.
--add Share additional libraries or enable settings to user.
--remove Remove shared libraries or disable settings from user.
2018-02-05 06:20:51 +00:00
--user [ ...] Space separated list of case sensitive names to process. Allowed names are:
2017-09-09 02:50:56 +00:00
(choices: All users names)
2018-03-14 17:21:33 +00:00
--allUsers Select all users.
2018-02-05 06:20:51 +00:00
--libraries [ ...]
2017-09-09 02:50:56 +00:00
Space separated list of case sensitive names to process. Allowed names are:
(choices: All library names)
2018-02-05 06:20:51 +00:00
(default: All Libraries)
2018-03-14 17:21:33 +00:00
--allLibraries Select all libraries.
2018-08-08 15:59:35 +00:00
--backup Backup share settings from json file
--restore Restore share settings from json file
Filename of json file to use.
(choices: %(json files found in cwd)s)
# Plex Pass member only settings:
--kill Kill user's current stream(s). Include message to override default message
2018-02-05 06:20:51 +00:00
--sync Allow user to sync content
--camera Allow user to upload photos
--channel Allow user to utilize installed channels
--movieRatings Add rating restrictions to movie library types
--movieLabels Add label restrictions to movie library types
--tvRatings Add rating restrictions to show library types
--tvLabels Add label restrictions to show library types
--musicLabels Add label restrictions to music library types
2017-09-09 02:50:56 +00:00
Usage:
plex_api_share.py --user USER --shared
- Current shares for USER: ['Movies', 'Music']
2018-02-05 06:20:51 +00:00
plex_api_share.py --share --user USER --libraries Movies
- Shared libraries: ['Movies'] with USER
plex_api_share.py --share --allUsers --libraries Movies
2017-09-09 02:50:56 +00:00
- Shared libraries: ['Movies'] with USER
2018-02-05 06:20:51 +00:00
- Shared libraries: ['Movies'] with USER1 ...
2017-09-09 02:50:56 +00:00
2018-02-05 06:20:51 +00:00
plex_api_share.py --share --user USER --libraries Movies "TV Shows"
2017-09-09 02:50:56 +00:00
- Shared libraries: ['Movies', 'TV Shows'] with USER
* Double Quote libraries with spaces
plex_api_share.py --share --user USER --allLibraries
- Shared all libraries with USER.
plex_api_share.py --user USER --add --libraries Movies
- Adds Movies library share to USER
2018-03-14 17:21:33 +00:00
plex_api_share.py --allUsers --remove --libraries Movies
- Removes Movies library share from all Users
plex_api_share.py --unshare --user USER
- Unshared all libraries with USER.
- USER is still exists as a Friend or Home User
2017-09-09 02:50:56 +00:00
2018-08-05 23:06:10 +00:00
plex_api_share.py --backup
- Backup all user shares to a json file
2018-11-09 06:33:47 +00:00
plex_api_share.py --backup --user USER
- Backup USER shares to a json file
2018-08-05 23:06:10 +00:00
plex_api_share.py --restore
- Only restore all Plex user's shares and settings from backup json file
plex_api_share.py --restore --user USER
- Only restore USER's Plex shares and settings from backup json file
plex_api_share.py --user USER --add --sync
- Enable sync feature for USER
plex_api_share.py --user USER --remove --sync
- Disable sync feature for USER
2018-08-05 23:06:10 +00:00
2018-02-05 06:20:51 +00:00
Excluding;
2018-02-05 06:20:51 +00:00
--user becomes excluded if --allUsers is set
plex_api_share.py --share --allUsers --user USER --libraries Movies
2018-02-05 06:20:51 +00:00
- Shared libraries: ['Movies' ]with USER1.
- Shared libraries: ['Movies'] with USER2 ... all users but USER
--libraries becomes excluded if --allLibraries is set
plex_api_share.py --share -u USER --allLibraries --libraries Movies
- Shared [all libraries but Movies] with USER.
2017-09-09 02:50:56 +00:00
'''
2018-07-25 16:33:10 +00:00
from plexapi.server import PlexServer, CONFIG
2018-08-05 22:13:15 +00:00
import time
2017-09-09 02:50:56 +00:00
import argparse
2018-02-05 06:20:51 +00:00
import requests
2018-08-04 07:05:29 +00:00
import os
2018-03-14 17:21:33 +00:00
import json
2017-09-09 02:50:56 +00:00
2018-07-25 16:33:10 +00:00
PLEX_URL = ''
PLEX_TOKEN = ''
if not PLEX_URL:
PLEX_URL = CONFIG.data['auth'].get('server_baseurl', '')
if not PLEX_TOKEN:
PLEX_TOKEN = CONFIG.data['auth'].get('server_token', '')
2018-08-08 15:59:35 +00:00
DEFAULT_MESSAGE = "Stream is being killed by admin."
2018-02-05 06:20:51 +00:00
sess = requests.Session()
2018-07-25 16:33:10 +00:00
# Ignore verifying the SSL certificate
sess.verify = False # '/path/to/certfile'
# If verify is set to a path to a directory,
# the directory must have been processed using the c_rehash utility supplied
# with OpenSSL.
if sess.verify is False:
# Disable the warning that the request is insecure, we know that...
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
2018-02-05 06:20:51 +00:00
plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
user_lst = [x.title for x in plex.myPlexAccount().users() if x.title]
2017-09-09 02:50:56 +00:00
sections_lst = [x.title for x in plex.library.sections()]
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)],
key=os.path.getmtime)
2018-08-05 22:13:15 +00:00
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:
my_server_names.append(res.name)
2017-09-09 02:50:56 +00:00
def get_ratings_lst(section_id):
headers = {'Accept': 'application/json'}
params = {'X-Plex-Token': PLEX_TOKEN}
2018-08-12 02:21:16 +00:00
content = sess.get("{}/library/sections/{}/contentRating".format(PLEX_URL, section_id),
headers=headers, params=params)
ratings_keys = content.json()['MediaContainer']['Directory']
ratings_lst = [x['title'] for x in ratings_keys]
return ratings_lst
2018-08-04 07:05:29 +00:00
def filter_clean(filter_type):
clean = ''
try:
clean = dict(item.split("=") for item in filter_type.split("|"))
for k, v in clean.items():
labels = v.replace('%20', ' ')
labels = labels.split('%2C')
clean[k] = labels
except Exception as e:
pass
return clean
def find_shares(user):
account = plex.myPlexAccount()
user_acct = account.user(user)
2018-08-05 22:13:15 +00:00
user_backup = {
2018-08-05 22:28:05 +00:00
'title': user_acct.title,
'username': user_acct.username,
'email': user_acct.email,
'userID': user_acct.id,
2018-08-05 22:13:15 +00:00
'allowSync': user_acct.allowSync,
'camera': user_acct.allowCameraUpload,
'channels': user_acct.allowChannels,
'filterMovies': filter_clean(user_acct.filterMovies),
'filterTelevision': filter_clean(user_acct.filterTelevision),
'filterMusic': filter_clean(user_acct.filterMusic),
'serverName': plex.friendlyName,
'sections': ""}
2018-08-05 22:13:15 +00:00
for server in user_acct.servers:
if server.name == plex.friendlyName:
2018-08-05 22:13:15 +00:00
sections = []
for section in server.sections():
if section.shared == True:
sections.append(section.title)
user_backup['sections'] = sections
2018-08-05 22:13:15 +00:00
return user_backup
def kill_session(user, message):
reason = DEFAULT_MESSAGE
for session in plex.sessions():
# Check for users stream
if session.usernames[0] in user:
title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title
print('{user} was watching {title}. Killing stream and unsharing.'.format(
user=user, title=title))
if message:
reason = message
session.stop(reason=reason)
2018-02-05 06:20:51 +00:00
def share(user, sections, allowSync, camera, channels, filterMovies, filterTelevision, filterMusic):
plex.myPlexAccount().updateFriend(user=user, server=plex, sections=sections, allowSync=allowSync,
allowCameraUpload=camera, allowChannels=channels, filterMovies=filterMovies,
filterTelevision=filterTelevision, filterMusic=filterMusic)
if sections:
print('{user}\'s updated shared libraries: \n{sections}'.format(sections=sections, user=user))
if allowSync == True:
print('Sync: Enabled')
if allowSync == False:
print('Sync: Disabled')
if camera == True:
print('Camera Upload: Enabled')
if camera == False:
print('Camera Upload: Disabled')
if channels == True:
print('Plugins: Enabled')
if channels == False:
print('Plugins: Disabled')
if filterMovies:
print('Movie Filters: {}'.format(filterMovies))
if filterMovies == {}:
print('Movie Filters:')
if filterTelevision:
print('Show Filters: {}'.format(filterTelevision))
if filterTelevision == {}:
print('Show Filters:')
if filterMusic:
print('Music Filters: {}'.format(filterMusic))
if filterMusic == {} and filterMusic != None:
print('Music Filters:')
2017-09-09 02:50:56 +00:00
def unshare(user, sections):
plex.myPlexAccount().updateFriend(user=user, server=plex, removeSections=True, sections=sections)
2018-11-09 04:13:41 +00:00
print('Unshared all libraries from {user}.'.format(user=user))
2017-09-09 02:50:56 +00:00
2017-09-09 02:50:56 +00:00
if __name__ == "__main__":
2018-08-05 22:13:15 +00:00
timestr = time.strftime("%Y%m%d-%H%M%S")
2018-08-04 07:05:29 +00:00
2017-09-09 02:50:56 +00:00
parser = argparse.ArgumentParser(description="Share or unshare libraries.",
formatter_class=argparse.RawTextHelpFormatter)
2018-02-05 13:47:31 +00:00
parser.add_argument('--share', default=False, action='store_true',
2018-02-05 06:20:51 +00:00
help='To share libraries.')
parser.add_argument('--shared', default=False, action='store_true',
help='Display user\'s shared libraries.')
2018-02-05 13:47:31 +00:00
parser.add_argument('--unshare', default=False, action='store_true',
2018-02-05 06:20:51 +00:00
help='To unshare all libraries.')
parser.add_argument('--add', default=False, action='store_true',
help='Share additional libraries or enable settings to user..')
parser.add_argument('--remove', default=False, action='store_true',
help='Remove shared libraries or disable settings from user.')
2018-02-05 06:20:51 +00:00
parser.add_argument('--user', nargs='+', choices=user_lst, metavar='',
2017-09-09 02:50:56 +00:00
help='Space separated list of case sensitive names to process. Allowed names are: \n'
'(choices: %(choices)s)')
2018-02-05 13:47:31 +00:00
parser.add_argument('--allUsers', default=False, action='store_true',
2018-02-05 06:20:51 +00:00
help='Select all users.')
parser.add_argument('--libraries', nargs='+', default=False, choices=sections_lst, metavar='',
2017-09-09 02:50:56 +00:00
help='Space separated list of case sensitive names to process. Allowed names are: \n'
'(choices: %(choices)s')
2018-02-05 13:47:31 +00:00
parser.add_argument('--allLibraries', default=False, action='store_true',
2018-03-14 17:21:33 +00:00
help='Select all libraries.')
2018-08-04 07:05:29 +00:00
parser.add_argument('--backup', default=False, action='store_true',
help='Backup share settings from json file.')
parser.add_argument('--restore', type=str, choices=json_check, metavar='',
2018-08-04 07:05:29 +00:00
help='Restore share settings from json file.\n'
'Filename of json file to use.\n'
'(choices: %(choices)s)')
2018-08-08 15:50:14 +00:00
# For Plex Pass members
if plex.myPlexSubscription == True:
movie_ratings = []
show_ratings = []
for movie in movies_keys:
movie_ratings += get_ratings_lst(movie)
for show in show_keys:
show_ratings += get_ratings_lst(show)
parser.add_argument('--kill', default=None, nargs='?',
2018-08-08 15:50:14 +00:00
help='Kill user\'s current stream(s). Include message to override default message.')
parser.add_argument('--sync', default=None, action='store_true',
2018-08-08 15:50:14 +00:00
help='Use to allow user to sync content.')
parser.add_argument('--camera', default=None, action='store_true',
2018-08-08 15:50:14 +00:00
help='Use to allow user to upload photos.')
parser.add_argument('--channels', default=None, action='store_true',
2018-08-08 15:50:14 +00:00
help='Use to allow user to utilize installed channels.')
parser.add_argument('--movieRatings', nargs='+', choices=list(set(movie_ratings)), metavar='',
help='Use to add rating restrictions to movie library types.\n'
'Space separated list of case sensitive names to process. Allowed names are: \n'
'(choices: %(choices)s')
parser.add_argument('--movieLabels', nargs='+', metavar='',
help='Use to add label restrictions for movie library types.')
parser.add_argument('--tvRatings', nargs='+', choices=list(set(show_ratings)), metavar='',
help='Use to add rating restrictions for show library types.\n'
'Space separated list of case sensitive names to process. Allowed names are: \n'
'(choices: %(choices)s')
parser.add_argument('--tvLabels', nargs='+', metavar='',
help='Use to add label restrictions for show library types.')
parser.add_argument('--musicLabels', nargs='+', metavar='',
help='Use to add label restrictions for music library types.')
2017-09-09 02:50:56 +00:00
opts = parser.parse_args()
2018-02-05 06:20:51 +00:00
users = ''
libraries = ''
2018-08-08 15:50:14 +00:00
# Plex Pass additional share options
kill = None
sync = None
camera = None
channels = None
filterMovies = None
filterTelevision = None
filterMusic = None
2018-08-08 15:50:14 +00:00
try:
if opts.kill:
kill = opts.kill
if opts.sync:
sync = opts.sync
if opts.camera:
camera = opts.camera
if opts.channels:
channels = opts.channels
if opts.movieLabels or opts.movieRatings:
filterMovies = {}
2018-08-08 15:50:14 +00:00
if opts.movieLabels:
filterMovies['label'] = opts.movieLabels
if opts.movieRatings:
filterMovies['contentRating'] = opts.movieRatings
if opts.tvLabels or opts.tvRatings:
filterTelevision = {}
2018-08-08 15:50:14 +00:00
if opts.tvLabels:
filterTelevision['label'] = opts.tvLabels
if opts.tvRatings:
filterTelevision['contentRating'] = opts.tvRatings
if opts.musicLabels:
filterMusic = {}
2018-08-08 15:50:14 +00:00
filterMusic['label'] = opts.musicLabels
except AttributeError:
print('No Plex Pass moving on...')
2018-02-05 06:20:51 +00:00
# Defining users
if opts.allUsers and not opts.user:
users = user_lst
elif not opts.allUsers and opts.user:
users = opts.user
elif opts.allUsers and opts.user:
# If allUsers is used then any users listed will be excluded
for user in opts.user:
user_lst.remove(user)
users = user_lst
# Defining libraries
if opts.allLibraries and not opts.libraries:
libraries = sections_lst
elif not opts.allLibraries and opts.libraries:
libraries = opts.libraries
elif opts.allLibraries and opts.libraries:
# If allLibraries is used then any libraries listed will be excluded
for library in opts.libraries:
sections_lst.remove(library)
libraries = sections_lst
# Share, Unshare, Kill, Add, or Remove
2018-02-05 06:20:51 +00:00
for user in users:
user_shares = find_shares(user)
user_shares_lst = user_shares['sections']
if libraries:
if opts.share:
2018-08-08 15:50:14 +00:00
share(user, libraries, sync, camera, channels, filterMovies, filterTelevision,
filterMusic)
if opts.add and user_shares_lst:
libraries = libraries + user_shares_lst
libraries = list(set(libraries))
2018-08-08 15:50:14 +00:00
share(user, libraries, sync, camera, channels, filterMovies, filterTelevision,
filterMusic)
if opts.remove and user_shares_lst:
libraries = [sect for sect in user_shares_lst if sect not in libraries]
2018-08-08 15:50:14 +00:00
share(user, libraries, sync, camera, channels, filterMovies, filterTelevision,
filterMusic)
else:
if opts.add:
# Add/Enable settings independently of libraries
libraries = user_shares_lst
share(user, libraries, sync, camera, channels, filterMovies, filterTelevision,
filterMusic)
if opts.remove:
# Remove/Disable settings independently of libraries
# If remove and setting arg is True then flip setting to false to disable
if sync:
sync = False
if camera:
camera = False
if channels:
channels = False
# Filters are cleared
# todo-me clear completely or pop arg values?
if filterMovies:
filterMovies = {}
if filterTelevision:
filterTelevision = {}
if filterMusic:
filterMusic = {}
share(user, libraries, sync, camera, channels, filterMovies, filterTelevision,
filterMusic)
if opts.shared:
user_json = json.dumps(user_shares, indent=4, sort_keys=True)
2018-08-04 07:05:29 +00:00
print('Current share settings for {}: {}'.format(user, user_json))
2018-08-08 15:50:14 +00:00
if opts.unshare and kill:
kill_session(user, kill)
2018-08-05 22:13:15 +00:00
time.sleep(3)
unshare(user, sections_lst)
2018-02-05 06:20:51 +00:00
elif opts.unshare:
2018-01-03 15:57:00 +00:00
unshare(user, sections_lst)
2018-08-08 15:50:14 +00:00
elif kill:
kill_session(user, kill)
2018-08-05 22:13:15 +00:00
if opts.backup:
print('Backing up share information...')
users_shares = []
2018-11-09 06:33:47 +00:00
# If user arg is defined then abide, else backup all
if not users:
users = user_lst
for user in users:
2018-08-05 22:13:15 +00:00
# print('...Found {}'.format(user))
users_shares.append(find_shares(user))
json_file = '{}_Plex_share_backup_{}.json'.format(plex.friendlyName, timestr)
2018-08-05 22:13:15 +00:00
with open(json_file, 'w') as fp:
2018-08-05 23:02:59 +00:00
json.dump(users_shares, fp, indent=4, sort_keys=True)
if opts.restore:
print('Using existing .json to restore Plex shares.')
with open(''.join(opts.restore)) as json_data:
shares_file = json.load(json_data)
for user in shares_file:
2018-08-13 10:54:59 +00:00
# If user arg is defined then abide, else restore all
if users:
if user['title'] in users:
2018-08-05 23:02:59 +00:00
print('Restoring user {}\'s shares and settings...'.format(user['title']))
2018-08-13 10:54:59 +00:00
share(user['title'], user['sections'], user['allowSync'], user['camera'],
2018-08-05 23:02:59 +00:00
user['channels'], user['filterMovies'], user['filterTelevision'],
2018-08-13 10:54:59 +00:00
user['filterMusic'])
else:
print('Restoring user {}\'s shares and settings...'.format(user['title']))
share(user['title'], user['sections'], user['allowSync'], user['camera'],
user['channels'], user['filterMovies'], user['filterTelevision'],
user['filterMusic'])