From 1adcc9e41b2a2e3af05d5ec8d3e9e744285fe873 Mon Sep 17 00:00:00 2001 From: Blacktwin Date: Mon, 25 Feb 2019 00:20:45 -0500 Subject: [PATCH] #144 Added ability to sync watched status across all owned servers --- utility/sync_watch_status.py | 170 +++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 56 deletions(-) diff --git a/utility/sync_watch_status.py b/utility/sync_watch_status.py index 458cec1..78181e1 100644 --- a/utility/sync_watch_status.py +++ b/utility/sync_watch_status.py @@ -31,7 +31,7 @@ Script Arguments: Taultulli > Settings > Notification Agents > New Script > Script Arguments: Select: Notify on Watched - Arguments: --ratingKey {rating_key} --userTo "Username2" "Username3" --userFrom {username} + Arguments: --ratingKey {rating_key} --userTo "Username2=Server1" "Username3=Server1" --userFrom {username}={server_name} Save Close @@ -39,28 +39,26 @@ Taultulli > Settings > Notification Agents > New Script > Script Arguments: Example: Set in Tautulli in script notification agent or run manually - plex_api_share.py --userFrom USER1 --userTo USER2 --libraries Movies + sync_watch_status.py --userFrom USER1=Server --userTo USER2=Server --libraries Movies - Synced watch status of {title from library} to {USER2}'s account. - plex_api_share.py --userFrom USER1 --userTo USER2 USER3 --allLibraries + sync_watch_status.py --userFrom USER1=Server --userTo USER2=Server USER3=Server --allLibraries - Synced watch status of {title from library} to {USER2 or USER3}'s account. Excluding; --libraries becomes excluded if --allLibraries is set - sync_watch_status.py --userFrom USER1 --userTo USER2 --allLibraries --libraries Movies + sync_watch_status.py --userFrom USER1=Server --userTo USER2=Server --allLibraries --libraries Movies - Shared [all libraries but Movies] with USER. """ import requests import argparse +from plexapi.myplex import MyPlexAccount from plexapi.server import PlexServer, CONFIG -# Using CONFIG file -PLEX_URL = '' -PLEX_TOKEN = '' -if not PLEX_URL: - PLEX_URL = CONFIG.data['auth'].get('server_baseurl', '') +# Using CONFIG file +PLEX_TOKEN = '' if not PLEX_TOKEN: PLEX_TOKEN = CONFIG.data['auth'].get('server_token', '') @@ -78,41 +76,107 @@ if sess.verify is False: urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) -plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess) +account = MyPlexAccount(PLEX_TOKEN) -sections_lst = [x.title for x in plex.library.sections()] -user_lst = [x.title for x in plex.myPlexAccount().users()] -servers_dict = {acct.name: acct for acct in plex.myPlexAccount().resources() - if 'server' in [acct.provides] and acct.ownerid == 0} -# Adding admin account name to list -user_lst.append(plex.myPlexAccount().title) +sections_lst = [] +user_servers = {} +admin_servers = {} +server_users = account.users() +user_server_dict = {'data': {}} +user_data = user_server_dict['data'] + +# Finding and connecting to owned servers. +print('Connecting to admin owned servers.') +for resource in account.resources(): + if 'server' in [resource.provides] and resource.ownerid == 0: + server_connect = resource.connect() + admin_servers[resource.name] = server_connect + # Pull section names to check against + server_sections = [section.title for section in server_connect.library.sections()] + sections_lst += server_sections + +sections_lst = list(set(sections_lst)) + +# Add admin account +user_data[account.title] = {'account': account, + 'servers': admin_servers} + +# Finding what user has access to which admin owned servers +for user in server_users: + for server in user.servers: + if admin_servers.get(server.name): + user_servers[server.name] = admin_servers.get(server.name) + if not user_data.get(user.title): + user_data[user.title] = {'account': user, + 'servers': user_servers} -def get_account(user): - if user == plex.myPlexAccount().title: - account = plex +def get_account(user, server): + """ + Parameters + ---------- + user: str + User's name + server: str + Server's name + + Returns + ------- + User server class + + """ + print('Checking {} on {}'.format(user, server)) + if user_server_dict['data'][user]['servers'].get(server): + user_server = user_server_dict['data'][user]['servers'].get(server) + baseurl = user_server._baseurl.split('.') + url = ''.join([baseurl[0].replace('-', '.'), + baseurl[-1].replace('direct', '')]) + if user == MyPlexAccount(PLEX_TOKEN).title: + token = PLEX_TOKEN + else: + userAccount = user_server.myPlexAccount().user(user) + token = userAccount.get_token(user_server.machineIdentifier) + account = PlexServer(baseurl=url, token=token, session=sess) + return account else: - # Access Plex User's Account - userAccount = plex.myPlexAccount().user(user) - token = userAccount.get_token(plex.machineIdentifier) - account = PlexServer(PLEX_URL, token) - return account + print('{} is not shared to {}'.format(user, server)) + exit() def mark_watached(sectionFrom, accountTo, userTo): + """ + Parameters + ---------- + sectionFrom: class + Section class of sync from server + accountTo: class + User's server class of sync to user + + """ # Check sections for watched items + print('Marking watched...') + sectionTo = accountTo.library.section(sectionFrom.title) for item in sectionFrom.search(unwatched=False): title = item.title.encode('utf-8') - # Check movie media type - if item.type == 'movie': - accountTo.fetchItem(item.key).markWatched() - print('Synced watch status of {} to {}\'s account.'.format(title, userTo)) - # Check show media type - elif item.type == 'show': - for episode in sectionFrom.searchEpisodes(unwatched=False, title=title): - ep_title = episode.title.encode('utf-8') - accountTo.fetchItem(episode.key).markWatched() - print('Synced watch status of {} - {} to {}\'s account.'.format(title, ep_title, userTo)) + try: + # Check movie media type + if item.type == 'movie': + watch_check = sectionTo.get(item.title) + fetch_check = sectionTo.fetchItem(watch_check.key) + if not fetch_check.isWatched: + fetch_check.markWatched() + print('Synced watch status of {} to {}\'s account.'.format(title, userTo)) + # Check show media type + elif item.type == 'show': + for episode in sectionFrom.searchEpisodes(unwatched=False, title=title): + ep_title = episode.title.encode('utf-8') + watch_check = sectionTo.get(item.title) + fetch_check = sectionTo.fetchItem(watch_check.key) + if not fetch_check.isWatched: + fetch_check.markWatched() + print('Synced watch status of {} - {} to {}\'s account.'.format(title, ep_title, userTo)) + except Exception: + pass if __name__ == '__main__': @@ -125,23 +189,16 @@ if __name__ == '__main__': '(choices: %(choices)s)') parser.add_argument('--allLibraries', action='store_true', help='Select all libraries.') - parser.add_argument('--servers', nargs='*', choices=servers_dict.keys(), - help='Space separated list of case sensitive names to process. Allowed names are: \n' - '(choices: %(choices)s)') parser.add_argument('--ratingKey', nargs=1, help='Rating key of item whose watch status is to be synced.') - requiredNamed.add_argument('--userFrom', choices=user_lst, metavar='username', required=True, - help='Space separated list of case sensitive names to process. Allowed names are: \n' - '(choices: %(choices)s)') - requiredNamed.add_argument('--userTo', nargs='*', choices=user_lst, metavar='usernames', required=True, - help='Space separated list of case sensitive names to process. Allowed names are: \n' - '(choices: %(choices)s)') + requiredNamed.add_argument('--userFrom', metavar='user=server', required=True, + type=lambda kv: kv.split("="), + help='Select user and server to sync from') + requiredNamed.add_argument('--userTo', nargs='*', metavar='user=server', required=True, + type=lambda kv: kv.split("="), + help='Select user and server to sync to.') opts = parser.parse_args() - # print(opts) - - # Create Sync-From user account - plexFrom = get_account(opts.userFrom) # Defining libraries libraries = '' @@ -155,10 +212,12 @@ if __name__ == '__main__': sections_lst.remove(library) libraries = sections_lst + # Create Sync-From user account + plexFrom = get_account(opts.userFrom[0], opts.userFrom[1]) + # Go through list of users for user in opts.userTo: - # Create Sync-To user account - plexTo = get_account(user) + plexTo = get_account(user[0], user[1]) if libraries: # Go through Libraries for library in libraries: @@ -166,12 +225,10 @@ if __name__ == '__main__': print('Checking library: {}'.format(library)) # Check library for watched items section = plexFrom.library.section(library) - mark_watached(section, plexTo, user) + mark_watached(section, plexTo, user[0]) except Exception as e: if str(e).startswith('Unknown'): print('Library ({}) does not have a watch status.'.format(library)) - elif str(e).startswith('Invalid'): - print('Library ({}) not shared to user: {}.'.format(library, opts.userFrom)) elif str(e).startswith('(404)'): print('Library ({}) not shared to user: {}.'.format(library, user)) else: @@ -179,9 +236,10 @@ if __name__ == '__main__': pass # Check rating key from Tautulli elif opts.ratingKey: - item = plexTo.fetchItem(opts.ratingKey) - title = item.title.encode('utf-8') - print('Syncing watch status of {} to {}\'s account.'.format(title, user)) - item.markWatched() + for key in opts.ratingKey: + item = plexTo.fetchItem(int(key)) + title = item.title.encode('utf-8') + print('Syncing watch status of {} to {}\'s account.'.format(title, user[0])) + item.markWatched() else: print('No libraries or rating key provided.')