Add location based matching
parent
8b7fc5e323
commit
c104973f95
|
|
@ -1,16 +1,16 @@
|
||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
// Use IntelliSense to learn about possible attributes.
|
||||||
// Hover to view descriptions of existing attributes.
|
// Hover to view descriptions of existing attributes.
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Python",
|
"name": "Python: Main",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "main.py",
|
"program": "main.py",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": true
|
"justMyCode": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,8 @@ def generate_library_guids_dict(user_list: dict, generate_output: int):
|
||||||
if provider_key.lower() not in show_output_dict:
|
if provider_key.lower() not in show_output_dict:
|
||||||
show_output_dict[provider_key.lower()] = []
|
show_output_dict[provider_key.lower()] = []
|
||||||
if provider_key.lower() == "locations":
|
if provider_key.lower() == "locations":
|
||||||
show_output_dict[provider_key.lower()].append(provider_value)
|
for show_location in provider_value:
|
||||||
|
show_output_dict[provider_key.lower()].append(show_location)
|
||||||
else:
|
else:
|
||||||
show_output_dict[provider_key.lower()].append(provider_value.lower())
|
show_output_dict[provider_key.lower()].append(provider_value.lower())
|
||||||
|
|
||||||
|
|
@ -109,7 +110,8 @@ def generate_library_guids_dict(user_list: dict, generate_output: int):
|
||||||
if episode_key.lower() not in episode_output_dict:
|
if episode_key.lower() not in episode_output_dict:
|
||||||
episode_output_dict[episode_key.lower()] = []
|
episode_output_dict[episode_key.lower()] = []
|
||||||
if episode_key == "locations":
|
if episode_key == "locations":
|
||||||
episode_output_dict[episode_key.lower()].append(episode_value)
|
for episode_location in episode_value:
|
||||||
|
episode_output_dict[episode_key.lower()].append(episode_location)
|
||||||
else:
|
else:
|
||||||
episode_output_dict[episode_key.lower()].append(episode_value.lower())
|
episode_output_dict[episode_key.lower()].append(episode_value.lower())
|
||||||
|
|
||||||
|
|
@ -119,7 +121,8 @@ def generate_library_guids_dict(user_list: dict, generate_output: int):
|
||||||
if movie_key.lower() not in movies_output_dict:
|
if movie_key.lower() not in movies_output_dict:
|
||||||
movies_output_dict[movie_key.lower()] = []
|
movies_output_dict[movie_key.lower()] = []
|
||||||
if movie_key == "locations":
|
if movie_key == "locations":
|
||||||
movies_output_dict[movie_key.lower()].append(movie_value)
|
for movie_location in movie_value:
|
||||||
|
movies_output_dict[movie_key.lower()].append(movie_location)
|
||||||
else:
|
else:
|
||||||
movies_output_dict[movie_key.lower()].append(movie_value.lower())
|
movies_output_dict[movie_key.lower()].append(movie_value.lower())
|
||||||
|
|
||||||
|
|
|
||||||
128
src/jellyfin.py
128
src/jellyfin.py
|
|
@ -39,7 +39,7 @@ class Jellyfin():
|
||||||
response = self.session.post(self.baseurl + query, headers=headers)
|
response = self.session.post(self.baseurl + query, headers=headers)
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -71,40 +71,49 @@ class Jellyfin():
|
||||||
# Movies
|
# Movies
|
||||||
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", "get")
|
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Filters=IsPlayed&Fields=ItemCounts,ProviderIds,MediaSources", "get")
|
||||||
for movie in watched["Items"]:
|
for movie in watched["Items"]:
|
||||||
if movie["UserData"]["Played"] == True:
|
if movie["UserData"]["Played"] == True:
|
||||||
|
movie_guids = {}
|
||||||
|
movie_guids["title"] = movie["Name"]
|
||||||
if movie["ProviderIds"]:
|
if movie["ProviderIds"]:
|
||||||
# Lowercase movie["ProviderIds"] keys
|
# Lowercase movie["ProviderIds"] keys
|
||||||
movie["ProviderIds"] = {k.lower(): v for k, v in movie["ProviderIds"].items()}
|
movie_guids = {k.lower(): v for k, v in movie["ProviderIds"].items()}
|
||||||
user_watched[user_name][library_title].append(movie["ProviderIds"])
|
if movie["MediaSources"]:
|
||||||
|
movie_guids["locations"] = tuple([x["Path"].split("/")[-1] for x in movie["MediaSources"]])
|
||||||
|
user_watched[user_name][library_title].append(movie_guids)
|
||||||
|
|
||||||
# TV Shows
|
# TV Shows
|
||||||
if library_type == "Episode":
|
if library_type == "Episode":
|
||||||
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", "get")
|
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Fields=ItemCounts,ProviderIds,Path", "get")
|
||||||
watched_shows = [x for x in watched["Items"] if x["Type"] == "Series"]
|
watched_shows = [x for x in watched["Items"] if x["Type"] == "Series"]
|
||||||
|
|
||||||
for show in watched_shows:
|
for show in watched_shows:
|
||||||
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 = 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")
|
seasons = self.query(f"/Shows/{show['Id']}/Seasons?userId={user_id}&Fields=ItemCounts,ProviderIds", "get")
|
||||||
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", "get")
|
episodes = self.query(f"/Shows/{show['Id']}/Episodes?seasonId={season['Id']}&userId={user_id}&Fields=ItemCounts,ProviderIds,MediaSources", "get")
|
||||||
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"]:
|
if episode["ProviderIds"] or episode["MediaSources"]:
|
||||||
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 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][season["Name"]] = []
|
||||||
|
|
||||||
# Lowercase episode["ProviderIds"] keys
|
# Lowercase episode["ProviderIds"] keys
|
||||||
episode["ProviderIds"] = {k.lower(): v for k, v in episode["ProviderIds"].items()}
|
episode_guids = {}
|
||||||
user_watched[user_name][library_title][show_guids][season["Name"]].append(episode["ProviderIds"])
|
if episode["ProviderIds"]:
|
||||||
|
episode_guids = {k.lower(): v for k, v in episode["ProviderIds"].items()}
|
||||||
|
if 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)
|
||||||
|
|
||||||
return user_watched
|
return user_watched
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -163,55 +172,86 @@ class Jellyfin():
|
||||||
if library_type == "Movie":
|
if library_type == "Movie":
|
||||||
_, _, videos_movies_ids = generate_library_guids_dict(videos, 2)
|
_, _, videos_movies_ids = generate_library_guids_dict(videos, 2)
|
||||||
|
|
||||||
jellyfin_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds", "get")
|
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")
|
||||||
for jellyfin_video in jellyfin_search["Items"]:
|
for jellyfin_video in jellyfin_search["Items"]:
|
||||||
if str_to_bool(jellyfin_video["UserData"]["Played"]) == False:
|
movie_found = False
|
||||||
jellyfin_video_id = jellyfin_video["Id"]
|
|
||||||
|
|
||||||
for movie_provider_source, movie_provider_id in jellyfin_video["ProviderIds"].items():
|
if "MediaSources" in jellyfin_video:
|
||||||
if movie_provider_source.lower() in videos_movies_ids:
|
for movie_location in jellyfin_video["MediaSources"]:
|
||||||
if movie_provider_id.lower() in videos_movies_ids[movie_provider_source.lower()]:
|
if movie_location["Path"].split("/")[-1] in videos_movies_ids["locations"]:
|
||||||
msg = f"{jellyfin_video['Name']} as watched for {user} in {library} for Jellyfin"
|
movie_found = True
|
||||||
if not dryrun:
|
|
||||||
logger(f"Marking {msg}", 0)
|
|
||||||
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_video_id}", "post")
|
|
||||||
else:
|
|
||||||
logger(f"Dryrun {msg}", 0)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if not movie_found:
|
||||||
|
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()]:
|
||||||
|
movie_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if movie_found:
|
||||||
|
jellyfin_video_id = jellyfin_video["Id"]
|
||||||
|
msg = f"{jellyfin_video['Name']} as watched for {user} in {library} for Jellyfin"
|
||||||
|
if not dryrun:
|
||||||
|
logger(f"Marking {msg}", 0)
|
||||||
|
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_video_id}", "post")
|
||||||
|
else:
|
||||||
|
logger(f"Dryrun {msg}", 0)
|
||||||
|
|
||||||
|
|
||||||
# TV Shows
|
# TV Shows
|
||||||
if library_type == "Episode":
|
if library_type == "Episode":
|
||||||
videos_shows_ids, videos_episode_ids, _ = generate_library_guids_dict(videos, 3)
|
videos_shows_ids, videos_episode_ids, _ = generate_library_guids_dict(videos, 3)
|
||||||
|
|
||||||
jellyfin_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=false&ParentId={library_id}&isPlayed=false&Fields=ItemCounts,ProviderIds", "get")
|
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_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:
|
||||||
show_found = False
|
show_found = False
|
||||||
for show_provider_source, show_provider_id in jellyfin_show["ProviderIds"].items():
|
|
||||||
if show_provider_source.lower() in videos_shows_ids:
|
|
||||||
if show_provider_id.lower() in videos_shows_ids[show_provider_source.lower()]:
|
|
||||||
show_found = True
|
|
||||||
jellyfin_show_id = jellyfin_show["Id"]
|
|
||||||
jellyfin_episodes = self.query(f"/Shows/{jellyfin_show_id}/Episodes?userId={user_id}&Fields=ItemCounts,ProviderIds", "get")
|
|
||||||
for jellyfin_episode in jellyfin_episodes["Items"]:
|
|
||||||
if str_to_bool(jellyfin_episode["UserData"]["Played"]) == False:
|
|
||||||
jellyfin_episode_id = jellyfin_episode["Id"]
|
|
||||||
|
|
||||||
for episode_provider_source, episode_provider_id in jellyfin_episode["ProviderIds"].items():
|
if jellyfin_show["Name"] == "The 13 Ghosts of Scooby-Doo":
|
||||||
if episode_provider_source.lower() in videos_episode_ids:
|
print(jellyfin_show)
|
||||||
if episode_provider_id.lower() in videos_episode_ids[episode_provider_source.lower()]:
|
|
||||||
msg = f"{jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode['IndexNumber']} {jellyfin_episode['Name']} as watched for {user} in {library} for Jellyfin"
|
if "Path" in jellyfin_show:
|
||||||
if not dryrun:
|
if jellyfin_show["Path"].split("/")[-1] in videos_shows_ids["locations"]:
|
||||||
logger(f"Marked {msg}", 0)
|
show_found = True
|
||||||
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_episode_id}", "post")
|
|
||||||
else:
|
if not show_found:
|
||||||
logger(f"Dryrun {msg}", 0)
|
for show_provider_source, show_provider_id in jellyfin_show["ProviderIds"].items():
|
||||||
break
|
if show_provider_source.lower() in videos_shows_ids:
|
||||||
|
if show_provider_id.lower() in videos_shows_ids[show_provider_source.lower()]:
|
||||||
|
show_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if show_found:
|
||||||
|
jellyfin_show_id = jellyfin_show["Id"]
|
||||||
|
jellyfin_episodes = self.query(f"/Shows/{jellyfin_show_id}/Episodes?userId={user_id}&Fields=ItemCounts,ProviderIds,MediaSources", "get")
|
||||||
|
|
||||||
|
for jellyfin_episode in jellyfin_episodes["Items"]:
|
||||||
|
episode_found = False
|
||||||
|
|
||||||
|
if "MediaSources" in jellyfin_episode:
|
||||||
|
for episode_location in jellyfin_episode["MediaSources"]:
|
||||||
|
if episode_location["Path"].split("/")[-1] in videos_episode_ids["locations"]:
|
||||||
|
episode_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not episode_found:
|
||||||
|
for episode_provider_source, episode_provider_id in jellyfin_episode["ProviderIds"].items():
|
||||||
|
if episode_provider_source.lower() in videos_episode_ids:
|
||||||
|
if episode_provider_id.lower() in videos_episode_ids[episode_provider_source.lower()]:
|
||||||
|
episode_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if episode_found:
|
||||||
|
jellyfin_episode_id = jellyfin_episode["Id"]
|
||||||
|
msg = f"{jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode['IndexNumber']} {jellyfin_episode['Name']} as watched for {user} in {library} for Jellyfin"
|
||||||
|
if not dryrun:
|
||||||
|
logger(f"Marked {msg}", 0)
|
||||||
|
self.query(f"/Users/{user_id}/PlayedItems/{jellyfin_episode_id}", "post")
|
||||||
|
else:
|
||||||
|
logger(f"Dryrun {msg}", 0)
|
||||||
|
|
||||||
if show_found:
|
|
||||||
break
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger(f"Jellyfin: Error updating watched for {user} in library {library}", 2)
|
logger(f"Jellyfin: Error updating watched for {user} in library {library}", 2)
|
||||||
raise Exception(e)
|
raise Exception(e)
|
||||||
|
|
|
||||||
56
src/main.py
56
src/main.py
|
|
@ -40,32 +40,54 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
|
||||||
|
|
||||||
# Movies
|
# Movies
|
||||||
if isinstance(watched_list_1[user_1][library_1], list):
|
if isinstance(watched_list_1[user_1][library_1], list):
|
||||||
for item in watched_list_1[user_1][library_1]:
|
_, _, movies_watched_list_2_keys_dict = generate_library_guids_dict(watched_list_2[user_2][library_2], 2)
|
||||||
for watch_list_1_key, watch_list_1_value in item.items():
|
for movie in watched_list_1[user_1][library_1]:
|
||||||
for watch_list_2_item in watched_list_2[user_2][library_2]:
|
movie_found = False
|
||||||
for watch_list_2_item_key, watch_list_2_item_value in watch_list_2_item.items():
|
for movie_key, movie_value in movie.items():
|
||||||
if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value:
|
if movie_key == "locations":
|
||||||
if item in modified_watched_list_1[user_1][library_1]:
|
for location in movie_value:
|
||||||
logger(f"Removing {item} from {library_1}", 3)
|
if location in movies_watched_list_2_keys_dict["locations"]:
|
||||||
modified_watched_list_1[user_1][library_1].remove(item)
|
movie_found = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if movie_key in movies_watched_list_2_keys_dict.keys():
|
||||||
|
if movie_value in movies_watched_list_2_keys_dict[movie_key]:
|
||||||
|
movie_found = True
|
||||||
|
|
||||||
|
if movie_found:
|
||||||
|
logger(f"Removing {movie} from {library_1}", 3)
|
||||||
|
modified_watched_list_1[user_1][library_1].remove(movie)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
# 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], 1)
|
show_watched_list_2_keys_dict, episode_watched_list_2_keys_dict, _ = generate_library_guids_dict(watched_list_2[user_2][library_2], 3)
|
||||||
|
|
||||||
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)
|
||||||
for season in watched_list_1[user_1][library_1][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]:
|
for episode in watched_list_1[user_1][library_1][show_key_1][season]:
|
||||||
for episode_key, episode_item in episode.items():
|
episode_found = False
|
||||||
# If episode_key and episode_item are in episode_watched_list_2_keys_dict exactly, then remove from watch_list_1
|
for episode_key, episode_value in episode.items():
|
||||||
if episode_key in episode_watched_list_2_keys_dict.keys():
|
# If episode_key and episode_value are in episode_watched_list_2_keys_dict exactly, then remove from watch_list_1
|
||||||
if episode_item in episode_watched_list_2_keys_dict[episode_key]:
|
if episode_key == "locations":
|
||||||
if episode in modified_watched_list_1[user_1][library_1][show_key_1][season]:
|
for location in episode_value:
|
||||||
logger(f"Removing {show_key_dict['title']} {episode} from {library_1}", 3)
|
if location in episode_watched_list_2_keys_dict["locations"]:
|
||||||
modified_watched_list_1[user_1][library_1][show_key_1][season].remove(episode)
|
episode_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
if episode_key in episode_watched_list_2_keys_dict.keys():
|
||||||
|
if episode_value in episode_watched_list_2_keys_dict[episode_key]:
|
||||||
|
episode_found = True
|
||||||
|
|
||||||
|
if episode_found:
|
||||||
|
if episode in modified_watched_list_1[user_1][library_1][show_key_1][season]:
|
||||||
|
logger(f"Removing {show_key_dict['title']} {episode} from {library_1}", 3)
|
||||||
|
modified_watched_list_1[user_1][library_1][show_key_1][season].remove(episode)
|
||||||
|
break
|
||||||
|
|
||||||
# Remove empty seasons
|
# Remove empty seasons
|
||||||
if len(modified_watched_list_1[user_1][library_1][show_key_1][season]) == 0:
|
if len(modified_watched_list_1[user_1][library_1][show_key_1][season]) == 0:
|
||||||
|
|
@ -154,7 +176,7 @@ def setup_black_white_lists(blacklist_library: str, whitelist_library: str, blac
|
||||||
user_other = search_mapping(user_mapping, user)
|
user_other = search_mapping(user_mapping, user)
|
||||||
if user_other:
|
if user_other:
|
||||||
temp_users.append(user_other)
|
temp_users.append(user_other)
|
||||||
|
|
||||||
blacklist_users = blacklist_users + temp_users
|
blacklist_users = blacklist_users + temp_users
|
||||||
else:
|
else:
|
||||||
blacklist_users = []
|
blacklist_users = []
|
||||||
|
|
|
||||||
130
src/plex.py
130
src/plex.py
|
|
@ -64,26 +64,32 @@ class Plex:
|
||||||
user_watched[user_name][library.title] = []
|
user_watched[user_name][library.title] = []
|
||||||
|
|
||||||
library_videos = user_plex.library.section(library.title)
|
library_videos = user_plex.library.section(library.title)
|
||||||
for video in library_videos.search(unmatched=False, unwatched=False):
|
for video in library_videos.search(unwatched=False):
|
||||||
guids = {}
|
movie_guids = {}
|
||||||
for guid in video.guids:
|
for guid in video.guids:
|
||||||
guid_source = re.search(r'(.*)://', guid.id).group(1).lower()
|
guid_source = re.search(r'(.*)://', guid.id).group(1).lower()
|
||||||
guid_id = re.search(r'://(.*)', guid.id).group(1)
|
guid_id = re.search(r'://(.*)', guid.id).group(1)
|
||||||
guids[guid_source] = guid_id
|
movie_guids[guid_source] = guid_id
|
||||||
user_watched[user_name][library.title].append(guids)
|
|
||||||
|
movie_guids["title"] = video.title
|
||||||
|
movie_guids["locations"] = tuple([x.split("/")[-1] for x in video.locations])
|
||||||
|
|
||||||
|
user_watched[user_name][library.title].append(movie_guids)
|
||||||
|
|
||||||
elif library.type == "show":
|
elif library.type == "show":
|
||||||
user_watched[user_name][library.title] = {}
|
user_watched[user_name][library.title] = {}
|
||||||
|
|
||||||
library_videos = user_plex.library.section(library.title)
|
library_videos = user_plex.library.section(library.title)
|
||||||
for show in library_videos.search(unmatched=False, unwatched=False):
|
for show in library_videos.search(unwatched=False):
|
||||||
show_guids = {}
|
show_guids = {}
|
||||||
for show_guid in show.guids:
|
for show_guid in show.guids:
|
||||||
show_guids["title"] = show.title
|
|
||||||
# Extract after :// from guid.id
|
# Extract after :// from guid.id
|
||||||
show_guid_source = re.search(r'(.*)://', show_guid.id).group(1).lower()
|
show_guid_source = re.search(r'(.*)://', show_guid.id).group(1).lower()
|
||||||
show_guid_id = re.search(r'://(.*)', show_guid.id).group(1)
|
show_guid_id = re.search(r'://(.*)', show_guid.id).group(1)
|
||||||
show_guids[show_guid_source] = show_guid_id
|
show_guids[show_guid_source] = show_guid_id
|
||||||
|
|
||||||
|
show_guids["title"] = show.title
|
||||||
|
show_guids["locations"] = tuple([x.split("/")[-1] for x in show.locations])
|
||||||
show_guids = frozenset(show_guids.items())
|
show_guids = frozenset(show_guids.items())
|
||||||
|
|
||||||
for season in show.seasons():
|
for season in show.seasons():
|
||||||
|
|
@ -97,6 +103,7 @@ class Plex:
|
||||||
guid_id = re.search(r'://(.*)', guid.id).group(1)
|
guid_id = re.search(r'://(.*)', guid.id).group(1)
|
||||||
episode_guids_temp[guid_source] = guid_id
|
episode_guids_temp[guid_source] = guid_id
|
||||||
|
|
||||||
|
episode_guids_temp["locations"] = tuple([x.split("/")[-1] for x in episode.locations])
|
||||||
episode_guids.append(episode_guids_temp)
|
episode_guids.append(episode_guids_temp)
|
||||||
|
|
||||||
if episode_guids:
|
if episode_guids:
|
||||||
|
|
@ -159,56 +166,83 @@ class Plex:
|
||||||
|
|
||||||
if library_videos.type == "movie":
|
if library_videos.type == "movie":
|
||||||
_, _, videos_movies_ids = generate_library_guids_dict(videos, 2)
|
_, _, videos_movies_ids = generate_library_guids_dict(videos, 2)
|
||||||
for movies_search in library_videos.search(unmatched=False, unwatched=True):
|
for movies_search in library_videos.search(unwatched=True):
|
||||||
for movie_guid in movies_search.guids:
|
movie_found = False
|
||||||
movie_guid_source = re.search(r'(.*)://', movie_guid.id).group(1).lower()
|
for movie_location in movies_search.locations:
|
||||||
movie_guid_id = re.search(r'://(.*)', movie_guid.id).group(1)
|
if movie_location.split("/")[-1] in videos_movies_ids["locations"]:
|
||||||
|
movie_found = True
|
||||||
|
break
|
||||||
|
|
||||||
# If movie provider source and movie provider id are in videos_movie_ids exactly, then the movie is in the list
|
if not movie_found:
|
||||||
if movie_guid_source in videos_movies_ids.keys():
|
for movie_guid in movies_search.guids:
|
||||||
if movie_guid_id in videos_movies_ids[movie_guid_source]:
|
movie_guid_source = re.search(r'(.*)://', movie_guid.id).group(1).lower()
|
||||||
if movies_search.viewCount == 0:
|
movie_guid_id = re.search(r'://(.*)', movie_guid.id).group(1)
|
||||||
msg = f"{movies_search.title} as watched for {user.title} in {library} for Plex"
|
|
||||||
if not dryrun:
|
# If movie provider source and movie provider id are in videos_movie_ids exactly, then the movie is in the list
|
||||||
logger(f"Marked {msg}", 0)
|
if movie_guid_source in videos_movies_ids.keys():
|
||||||
movies_search.markWatched()
|
if movie_guid_id in videos_movies_ids[movie_guid_source]:
|
||||||
else:
|
movie_found = True
|
||||||
logger(f"Dryrun {msg}", 0)
|
break
|
||||||
break
|
|
||||||
|
if movie_found:
|
||||||
|
if movies_search.viewCount == 0:
|
||||||
|
msg = f"{movies_search.title} as watched for {user.title} in {library} for Plex"
|
||||||
|
if not dryrun:
|
||||||
|
logger(f"Marked {msg}", 0)
|
||||||
|
movies_search.markWatched()
|
||||||
|
else:
|
||||||
|
logger(f"Dryrun {msg}", 0)
|
||||||
|
|
||||||
|
|
||||||
elif library_videos.type == "show":
|
elif library_videos.type == "show":
|
||||||
videos_shows_ids, videos_episode_ids, _ = generate_library_guids_dict(videos, 3)
|
videos_shows_ids, videos_episode_ids, _ = generate_library_guids_dict(videos, 3)
|
||||||
|
|
||||||
for show_search in library_videos.search(unmatched=False, unwatched=True):
|
for show_search in library_videos.search(unwatched=True):
|
||||||
show_found = False
|
show_found = False
|
||||||
for show_guid in show_search.guids:
|
for show_location in show_search.locations:
|
||||||
show_guid_source = re.search(r'(.*)://', show_guid.id).group(1).lower()
|
if show_location.split("/")[-1] in videos_shows_ids["locations"]:
|
||||||
show_guid_id = re.search(r'://(.*)', show_guid.id).group(1)
|
show_found = True
|
||||||
|
|
||||||
# If show provider source and show provider id are in videos_shows_ids exactly, then the show is in the list
|
|
||||||
if show_guid_source in videos_shows_ids.keys():
|
|
||||||
if show_guid_id in videos_shows_ids[show_guid_source]:
|
|
||||||
show_found = True
|
|
||||||
for episode_search in show_search.episodes():
|
|
||||||
for episode_guid in episode_search.guids:
|
|
||||||
episode_guid_source = re.search(r'(.*)://', episode_guid.id).group(1).lower()
|
|
||||||
episode_guid_id = re.search(r'://(.*)', episode_guid.id).group(1)
|
|
||||||
|
|
||||||
# If episode provider source and episode provider id are in videos_episode_ids exactly, then the episode is in the list
|
|
||||||
if episode_guid_source in videos_episode_ids.keys():
|
|
||||||
if episode_guid_id in videos_episode_ids[episode_guid_source]:
|
|
||||||
if episode_search.viewCount == 0:
|
|
||||||
msg = f"{show_search.title} {episode_search.title} as watched for {user.title} in {library} for Plex"
|
|
||||||
if not dryrun:
|
|
||||||
logger(f"Marked {msg}", 0)
|
|
||||||
episode_search.markWatched()
|
|
||||||
else:
|
|
||||||
logger(f"Dryrun {msg}", 0)
|
|
||||||
break
|
|
||||||
|
|
||||||
if show_found:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if not show_found:
|
||||||
|
for show_guid in show_search.guids:
|
||||||
|
show_guid_source = re.search(r'(.*)://', show_guid.id).group(1).lower()
|
||||||
|
show_guid_id = re.search(r'://(.*)', show_guid.id).group(1)
|
||||||
|
|
||||||
|
# If show provider source and show provider id are in videos_shows_ids exactly, then the show is in the list
|
||||||
|
if show_guid_source in videos_shows_ids.keys():
|
||||||
|
if show_guid_id in videos_shows_ids[show_guid_source]:
|
||||||
|
show_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if show_found:
|
||||||
|
for episode_search in show_search.episodes():
|
||||||
|
episode_found = False
|
||||||
|
|
||||||
|
for episode_location in episode_search.locations:
|
||||||
|
if episode_location.split("/")[-1] in videos_episode_ids["locations"]:
|
||||||
|
episode_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not episode_found:
|
||||||
|
for episode_guid in episode_search.guids:
|
||||||
|
episode_guid_source = re.search(r'(.*)://', episode_guid.id).group(1).lower()
|
||||||
|
episode_guid_id = re.search(r'://(.*)', episode_guid.id).group(1)
|
||||||
|
|
||||||
|
# If episode provider source and episode provider id are in videos_episode_ids exactly, then the episode is in the list
|
||||||
|
if episode_guid_source in videos_episode_ids.keys():
|
||||||
|
if episode_guid_id in videos_episode_ids[episode_guid_source]:
|
||||||
|
episode_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if episode_found:
|
||||||
|
if episode_search.viewCount == 0:
|
||||||
|
msg = f"{show_search.title} {episode_search.title} as watched for {user.title} in {library} for Plex"
|
||||||
|
if not dryrun:
|
||||||
|
logger(f"Marked {msg}", 0)
|
||||||
|
episode_search.markWatched()
|
||||||
|
else:
|
||||||
|
logger(f"Dryrun {msg}", 0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger(f"Plex: Failed to update watched for {user.title} in library {library}, Error: {e}", 2)
|
logger(f"Plex: Failed to update watched for {user.title} in library {library}, Error: {e}", 2)
|
||||||
raise Exception(e)
|
raise Exception(e)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# getting the name of the directory
|
# getting the name of the directory
|
||||||
# where the this file is present.
|
# where the this file is present.
|
||||||
current = os.path.dirname(os.path.realpath(__file__))
|
current = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
# Getting the parent directory name
|
# Getting the parent directory name
|
||||||
# where the current directory is present.
|
# where the current directory is present.
|
||||||
parent = os.path.dirname(current)
|
parent = os.path.dirname(current)
|
||||||
|
|
||||||
# adding the parent directory to
|
# adding the parent directory to
|
||||||
# the sys.path.
|
# the sys.path.
|
||||||
sys.path.append(parent)
|
sys.path.append(parent)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# getting the name of the directory
|
# getting the name of the directory
|
||||||
# where the this file is present.
|
# where the this file is present.
|
||||||
current = os.path.dirname(os.path.realpath(__file__))
|
current = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
# Getting the parent directory name
|
# Getting the parent directory name
|
||||||
# where the current directory is present.
|
# where the current directory is present.
|
||||||
parent = os.path.dirname(current)
|
parent = os.path.dirname(current)
|
||||||
|
|
||||||
# adding the parent directory to
|
# adding the parent directory to
|
||||||
# the sys.path.
|
# the sys.path.
|
||||||
sys.path.append(parent)
|
sys.path.append(parent)
|
||||||
|
|
||||||
|
|
@ -21,6 +21,12 @@ tv_shows_watched_list_1 = {
|
||||||
{'imdb': 'tt0550489', 'tmdb': '282843', 'tvdb': '176357', 'locations': ('Criminal Minds S01E01 Extreme Aggressor WEBDL-720p.mkv',)},
|
{'imdb': 'tt0550489', 'tmdb': '282843', 'tvdb': '176357', 'locations': ('Criminal Minds S01E01 Extreme Aggressor WEBDL-720p.mkv',)},
|
||||||
{'imdb': 'tt0550487', 'tmdb': '282861', 'tvdb': '300385', 'locations': ('Criminal Minds S01E02 Compulsion WEBDL-720p.mkv',)}
|
{'imdb': 'tt0550487', 'tmdb': '282861', 'tvdb': '300385', 'locations': ('Criminal Minds S01E02 Compulsion WEBDL-720p.mkv',)}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
frozenset({("title", "Test"), ("locations", ("Test",))}): {
|
||||||
|
"Season 1": [
|
||||||
|
{'locations': ('Test S01E01.mkv',)},
|
||||||
|
{'locations': ('Test S01E02.mkv',)}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +41,12 @@ tv_shows_watched_list_2 = {
|
||||||
{'imdb': 'tt0550487', 'tmdb': '282861', 'tvdb': '300385', 'locations': ('Criminal Minds S01E02 Compulsion WEBDL-720p.mkv',)},
|
{'imdb': 'tt0550487', 'tmdb': '282861', 'tvdb': '300385', 'locations': ('Criminal Minds S01E02 Compulsion WEBDL-720p.mkv',)},
|
||||||
{'imdb': 'tt0550498', 'tmdb': '282865', 'tvdb': '300474', 'locations': ("Criminal Minds S01E03 Won't Get Fooled Again WEBDL-720p.mkv",)}
|
{'imdb': 'tt0550498', 'tmdb': '282865', 'tvdb': '300474', 'locations': ("Criminal Minds S01E03 Won't Get Fooled Again WEBDL-720p.mkv",)}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
frozenset({("title", "Test"), ("locations", ("Test",))}): {
|
||||||
|
"Season 1": [
|
||||||
|
{'locations': ('Test S01E02.mkv',)},
|
||||||
|
{'locations': ('Test S01E03.mkv',)}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +69,11 @@ expected_tv_show_watched_list_1 = {
|
||||||
"Season 1": [
|
"Season 1": [
|
||||||
{'imdb': 'tt0550489', 'tmdb': '282843', 'tvdb': '176357', 'locations': ('Criminal Minds S01E01 Extreme Aggressor WEBDL-720p.mkv',)}
|
{'imdb': 'tt0550489', 'tmdb': '282843', 'tvdb': '176357', 'locations': ('Criminal Minds S01E01 Extreme Aggressor WEBDL-720p.mkv',)}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
frozenset({("title", "Test"), ("locations", ("Test",))}): {
|
||||||
|
"Season 1": [
|
||||||
|
{'locations': ('Test S01E01.mkv',)}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +86,11 @@ expected_tv_show_watched_list_2 = {
|
||||||
"Season 1": [
|
"Season 1": [
|
||||||
{'imdb': 'tt0550498', 'tmdb': '282865', 'tvdb': '300474', 'locations': ("Criminal Minds S01E03 Won't Get Fooled Again WEBDL-720p.mkv",)}
|
{'imdb': 'tt0550498', 'tmdb': '282865', 'tvdb': '300474', 'locations': ("Criminal Minds S01E03 Won't Get Fooled Again WEBDL-720p.mkv",)}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
frozenset({("title", "Test"), ("locations", ("Test",))}): {
|
||||||
|
"Season 1": [
|
||||||
|
{'locations': ('Test S01E03.mkv',)}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +128,7 @@ def test_simple_cleanup_watched():
|
||||||
, "Movies": expected_movie_watched_list_2
|
, "Movies": expected_movie_watched_list_2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return_watched_list_1 = cleanup_watched(user_watched_list_1, user_watched_list_2)
|
return_watched_list_1 = cleanup_watched(user_watched_list_1, user_watched_list_2)
|
||||||
return_watched_list_2 = cleanup_watched(user_watched_list_2, user_watched_list_1)
|
return_watched_list_2 = cleanup_watched(user_watched_list_2, user_watched_list_1)
|
||||||
|
|
||||||
|
|
@ -149,6 +171,6 @@ def test_mapping_cleanup_watched():
|
||||||
|
|
||||||
return_watched_list_1 = cleanup_watched(user_watched_list_1, user_watched_list_2, user_mapping=user_mapping, library_mapping=library_mapping)
|
return_watched_list_1 = cleanup_watched(user_watched_list_1, user_watched_list_2, user_mapping=user_mapping, library_mapping=library_mapping)
|
||||||
return_watched_list_2 = cleanup_watched(user_watched_list_2, user_watched_list_1, user_mapping=user_mapping, library_mapping=library_mapping)
|
return_watched_list_2 = cleanup_watched(user_watched_list_2, user_watched_list_1, user_mapping=user_mapping, library_mapping=library_mapping)
|
||||||
|
|
||||||
assert return_watched_list_1 == expected_watched_list_1
|
assert return_watched_list_1 == expected_watched_list_1
|
||||||
assert return_watched_list_2 == expected_watched_list_2
|
assert return_watched_list_2 == expected_watched_list_2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue