Support user mapping
parent
768103f113
commit
8d39bd6d33
|
|
@ -1,6 +1,7 @@
|
||||||
DRYRUN = "True"
|
DRYRUN = "True"
|
||||||
SLEEP_DURATION = "3600"
|
SLEEP_DURATION = "3600"
|
||||||
LOGFILE = "log.log"
|
LOGFILE = "log.log"
|
||||||
|
#USER_MAPPING = { "test2": "test" }
|
||||||
|
|
||||||
PLEX_BASEURL = "http://localhost:32400"
|
PLEX_BASEURL = "http://localhost:32400"
|
||||||
PLEX_TOKEN = "SuperSecretToken"
|
PLEX_TOKEN = "SuperSecretToken"
|
||||||
|
|
|
||||||
204
main.py
204
main.py
|
|
@ -1,78 +1,78 @@
|
||||||
import copy, os, traceback
|
import copy, os, traceback, json
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from time import sleep
|
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.plex import Plex
|
||||||
from src.jellyfin import Jellyfin
|
from src.jellyfin import Jellyfin
|
||||||
|
|
||||||
load_dotenv(override=True)
|
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)
|
modified_watched_list_1 = copy.deepcopy(watched_list_1)
|
||||||
|
|
||||||
# remove entries from plex_watched that are in jellyfin_watched
|
# remove entries from plex_watched that are in jellyfin_watched
|
||||||
for user in watched_list_1:
|
for user_1 in watched_list_1:
|
||||||
if user in modified_watched_list_1:
|
user_2 = search_mapping(user_mapping, user_1)
|
||||||
for library in watched_list_1[user]:
|
if user_1 in modified_watched_list_1:
|
||||||
if library in modified_watched_list_1[user]:
|
for library in watched_list_1[user_1]:
|
||||||
for item in watched_list_1[user][library]:
|
if library in modified_watched_list_1[user_1]:
|
||||||
if item in modified_watched_list_1[user][library]:
|
for item in watched_list_1[user_1][library]:
|
||||||
if user in watched_list_2 and library in watched_list_2[user]:
|
if item in modified_watched_list_1[user_1][library]:
|
||||||
# Movies
|
if user_1 in watched_list_2:
|
||||||
if isinstance(watched_list_1[user][library], list):
|
user = user_1
|
||||||
for watch_list_1_key, watch_list_1_value in item.items():
|
elif user_2 in watched_list_2:
|
||||||
for watch_list_2_item in watched_list_2[user][library]:
|
user = user_2
|
||||||
for watch_list_2_item_key, watch_list_2_item_value in watch_list_2_item.items():
|
else:
|
||||||
if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value:
|
print(f"User {user_1} and {user_2} not found in watched list 2")
|
||||||
if item in modified_watched_list_1[user][library]:
|
user = None
|
||||||
modified_watched_list_1[user][library].remove(item)
|
|
||||||
|
if user:
|
||||||
# TV Shows
|
if library in watched_list_2[user]:
|
||||||
elif isinstance(watched_list_1[user][library], dict):
|
# Movies
|
||||||
if item in watched_list_2[user][library]:
|
if isinstance(watched_list_1[user_1][library], list):
|
||||||
for season in watched_list_1[user][library][item]:
|
for watch_list_1_key, watch_list_1_value in item.items():
|
||||||
if season in watched_list_2[user][library][item]:
|
for watch_list_2_item in watched_list_2[user][library]:
|
||||||
for episode in watched_list_1[user][library][item][season]:
|
for watch_list_2_item_key, watch_list_2_item_value in watch_list_2_item.items():
|
||||||
for watch_list_1_episode_key, watch_list_1_episode_value in episode.items():
|
if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value:
|
||||||
for watch_list_2_episode in watched_list_2[user][library][item][season]:
|
if item in modified_watched_list_1[user_1][library]:
|
||||||
for watch_list_2_episode_key, watch_list_2_episode_value in watch_list_2_episode.items():
|
modified_watched_list_1[user_1][library].remove(item)
|
||||||
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]:
|
# TV Shows
|
||||||
modified_watched_list_1[user][library][item][season].remove(episode)
|
elif isinstance(watched_list_1[user_1][library], dict):
|
||||||
|
if item in watched_list_2[user][library]:
|
||||||
# If season is empty, remove season
|
for season in watched_list_1[user_1][library][item]:
|
||||||
if len(modified_watched_list_1[user][library][item][season]) == 0:
|
if season in watched_list_2[user][library][item]:
|
||||||
if season in modified_watched_list_1[user][library][item]:
|
for episode in watched_list_1[user_1][library][item][season]:
|
||||||
del modified_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_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 the show is empty, remove the show
|
||||||
if len(modified_watched_list_1[user][library][item]) == 0:
|
if len(modified_watched_list_1[user_1][library][item]) == 0:
|
||||||
if item in modified_watched_list_1[user][library]:
|
if item in modified_watched_list_1[user_1][library]:
|
||||||
del modified_watched_list_1[user][library][item]
|
del modified_watched_list_1[user_1][library][item]
|
||||||
|
|
||||||
# If library is empty then remove it
|
# If library is empty then remove it
|
||||||
if len(modified_watched_list_1[user][library]) == 0:
|
if len(modified_watched_list_1[user_1][library]) == 0:
|
||||||
if library in modified_watched_list_1[user]:
|
if library in modified_watched_list_1[user_1]:
|
||||||
del modified_watched_list_1[user][library]
|
del modified_watched_list_1[user_1][library]
|
||||||
|
|
||||||
# If user is empty delete user
|
# If user is empty delete user
|
||||||
if len(modified_watched_list_1[user]) == 0:
|
if len(modified_watched_list_1[user_1]) == 0:
|
||||||
del modified_watched_list_1[user]
|
del modified_watched_list_1[user_1]
|
||||||
|
|
||||||
return modified_watched_list_1
|
return modified_watched_list_1
|
||||||
|
|
||||||
def main():
|
def setup_black_white_lists():
|
||||||
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()
|
|
||||||
|
|
||||||
blacklist_library = os.getenv("BLACKLIST_LIBRARY")
|
blacklist_library = os.getenv("BLACKLIST_LIBRARY")
|
||||||
if blacklist_library:
|
if blacklist_library:
|
||||||
if len(blacklist_library) > 0:
|
if len(blacklist_library) > 0:
|
||||||
|
|
@ -130,43 +130,95 @@ def main():
|
||||||
whitelist_users = []
|
whitelist_users = []
|
||||||
logger(f"Whitelist Users: {whitelist_users}", 1)
|
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
|
# generate list of users from plex.users
|
||||||
plex_users = [ x.title.lower() for x in plex.users ]
|
plex_users = [ x.title.lower() for x in plex.users ]
|
||||||
jellyfin_users = [ key.lower() for key in jellyfin.users.keys() ]
|
jellyfin_users = [ key.lower() for key in jellyfin.users.keys() ]
|
||||||
|
|
||||||
# combined list of overlapping users from plex and jellyfin
|
# 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)
|
logger(f"User list that exist on both servers {users}", 1)
|
||||||
|
|
||||||
users_filtered = []
|
users_filtered = {}
|
||||||
for user in users:
|
for user in users:
|
||||||
# whitelist_user is not empty and user lowercase is not in whitelist lowercase
|
# 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:
|
if len(whitelist_users) > 0:
|
||||||
logger(f"{user} is not in whitelist", 1)
|
if user not in whitelist_users and users[user] not in whitelist_users:
|
||||||
else:
|
logger(f"{user} or {users[user]} is not in whitelist", 1)
|
||||||
if user.lower() not in blacklist_users:
|
break
|
||||||
users_filtered.append(user)
|
|
||||||
|
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 = []
|
plex_users = []
|
||||||
for plex_user in 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)
|
plex_users.append(plex_user)
|
||||||
|
|
||||||
jellyfin_users = {}
|
jellyfin_users = {}
|
||||||
for jellyfin_user, jellyfin_id in jellyfin.users.items():
|
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
|
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"plex_users: {plex_users}", 1)
|
||||||
logger(f"jellyfin_users: {jellyfin_users}", 1)
|
logger(f"jellyfin_users: {jellyfin_users}", 1)
|
||||||
|
|
||||||
if len(plex_users) == 0:
|
return plex_users, jellyfin_users
|
||||||
logger("No users found", 2)
|
|
||||||
raise Exception("No users found")
|
|
||||||
|
|
||||||
if len(jellyfin_users) == 0:
|
def main():
|
||||||
logger("No users found", 2)
|
logfile = os.getenv("LOGFILE","log.log")
|
||||||
raise Exception("No users found")
|
# 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)
|
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)
|
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)
|
plex_watched_filtered = copy.deepcopy(plex_watched)
|
||||||
jellyfin_watched_filtered = copy.deepcopy(jellyfin_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)
|
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)
|
logger(f"jellyfin_watched that needs to be synced to plex:\n{jellyfin_watched}", 1)
|
||||||
|
|
||||||
# Update watched status
|
# Update watched status
|
||||||
plex.update_watched(jellyfin_watched, dryrun)
|
plex.update_watched(jellyfin_watched, user_mapping, dryrun)
|
||||||
jellyfin.update_watched(plex_watched, dryrun)
|
jellyfin.update_watched(plex_watched, user_mapping, dryrun)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
@ -202,7 +254,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
||||||
logger(traceback.format_exc(), 2)
|
logger(traceback.format_exc(), 2)
|
||||||
logger("Retrying in {sleep_timer}", log_type=0)
|
logger(f"Retrying in {sleep_timer}", log_type=0)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger("Exiting", log_type=0)
|
logger("Exiting", log_type=0)
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,12 @@ def str_to_bool(value: any) -> bool:
|
||||||
if not value:
|
if not value:
|
||||||
return False
|
return False
|
||||||
return str(value).lower() in ("y", "yes", "t", "true", "on", "1")
|
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
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import requests, os
|
import requests, os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from src.functions import logger
|
from src.functions import logger, search_mapping
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
@ -67,14 +67,15 @@ class Jellyfin():
|
||||||
|
|
||||||
for library in libraries:
|
for library in libraries:
|
||||||
library_title = library["Name"]
|
library_title = library["Name"]
|
||||||
logger(f"Jellyfin: Generating watched for {user_name} in library {library_title}", 0)
|
|
||||||
|
|
||||||
library_id = library["Id"]
|
library_id = library["Id"]
|
||||||
# if whitelist is not empty and library is not in whitelist
|
# 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
|
pass
|
||||||
else:
|
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")
|
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:
|
if len(watched["Items"]) == 0:
|
||||||
|
|
@ -129,8 +130,18 @@ class Jellyfin():
|
||||||
|
|
||||||
return users_watched
|
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():
|
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
|
user_id = None
|
||||||
for key, value in self.users.items():
|
for key, value in self.users.items():
|
||||||
|
|
|
||||||
16
src/plex.py
16
src/plex.py
|
|
@ -1,7 +1,7 @@
|
||||||
import re, os
|
import re, os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from src.functions import logger
|
from src.functions import logger, search_mapping
|
||||||
from plexapi.server import PlexServer
|
from plexapi.server import PlexServer
|
||||||
from plexapi.myplex import MyPlexAccount
|
from plexapi.myplex import MyPlexAccount
|
||||||
|
|
||||||
|
|
@ -119,13 +119,25 @@ class Plex:
|
||||||
|
|
||||||
return users_watched
|
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():
|
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):
|
for index, value in enumerate(self.users):
|
||||||
if user.lower() == value.title.lower():
|
if user.lower() == value.title.lower():
|
||||||
user = self.users[index]
|
user = self.users[index]
|
||||||
break
|
break
|
||||||
|
|
||||||
|
print(user)
|
||||||
if self.admin_user == user:
|
if self.admin_user == user:
|
||||||
user_plex = self.plex
|
user_plex = self.plex
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue