From 8d39bd6d33370adf815535acf362890cb96054b0 Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Mon, 23 May 2022 04:18:02 -0600 Subject: [PATCH] Support user mapping --- .env.sample | 1 + main.py | 204 +++++++++++++++++++++++++++++------------------ src/functions.py | 9 +++ src/jellyfin.py | 21 +++-- src/plex.py | 16 +++- 5 files changed, 168 insertions(+), 83 deletions(-) diff --git a/.env.sample b/.env.sample index 9d88ece..e8d7094 100644 --- a/.env.sample +++ b/.env.sample @@ -1,6 +1,7 @@ DRYRUN = "True" SLEEP_DURATION = "3600" LOGFILE = "log.log" +#USER_MAPPING = { "test2": "test" } PLEX_BASEURL = "http://localhost:32400" PLEX_TOKEN = "SuperSecretToken" diff --git a/main.py b/main.py index f4d0c35..5eef55e 100644 --- a/main.py +++ b/main.py @@ -1,78 +1,78 @@ -import copy, os, traceback +import copy, os, traceback, json from dotenv import load_dotenv from time import sleep -from src.functions import logger, str_to_bool +from src.functions import logger, str_to_bool, search_mapping from src.plex import Plex from src.jellyfin import Jellyfin load_dotenv(override=True) -def cleanup_watched(watched_list_1, watched_list_2): +def cleanup_watched(watched_list_1, watched_list_2, user_mapping): modified_watched_list_1 = copy.deepcopy(watched_list_1) # remove entries from plex_watched that are in jellyfin_watched - for user in watched_list_1: - if user in modified_watched_list_1: - for library in watched_list_1[user]: - if library in modified_watched_list_1[user]: - for item in watched_list_1[user][library]: - if item in modified_watched_list_1[user][library]: - if user in watched_list_2 and library in watched_list_2[user]: - # Movies - if isinstance(watched_list_1[user][library], list): - for watch_list_1_key, watch_list_1_value in item.items(): - for watch_list_2_item in watched_list_2[user][library]: - for watch_list_2_item_key, watch_list_2_item_value in watch_list_2_item.items(): - if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value: - if item in modified_watched_list_1[user][library]: - modified_watched_list_1[user][library].remove(item) - - # TV Shows - elif isinstance(watched_list_1[user][library], dict): - if item in watched_list_2[user][library]: - for season in watched_list_1[user][library][item]: - if season in watched_list_2[user][library][item]: - for episode in watched_list_1[user][library][item][season]: - for watch_list_1_episode_key, watch_list_1_episode_value in episode.items(): - for watch_list_2_episode in watched_list_2[user][library][item][season]: - for watch_list_2_episode_key, watch_list_2_episode_value in watch_list_2_episode.items(): - if watch_list_1_episode_key == watch_list_2_episode_key and watch_list_1_episode_value == watch_list_2_episode_value: - if episode in modified_watched_list_1[user][library][item][season]: - modified_watched_list_1[user][library][item][season].remove(episode) - - # If season is empty, remove season - if len(modified_watched_list_1[user][library][item][season]) == 0: - if season in modified_watched_list_1[user][library][item]: - del modified_watched_list_1[user][library][item][season] + for user_1 in watched_list_1: + user_2 = search_mapping(user_mapping, user_1) + if user_1 in modified_watched_list_1: + for library in watched_list_1[user_1]: + if library in modified_watched_list_1[user_1]: + for item in watched_list_1[user_1][library]: + if item in modified_watched_list_1[user_1][library]: + if user_1 in watched_list_2: + user = user_1 + elif user_2 in watched_list_2: + user = user_2 + else: + print(f"User {user_1} and {user_2} not found in watched list 2") + user = None + + if user: + if library in watched_list_2[user]: + # Movies + if isinstance(watched_list_1[user_1][library], list): + for watch_list_1_key, watch_list_1_value in item.items(): + for watch_list_2_item in watched_list_2[user][library]: + for watch_list_2_item_key, watch_list_2_item_value in watch_list_2_item.items(): + if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value: + if item in modified_watched_list_1[user_1][library]: + modified_watched_list_1[user_1][library].remove(item) + + # TV Shows + elif isinstance(watched_list_1[user_1][library], dict): + if item in watched_list_2[user][library]: + for season in watched_list_1[user_1][library][item]: + if season in watched_list_2[user][library][item]: + for episode in watched_list_1[user_1][library][item][season]: + for watch_list_1_episode_key, watch_list_1_episode_value in episode.items(): + for watch_list_2_episode in watched_list_2[user][library][item][season]: + for watch_list_2_episode_key, watch_list_2_episode_value in watch_list_2_episode.items(): + if watch_list_1_episode_key == watch_list_2_episode_key and watch_list_1_episode_value == watch_list_2_episode_value: + if episode in modified_watched_list_1[user_1][library][item][season]: + modified_watched_list_1[user_1][library][item][season].remove(episode) + + # If season is empty, remove season + if len(modified_watched_list_1[user_1][library][item][season]) == 0: + if season in modified_watched_list_1[user_1][library][item]: + del modified_watched_list_1[user_1][library][item][season] - # If the show is empty, remove the show - if len(modified_watched_list_1[user][library][item]) == 0: - if item in modified_watched_list_1[user][library]: - del modified_watched_list_1[user][library][item] + # If the show is empty, remove the show + if len(modified_watched_list_1[user_1][library][item]) == 0: + if item in modified_watched_list_1[user_1][library]: + del modified_watched_list_1[user_1][library][item] # If library is empty then remove it - if len(modified_watched_list_1[user][library]) == 0: - if library in modified_watched_list_1[user]: - del modified_watched_list_1[user][library] + if len(modified_watched_list_1[user_1][library]) == 0: + if library in modified_watched_list_1[user_1]: + del modified_watched_list_1[user_1][library] # If user is empty delete user - if len(modified_watched_list_1[user]) == 0: - del modified_watched_list_1[user] + if len(modified_watched_list_1[user_1]) == 0: + del modified_watched_list_1[user_1] return modified_watched_list_1 -def main(): - logfile = os.getenv("LOGFILE","log.log") - # Delete logfile if it exists - if os.path.exists(logfile): - os.remove(logfile) - - dryrun = str_to_bool(os.getenv("DRYRUN", "False")) - logger(f"Dryrun: {dryrun}", 1) - plex = Plex() - jellyfin = Jellyfin() - +def setup_black_white_lists(): blacklist_library = os.getenv("BLACKLIST_LIBRARY") if blacklist_library: if len(blacklist_library) > 0: @@ -130,43 +130,95 @@ def main(): whitelist_users = [] logger(f"Whitelist Users: {whitelist_users}", 1) + return blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, blacklist_users, whitelist_users + +def setup_users(plex, jellyfin, blacklist_users, whitelist_users, user_mapping=None): + # generate list of users from plex.users plex_users = [ x.title.lower() for x in plex.users ] jellyfin_users = [ key.lower() for key in jellyfin.users.keys() ] # combined list of overlapping users from plex and jellyfin - users = [x for x in plex_users if x in jellyfin_users] + users = {} + + for plex_user in plex_users: + if user_mapping: + jellyfin_plex_mapped_user = search_mapping(user_mapping, plex_user) + if jellyfin_plex_mapped_user: + users[plex_user] = jellyfin_plex_mapped_user + break + + if plex_user in jellyfin_users: + users[plex_user] = plex_user + + for jellyfin_user in jellyfin_users: + if user_mapping: + plex_jellyfin_mapped_user = search_mapping(user_mapping, jellyfin_user) + if plex_jellyfin_mapped_user: + users[plex_jellyfin_mapped_user] = jellyfin_user + break + + if jellyfin_user in plex_users: + users[jellyfin_user] = jellyfin_user + logger(f"User list that exist on both servers {users}", 1) - users_filtered = [] + users_filtered = {} for user in users: # whitelist_user is not empty and user lowercase is not in whitelist lowercase - if len(whitelist_users) > 0 and user.lower() not in whitelist_users: - logger(f"{user} is not in whitelist", 1) - else: - if user.lower() not in blacklist_users: - users_filtered.append(user) + if len(whitelist_users) > 0: + if user not in whitelist_users and users[user] not in whitelist_users: + logger(f"{user} or {users[user]} is not in whitelist", 1) + break + + if user not in blacklist_users and users[user] not in blacklist_users: + users_filtered[user] = users[user] + logger(f"Filtered user list {users_filtered}", 1) + plex_users = [] for plex_user in plex.users: - if plex_user.title.lower() in users_filtered: + if plex_user.title.lower() in users_filtered.keys() or plex_user.title.lower() in users_filtered.values(): plex_users.append(plex_user) jellyfin_users = {} for jellyfin_user, jellyfin_id in jellyfin.users.items(): - if jellyfin_user.lower() in users_filtered: + if jellyfin_user.lower() in users_filtered.keys() or jellyfin_user.lower() in users_filtered.values(): jellyfin_users[jellyfin_user] = jellyfin_id + if len(plex_users) == 0: + raise Exception("No plex users found") + + if len(jellyfin_users) == 0: + raise Exception("No jellyfin users found") + logger(f"plex_users: {plex_users}", 1) logger(f"jellyfin_users: {jellyfin_users}", 1) - if len(plex_users) == 0: - logger("No users found", 2) - raise Exception("No users found") + return plex_users, jellyfin_users - if len(jellyfin_users) == 0: - logger("No users found", 2) - raise Exception("No users found") +def main(): + logfile = os.getenv("LOGFILE","log.log") + # Delete logfile if it exists + if os.path.exists(logfile): + os.remove(logfile) + + dryrun = str_to_bool(os.getenv("DRYRUN", "False")) + logger(f"Dryrun: {dryrun}", 1) + + user_mapping = os.getenv("USER_MAPPING") + if user_mapping: + user_mapping = json.loads(user_mapping.lower()) + logger(f"User Mapping: {user_mapping}", 1) + + plex = Plex() + jellyfin = Jellyfin() + + # Create (black/white)lists + blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, blacklist_users, whitelist_users = setup_black_white_lists() + + # Create users list + plex_users, jellyfin_users = setup_users(plex, jellyfin, blacklist_users, whitelist_users, user_mapping) plex_watched = plex.get_plex_watched(plex_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type) jellyfin_watched = jellyfin.get_jellyfin_watched(jellyfin_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type) @@ -175,15 +227,15 @@ def main(): plex_watched_filtered = copy.deepcopy(plex_watched) jellyfin_watched_filtered = copy.deepcopy(jellyfin_watched) - plex_watched = cleanup_watched(plex_watched_filtered, jellyfin_watched_filtered) + plex_watched = cleanup_watched(plex_watched_filtered, jellyfin_watched_filtered, user_mapping) logger(f"plex_watched that needs to be synced to jellyfin:\n{plex_watched}", 1) - jellyfin_watched = cleanup_watched(jellyfin_watched_filtered, plex_watched_filtered) + jellyfin_watched = cleanup_watched(jellyfin_watched_filtered, plex_watched_filtered, user_mapping) logger(f"jellyfin_watched that needs to be synced to plex:\n{jellyfin_watched}", 1) # Update watched status - plex.update_watched(jellyfin_watched, dryrun) - jellyfin.update_watched(plex_watched, dryrun) + plex.update_watched(jellyfin_watched, user_mapping, dryrun) + jellyfin.update_watched(plex_watched, user_mapping, dryrun) if __name__ == "__main__": @@ -202,7 +254,7 @@ if __name__ == "__main__": logger(traceback.format_exc(), 2) - logger("Retrying in {sleep_timer}", log_type=0) + logger(f"Retrying in {sleep_timer}", log_type=0) except KeyboardInterrupt: logger("Exiting", log_type=0) diff --git a/src/functions.py b/src/functions.py index b5ce86d..90333ac 100644 --- a/src/functions.py +++ b/src/functions.py @@ -27,3 +27,12 @@ def str_to_bool(value: any) -> bool: if not value: return False return str(value).lower() in ("y", "yes", "t", "true", "on", "1") + +# Get mapped value +def search_mapping(dictionary: dict, key_value: str): + if key_value in dictionary.keys(): + return dictionary[key_value] + elif key_value in dictionary.values(): + return list(dictionary.keys())[list(dictionary.values()).index(key_value)] + else: + return None diff --git a/src/jellyfin.py b/src/jellyfin.py index 837ddae..85ed3e8 100644 --- a/src/jellyfin.py +++ b/src/jellyfin.py @@ -1,6 +1,6 @@ import requests, os from dotenv import load_dotenv -from src.functions import logger +from src.functions import logger, search_mapping load_dotenv(override=True) @@ -67,14 +67,15 @@ class Jellyfin(): for library in libraries: library_title = library["Name"] - logger(f"Jellyfin: Generating watched for {user_name} in library {library_title}", 0) library_id = library["Id"] # if whitelist is not empty and library is not in whitelist - if len(whitelist_library) > 0 and library_title.lower() not in [x.lower() for x in whitelist_library]: + if len(whitelist_library) > 0 and library_title.lower() not in whitelist_library: pass else: - if library_title.lower() not in [x.lower() for x in blacklist_library]: + logger(f"Jellyfin: Generating watched for {user_name} in library {library_title}", 0) + + if library_title.lower() not in blacklist_library: watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Filters=IsPlayed&limit=1", "get") if len(watched["Items"]) == 0: @@ -129,8 +130,18 @@ class Jellyfin(): return users_watched - def update_watched(self, watched_list, dryrun=False): + def update_watched(self, watched_list, user_mapping, dryrun=False): for user, libraries in watched_list.items(): + other = None + if user in user_mapping.keys(): + other = user_mapping[user] + + elif user in user_mapping.values(): + other = search_mapping(user_mapping, user) + + if other: + logger(f"Swapping user {user} with {other}", 1) + user = other user_id = None for key, value in self.users.items(): diff --git a/src/plex.py b/src/plex.py index 3c6213c..82987ea 100644 --- a/src/plex.py +++ b/src/plex.py @@ -1,7 +1,7 @@ import re, os from dotenv import load_dotenv -from src.functions import logger +from src.functions import logger, search_mapping from plexapi.server import PlexServer from plexapi.myplex import MyPlexAccount @@ -119,13 +119,25 @@ class Plex: return users_watched - def update_watched(self, watched_list, dryrun=False): + def update_watched(self, watched_list, user_mapping, dryrun=False): for user, libraries in watched_list.items(): + other = None + if user in user_mapping.keys(): + other = user_mapping[user] + + elif user in user_mapping.values(): + other = search_mapping(user_mapping, user) + + if other: + logger(f"Swapping user {user} with {other}", 1) + user = other + for index, value in enumerate(self.users): if user.lower() == value.title.lower(): user = self.users[index] break + print(user) if self.admin_user == user: user_plex = self.plex else: