Use async for jellyfin (#23)

* Use async

* Massive jellyfin watched speedup

Co-authored-by: Luigi311 <luigi311.lg@gmail.com>
pull/26/head
Luigi311 2022-07-10 01:30:12 -06:00 committed by GitHub
parent 1efb4d8543
commit 88a7526721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2623 additions and 2530 deletions

View File

@ -1,3 +1,4 @@
plexapi plexapi
requests requests
python-dotenv python-dotenv
aiohttp

View File

@ -133,6 +133,17 @@ def generate_library_guids_dict(user_list: dict):
return show_output_dict, episode_output_dict, movies_output_dict return show_output_dict, episode_output_dict, movies_output_dict
def combine_watched_dicts(dicts: list):
combined_dict = {}
for dict in dicts:
for key, value in dict.items():
if key not in combined_dict:
combined_dict[key] = {}
for subkey, subvalue in value.items():
combined_dict[key][subkey] = subvalue
return combined_dict
def future_thread_executor(args: list, workers: int = -1): def future_thread_executor(args: list, workers: int = -1):
futures_list = [] futures_list = []
results = [] results = []

View File

@ -1,11 +1,10 @@
import requests import asyncio, aiohttp
from src.functions import logger, search_mapping, str_to_bool, check_skip_logic, generate_library_guids_dict, future_thread_executor from src.functions import logger, search_mapping, str_to_bool, check_skip_logic, generate_library_guids_dict, future_thread_executor, combine_watched_dicts
class Jellyfin(): class Jellyfin():
def __init__(self, baseurl, token): def __init__(self, baseurl, token):
self.baseurl = baseurl self.baseurl = baseurl
self.token = token self.token = token
self.session = requests.Session()
if not self.baseurl: if not self.baseurl:
raise Exception("Jellyfin baseurl not set") raise Exception("Jellyfin baseurl not set")
@ -13,21 +12,16 @@ class Jellyfin():
if not self.token: if not self.token:
raise Exception("Jellyfin token not set") raise Exception("Jellyfin token not set")
self.users = self.get_users() self.users = asyncio.run(self.get_users())
def query(self, query, query_type): async def query(self, query, query_type, session, identifiers=None):
try: try:
response = None results = None
headers = { headers = {
"Accept": "application/json", "Accept": "application/json",
"X-Emby-Token": self.token "X-Emby-Token": self.token
} }
if query_type == "get":
response = self.session.get(self.baseurl + query, headers=headers)
elif query_type == "post":
authorization = ( authorization = (
'MediaBrowser , ' 'MediaBrowser , '
'Client="other", ' 'Client="other", '
@ -36,20 +30,32 @@ class Jellyfin():
'Version="0.0.0"' 'Version="0.0.0"'
) )
headers["X-Emby-Authorization"] = authorization headers["X-Emby-Authorization"] = authorization
response = self.session.post(self.baseurl + query, headers=headers)
return response.json() if query_type == "get":
async with session.get(self.baseurl + query, headers=headers) as response:
results = await response.json()
elif query_type == "post":
async with session.post(self.baseurl + query, headers=headers) as response:
results = await response.json()
# append identifiers to results
if identifiers:
results["Identifiers"] = identifiers
return results
except Exception as e: except Exception as e:
logger(f"Jellyfin: Query failed {e}", 2) logger(f"Jellyfin: Query failed {e}", 2)
raise Exception(e) raise Exception(e)
def get_users(self):
async def get_users(self):
try: try:
users = {} users = {}
query = "/Users" query_string = "/Users"
response = self.query(query, "get") async with aiohttp.ClientSession() as session:
response = await self.query(query_string, "get", session)
# If reponse is not empty # If reponse is not empty
if response: if response:
@ -61,7 +67,8 @@ class Jellyfin():
logger(f"Jellyfin: Get users failed {e}", 2) logger(f"Jellyfin: Get users failed {e}", 2)
raise Exception(e) raise Exception(e)
def get_user_watched(self, user_name, user_id, library_type, library_id, library_title):
async def get_user_watched(self, user_name, user_id, library_type, library_id, library_title):
try: try:
user_name = user_name.lower() user_name = user_name.lower()
user_watched = {} user_watched = {}
@ -69,51 +76,84 @@ class Jellyfin():
logger(f"Jellyfin: Generating watched for {user_name} in library {library_title}", 0) logger(f"Jellyfin: Generating watched for {user_name} in library {library_title}", 0)
# Movies # Movies
async with aiohttp.ClientSession() as session:
if library_type == "Movie": if library_type == "Movie":
user_watched[user_name][library_title] = [] user_watched[user_name][library_title] = []
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Filters=IsPlayed&Fields=ItemCounts,ProviderIds,MediaSources", "get") watched = await self.query(f"/Users/{user_id}/Items?ParentId={library_id}&Filters=IsPlayed&Fields=ItemCounts,ProviderIds,MediaSources", "get", session)
for movie in watched["Items"]: for movie in watched["Items"]:
if movie["UserData"]["Played"] == True: if movie["UserData"]["Played"] == True:
movie_guids = {} movie_guids = {}
movie_guids["title"] = movie["Name"] movie_guids["title"] = movie["Name"]
if movie["ProviderIds"]: if "ProviderIds" in movie:
# Lowercase movie["ProviderIds"] keys # Lowercase movie["ProviderIds"] keys
movie_guids = {k.lower(): v for k, v in movie["ProviderIds"].items()} movie_guids = {k.lower(): v for k, v in movie["ProviderIds"].items()}
if movie["MediaSources"]: if "MediaSources" in movie:
movie_guids["locations"] = tuple([x["Path"].split("/")[-1] for x in movie["MediaSources"]]) movie_guids["locations"] = tuple([x["Path"].split("/")[-1] for x in movie["MediaSources"]])
user_watched[user_name][library_title].append(movie_guids) user_watched[user_name][library_title].append(movie_guids)
# TV Shows # TV Shows
if library_type == "Episode": if library_type == "Series":
user_watched[user_name][library_title] = {} user_watched[user_name][library_title] = {}
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Fields=ItemCounts,ProviderIds,Path", "get") watched_shows = await self.query(f"/Users/{user_id}/Items?ParentId={library_id}&isPlaceHolder=false&Fields=ProviderIds,Path,RecursiveItemCount", "get", session)
watched_shows = [x for x in watched["Items"] if x["Type"] == "Series"] watched_shows_filtered = []
for show in watched_shows["Items"]:
for show in watched_shows: if "PlayedPercentage" in show["UserData"]:
if show["UserData"]["PlayedPercentage"] > 0:
watched_shows_filtered.append(show)
seasons_tasks = []
for show in watched_shows_filtered:
show_guids = {k.lower(): v for k, v in show["ProviderIds"].items()} show_guids = {k.lower(): v for k, v in show["ProviderIds"].items()}
show_guids["title"] = show["Name"] show_guids["title"] = show["Name"]
show_guids["locations"] = tuple([show["Path"].split("/")[-1]]) show_guids["locations"] = tuple([show["Path"].split("/")[-1]])
show_guids = frozenset(show_guids.items()) show_guids = frozenset(show_guids.items())
seasons = self.query(f"/Shows/{show['Id']}/Seasons?userId={user_id}&Fields=ItemCounts,ProviderIds", "get") identifiers = {"show_guids": show_guids, "show_id": show["Id"]}
task = asyncio.ensure_future(self.query(f"/Shows/{show['Id']}/Seasons?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,RecursiveItemCount", "get", session, frozenset(identifiers.items())))
seasons_tasks.append(task)
seasons_watched = await asyncio.gather(*seasons_tasks)
seasons_watched_filtered = []
for seasons in seasons_watched:
seasons_watched_filtered_dict = {}
seasons_watched_filtered_dict["Identifiers"] = seasons["Identifiers"]
seasons_watched_filtered_dict["Items"] = []
for season in seasons["Items"]:
if "PlayedPercentage" in season["UserData"]:
if season["UserData"]["PlayedPercentage"] > 0:
seasons_watched_filtered_dict["Items"].append(season)
if seasons_watched_filtered_dict["Items"]:
seasons_watched_filtered.append(seasons_watched_filtered_dict)
episodes_tasks = []
for seasons in seasons_watched_filtered:
if len(seasons["Items"]) > 0: if len(seasons["Items"]) > 0:
for season in seasons["Items"]: for season in seasons["Items"]:
episodes = self.query(f"/Shows/{show['Id']}/Episodes?seasonId={season['Id']}&userId={user_id}&Fields=ItemCounts,ProviderIds,MediaSources", "get") season_identifiers = dict(seasons["Identifiers"])
season_identifiers["season_id"] = season["Id"]
season_identifiers["season_name"] = season["Name"]
task = asyncio.ensure_future(self.query(f"/Shows/{season_identifiers['show_id']}/Episodes?seasonId={season['Id']}&userId={user_id}&isPlaceHolder=false&isPlayed=true&Fields=ProviderIds,MediaSources", "get", session, frozenset(season_identifiers.items())))
episodes_tasks.append(task)
watched_episodes = await asyncio.gather(*episodes_tasks)
for episodes in watched_episodes:
if len(episodes["Items"]) > 0: if len(episodes["Items"]) > 0:
for episode in episodes["Items"]: for episode in episodes["Items"]:
if episode["UserData"]["Played"] == True: if episode["UserData"]["Played"] == True:
if episode["ProviderIds"] or episode["MediaSources"]: if "ProviderIds" in episode or "MediaSources" in episode:
episode_identifiers = dict(episodes["Identifiers"])
show_guids = episode_identifiers["show_guids"]
if show_guids not in user_watched[user_name][library_title]: if show_guids not in user_watched[user_name][library_title]:
user_watched[user_name][library_title][show_guids] = {} user_watched[user_name][library_title][show_guids] = {}
if season["Name"] not in user_watched[user_name][library_title][show_guids]: if episode_identifiers["season_name"] not in user_watched[user_name][library_title][show_guids]:
user_watched[user_name][library_title][show_guids][season["Name"]] = [] user_watched[user_name][library_title][show_guids][episode_identifiers["season_name"]] = []
# Lowercase episode["ProviderIds"] keys
episode_guids = {} episode_guids = {}
if episode["ProviderIds"]: if "ProviderIds" in episode:
episode_guids = {k.lower(): v for k, v in episode["ProviderIds"].items()} episode_guids = {k.lower(): v for k, v in episode["ProviderIds"].items()}
if episode["MediaSources"]: if "MediaSources" in episode:
episode_guids["locations"] = tuple([x["Path"].split("/")[-1] for x in episode["MediaSources"]]) episode_guids["locations"] = tuple([x["Path"].split("/")[-1] for x in episode["MediaSources"]])
user_watched[user_name][library_title][show_guids][season["Name"]].append(episode_guids) user_watched[user_name][library_title][show_guids][episode_identifiers["season_name"]].append(episode_guids)
return user_watched return user_watched
except Exception as e: except Exception as e:
@ -121,26 +161,30 @@ class Jellyfin():
raise Exception(e) raise Exception(e)
def get_watched(self, users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping=None): async def get_users_watched(self, user_name, user_id, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping):
try: try:
users_watched = {}
args = []
for user_name, user_id in users.items():
# Get all libraries # Get all libraries
user_name = user_name.lower() user_name = user_name.lower()
tasks_watched = []
libraries = self.query(f"/Users/{user_id}/Views", "get")["Items"] tasks_libraries = []
async with aiohttp.ClientSession() as session:
for library in libraries: libraries = await self.query(f"/Users/{user_id}/Views", "get", session)
library_title = library["Name"] for library in libraries["Items"]:
library_id = library["Id"] library_id = library["Id"]
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Filters=IsPlayed&limit=1", "get") library_title = library["Name"]
identifiers = {"library_id": library_id, "library_title": library_title}
task = asyncio.ensure_future(self.query(f"/Users/{user_id}/Items?ParentId={library_id}&Filters=IsPlayed&limit=1", "get", session, identifiers=identifiers))
tasks_libraries.append(task)
libraries = await asyncio.gather(*tasks_libraries, return_exceptions=True)
for watched in libraries:
if len(watched["Items"]) == 0: if len(watched["Items"]) == 0:
logger(f"Jellyfin: No watched items found in library {library_title}", 1)
continue continue
else:
library_id = watched["Identifiers"]["library_id"]
library_title = watched["Identifiers"]["library_title"]
library_type = watched["Items"][0]["Type"] library_type = watched["Items"][0]["Type"]
skip_reason = check_skip_logic(library_title, library_type, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping) skip_reason = check_skip_logic(library_title, library_type, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping)
@ -149,10 +193,28 @@ class Jellyfin():
logger(f"Jellyfin: Skipping library {library_title} {skip_reason}", 1) logger(f"Jellyfin: Skipping library {library_title} {skip_reason}", 1)
continue continue
args.append([self.get_user_watched, user_name, user_id, library_type, library_id, library_title]) # Get watched for user
task = asyncio.ensure_future(self.get_user_watched(user_name, user_id, library_type, library_id, library_title))
tasks_watched.append(task)
for user_watched in future_thread_executor(args): watched = await asyncio.gather(*tasks_watched, return_exceptions=True)
for user, user_watched_temp in user_watched.items(): return watched
except Exception as e:
logger(f"Jellyfin: Failed to get users watched, Error: {e}", 2)
raise Exception(e)
async def get_watched(self, users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping=None):
try:
users_watched = {}
watched = []
for user_name, user_id in users.items():
watched.append(await self.get_users_watched(user_name, user_id, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping))
for user_watched in watched:
user_watched_temp = combine_watched_dicts(user_watched)
for user, user_watched_temp in user_watched_temp.items():
if user not in users_watched: if user not in users_watched:
users_watched[user] = {} users_watched[user] = {}
users_watched[user].update(user_watched_temp) users_watched[user].update(user_watched_temp)
@ -162,15 +224,16 @@ class Jellyfin():
logger(f"Jellyfin: Failed to get watched, Error: {e}", 2) logger(f"Jellyfin: Failed to get watched, Error: {e}", 2)
raise Exception(e) raise Exception(e)
def update_user_watched(self, user_name, user_id, library, library_id, videos, dryrun):
async def update_user_watched(self, user_name, user_id, library, library_id, videos, dryrun):
try: try:
logger(f"Jellyfin: Updating watched for {user_name} in library {library}", 1) logger(f"Jellyfin: Updating watched for {user_name} in library {library}", 1)
videos_shows_ids, videos_episodes_ids, videos_movies_ids = generate_library_guids_dict(videos) videos_shows_ids, videos_episodes_ids, videos_movies_ids = generate_library_guids_dict(videos)
logger(f"Jellyfin: mark list\nShows: {videos_shows_ids}\nEpisodes: {videos_episodes_ids}\nMovies: {videos_movies_ids}", 1) logger(f"Jellyfin: mark list\nShows: {videos_shows_ids}\nEpisodes: {videos_episodes_ids}\nMovies: {videos_movies_ids}", 1)
async with aiohttp.ClientSession() as session:
if videos_movies_ids: if videos_movies_ids:
jellyfin_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds,MediaSources", "get") jellyfin_search = await self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds,MediaSources", "get", session)
for jellyfin_video in jellyfin_search["Items"]: for jellyfin_video in jellyfin_search["Items"]:
movie_found = False movie_found = False
@ -192,7 +255,7 @@ class Jellyfin():
msg = f"{jellyfin_video['Name']} as watched for {user_name} in {library} for Jellyfin" msg = f"{jellyfin_video['Name']} as watched for {user_name} in {library} for Jellyfin"
if not dryrun: if not dryrun:
logger(f"Marking {msg}", 0) logger(f"Marking {msg}", 0)
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_video_id}", "post") await self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_video_id}", "post", session)
else: else:
logger(f"Dryrun {msg}", 0) logger(f"Dryrun {msg}", 0)
else: else:
@ -202,7 +265,7 @@ class Jellyfin():
# TV Shows # TV Shows
if videos_shows_ids and videos_episodes_ids: if videos_shows_ids and videos_episodes_ids:
jellyfin_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds,Path", "get") jellyfin_search = await self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds,Path", "get", session)
jellyfin_shows = [x for x in jellyfin_search["Items"]] jellyfin_shows = [x for x in jellyfin_search["Items"]]
for jellyfin_show in jellyfin_shows: for jellyfin_show in jellyfin_shows:
@ -222,7 +285,7 @@ class Jellyfin():
if show_found: if show_found:
logger(f"Jellyfin: Updating watched for {user_name} in library {library} for show {jellyfin_show['Name']}", 1) logger(f"Jellyfin: Updating watched for {user_name} in library {library} for show {jellyfin_show['Name']}", 1)
jellyfin_show_id = jellyfin_show["Id"] jellyfin_show_id = jellyfin_show["Id"]
jellyfin_episodes = self.query(f"/Shows/{jellyfin_show_id}/Episodes?userId={user_id}&Fields=ItemCounts,ProviderIds,MediaSources", "get") jellyfin_episodes = await self.query(f"/Shows/{jellyfin_show_id}/Episodes?userId={user_id}&Fields=ItemCounts,ProviderIds,MediaSources", "get", session)
for jellyfin_episode in jellyfin_episodes["Items"]: for jellyfin_episode in jellyfin_episodes["Items"]:
episode_found = False episode_found = False
@ -245,7 +308,7 @@ class Jellyfin():
msg = f"{jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode['Name']} as watched for {user_name} in {library} for Jellyfin" msg = f"{jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode['Name']} as watched for {user_name} in {library} for Jellyfin"
if not dryrun: if not dryrun:
logger(f"Marked {msg}", 0) logger(f"Marked {msg}", 0)
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_episode_id}", "post") await self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_episode_id}", "post", session)
else: else:
logger(f"Dryrun {msg}", 0) logger(f"Dryrun {msg}", 0)
else: else:
@ -261,9 +324,10 @@ class Jellyfin():
raise Exception(e) raise Exception(e)
def update_watched(self, watched_list, user_mapping=None, library_mapping=None, dryrun=False): async def update_watched(self, watched_list, user_mapping=None, library_mapping=None, dryrun=False):
try: try:
args = [] tasks = []
async with aiohttp.ClientSession() as session:
for user, libraries in watched_list.items(): for user, libraries in watched_list.items():
logger(f"Jellyfin: Updating for entry {user}, {libraries}", 1) logger(f"Jellyfin: Updating for entry {user}, {libraries}", 1)
user_other = None user_other = None
@ -289,7 +353,8 @@ class Jellyfin():
logger(f"{user} {user_other} not found in Jellyfin", 2) logger(f"{user} {user_other} not found in Jellyfin", 2)
continue continue
jellyfin_libraries = self.query(f"/Users/{user_id}/Views", "get")["Items"] jellyfin_libraries = await self.query(f"/Users/{user_id}/Views", "get", session)
jellyfin_libraries = [x for x in jellyfin_libraries["Items"]]
for library, videos in libraries.items(): for library, videos in libraries.items():
library_other = None library_other = None
@ -319,9 +384,10 @@ class Jellyfin():
continue continue
if library_id: if library_id:
args.append([self.update_user_watched, user_name, user_id, library, library_id, videos, dryrun]) task = self.update_user_watched(user_name, user_id, library, library_id, videos, dryrun)
tasks.append(task)
future_thread_executor(args) await asyncio.gather(*tasks, return_exceptions=True)
except Exception as e: except Exception as e:
logger(f"Jellyfin: Error updating watched", 2) logger(f"Jellyfin: Error updating watched", 2)
raise Exception(e) raise Exception(e)

