Use pydantic for structure
Complete redesign of everything using pydantic to create the watched structure. This brings in type checking support and simplifies a lot of things Signed-off-by: Luis Garcia <git@luigi311.com>
This commit is contained in:
626
src/plex.py
626
src/plex.py
@@ -1,6 +1,5 @@
|
||||
import os, requests, traceback
|
||||
import os, requests
|
||||
from dotenv import load_dotenv
|
||||
from typing import Dict, Union, FrozenSet
|
||||
|
||||
from urllib3.poolmanager import PoolManager
|
||||
from math import floor
|
||||
@@ -14,13 +13,18 @@ from plexapi.myplex import MyPlexAccount
|
||||
from src.functions import (
|
||||
logger,
|
||||
search_mapping,
|
||||
future_thread_executor,
|
||||
contains_nested,
|
||||
log_marked,
|
||||
str_to_bool,
|
||||
)
|
||||
from src.library import generate_library_guids_dict
|
||||
|
||||
from src.watched import (
|
||||
LibraryData,
|
||||
MediaIdentifiers,
|
||||
MediaItem,
|
||||
WatchedStatus,
|
||||
Series,
|
||||
UserData,
|
||||
check_same_identifiers,
|
||||
)
|
||||
|
||||
load_dotenv(override=True)
|
||||
|
||||
@@ -40,27 +44,23 @@ class HostNameIgnoringAdapter(RequestsHTTPAdapter):
|
||||
)
|
||||
|
||||
|
||||
def extract_guids_from_item(item: Union[Movie, Show, Episode]) -> Dict[str, str]:
|
||||
def extract_guids_from_item(item: Movie | Show | Episode) -> dict[str, str]:
|
||||
# If GENERATE_GUIDS is set to False, then return an empty dict
|
||||
if not generate_guids:
|
||||
return {}
|
||||
|
||||
guids: Dict[str, str] = dict(
|
||||
guids: dict[str, str] = dict(
|
||||
guid.id.split("://")
|
||||
for guid in item.guids
|
||||
if guid.id is not None and len(guid.id.strip()) > 0
|
||||
)
|
||||
|
||||
if len(guids) == 0:
|
||||
logger(
|
||||
f"Plex: Failed to get any guids for {item.title}",
|
||||
1,
|
||||
)
|
||||
|
||||
return guids
|
||||
|
||||
|
||||
def get_guids(item: Union[Movie, Episode], completed=True):
|
||||
def extract_identifiers_from_item(item: Movie | Show | Episode) -> MediaIdentifiers:
|
||||
guids = extract_guids_from_item(item)
|
||||
|
||||
if not item.locations:
|
||||
logger(
|
||||
f"Plex: {item.title} has no locations",
|
||||
@@ -73,356 +73,156 @@ def get_guids(item: Union[Movie, Episode], completed=True):
|
||||
1,
|
||||
)
|
||||
|
||||
return {
|
||||
"title": item.title,
|
||||
"locations": (
|
||||
return MediaIdentifiers(
|
||||
title=item.title,
|
||||
locations=(
|
||||
tuple([location.split("/")[-1] for location in item.locations])
|
||||
if generate_locations
|
||||
else tuple()
|
||||
),
|
||||
"status": {
|
||||
"completed": completed,
|
||||
"time": item.viewOffset,
|
||||
},
|
||||
} | extract_guids_from_item(
|
||||
item
|
||||
) # Merge the metadata and guid dictionaries
|
||||
imdb_id=guids.get("imdb", None),
|
||||
tvdb_id=guids.get("tvdb", None),
|
||||
tmdb_id=guids.get("tmdb", None),
|
||||
)
|
||||
|
||||
|
||||
def get_user_library_watched_show(show, process_episodes, threads=None):
|
||||
def get_mediaitem(item: Movie | Episode, completed=True) -> MediaItem:
|
||||
return MediaItem(
|
||||
identifiers=extract_identifiers_from_item(item),
|
||||
status=WatchedStatus(completed=completed, time=item.viewOffset),
|
||||
)
|
||||
|
||||
|
||||
def update_user_watched(
|
||||
user: MyPlexAccount,
|
||||
user_plex: PlexServer,
|
||||
library_data: LibraryData,
|
||||
library_name: str,
|
||||
dryrun: bool,
|
||||
):
|
||||
try:
|
||||
show_guids: FrozenSet = frozenset(
|
||||
(
|
||||
{
|
||||
"title": show.title,
|
||||
"locations": (
|
||||
tuple([location.split("/")[-1] for location in show.locations])
|
||||
if generate_locations
|
||||
else tuple()
|
||||
),
|
||||
}
|
||||
| extract_guids_from_item(show)
|
||||
).items() # Merge the metadata and guid dictionaries
|
||||
)
|
||||
|
||||
episode_guids_args = []
|
||||
|
||||
for episode in process_episodes:
|
||||
episode_guids_args.append([get_guids, episode, episode.isWatched])
|
||||
|
||||
episode_guids_results = future_thread_executor(
|
||||
episode_guids_args, threads=threads
|
||||
)
|
||||
|
||||
episode_guids = []
|
||||
for index, episode in enumerate(process_episodes):
|
||||
episode_guids.append(episode_guids_results[index])
|
||||
|
||||
return show_guids, episode_guids
|
||||
except Exception:
|
||||
return {}, {}
|
||||
|
||||
|
||||
def get_user_library_watched(user, user_plex, library):
|
||||
user_name: str = user.username.lower() if user.username else user.title.lower()
|
||||
try:
|
||||
logger(
|
||||
f"Plex: Generating watched for {user_name} in library {library.title}",
|
||||
0,
|
||||
)
|
||||
|
||||
library_videos = user_plex.library.section(library.title)
|
||||
|
||||
if library.type == "movie":
|
||||
watched = []
|
||||
|
||||
args = [
|
||||
[get_guids, video, video.isWatched]
|
||||
for video in library_videos.search(unwatched=False)
|
||||
+ library_videos.search(inProgress=True)
|
||||
if video.isWatched or video.viewOffset >= 60000
|
||||
]
|
||||
|
||||
for guid in future_thread_executor(args, threads=len(args)):
|
||||
logger(f"Plex: Adding {guid['title']} to {user_name} watched list", 3)
|
||||
watched.append(guid)
|
||||
elif library.type == "show":
|
||||
watched = {}
|
||||
|
||||
# Get all watched shows and partially watched shows
|
||||
parallel_show_task = []
|
||||
parallel_episodes_task = []
|
||||
|
||||
for show in library_videos.search(unwatched=False) + library_videos.search(
|
||||
inProgress=True
|
||||
):
|
||||
process_episodes = []
|
||||
for episode in show.episodes():
|
||||
if episode.isWatched or episode.viewOffset >= 60000:
|
||||
process_episodes.append(episode)
|
||||
|
||||
# Shows with more than 24 episodes has its episodes processed in parallel
|
||||
# Shows with less than 24 episodes has its episodes processed in serial but the shows are processed in parallel
|
||||
if len(process_episodes) >= 24:
|
||||
parallel_episodes_task.append(
|
||||
[
|
||||
get_user_library_watched_show,
|
||||
show,
|
||||
process_episodes,
|
||||
len(process_episodes),
|
||||
]
|
||||
)
|
||||
else:
|
||||
parallel_show_task.append(
|
||||
[get_user_library_watched_show, show, process_episodes, 1]
|
||||
)
|
||||
|
||||
for show_guids, episode_guids in future_thread_executor(
|
||||
parallel_show_task, threads=len(parallel_show_task)
|
||||
) + future_thread_executor(parallel_episodes_task, threads=1):
|
||||
if show_guids and episode_guids:
|
||||
watched[show_guids] = episode_guids
|
||||
logger(
|
||||
f"Plex: Added {episode_guids} to {user_name} watched list",
|
||||
3,
|
||||
)
|
||||
|
||||
else:
|
||||
watched = None
|
||||
|
||||
logger(f"Plex: Got watched for {user_name} in library {library.title}", 1)
|
||||
logger(f"Plex: {watched}", 3)
|
||||
|
||||
return {user_name: {library.title: watched} if watched is not None else {}}
|
||||
except Exception as e:
|
||||
logger(
|
||||
f"Plex: Failed to get watched for {user_name} in library {library.title}, Error: {e}",
|
||||
2,
|
||||
)
|
||||
return {}
|
||||
|
||||
|
||||
def find_video(plex_search, video_ids, videos=None):
|
||||
try:
|
||||
if not generate_guids and not generate_locations:
|
||||
return None
|
||||
|
||||
if generate_locations:
|
||||
for location in plex_search.locations:
|
||||
if (
|
||||
contains_nested(location.split("/")[-1], video_ids["locations"])
|
||||
is not None
|
||||
):
|
||||
episode_videos = []
|
||||
if videos:
|
||||
for show, episodes in videos.items():
|
||||
show = {k: v for k, v in show}
|
||||
if (
|
||||
contains_nested(
|
||||
location.split("/")[-1], show["locations"]
|
||||
)
|
||||
is not None
|
||||
):
|
||||
for episode in episodes:
|
||||
episode_videos.append(episode)
|
||||
|
||||
return episode_videos
|
||||
|
||||
if generate_guids:
|
||||
for guid in plex_search.guids:
|
||||
guid_source, guid_id = guid.id.split("://")
|
||||
|
||||
# If show provider source and show provider id are in videos_shows_ids exactly, then the show is in the list
|
||||
if guid_source in video_ids.keys():
|
||||
if guid_id in video_ids[guid_source]:
|
||||
episode_videos = []
|
||||
if videos:
|
||||
for show, episodes in videos.items():
|
||||
show = {k: v for k, v in show}
|
||||
if guid_source in show.keys():
|
||||
if guid_id == show[guid_source]:
|
||||
for episode in episodes:
|
||||
episode_videos.append(episode)
|
||||
|
||||
return episode_videos
|
||||
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_video_status(plex_search, video_ids, videos):
|
||||
try:
|
||||
if not generate_guids and not generate_locations:
|
||||
return None
|
||||
|
||||
if generate_locations:
|
||||
for location in plex_search.locations:
|
||||
if (
|
||||
contains_nested(location.split("/")[-1], video_ids["locations"])
|
||||
is not None
|
||||
):
|
||||
for video in videos:
|
||||
if (
|
||||
contains_nested(location.split("/")[-1], video["locations"])
|
||||
is not None
|
||||
):
|
||||
return video["status"]
|
||||
|
||||
if generate_guids:
|
||||
for guid in plex_search.guids:
|
||||
guid_source, guid_id = guid.id.split("://")
|
||||
|
||||
# If show provider source and show provider id are in videos_shows_ids exactly, then the show is in the list
|
||||
if guid_source in video_ids.keys():
|
||||
if guid_id in video_ids[guid_source]:
|
||||
for video in videos:
|
||||
if guid_source in video.keys():
|
||||
if guid_id == video[guid_source]:
|
||||
return video["status"]
|
||||
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def update_user_watched(user, user_plex, library, watched_videos, dryrun):
|
||||
try:
|
||||
logger(f"Plex: Updating watched for {user.title} in library {library}", 1)
|
||||
(
|
||||
watched_shows_ids,
|
||||
watched_episodes_ids,
|
||||
watched_movies_ids,
|
||||
) = generate_library_guids_dict(watched_videos)
|
||||
|
||||
if (
|
||||
not watched_movies_ids
|
||||
and not watched_shows_ids
|
||||
and not watched_episodes_ids
|
||||
):
|
||||
logger(
|
||||
f"Jellyfin: No videos to mark as watched for {user.title} in library {library}",
|
||||
1,
|
||||
)
|
||||
|
||||
# If there are no movies or shows to update, exit early.
|
||||
if not library_data.series and not library_data.movies:
|
||||
return
|
||||
|
||||
logger(
|
||||
f"Plex: mark list\nShows: {watched_shows_ids}\nEpisodes: {watched_episodes_ids}\nMovies: {watched_movies_ids}",
|
||||
1,
|
||||
)
|
||||
logger(f"Plex: Updating watched for {user.title} in library {library_name}", 1)
|
||||
library_section = user_plex.library.section(library_name)
|
||||
|
||||
library_videos = user_plex.library.section(library)
|
||||
if watched_movies_ids:
|
||||
for plex_movie in library_videos.search(unwatched=True):
|
||||
watched_movie_status = get_video_status(
|
||||
plex_movie, watched_movies_ids, watched_videos
|
||||
)
|
||||
if watched_movie_status:
|
||||
if watched_movie_status["completed"]:
|
||||
msg = f"Plex: {plex_movie.title} as watched for {user.title} in {library}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_movie.markWatched()
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library,
|
||||
plex_movie.title,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
elif watched_movie_status["time"] > 60_000:
|
||||
msg = f"Plex: {plex_movie.title} as partially watched for {floor(watched_movie_status['time'] / 60_000)} minutes for {user.title} in {library}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_movie.updateTimeline(watched_movie_status["time"])
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library,
|
||||
plex_movie.title,
|
||||
duration=watched_movie_status["time"],
|
||||
)
|
||||
else:
|
||||
logger(
|
||||
f"Plex: Skipping movie {plex_movie.title} as it is not in mark list for {user.title}",
|
||||
1,
|
||||
)
|
||||
|
||||
if watched_shows_ids and watched_episodes_ids:
|
||||
for plex_show in library_videos.search(unwatched=True):
|
||||
watched_show_episodes_status = find_video(
|
||||
plex_show, watched_shows_ids, watched_videos
|
||||
)
|
||||
if watched_show_episodes_status:
|
||||
for plex_episode in plex_show.episodes():
|
||||
watched_episode_status = get_video_status(
|
||||
plex_episode,
|
||||
watched_episodes_ids,
|
||||
watched_show_episodes_status,
|
||||
)
|
||||
if watched_episode_status:
|
||||
if watched_episode_status["completed"]:
|
||||
msg = f"Plex: {plex_show.title} {plex_episode.title} as watched for {user.title} in {library}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_episode.markWatched()
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library,
|
||||
plex_show.title,
|
||||
plex_episode.title,
|
||||
)
|
||||
# Update movies.
|
||||
if library_data.movies:
|
||||
# Search for Plex movies that are currently marked as unwatched.
|
||||
for plex_movie in library_section.search(unwatched=True):
|
||||
plex_identifiers = extract_identifiers_from_item(plex_movie)
|
||||
# Check each stored movie for a match.
|
||||
for stored_movie in library_data.movies:
|
||||
if check_same_identifiers(
|
||||
plex_identifiers, stored_movie.identifiers
|
||||
):
|
||||
# If the stored movie is marked as watched (or has enough progress),
|
||||
# update the Plex movie accordingly.
|
||||
if stored_movie.status.completed:
|
||||
msg = f"Plex: {plex_movie.title} as watched for {user.title} in {library_name}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_movie.markWatched()
|
||||
else:
|
||||
msg = f"Plex: {plex_show.title} {plex_episode.title} as partially watched for {floor(watched_episode_status['time'] / 60_000)} minutes for {user.title} in {library}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_episode.updateTimeline(
|
||||
watched_episode_status["time"]
|
||||
)
|
||||
else:
|
||||
logger(msg, 6)
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library,
|
||||
plex_show.title,
|
||||
plex_episode.title,
|
||||
watched_episode_status["time"],
|
||||
)
|
||||
else:
|
||||
logger(
|
||||
f"Plex: Skipping episode {plex_episode.title} as it is not in mark list for {user.title}",
|
||||
3,
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library_name,
|
||||
plex_movie.title,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
else:
|
||||
logger(
|
||||
f"Plex: Skipping show {plex_show.title} as it is not in mark list for {user.title}",
|
||||
3,
|
||||
)
|
||||
else:
|
||||
msg = f"Plex: {plex_movie.title} as partially watched for {floor(stored_movie.status.time / 60_000)} minutes for {user.title} in {library_name}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_movie.updateTimeline(stored_movie.status.time)
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library_name,
|
||||
plex_movie.title,
|
||||
duration=stored_movie.status.time,
|
||||
)
|
||||
# Once matched, no need to check further.
|
||||
break
|
||||
|
||||
# Update TV Shows (series/episodes).
|
||||
if library_data.series:
|
||||
# For each Plex show in the library section:
|
||||
plex_shows = library_section.search(unwatched=True)
|
||||
for plex_show in plex_shows:
|
||||
# Extract identifiers from the Plex show.
|
||||
plex_show_identifiers = extract_identifiers_from_item(plex_show)
|
||||
# Try to find a matching series in your stored library.
|
||||
for stored_series in library_data.series:
|
||||
if check_same_identifiers(
|
||||
plex_show_identifiers, stored_series.identifiers
|
||||
):
|
||||
logger(f"Found matching show for '{plex_show.title}'", 1)
|
||||
# Now update episodes.
|
||||
# Get the list of Plex episodes for this show.
|
||||
plex_episodes = plex_show.episodes()
|
||||
for plex_episode in plex_episodes:
|
||||
plex_episode_identifiers = extract_identifiers_from_item(
|
||||
plex_episode
|
||||
)
|
||||
for stored_ep in stored_series.episodes:
|
||||
if check_same_identifiers(
|
||||
plex_episode_identifiers, stored_ep.identifiers
|
||||
):
|
||||
if stored_ep.status.completed:
|
||||
msg = f"Plex: {plex_show.title} {plex_episode.title} as watched for {user.title} in {library_name}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_episode.markWatched()
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library_name,
|
||||
plex_show.title,
|
||||
plex_episode.title,
|
||||
)
|
||||
else:
|
||||
msg = f"Plex: {plex_show.title} {plex_episode.title} as partially watched for {floor(stored_ep.status.time / 60_000)} minutes for {user.title} in {library_name}"
|
||||
if not dryrun:
|
||||
logger(msg, 5)
|
||||
plex_episode.updateTimeline(
|
||||
stored_ep.status.time
|
||||
)
|
||||
else:
|
||||
logger(msg, 6)
|
||||
|
||||
log_marked(
|
||||
"Plex",
|
||||
user_plex.friendlyName,
|
||||
user.title,
|
||||
library_name,
|
||||
plex_show.title,
|
||||
plex_episode.title,
|
||||
stored_ep.status.time,
|
||||
)
|
||||
break # Found a matching episode.
|
||||
break # Found a matching show.
|
||||
except Exception as e:
|
||||
logger(
|
||||
f"Plex: Failed to update watched for {user.title} in library {library}, Error: {e}",
|
||||
f"Plex: Failed to update watched for {user.title} in library {library_name}, Error: {e}",
|
||||
2,
|
||||
)
|
||||
logger(traceback.format_exc(), 2)
|
||||
raise e
|
||||
|
||||
|
||||
# class plex accept base url and token and username and password but default with none
|
||||
@@ -505,10 +305,77 @@ class Plex:
|
||||
logger(f"Plex: Failed to get libraries, Error: {e}", 2)
|
||||
raise Exception(e)
|
||||
|
||||
def get_watched(self, users, sync_libraries):
|
||||
def get_user_library_watched(self, user, user_plex, library) -> LibraryData:
|
||||
user_name: str = user.username.lower() if user.username else user.title.lower()
|
||||
try:
|
||||
# Get all libraries
|
||||
users_watched = {}
|
||||
logger(
|
||||
f"Plex: Generating watched for {user_name} in library {library.title}",
|
||||
0,
|
||||
)
|
||||
watched = LibraryData(title=library.title)
|
||||
|
||||
library_videos = user_plex.library.section(library.title)
|
||||
|
||||
if library.type == "movie":
|
||||
for video in library_videos.search(
|
||||
unwatched=False
|
||||
) + library_videos.search(inProgress=True):
|
||||
if video.isWatched or video.viewOffset >= 60000:
|
||||
watched.movies.append(get_mediaitem(video, video.isWatched))
|
||||
|
||||
elif library.type == "show":
|
||||
# Keep track of processed shows to reduce duplicate shows
|
||||
processed_shows = []
|
||||
for show in library_videos.search(
|
||||
unwatched=False
|
||||
) + library_videos.search(inProgress=True):
|
||||
if show.key in processed_shows:
|
||||
continue
|
||||
processed_shows.append(show.key)
|
||||
show_guids = extract_guids_from_item(show)
|
||||
episode_mediaitem = []
|
||||
for episode in show.episodes():
|
||||
if episode.isWatched or episode.viewOffset >= 60000:
|
||||
|
||||
episode_mediaitem.append(
|
||||
get_mediaitem(episode, episode.isWatched)
|
||||
)
|
||||
|
||||
if episode_mediaitem:
|
||||
watched.series.append(
|
||||
Series(
|
||||
identifiers=MediaIdentifiers(
|
||||
title=show.title,
|
||||
locations=(
|
||||
tuple(
|
||||
[
|
||||
location.split("/")[-1]
|
||||
for location in show.locations
|
||||
]
|
||||
)
|
||||
if generate_locations
|
||||
else tuple()
|
||||
),
|
||||
imdb_id=show_guids.get("imdb", None),
|
||||
tvdb_id=show_guids.get("tvdb", None),
|
||||
tmdb_id=show_guids.get("tmdb", None),
|
||||
),
|
||||
episodes=episode_mediaitem,
|
||||
)
|
||||
)
|
||||
|
||||
return watched
|
||||
|
||||
except Exception as e:
|
||||
logger(
|
||||
f"Plex: Failed to get watched for {user_name} in library {library.title}, Error: {e}",
|
||||
2,
|
||||
)
|
||||
return LibraryData(title=library.title)
|
||||
|
||||
def get_watched(self, users, sync_libraries) -> dict[str, UserData]:
|
||||
try:
|
||||
users_watched: dict[str, UserData] = {}
|
||||
|
||||
for user in users:
|
||||
if self.admin_user == user:
|
||||
@@ -525,7 +392,6 @@ class Plex:
|
||||
f"Plex: Failed to get token for {user.title}, skipping",
|
||||
2,
|
||||
)
|
||||
users_watched[user.title] = {}
|
||||
continue
|
||||
|
||||
libraries = user_plex.library.sections()
|
||||
@@ -534,12 +400,16 @@ class Plex:
|
||||
if library.title not in sync_libraries:
|
||||
continue
|
||||
|
||||
user_watched = get_user_library_watched(user, user_plex, library)
|
||||
library_data = self.get_user_library_watched(
|
||||
user, user_plex, library
|
||||
)
|
||||
|
||||
for user_watched, user_watched_temp in user_watched.items():
|
||||
if user_watched not in users_watched:
|
||||
users_watched[user_watched] = {}
|
||||
users_watched[user_watched].update(user_watched_temp)
|
||||
if user.title.lower() not in users_watched:
|
||||
users_watched[user.title.lower()] = UserData()
|
||||
|
||||
users_watched[user.title.lower()].libraries[
|
||||
library.title
|
||||
] = library_data
|
||||
|
||||
return users_watched
|
||||
except Exception as e:
|
||||
@@ -547,12 +417,14 @@ class Plex:
|
||||
raise Exception(e)
|
||||
|
||||
def update_watched(
|
||||
self, watched_list, user_mapping=None, library_mapping=None, dryrun=False
|
||||
self,
|
||||
watched_list: dict[str, UserData],
|
||||
user_mapping=None,
|
||||
library_mapping=None,
|
||||
dryrun=False,
|
||||
):
|
||||
try:
|
||||
args = []
|
||||
|
||||
for user, libraries in watched_list.items():
|
||||
for user, user_data in watched_list.items():
|
||||
user_other = None
|
||||
# If type of user is dict
|
||||
if user_mapping:
|
||||
@@ -596,48 +468,46 @@ class Plex:
|
||||
)
|
||||
continue
|
||||
|
||||
for library, watched_videos in libraries.items():
|
||||
for library_name in user_data.libraries:
|
||||
library_data = user_data.libraries[library_name]
|
||||
library_other = None
|
||||
if library_mapping:
|
||||
library_other = search_mapping(library_mapping, library)
|
||||
|
||||
library_other = search_mapping(library_mapping, library_name)
|
||||
# if library in plex library list
|
||||
library_list = user_plex.library.sections()
|
||||
if library.lower() not in [x.title.lower() for x in library_list]:
|
||||
if library_name.lower() not in [
|
||||
x.title.lower() for x in library_list
|
||||
]:
|
||||
if library_other:
|
||||
if library_other.lower() in [
|
||||
x.title.lower() for x in library_list
|
||||
]:
|
||||
logger(
|
||||
f"Plex: Library {library} not found, but {library_other} found, using {library_other}",
|
||||
f"Plex: Library {library_name} not found, but {library_other} found, using {library_other}",
|
||||
1,
|
||||
)
|
||||
library = library_other
|
||||
library_name = library_other
|
||||
else:
|
||||
logger(
|
||||
f"Plex: Library {library} or {library_other} not found in library list",
|
||||
f"Plex: Library {library_name} or {library_other} not found in library list",
|
||||
1,
|
||||
)
|
||||
continue
|
||||
else:
|
||||
logger(
|
||||
f"Plex: Library {library} not found in library list",
|
||||
f"Plex: Library {library_name} not found in library list",
|
||||
1,
|
||||
)
|
||||
continue
|
||||
|
||||
args.append(
|
||||
[
|
||||
update_user_watched,
|
||||
user,
|
||||
user_plex,
|
||||
library,
|
||||
watched_videos,
|
||||
dryrun,
|
||||
]
|
||||
update_user_watched(
|
||||
user,
|
||||
user_plex,
|
||||
library_data,
|
||||
library_name,
|
||||
dryrun,
|
||||
)
|
||||
|
||||
future_thread_executor(args)
|
||||
except Exception as e:
|
||||
logger(f"Plex: Failed to update watched, Error: {e}", 2)
|
||||
raise Exception(e)
|
||||
|
||||
Reference in New Issue
Block a user