Jellyfin: Add generate_guids/locations. Cleanup

Signed-off-by: Luigi311 <git@luigi311.com>
This commit is contained in:
Luigi311
2024-01-16 02:53:36 -07:00
parent 84b98db36b
commit cc706938ce

View File

@@ -8,6 +8,7 @@ from src.functions import (
search_mapping, search_mapping,
contains_nested, contains_nested,
log_marked, log_marked,
str_to_bool,
) )
from src.library import ( from src.library import (
check_skip_logic, check_skip_logic,
@@ -19,60 +20,78 @@ from src.watched import (
load_dotenv(override=True) load_dotenv(override=True)
generate_guids = str_to_bool(os.getenv("GENERATE_GUIDS", "True"))
generate_locations = str_to_bool(os.getenv("GENERATE_LOCATIONS", "True"))
def get_movie_guids(movie):
if "ProviderIds" in movie: def get_guids(item):
logger( guids = {"title": item["Name"]}
f"Jellyfin: {movie.get('Name')} {movie['ProviderIds']} {movie['MediaSources']}",
3, if "ProviderIds" in item:
guids.update({k.lower(): v for k, v in item["ProviderIds"].items()})
if "MediaSources" in item:
guids["locations"] = tuple(
[x["Path"].split("/")[-1] for x in item["MediaSources"] if "Path" in x]
) )
else: else:
logger( guids["locations"] = tuple()
f"Jellyfin: {movie.get('Name')} {movie['MediaSources']['Path']}",
3,
)
# Create a dictionary for the movie with its title guids["status"] = {
movie_guids = {"title": movie["Name"]} "completed": item["UserData"]["Played"],
# If the movie has provider IDs, add them to the dictionary
if "ProviderIds" in movie:
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(
[x["Path"].split("/")[-1] for x in movie["MediaSources"]]
)
else:
movie_guids["locations"] = tuple()
movie_guids["status"] = {
"completed": movie["UserData"]["Played"],
# Convert ticks to milliseconds to match Plex # Convert ticks to milliseconds to match Plex
"time": floor(movie["UserData"]["PlaybackPositionTicks"] / 10000), "time": floor(item["UserData"]["PlaybackPositionTicks"] / 10000),
} }
return movie_guids return guids
def get_episode_guids(episode): def get_video_status(jellyfin_video, videos_ids, videos):
# Create a dictionary for the episode with its provider IDs and media sources video_status = None
episode_dict = {k.lower(): v for k, v in episode["ProviderIds"].items()}
episode_dict["title"] = episode["Name"]
episode_dict["locations"] = tuple() if generate_locations:
if "MediaSources" in episode: if "MediaSources" in jellyfin_video:
for x in episode["MediaSources"]: for video_location in jellyfin_video["MediaSources"]:
if "Path" in x: if "Path" in video_location:
episode_dict["locations"] += (x["Path"].split("/")[-1],) if (
contains_nested(
video_location["Path"].split("/")[-1],
videos_ids["locations"],
)
is not None
):
for video in videos:
if (
contains_nested(
video_location["Path"].split("/")[-1],
video["locations"],
)
is not None
):
video_status = video["status"]
break
break
episode_dict["status"] = { if generate_guids:
"completed": episode["UserData"]["Played"], if not video_status:
"time": floor(episode["UserData"]["PlaybackPositionTicks"] / 10000), for (
} video_provider_source,
video_provider_id,
) in jellyfin_video["ProviderIds"].items():
if video_provider_source.lower() in videos_ids:
if (
video_provider_id.lower()
in videos_ids[video_provider_source.lower()]
):
for video in videos:
if video_provider_id.lower() in video.get(
video_provider_source.lower(), []
):
video_status = video["status"]
break
break
return episode_dict return video_status
class Jellyfin: class Jellyfin:
@@ -200,43 +219,27 @@ class Jellyfin:
"get", "get",
) )
for movie in watched["Items"]: for movie in watched["Items"] + in_progress["Items"]:
if "MediaSources" in movie and movie["MediaSources"] != {}: if "MediaSources" in movie and movie["MediaSources"] != {}:
logger( # Skip if not watched or watched less than a minute
f"Jellyfin: Adding {movie.get('Name')} to {user_name} watched list", if (
3, movie["UserData"]["Played"] == True
) or movie["UserData"]["PlaybackPositionTicks"] > 600000000
):
logger(
f"Jellyfin: Adding {movie.get('Name')} to {user_name} watched list",
3,
)
# Get the movie's GUIDs # Get the movie's GUIDs
movie_guids = get_movie_guids(movie) movie_guids = get_guids(movie)
# Append the movie dictionary to the list for the given user and library # Append the movie dictionary to the list for the given user and library
user_watched[user_name][library_title].append(movie_guids) user_watched[user_name][library_title].append(movie_guids)
logger( logger(
f"Jellyfin: Added {movie_guids} to {user_name} watched list", f"Jellyfin: Added {movie_guids} to {user_name} watched list",
3, 3,
) )
# Get all partially watched movies greater than 1 minute
for movie in in_progress["Items"]:
if "MediaSources" in movie and movie["MediaSources"] != {}:
if movie["UserData"]["PlaybackPositionTicks"] < 600000000:
continue
logger(
f"Jellyfin: Adding {movie.get('Name')} to {user_name} watched list",
3,
)
# Get the movie's GUIDs
movie_guids = get_movie_guids(movie)
# Append the movie dictionary to the list for the given user and library
user_watched[user_name][library_title].append(movie_guids)
logger(
f"Jellyfin: Added {movie_guids} to {user_name} watched list",
3,
)
# TV Shows # TV Shows
if library_type in ["Series", "Episode"]: if library_type in ["Series", "Episode"]:
@@ -257,7 +260,7 @@ class Jellyfin:
if show["UserData"]["PlayedPercentage"] > 0: if show["UserData"]["PlayedPercentage"] > 0:
watched_shows_filtered.append(show) watched_shows_filtered.append(show)
# Create a list of tasks to retrieve the seasons of each watched show # Retrieve the seasons of each watched show
seasons_watched = [] seasons_watched = []
for show in watched_shows_filtered: for show in watched_shows_filtered:
logger( logger(
@@ -277,13 +280,14 @@ class Jellyfin:
"show_id": show["Id"], "show_id": show["Id"],
} }
season_task = self.query( seasons_watched.append(
f"/Shows/{show['Id']}/Seasons" self.query(
+ f"?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,RecursiveItemCount", f"/Shows/{show['Id']}/Seasons"
"get", + f"?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,RecursiveItemCount",
identifiers=frozenset(show_identifiers.items()), "get",
identifiers=frozenset(show_identifiers.items()),
)
) )
seasons_watched.append(season_task)
# Filter the list of seasons to only include those that have been partially or fully watched # Filter the list of seasons to only include those that have been partially or fully watched
seasons_watched_filtered = [] seasons_watched_filtered = []
@@ -343,7 +347,7 @@ class Jellyfin:
or episode["UserData"]["PlaybackPositionTicks"] or episode["UserData"]["PlaybackPositionTicks"]
> 600000000 > 600000000
): ):
episode_dict = get_episode_guids(episode) episode_dict = get_guids(episode)
# Add the episode dictionary to the season's list of episodes # Add the episode dictionary to the season's list of episodes
season_dict["Episodes"].append(episode_dict) season_dict["Episodes"].append(episode_dict)
@@ -405,7 +409,7 @@ class Jellyfin:
try: try:
# Get all libraries # Get all libraries
user_name = user_name.lower() user_name = user_name.lower()
tasks_watched = [] watched = []
libraries = [] libraries = []
@@ -417,25 +421,26 @@ class Jellyfin:
"library_id": library_id, "library_id": library_id,
"library_title": library_title, "library_title": library_title,
} }
task = self.query( libraries.append(
f"/Users/{user_id}/Items" self.query(
+ f"?ParentId={library_id}&Filters=IsPlayed&Recursive=True&excludeItemTypes=Folder&limit=100", f"/Users/{user_id}/Items"
"get", + f"?ParentId={library_id}&Filters=IsPlayed&Recursive=True&excludeItemTypes=Folder&limit=100",
identifiers=identifiers, "get",
identifiers=identifiers,
)
) )
libraries.append(task)
for watched in libraries: for library in libraries:
if len(watched["Items"]) == 0: if len(library["Items"]) == 0:
continue continue
library_id = watched["Identifiers"]["library_id"] library_id = library["Identifiers"]["library_id"]
library_title = watched["Identifiers"]["library_title"] library_title = library["Identifiers"]["library_title"]
# Get all library types excluding "Folder" # Get all library types excluding "Folder"
types = set( types = set(
[ [
x["Type"] x["Type"]
for x in watched["Items"] for x in library["Items"]
if x["Type"] in ["Movie", "Series", "Episode"] if x["Type"] in ["Movie", "Series", "Episode"]
] ]
) )
@@ -459,7 +464,7 @@ class Jellyfin:
# If there are multiple types in library raise error # If there are multiple types in library raise error
if types is None or len(types) < 1: if types is None or len(types) < 1:
all_types = set([x["Type"] for x in watched["Items"]]) all_types = set([x["Type"] for x in library["Items"]])
logger( logger(
f"Jellyfin: Skipping Library {library_title} found types: {types}, all types: {all_types}", f"Jellyfin: Skipping Library {library_title} found types: {types}, all types: {all_types}",
1, 1,
@@ -468,16 +473,15 @@ class Jellyfin:
for library_type in types: for library_type in types:
# Get watched for user # Get watched for user
task = self.get_user_library_watched( watched.append(
user_name, self.get_user_library_watched(
user_id, user_name,
library_type, user_id,
library_id, library_type,
library_title, library_id,
library_title,
)
) )
tasks_watched.append(task)
watched = tasks_watched
return watched return watched
except Exception as e: except Exception as e:
@@ -535,6 +539,18 @@ class Jellyfin:
videos_movies_ids, videos_movies_ids,
) = generate_library_guids_dict(videos) ) = generate_library_guids_dict(videos)
if (
not videos_movies_ids
and not videos_shows_ids
and not videos_episodes_ids
):
logger(
f"Jellyfin: No videos to mark as watched for {user_name} in library {library}",
1,
)
return
logger( logger(
f"Jellyfin: mark list\nShows: {videos_shows_ids}\nEpisodes: {videos_episodes_ids}\nMovies: {videos_movies_ids}", f"Jellyfin: mark list\nShows: {videos_shows_ids}\nEpisodes: {videos_episodes_ids}\nMovies: {videos_movies_ids}",
1, 1,
@@ -548,47 +564,9 @@ class Jellyfin:
"get", "get",
) )
for jellyfin_video in jellyfin_search["Items"]: for jellyfin_video in jellyfin_search["Items"]:
movie_status = None movie_status = get_video_status(
jellyfin_video, videos_movies_ids, videos
if "MediaSources" in jellyfin_video: )
for movie_location in jellyfin_video["MediaSources"]:
if "Path" in movie_location:
if (
contains_nested(
movie_location["Path"].split("/")[-1],
videos_movies_ids["locations"],
)
is not None
):
for video in videos:
if (
contains_nested(
movie_location["Path"].split("/")[-1],
video["locations"],
)
is not None
):
movie_status = video["status"]
break
break
if not movie_status:
for (
movie_provider_source,
movie_provider_id,
) in jellyfin_video["ProviderIds"].items():
if movie_provider_source.lower() in videos_movies_ids:
if (
movie_provider_id.lower()
in videos_movies_ids[movie_provider_source.lower()]
):
for video in videos:
if movie_provider_id.lower() in video.get(
movie_provider_source.lower(), []
):
movie_status = video["status"]
break
break
if movie_status: if movie_status:
jellyfin_video_id = jellyfin_video["Id"] jellyfin_video_id = jellyfin_video["Id"]
@@ -643,50 +621,56 @@ class Jellyfin:
for jellyfin_show in jellyfin_shows: for jellyfin_show in jellyfin_shows:
show_found = False show_found = False
episode_videos = []
if "Path" in jellyfin_show: if generate_locations:
if ( if "Path" in jellyfin_show:
contains_nested( if (
jellyfin_show["Path"].split("/")[-1], contains_nested(
videos_shows_ids["locations"], jellyfin_show["Path"].split("/")[-1],
) videos_shows_ids["locations"],
is not None )
): is not None
show_found = True ):
episode_videos = [] show_found = True
for shows, seasons in videos.items():
show = {k: v for k, v in shows}
if (
contains_nested(
jellyfin_show["Path"].split("/")[-1],
show["locations"],
)
is not None
):
for season in seasons.values():
for episode in season:
episode_videos.append(episode)
for show, seasons in videos.items(): break
show = {k: v for k, v in show}
if (
contains_nested(
jellyfin_show["Path"].split("/")[-1],
show["locations"],
)
is not None
):
for season in seasons.values():
for episode in season:
episode_videos.append(episode)
if not show_found: if generate_guids:
for show_provider_source, show_provider_id in jellyfin_show[ if not show_found:
"ProviderIds" for show_provider_source, show_provider_id in jellyfin_show[
].items(): "ProviderIds"
if show_provider_source.lower() in videos_shows_ids: ].items():
if ( if show_provider_source.lower() in videos_shows_ids:
show_provider_id.lower() if (
in videos_shows_ids[show_provider_source.lower()] show_provider_id.lower()
): in videos_shows_ids[
show_found = True show_provider_source.lower()
episode_videos = [] ]
for show, seasons in videos.items(): ):
show = {k: v for k, v in show} show_found = True
if show_provider_id.lower() in show.get( for show, seasons in videos.items():
show_provider_source.lower(), [] show = {k: v for k, v in show}
): if show_provider_id.lower() in show.get(
for season in seasons.values(): show_provider_source.lower(), []
for episode in season: ):
episode_videos.append(episode) for season in seasons.values():
for episode in season:
episode_videos.append(episode)
break
if show_found: if show_found:
logger( logger(
@@ -701,65 +685,9 @@ class Jellyfin:
) )
for jellyfin_episode in jellyfin_episodes["Items"]: for jellyfin_episode in jellyfin_episodes["Items"]:
episode_status = None episode_status = get_video_status(
jellyfin_episode, videos_episodes_ids, episode_videos
if "MediaSources" in jellyfin_episode: )
for episode_location in jellyfin_episode[
"MediaSources"
]:
if "Path" in episode_location:
if (
contains_nested(
episode_location["Path"].split("/")[-1],
videos_episodes_ids["locations"],
)
is not None
):
for episode in episode_videos:
if (
contains_nested(
episode_location["Path"].split(
"/"
)[-1],
episode["locations"],
)
is not None
):
episode_status = episode["status"]
break
break
if not episode_status:
for (
episode_provider_source,
episode_provider_id,
) in jellyfin_episode["ProviderIds"].items():
if (
episode_provider_source.lower()
in videos_episodes_ids
):
if (
episode_provider_id.lower()
in videos_episodes_ids[
episode_provider_source.lower()
]
):
for episode in episode_videos:
if (
episode_provider_source.lower()
in episode
):
if (
episode_provider_id.lower()
in episode[
episode_provider_source.lower()
]
):
episode_status = episode[
"status"
]
break
break
if episode_status: if episode_status:
jellyfin_episode_id = jellyfin_episode["Id"] jellyfin_episode_id = jellyfin_episode["Id"]
@@ -815,16 +743,6 @@ class Jellyfin:
3, 3,
) )
if (
not videos_movies_ids
and not videos_shows_ids
and not videos_episodes_ids
):
logger(
f"Jellyfin: No videos to mark as watched for {user_name} in library {library}",
1,
)
except Exception as e: except Exception as e:
logger( logger(
f"Jellyfin: Error updating watched for {user_name} in library {library}, {e}", f"Jellyfin: Error updating watched for {user_name} in library {library}, {e}",
@@ -837,8 +755,6 @@ class Jellyfin:
self, watched_list, user_mapping=None, library_mapping=None, dryrun=False self, watched_list, user_mapping=None, library_mapping=None, dryrun=False
): ):
try: try:
tasks = []
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