View File

@ -1,6 +1,6 @@
import copy, os, traceback, json import copy, os, traceback, json, asyncio
from dotenv import load_dotenv from dotenv import load_dotenv
from time import sleep from time import sleep, perf_counter
from src.functions import logger, str_to_bool, search_mapping, generate_library_guids_dict, future_thread_executor from src.functions import logger, str_to_bool, search_mapping, generate_library_guids_dict, future_thread_executor
from src.plex import Plex from src.plex import Plex
@ -38,13 +38,15 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
logger(f"library {library_1} and {library_other} not found in watched list 2", 1) logger(f"library {library_1} and {library_other} not found in watched list 2", 1)
continue continue
_, episode_watched_list_2_keys_dict, movies_watched_list_2_keys_dict = generate_library_guids_dict(watched_list_2[user_2][library_2])
# Movies # Movies
if isinstance(watched_list_1[user_1][library_1], list): if isinstance(watched_list_1[user_1][library_1], list):
_, _, movies_watched_list_2_keys_dict = generate_library_guids_dict(watched_list_2[user_2][library_2])
for movie in watched_list_1[user_1][library_1]: for movie in watched_list_1[user_1][library_1]:
movie_found = False movie_found = False
for movie_key, movie_value in movie.items(): for movie_key, movie_value in movie.items():
if movie_key == "locations": if movie_key == "locations":
if "locations" in movies_watched_list_2_keys_dict.keys():
for location in movie_value: for location in movie_value:
if location in movies_watched_list_2_keys_dict["locations"]: if location in movies_watched_list_2_keys_dict["locations"]:
movie_found = True movie_found = True
@ -63,7 +65,6 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
# TV Shows # TV Shows
elif isinstance(watched_list_1[user_1][library_1], dict): elif isinstance(watched_list_1[user_1][library_1], dict):
# Generate full list of provider ids for episodes in watch_list_2 to easily compare if they exist in watch_list_1 # Generate full list of provider ids for episodes in watch_list_2 to easily compare if they exist in watch_list_1
_, episode_watched_list_2_keys_dict, _ = generate_library_guids_dict(watched_list_2[user_2][library_2])
for show_key_1 in watched_list_1[user_1][library_1].keys(): for show_key_1 in watched_list_1[user_1][library_1].keys():
show_key_dict = dict(show_key_1) show_key_dict = dict(show_key_1)
@ -73,6 +74,7 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
for episode_key, episode_value in episode.items(): for episode_key, episode_value in episode.items():
# If episode_key and episode_value are in episode_watched_list_2_keys_dict exactly, then remove from watch_list_1 # If episode_key and episode_value are in episode_watched_list_2_keys_dict exactly, then remove from watch_list_1
if episode_key == "locations": if episode_key == "locations":
if "locations" in episode_watched_list_2_keys_dict.keys():
for location in episode_value: for location in episode_value:
if location in episode_watched_list_2_keys_dict["locations"]: if location in episode_watched_list_2_keys_dict["locations"]:
episode_found = True episode_found = True
@ -209,6 +211,8 @@ def setup_users(server_1, server_2, blacklist_users, whitelist_users, user_mappi
server_1_connection = server_1[1] server_1_connection = server_1[1]
server_2_type = server_2[0] server_2_type = server_2[0]
server_2_connection = server_2[1] server_2_connection = server_2[1]
print(f"Server 1: {server_1_type} {server_1_connection}")
print(f"Server 2: {server_2_type} {server_2_connection}")
server_1_users = [] server_1_users = []
if server_1_type == "plex": if server_1_type == "plex":
@ -302,6 +306,7 @@ def generate_server_connections():
plex_username = os.getenv("PLEX_USERNAME", None) plex_username = os.getenv("PLEX_USERNAME", None)
plex_password = os.getenv("PLEX_PASSWORD", None) plex_password = os.getenv("PLEX_PASSWORD", None)
plex_servername = os.getenv("PLEX_SERVERNAME", None) plex_servername = os.getenv("PLEX_SERVERNAME", None)
ssl_bypass = str_to_bool(os.getenv("SSL_BYPASS", "False"))
if plex_baseurl and plex_token: if plex_baseurl and plex_token:
plex_baseurl = plex_baseurl.split(",") plex_baseurl = plex_baseurl.split(",")
@ -311,7 +316,7 @@ def generate_server_connections():
raise Exception("PLEX_BASEURL and PLEX_TOKEN must have the same number of entries") raise Exception("PLEX_BASEURL and PLEX_TOKEN must have the same number of entries")
for i, url in enumerate(plex_baseurl): for i, url in enumerate(plex_baseurl):
servers.append(("plex", Plex(baseurl=url.strip(), token=plex_token[i].strip(), username=None, password=None, servername=None))) servers.append(("plex", Plex(baseurl=url.strip(), token=plex_token[i].strip(), username=None, password=None, servername=None, ssl_bypass=ssl_bypass)))
if plex_username and plex_password and plex_servername: if plex_username and plex_password and plex_servername:
plex_username = plex_username.split(",") plex_username = plex_username.split(",")
@ -322,7 +327,7 @@ def generate_server_connections():
raise Exception("PLEX_USERNAME, PLEX_PASSWORD and PLEX_SERVERNAME must have the same number of entries") raise Exception("PLEX_USERNAME, PLEX_PASSWORD and PLEX_SERVERNAME must have the same number of entries")
for i, username in enumerate(plex_username): for i, username in enumerate(plex_username):
servers.append(("plex", Plex(baseurl=None, token=None, username=username.strip(), password=plex_password[i].strip(), servername=plex_servername[i].strip()))) servers.append(("plex", Plex(baseurl=None, token=None, username=username.strip(), password=plex_password[i].strip(), servername=plex_servername[i].strip(), ssl_bypass=ssl_bypass)))
jellyfin_baseurl = os.getenv("JELLYFIN_BASEURL", None) jellyfin_baseurl = os.getenv("JELLYFIN_BASEURL", None)
jellyfin_token = os.getenv("JELLYFIN_TOKEN", None) jellyfin_token = os.getenv("JELLYFIN_TOKEN", None)
@ -339,11 +344,12 @@ def generate_server_connections():
return servers return servers
def main_loop(): def main_loop():
logfile = os.getenv("LOGFILE","log.log") logfile = os.getenv("LOGFILE","log.log")
# Delete logfile if it exists # Delete logfile if it exists
if os.path.exists(logfile): #if os.path.exists(logfile):
os.remove(logfile) # os.remove(logfile)
dryrun = str_to_bool(os.getenv("DRYRUN", "False")) dryrun = str_to_bool(os.getenv("DRYRUN", "False"))
logger(f"Dryrun: {dryrun}", 1) logger(f"Dryrun: {dryrun}", 1)
@ -389,12 +395,10 @@ def main_loop():
server_1_users, server_2_users = setup_users(server_1, server_2, blacklist_users, whitelist_users, user_mapping) server_1_users, server_2_users = setup_users(server_1, server_2, blacklist_users, whitelist_users, user_mapping)
logger("Creating watched lists", 1) logger("Creating watched lists", 1)
args = [[server_1_connection.get_watched, server_1_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping] server_1_watched = server_1_connection.get_watched(server_1_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping)
, [server_2_connection.get_watched, server_2_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping]] logger("Finished creating watched list server 1", 0)
server_2_watched = asyncio.run(server_2_connection.get_watched(server_2_users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping))
results = future_thread_executor(args) logger("Finished creating watched list server 2", 0)
server_1_watched = results[0]
server_2_watched = results[1]
logger(f"Server 1 watched: {server_1_watched}", 3) logger(f"Server 1 watched: {server_1_watched}", 3)
logger(f"Server 2 watched: {server_2_watched}", 3) logger(f"Server 2 watched: {server_2_watched}", 3)
@ -411,19 +415,22 @@ def main_loop():
logger(f"server 1 watched that needs to be synced to server 2:\n{server_1_watched_filtered}", 1) logger(f"server 1 watched that needs to be synced to server 2:\n{server_1_watched_filtered}", 1)
logger(f"server 2 watched that needs to be synced to server 1:\n{server_2_watched_filtered}", 1) logger(f"server 2 watched that needs to be synced to server 1:\n{server_2_watched_filtered}", 1)
args= [[server_1_connection.update_watched, server_2_watched_filtered, user_mapping, library_mapping, dryrun] server_1_connection.update_watched(server_2_watched_filtered, user_mapping, library_mapping, dryrun)
, [server_2_connection.update_watched, server_1_watched_filtered, user_mapping, library_mapping, dryrun]] asyncio.run(server_2_connection.update_watched(server_1_watched_filtered, user_mapping, library_mapping, dryrun))
future_thread_executor(args)
def main(): def main():
sleep_duration = float(os.getenv("SLEEP_DURATION", "3600")) sleep_duration = float(os.getenv("SLEEP_DURATION", "3600"))
times = []
while(True): while(True):
try: try:
start = perf_counter()
main_loop() main_loop()
end = perf_counter()
times.append(end - start)
logger(f"Looping in {sleep_duration}") logger(f"Looping in {sleep_duration}")
sleep(sleep_duration) sleep(sleep_duration)
except Exception as error: except Exception as error:
if isinstance(error, list): if isinstance(error, list):
for message in error: for message in error:
@ -437,5 +444,7 @@ def main():
sleep(sleep_duration) sleep(sleep_duration)
except KeyboardInterrupt: except KeyboardInterrupt:
if len(times) > 0:
logger(f"Average time: {sum(times) / len(times)}", 0)
logger("Exiting", log_type=0) logger("Exiting", log_type=0)
os._exit(0) os._exit(0)

View File

@ -14,14 +14,19 @@ class Plex:
self.username = username self.username = username
self.password = password self.password = password
self.servername = servername self.servername = servername
self.plex = self.login() self.plex = self.login(ssl_bypass)
self.admin_user = self.plex.myPlexAccount() self.admin_user = self.plex.myPlexAccount()
self.users = self.get_users() self.users = self.get_users()
def login(self): def login(self, ssl_bypass=False):
try: try:
if self.baseurl and self.token: if self.baseurl and self.token:
# Login via token # Login via token
if ssl_bypass:
session = requests.Session()
session.verify = False
plex = PlexServer(self.baseurl, self.token, session=session)
else:
plex = PlexServer(self.baseurl, self.token) plex = PlexServer(self.baseurl, self.token)
elif self.username and self.password and self.servername: elif self.username and self.password and self.servername:
# Login via plex account # Login via plex account
@ -52,6 +57,7 @@ class Plex:
logger(f"Plex: Failed to get users, Error: {e}", 2) logger(f"Plex: Failed to get users, Error: {e}", 2)
raise Exception(e) raise Exception(e)
def get_user_watched(self, user, user_plex, library): def get_user_watched(self, user, user_plex, library):
try: try:
user_name = user.title.lower() user_name = user.title.lower()