Do not mark shows/movies that do not exist

pull/33/head
Luigi311 2022-12-19 01:35:16 -07:00
parent 370e9bac63
commit e8faf52b2b
4 changed files with 217 additions and 109 deletions

View File

@ -170,7 +170,9 @@ def combine_watched_dicts(dicts: list):
# If the subkey already exists in the combined dictionary,
# check if the values are different and raise an exception if they are
if combined_dict[key][subkey] != subvalue:
raise ValueError(f"Conflicting values for subkey '{subkey}' under key '{key}'")
raise ValueError(
f"Conflicting values for subkey '{subkey}' under key '{key}'"
)
else:
# If the subkey does not exist in the combined dictionary, add it
combined_dict[key][subkey] = subvalue
@ -218,22 +220,56 @@ def cleanup_watched(
for show_key_1 in watched_list_1[user_1][library_1].keys():
show_key_dict = dict(show_key_1)
for season in watched_list_1[user_1][library_1][show_key_1]:
for episode in watched_list_1[user_1][library_1][show_key_1][season]:
if is_episode_in_dict(episode, episode_watched_list_2_keys_dict):
if episode in modified_watched_list_1[user_1][library_1][show_key_1][season]:
logger(f"Removing {episode} from {show_key_dict['title']}", 3)
modified_watched_list_1[user_1][library_1][show_key_1][season].remove(episode)
for episode in watched_list_1[user_1][library_1][show_key_1][
season
]:
if is_episode_in_dict(
episode, episode_watched_list_2_keys_dict
):
if (
episode
in modified_watched_list_1[user_1][library_1][
show_key_1
][season]
):
logger(
f"Removing {episode} from {show_key_dict['title']}",
3,
)
modified_watched_list_1[user_1][library_1][
show_key_1
][season].remove(episode)
# Remove empty seasons
if len(modified_watched_list_1[user_1][library_1][show_key_1][season]) == 0:
if season in modified_watched_list_1[user_1][library_1][show_key_1]:
logger(f"Removing {season} from {show_key_dict['title']} because it is empty", 3)
del modified_watched_list_1[user_1][library_1][show_key_1][season]
if (
len(
modified_watched_list_1[user_1][library_1][show_key_1][
season
]
)
== 0
):
if (
season
in modified_watched_list_1[user_1][library_1][
show_key_1
]
):
logger(
f"Removing {season} from {show_key_dict['title']} because it is empty",
3,
)
del modified_watched_list_1[user_1][library_1][
show_key_1
][season]
# Remove empty shows
if len(modified_watched_list_1[user_1][library_1][show_key_1]) == 0:
if show_key_1 in modified_watched_list_1[user_1][library_1]:
logger(f"Removing {show_key_dict['title']} because it is empty", 3)
logger(
f"Removing {show_key_dict['title']} because it is empty",
3,
)
del modified_watched_list_1[user_1][library_1][show_key_1]
for user_1 in watched_list_1:
@ -280,7 +316,7 @@ def is_movie_in_dict(movie, movies_watched_list_2_keys_dict):
# If the movie_value is in the movies_watched_list_2_keys_dict dictionary, return True
if movie_value in movies_watched_list_2_keys_dict[movie_key]:
return True
# If the loop completes without finding a match, return False
return False
@ -306,6 +342,7 @@ def is_episode_in_dict(episode, episode_watched_list_2_keys_dict):
# If the loop completes without finding a match, return False
return False
def future_thread_executor(args: list, workers: int = -1):
futures_list = []
results = []

View File

@ -85,8 +85,9 @@ class Jellyfin:
f"Jellyfin: Generating watched for {user_name} in library {library_title}",
0,
)
# Movies
async with aiohttp.ClientSession() as session:
# Movies
if library_type == "Movie":
user_watched[user_name][library_title] = []
watched = await self.query(
@ -95,16 +96,27 @@ class Jellyfin:
"get",
session,
)
for movie in watched["Items"]:
if movie["UserData"]["Played"] is True:
movie_guids = {}
movie_guids["title"] = movie["Name"]
# Check if the movie has been played
if (
movie["UserData"]["Played"] is True
and "MediaSources" in movie
and movie["MediaSources"] is not {}
):
# Create a dictionary for the movie with its title
movie_guids = {"title": movie["Name"]}
# If the movie has provider IDs, add them to the dictionary
if "ProviderIds" in movie:
# Lowercase movie["ProviderIds"] keys
movie_guids = {
k.lower(): v
for k, v in movie["ProviderIds"].items()
}
movie_guids.update(
{
k.lower(): v
for k, v in movie["ProviderIds"].items()
}
)
# If the movie has media sources, add them to the dictionary
if "MediaSources" in movie:
movie_guids["locations"] = tuple(
[
@ -112,22 +124,31 @@ class Jellyfin:
for x in movie["MediaSources"]
]
)
# Append the movie dictionary to the list for the given user and library
user_watched[user_name][library_title].append(movie_guids)
# TV Shows
if library_type == "Series":
# Initialize an empty dictionary for the given user and library
user_watched[user_name][library_title] = {}
# Retrieve a list of watched TV shows
watched_shows = await self.query(
f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&isPlaceHolder=false&Fields=ProviderIds,Path,RecursiveItemCount",
"get",
session,
)
# Filter the list of shows to only include those that have been partially or fully watched
watched_shows_filtered = []
for show in watched_shows["Items"]:
if "PlayedPercentage" in show["UserData"]:
if show["UserData"]["PlayedPercentage"] > 0:
watched_shows_filtered.append(show)
# Create a list of tasks to retrieve the seasons of each watched show
seasons_tasks = []
for show in watched_shows_filtered:
show_guids = {
@ -136,21 +157,26 @@ class Jellyfin:
show_guids["title"] = show["Name"]
show_guids["locations"] = tuple([show["Path"].split("/")[-1]])
show_guids = frozenset(show_guids.items())
identifiers = {"show_guids": show_guids, "show_id": show["Id"]}
task = asyncio.ensure_future(
show_identifiers = {
"show_guids": show_guids,
"show_id": show["Id"],
}
season_task = asyncio.ensure_future(
self.query(
f"/Shows/{show['Id']}/Seasons"
+ f"?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,RecursiveItemCount",
"get",
session,
frozenset(identifiers.items()),
frozenset(show_identifiers.items()),
)
)
seasons_tasks.append(task)
seasons_tasks.append(season_task)
# Retrieve the seasons for each watched show
seasons_watched = await asyncio.gather(*seasons_tasks)
seasons_watched_filtered = []
# Filter the list of seasons to only include those that have been partially or fully watched
seasons_watched_filtered = []
for seasons in seasons_watched:
seasons_watched_filtered_dict = {}
seasons_watched_filtered_dict["Identifiers"] = seasons[
@ -169,6 +195,7 @@ class Jellyfin:
seasons_watched_filtered_dict
)
# Create a list of tasks to retrieve the episodes of each watched season
episodes_tasks = []
for seasons in seasons_watched_filtered:
if len(seasons["Items"]) > 0:
@ -176,7 +203,7 @@ class Jellyfin:
season_identifiers = dict(seasons["Identifiers"])
season_identifiers["season_id"] = season["Id"]
season_identifiers["season_name"] = season["Name"]
task = asyncio.ensure_future(
episode_task = asyncio.ensure_future(
self.query(
f"/Shows/{season_identifiers['show_id']}/Episodes"
+ f"?seasonId={season['Id']}&userId={user_id}&isPlaceHolder=false&isPlayed=true&Fields=ProviderIds,MediaSources",
@ -185,62 +212,67 @@ class Jellyfin:
frozenset(season_identifiers.items()),
)
)
episodes_tasks.append(task)
episodes_tasks.append(episode_task)
# Retrieve the episodes for each watched season
watched_episodes = await asyncio.gather(*episodes_tasks)
for episodes in watched_episodes:
if len(episodes["Items"]) > 0:
for episode in episodes["Items"]:
if episode["UserData"]["Played"] is True:
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
]
):
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
][episode_identifiers["season_name"]] = []
episode_guids = {}
if "ProviderIds" in episode:
episode_guids = {
k.lower(): v
for k, v in episode[
"ProviderIds"
].items()
}
if "MediaSources" in episode:
episode_guids["locations"] = tuple(
[
x["Path"].split("/")[-1]
for x in episode["MediaSources"]
]
)
user_watched[user_name][library_title][
show_guids
][episode_identifiers["season_name"]].append(
episode_guids
)
logger(f"Jellyfin: Got watched for {user_name} in library {library_title}")
# Iterate through the watched episodes
for episodes in watched_episodes:
# If the season has any watched episodes
if len(episodes["Items"]) > 0:
# Create a dictionary for the season with its identifier and episodes
season_dict = {}
season_dict["Identifiers"] = dict(episodes["Identifiers"])
season_dict["Episodes"] = []
for episode in episodes["Items"]:
if (
episode["UserData"]["Played"] is True
and "MediaSources" in episode
and episode["MediaSources"] is not {}
):
# Create a dictionary for the episode with its provider IDs and media sources
episode_dict = {
k.lower(): v
for k, v in episode["ProviderIds"].items()
}
episode_dict["title"] = episode["Name"]
episode_dict["locations"] = tuple(
[
x["Path"].split("/")[-1]
for x in episode["MediaSources"]
]
)
# Add the episode dictionary to the season's list of episodes
season_dict["Episodes"].append(episode_dict)
# Add the season dictionary to the show's list of seasons
if (
season_dict["Identifiers"]["show_guids"]
not in user_watched[user_name][library_title]
):
user_watched[user_name][library_title][
season_dict["Identifiers"]["show_guids"]
] = {}
if (
season_dict["Identifiers"]["season_name"]
not in user_watched[user_name][library_title][
season_dict["Identifiers"]["show_guids"]
]
):
user_watched[user_name][library_title][
season_dict["Identifiers"]["show_guids"]
][season_dict["Identifiers"]["season_name"]] = []
user_watched[user_name][library_title][
season_dict["Identifiers"]["show_guids"]
][season_dict["Identifiers"]["season_name"]] = season_dict[
"Episodes"
]
logger(
f"Jellyfin: Got watched for {user_name} in library {library_title}", 1
)
return user_watched
except Exception as e:
logger(

View File

@ -13,6 +13,7 @@ from src.jellyfin import Jellyfin
load_dotenv(override=True)
def setup_black_white_lists(
blacklist_library: str,
whitelist_library: str,
@ -310,9 +311,28 @@ def generate_server_connections():
return servers
def get_server_watched(server_connection: list, users: dict, blacklist_library: list, whitelist_library: list, blacklist_library_type: list, whitelist_library_type: list, library_mapping: dict):
def get_server_watched(
server_connection: list,
users: dict,
blacklist_library: list,
whitelist_library: list,
blacklist_library_type: list,
whitelist_library_type: list,
library_mapping: dict,
):
if server_connection[0] == "plex":
return server_connection[1].get_watched(
users,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
)
elif server_connection[0] == "jellyfin":
return asyncio.run(
server_connection[1].get_watched(
users,
blacklist_library,
whitelist_library,
@ -320,22 +340,27 @@ def get_server_watched(server_connection: list, users: dict, blacklist_library:
whitelist_library_type,
library_mapping,
)
elif server_connection[0] == "jellyfin":
return asyncio.run(server_connection[1].get_watched(
users,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
))
)
def update_server_watched(server_connection: list, server_watched_filtered: dict, user_mapping: dict, library_mapping: dict, dryrun: bool):
def update_server_watched(
server_connection: list,
server_watched_filtered: dict,
user_mapping: dict,
library_mapping: dict,
dryrun: bool,
):
if server_connection[0] == "plex":
server_connection[1].update_watched(server_watched_filtered, user_mapping, library_mapping, dryrun)
server_connection[1].update_watched(
server_watched_filtered, user_mapping, library_mapping, dryrun
)
elif server_connection[0] == "jellyfin":
asyncio.run(server_connection[1].update_watched(server_watched_filtered, user_mapping, library_mapping, dryrun))
asyncio.run(
server_connection[1].update_watched(
server_watched_filtered, user_mapping, library_mapping, dryrun
)
)
def main_loop():
logfile = os.getenv("LOGFILE", "log.log")
@ -448,11 +473,19 @@ def main_loop():
)
update_server_watched(
server_1, server_2_watched_filtered, user_mapping, library_mapping, dryrun
server_1,
server_2_watched_filtered,
user_mapping,
library_mapping,
dryrun,
)
update_server_watched(
server_2, server_1_watched_filtered, user_mapping, library_mapping, dryrun
server_2,
server_1_watched_filtered,
user_mapping,
library_mapping,
dryrun,
)

View File

@ -15,11 +15,14 @@ from src.functions import (
# Bypass hostname validation for ssl. Taken from https://github.com/pkkid/python-plexapi/issues/143#issuecomment-775485186
class HostNameIgnoringAdapter(requests.adapters.HTTPAdapter):
def init_poolmanager(self, connections, maxsize, block=..., **pool_kwargs):
self.poolmanager = PoolManager(num_pools=connections,
maxsize=maxsize,
block=block,
assert_hostname=False,
**pool_kwargs)
self.poolmanager = PoolManager(
num_pools=connections,
maxsize=maxsize,
block=block,
assert_hostname=False,
**pool_kwargs,
)
def get_user_library_watched(user, user_plex, library):
try:
@ -39,8 +42,9 @@ def get_user_library_watched(user, user_plex, library):
for video in library_videos.search(unwatched=False):
movie_guids = {}
for guid in video.guids:
guid_source = re.search(r"(.*)://", guid.id).group(1).lower()
guid_id = re.search(r"://(.*)", guid.id).group(1)
# Extract source and id from guid.id
m = re.match(r"(.*)://(.*)", guid.id)
guid_source, guid_id = m.group(1).lower(), m.group(2)
movie_guids[guid_source] = guid_id
movie_guids["title"] = video.title
@ -68,7 +72,7 @@ def get_user_library_watched(user, user_plex, library):
[x.split("/")[-1] for x in show.locations]
)
show_guids = frozenset(show_guids.items())
# Get all watched episodes for show
episode_guids = {}
for episode in show.watched():
@ -86,15 +90,15 @@ def get_user_library_watched(user, user_plex, library):
if episode.parentTitle not in episode_guids:
episode_guids[episode.parentTitle] = []
episode_guids[episode.parentTitle].append(episode_guids_temp)
if episode_guids:
# append show, season, episode
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] = episode_guids
logger(f"Plex: Got watched for {user_name} in library {library.title}", 0)
logger(f"Plex: Got watched for {user_name} in library {library.title}", 1)
return user_watched
except Exception as e:
logger(
@ -316,7 +320,9 @@ class Plex:
user_plex = self.plex
else:
user_plex = self.login(
self.plex._baseurl, user.get_token(self.plex.machineIdentifier), self.ssl_bypass
self.plex._baseurl,
user.get_token(self.plex.machineIdentifier),
self.ssl_bypass,
)
libraries = user_plex.library.sections()
@ -412,7 +418,7 @@ class Plex:
continue
else:
logger(
f"Plex: Library {library} not found in library list",
f"Plex: Library {library} not found in library list",
1,
)
continue