Merge pull request #205 from luigi311/simplify_watched

Simplify get watched process
pull/223/head
Luigi311 2024-10-27 18:13:31 -06:00 committed by GitHub
commit 86f72997b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 319 additions and 386 deletions

View File

@ -37,12 +37,18 @@ def logger(message: str, log_type=0):
def log_marked(
username: str, library: str, movie_show: str, episode: str = None, duration=None
server_type: str,
server_name: str,
username: str,
library: str,
movie_show: str,
episode: str = None,
duration=None,
):
if mark_file is None:
return
output = f"{username}/{library}/{movie_show}"
output = f"{server_type}/{server_name}/{username}/{library}/{movie_show}"
if episode:
output += f"/{episode}"
@ -93,6 +99,20 @@ def search_mapping(dictionary: dict, key_value: str):
return None
# Return list of objects that exist in both lists including mappings
def match_list(list1, list2, list_mapping=None):
output = []
for element in list1:
if element in list2:
output.append(element)
elif list_mapping:
element_other = search_mapping(list_mapping, element)
if element_other in list2:
output.append(element)
return output
def future_thread_executor(
args: list, threads: int = None, override_threads: bool = False
):

View File

@ -13,13 +13,7 @@ from src.functions import (
log_marked,
str_to_bool,
)
from src.library import (
check_skip_logic,
generate_library_guids_dict,
)
from src.watched import (
combine_watched_dicts,
)
from src.library import generate_library_guids_dict
load_dotenv(override=True)
@ -112,7 +106,6 @@ class JellyfinEmby:
def __init__(self, server_type, baseurl, token, headers):
if server_type not in ["Jellyfin", "Emby"]:
raise Exception(f"Server type {server_type} not supported")
self.server_type = server_type
self.baseurl = baseurl
self.token = token
@ -127,6 +120,7 @@ class JellyfinEmby:
self.session = requests.Session()
self.users = self.get_users()
self.server_name = self.info(name_only=True)
def query(self, query, query_type, identifiers=None, json=None):
try:
@ -178,13 +172,15 @@ class JellyfinEmby:
)
raise Exception(e)
def info(self) -> str:
def info(self, name_only: bool = False) -> str:
try:
query_string = "/System/Info/Public"
response = self.query(query_string, "get")
if response:
if name_only:
return f"{response['ServerName']}"
return f"{self.server_type} {response['ServerName']}: {response['Version']}"
else:
return None
@ -223,13 +219,54 @@ class JellyfinEmby:
logger(f"{self.server_type}: Get users failed {e}", 2)
raise Exception(e)
def get_libraries(self):
try:
libraries = {}
# Theres no way to get all libraries so individually get list of libraries from all users
users = self.get_users()
for _, user_id in users.items():
user_libraries = self.query(f"/Users/{user_id}/Views", "get")
for library in user_libraries["Items"]:
library_id = library["Id"]
library_title = library["Name"]
# Get library items to check the type
media_info = self.query(
f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsPlayed&Recursive=True&excludeItemTypes=Folder&limit=100",
"get",
)
types = set(
[
x["Type"]
for x in media_info["Items"]
if x["Type"] in ["Movie", "Series", "Episode"]
]
)
all_types = set([x["Type"] for x in media_info["Items"]])
if not types:
logger(
f"{self.server_type}: Skipping Library {library_title} found wanted types: {all_types}",
1,
)
else:
libraries[library_title] = str(types)
return libraries
except Exception as e:
logger(f"{self.server_type}: Get libraries failed {e}", 2)
raise Exception(e)
def get_user_library_watched(
self, user_name, user_id, library_type, library_id, library_title
):
try:
user_name = user_name.lower()
user_watched = {}
user_watched[user_name] = {}
logger(
f"{self.server_type}: Generating watched for {user_name} in library {library_title}",
@ -238,7 +275,7 @@ class JellyfinEmby:
# Movies
if library_type == "Movie":
user_watched[user_name][library_title] = []
user_watched[library_title] = []
watched = self.query(
f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsPlayed&IncludeItemTypes=Movie&Recursive=True&Fields=ItemCounts,ProviderIds,MediaSources",
@ -274,7 +311,7 @@ class JellyfinEmby:
movie_guids = get_guids(self.server_type, movie)
# Append the movie dictionary to the list for the given user and library
user_watched[user_name][library_title].append(movie_guids)
user_watched[library_title].append(movie_guids)
logger(
f"{self.server_type}: Added {movie_guids} to {user_name} watched list",
3,
@ -283,7 +320,7 @@ class JellyfinEmby:
# TV Shows
if library_type in ["Series", "Episode"]:
# Initialize an empty dictionary for the given user and library
user_watched[user_name][library_title] = {}
user_watched[library_title] = {}
# Retrieve a list of watched TV shows
watched_shows = self.query(
@ -315,11 +352,6 @@ class JellyfinEmby:
if "Path" in show
else tuple()
)
show_display_name = (
show_guids["title"]
if show_guids["title"]
else show_guids["locations"]
)
show_guids = frozenset(show_guids.items())
@ -352,26 +384,22 @@ class JellyfinEmby:
if mark_episodes_list:
# Add the show dictionary to the user's watched list
if show_guids not in user_watched[user_name][library_title]:
user_watched[user_name][library_title][show_guids] = []
if show_guids not in user_watched[library_title]:
user_watched[library_title][show_guids] = []
user_watched[user_name][library_title][
show_guids
] = mark_episodes_list
user_watched[library_title][show_guids] = mark_episodes_list
for episode in mark_episodes_list:
logger(
f"{self.server_type}: Added {episode} to {user_name} {show_display_name} watched list",
1,
f"{self.server_type}: Added {episode} to {user_name} watched list",
3,
)
logger(
f"{self.server_type}: Got watched for {user_name} in library {library_title}",
1,
)
if library_title in user_watched[user_name]:
logger(
f"{self.server_type}: {user_watched[user_name][library_title]}", 3
)
if library_title in user_watched:
logger(f"{self.server_type}: {user_watched[library_title]}", 3)
return user_watched
except Exception as e:
@ -383,130 +411,64 @@ class JellyfinEmby:
logger(traceback.format_exc(), 2)
return {}
def get_users_watched(
self,
user_name,
user_id,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
):
def get_watched(self, users, sync_libraries):
try:
# Get all libraries
user_name = user_name.lower()
users_watched = {}
watched = []
libraries = []
for user_name, user_id in users.items():
libraries = []
all_libraries = self.query(f"/Users/{user_id}/Views", "get")
for library in all_libraries["Items"]:
library_id = library["Id"]
library_title = library["Name"]
identifiers = {
"library_id": library_id,
"library_title": library_title,
}
libraries.append(
self.query(
f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsPlayed&Recursive=True&excludeItemTypes=Folder&limit=100",
"get",
identifiers=identifiers,
all_libraries = self.query(f"/Users/{user_id}/Views", "get")
for library in all_libraries["Items"]:
library_id = library["Id"]
library_title = library["Name"]
if library_title not in sync_libraries:
continue
identifiers = {
"library_id": library_id,
"library_title": library_title,
}
libraries.append(
self.query(
f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsPlayed&Recursive=True&excludeItemTypes=Folder&limit=100",
"get",
identifiers=identifiers,
)
)
)
for library in libraries:
if len(library["Items"]) == 0:
continue
for library in libraries:
if len(library["Items"]) == 0:
continue
library_id = library["Identifiers"]["library_id"]
library_title = library["Identifiers"]["library_title"]
# Get all library types excluding "Folder"
types = set(
[
x["Type"]
for x in library["Items"]
if x["Type"] in ["Movie", "Series", "Episode"]
]
)
library_id = library["Identifiers"]["library_id"]
library_title = library["Identifiers"]["library_title"]
skip_reason = check_skip_logic(
library_title,
types,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
)
if skip_reason:
logger(
f"{self.server_type}: Skipping library {library_title}: {skip_reason}",
1,
# Get all library types excluding "Folder"
types = set(
[
x["Type"]
for x in library["Items"]
if x["Type"] in ["Movie", "Series", "Episode"]
]
)
continue
# If there are multiple types in library raise error
if types is None or len(types) < 1:
all_types = set([x["Type"] for x in library["Items"]])
logger(
f"{self.server_type}: Skipping Library {library_title} found types: {types}, all types: {all_types}",
1,
)
continue
for library_type in types:
# Get watched for user
watched.append(
self.get_user_library_watched(
for library_type in types:
# Get watched for user
watched = self.get_user_library_watched(
user_name,
user_id,
library_type,
library_id,
library_title,
)
)
return watched
except Exception as e:
logger(f"{self.server_type}: Failed to get users watched, Error: {e}", 2)
raise Exception(e)
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(
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_combine = combine_watched_dicts(user_watched)
for user, user_watched_temp in user_watched_combine.items():
if user not in users_watched:
users_watched[user] = {}
users_watched[user].update(user_watched_temp)
if user_name.lower() not in users_watched:
users_watched[user_name.lower()] = {}
users_watched[user_name.lower()].update(watched)
return users_watched
except Exception as e:
@ -570,6 +532,8 @@ class JellyfinEmby:
logger(msg, 6)
log_marked(
self.server_type,
self.server_name,
user_name,
library,
jellyfin_video.get("Name"),
@ -592,6 +556,8 @@ class JellyfinEmby:
logger(msg, 6)
log_marked(
self.server_type,
self.server_name,
user_name,
library,
jellyfin_video.get("Name"),
@ -698,6 +664,8 @@ class JellyfinEmby:
logger(msg, 6)
log_marked(
self.server_type,
self.server_name,
user_name,
library,
jellyfin_episode.get("SeriesName"),
@ -726,6 +694,8 @@ class JellyfinEmby:
logger(msg, 6)
log_marked(
self.server_type,
self.server_name,
user_name,
library,
jellyfin_episode.get("SeriesName"),

View File

@ -1,5 +1,6 @@
from src.functions import (
logger,
match_list,
search_mapping,
)
@ -129,6 +130,77 @@ def check_whitelist_logic(
return skip_reason
def filter_libaries(
server_libraries,
blacklist_library,
blacklist_library_type,
whitelist_library,
whitelist_library_type,
library_mapping=None,
):
filtered_libaries = []
for library in server_libraries:
skip_reason = check_skip_logic(
library,
server_libraries[library],
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
)
if skip_reason:
logger(f"Skipping library {library}: {skip_reason}", 1)
continue
filtered_libaries.append(library)
return filtered_libaries
def setup_libraries(
server_1,
server_2,
blacklist_library,
blacklist_library_type,
whitelist_library,
whitelist_library_type,
library_mapping=None,
):
server_1_libraries = server_1.get_libraries()
server_2_libraries = server_2.get_libraries()
logger(f"Server 1 libraries: {server_1_libraries}", 1)
logger(f"Server 2 libraries: {server_2_libraries}", 1)
# Filter out all blacklist, whitelist libaries
filtered_server_1_libraries = filter_libaries(
server_1_libraries,
blacklist_library,
blacklist_library_type,
whitelist_library,
whitelist_library_type,
library_mapping,
)
filtered_server_2_libraries = filter_libaries(
server_2_libraries,
blacklist_library,
blacklist_library_type,
whitelist_library,
whitelist_library_type,
library_mapping,
)
output_server_1_libaries = match_list(
filtered_server_1_libraries, filtered_server_2_libraries, library_mapping
)
output_server_2_libaries = match_list(
filtered_server_2_libraries, filtered_server_1_libraries, library_mapping
)
return output_server_1_libaries, output_server_2_libaries
def show_title_dict(user_list: dict):
try:
show_output_dict = {}

View File

@ -2,6 +2,7 @@ import os, traceback, json
from dotenv import load_dotenv
from time import sleep, perf_counter
from src.library import setup_libraries
from src.functions import (
logger,
str_to_bool,
@ -153,24 +154,24 @@ def main_loop():
server_1, server_2, blacklist_users, whitelist_users, user_mapping
)
logger("Creating watched lists", 1)
server_1_watched = server_1[1].get_watched(
server_1_users,
server_1_libraries, server_2_libraries = setup_libraries(
server_1[1],
server_2[1],
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library,
whitelist_library_type,
library_mapping,
)
logger("Creating watched lists", 1)
server_1_watched = server_1[1].get_watched(
server_1_users, server_1_libraries
)
logger("Finished creating watched list server 1", 1)
server_2_watched = server_2[1].get_watched(
server_2_users,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
server_2_users, server_2_libraries
)
logger("Finished creating watched list server 2", 1)

View File

@ -19,10 +19,7 @@ from src.functions import (
log_marked,
str_to_bool,
)
from src.library import (
check_skip_logic,
generate_library_guids_dict,
)
from src.library import generate_library_guids_dict
load_dotenv(override=True)
@ -186,7 +183,7 @@ def get_user_library_watched(user, user_plex, library):
if show_guids and episode_guids:
watched[show_guids] = episode_guids
logger(
f"Plex: Added {episode_guids} to {user_name} {show_guids} watched list",
f"Plex: Added {episode_guids} to {user_name} watched list",
3,
)
@ -317,7 +314,15 @@ def update_user_watched(user, user_plex, library, videos, dryrun):
else:
logger(msg, 6)
log_marked(user.title, library, movies_search.title, None, None)
log_marked(
"Plex",
user_plex.friendlyName,
user.title,
library,
movies_search.title,
None,
None,
)
elif video_status["time"] > 60_000:
msg = f"Plex: {movies_search.title} as partially watched for {floor(video_status['time'] / 60_000)} minutes for {user.title} in {library}"
if not dryrun:
@ -327,6 +332,8 @@ def update_user_watched(user, user_plex, library, videos, dryrun):
logger(msg, 6)
log_marked(
"Plex",
user_plex.friendlyName,
user.title,
library,
movies_search.title,
@ -358,6 +365,8 @@ def update_user_watched(user, user_plex, library, videos, dryrun):
logger(msg, 6)
log_marked(
"Plex",
user_plex.friendlyName,
user.title,
library,
show_search.title,
@ -372,6 +381,8 @@ def update_user_watched(user, user_plex, library, videos, dryrun):
logger(msg, 6)
log_marked(
"Plex",
user_plex.friendlyName,
user.title,
library,
show_search.title,
@ -466,15 +477,24 @@ class Plex:
logger(f"Plex: Failed to get users, Error: {e}", 2)
raise Exception(e)
def get_watched(
self,
users,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
):
def get_libraries(self):
try:
output = {}
libraries = self.plex.library.sections()
for library in libraries:
library_title = library.title
library_type = library.type
output[library_title] = library_type
return output
except Exception as e:
logger(f"Plex: Failed to get libraries, Error: {e}", 2)
raise Exception(e)
def get_watched(self, users, sync_libraries):
try:
# Get all libraries
users_watched = {}
@ -500,23 +520,7 @@ class Plex:
libraries = user_plex.library.sections()
for library in libraries:
library_title = library.title
library_type = library.type
skip_reason = check_skip_logic(
library_title,
library_type,
blacklist_library,
whitelist_library,
blacklist_library_type,
whitelist_library_type,
library_mapping,
)
if skip_reason:
logger(
f"Plex: Skipping library {library_title}: {skip_reason}", 1
)
if library.title not in sync_libraries:
continue
user_watched = get_user_library_watched(user, user_plex, library)

View File

@ -5,33 +5,6 @@ from src.functions import logger, search_mapping, contains_nested
from src.library import generate_library_guids_dict
def combine_watched_dicts(dicts: list):
# Ensure that the input is a list of dictionaries
if not all(isinstance(d, dict) for d in dicts):
raise ValueError("Input must be a list of dictionaries")
combined_dict = {}
for single_dict in dicts:
for key, value in single_dict.items():
if key not in combined_dict:
combined_dict[key] = {}
for subkey, subvalue in value.items():
if subkey in combined_dict[key]:
# 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}'"
)
else:
# If the subkey does not exist in the combined dictionary, add it
combined_dict[key][subkey] = subvalue
return combined_dict
def check_remove_entry(video, library, video_index, library_watched_list_2):
if video_index is not None:
if (

View File

@ -7,7 +7,7 @@ DRYRUN = "True"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -7,7 +7,7 @@ DRYRUN = "True"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -7,7 +7,7 @@ DRYRUN = "True"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -7,7 +7,7 @@ DRYRUN = "True"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -7,7 +7,7 @@ DRYRUN = "True"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -7,7 +7,7 @@ DRYRUN = "False"
DEBUG = "True"
## Debugging level, "info" is default, "debug" is more verbose
DEBUG_LEVEL = "info"
DEBUG_LEVEL = "debug"
## If set to true then the script will only run once and then exit
RUN_ONLY_ONCE = "True"

View File

@ -13,7 +13,7 @@ parent = os.path.dirname(current)
# the sys.path.
sys.path.append(parent)
from src.watched import cleanup_watched, combine_watched_dicts
from src.watched import cleanup_watched
tv_shows_watched_list_1 = {
frozenset(
@ -541,116 +541,3 @@ def test_mapping_cleanup_watched():
assert return_watched_list_1 == expected_watched_list_1
assert return_watched_list_2 == expected_watched_list_2
def test_combine_watched_dicts():
input_watched = [
{
"test3": {
"Anime Movies": [
{
"title": "Ponyo",
"tmdb": "12429",
"imdb": "tt0876563",
"locations": ("Ponyo (2008) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
{
"title": "Spirited Away",
"tmdb": "129",
"imdb": "tt0245429",
"locations": ("Spirited Away (2001) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
{
"title": "Castle in the Sky",
"tmdb": "10515",
"imdb": "tt0092067",
"locations": ("Castle in the Sky (1986) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
]
}
},
{"test3": {"Anime Shows": {}}},
{"test3": {"Cartoon Shows": {}}},
{
"test3": {
"Shows": {
frozenset(
{
("tmdb", "64464"),
("tvdb", "301824"),
("tvrage", "45210"),
("title", "11.22.63"),
("locations", ("11.22.63",)),
("imdb", "tt2879552"),
}
): [
{
"imdb": "tt4460418",
"title": "The Rabbit Hole",
"locations": (
"11.22.63 S01E01 The Rabbit Hole Bluray-1080p.mkv",
),
"status": {"completed": True, "time": 0},
}
]
}
}
},
{"test3": {"Subbed Anime": {}}},
]
expected = {
"test3": {
"Anime Movies": [
{
"title": "Ponyo",
"tmdb": "12429",
"imdb": "tt0876563",
"locations": ("Ponyo (2008) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
{
"title": "Spirited Away",
"tmdb": "129",
"imdb": "tt0245429",
"locations": ("Spirited Away (2001) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
{
"title": "Castle in the Sky",
"tmdb": "10515",
"imdb": "tt0092067",
"locations": ("Castle in the Sky (1986) Bluray-1080p.mkv",),
"status": {"completed": True, "time": 0},
},
],
"Anime Shows": {},
"Cartoon Shows": {},
"Shows": {
frozenset(
{
("tmdb", "64464"),
("tvdb", "301824"),
("tvrage", "45210"),
("title", "11.22.63"),
("locations", ("11.22.63",)),
("imdb", "tt2879552"),
}
): [
{
"imdb": "tt4460418",
"title": "The Rabbit Hole",
"locations": (
"11.22.63 S01E01 The Rabbit Hole Bluray-1080p.mkv",
),
"status": {"completed": True, "time": 0},
}
]
},
"Subbed Anime": {},
}
}
assert combine_watched_dicts(input_watched) == expected

View File

@ -74,74 +74,74 @@ def check_marklog(lines, expected_values):
def main():
args = parse_args()
expected_jellyfin = [
"jellyplex_watched/Movies/Five Nights at Freddy's",
"jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741",
"jellyplex_watched/Movies/The Family Plan",
"jellyplex_watched/Movies/Five Nights at Freddy's",
"jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/Five Nights at Freddy's",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741",
"Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan",
"Emby/Emby-Server/jellyplex_watched/Movies/Five Nights at Freddy's",
"Emby/Emby-Server/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
]
expected_emby = [
"jellyplex_watched/Movies/Tears of Steel",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429",
"JellyUser/Movies/Tears of Steel",
"JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/Tears of Steel",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Tears of Steel",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
]
expected_plex = [
"JellyUser/Movies/Big Buck Bunny",
"JellyUser/Movies/Killers of the Flower Moon/4",
"JellyUser/Shows/Doctor Who/The Unquiet Dead",
"JellyUser/Shows/Doctor Who/Aliens of London (1)/4",
"JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"jellyplex_watched/Movies/Big Buck Bunny",
"jellyplex_watched/Movies/The Family Plan",
"jellyplex_watched/Movies/Killers of the Flower Moon/4",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Big Buck Bunny",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Killers of the Flower Moon/4",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/The Unquiet Dead",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/Aliens of London (1)/4",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"Emby/Emby-Server/jellyplex_watched/Movies/Big Buck Bunny",
"Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan",
"Emby/Emby-Server/jellyplex_watched/Movies/Killers of the Flower Moon/4",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
]
expected_dry = expected_emby + expected_plex + expected_jellyfin
expected_write = [
"jellyplex_watched/Movies/Five Nights at Freddy's",
"jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741",
"JellyUser/Movies/Big Buck Bunny",
"JellyUser/Movies/Killers of the Flower Moon/4",
"JellyUser/Shows/Doctor Who/The Unquiet Dead",
"JellyUser/Shows/Doctor Who/Aliens of London (1)/4",
"JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"jellyplex_watched/Movies/Tears of Steel",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429",
"jellyplex_watched/Movies/Big Buck Bunny",
"jellyplex_watched/Movies/The Family Plan",
"jellyplex_watched/Movies/Five Nights at Freddy's",
"jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5",
"jellyplex_watched/Movies/Killers of the Flower Moon/4",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5",
"jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead",
"jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
"JellyUser/Movies/Tears of Steel",
"JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/Five Nights at Freddy's",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/301215",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/300670",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Aftermath",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/300741",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Big Buck Bunny",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Killers of the Flower Moon/4",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/The Unquiet Dead",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Doctor Who/Aliens of London (1)/4",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4",
"Plex/JellyPlex-CI/jellyplex_watched/Movies/Tears of Steel",
"Plex/JellyPlex-CI/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Parallels and Interiors/240429",
"Emby/Emby-Server/jellyplex_watched/Movies/Big Buck Bunny",
"Emby/Emby-Server/jellyplex_watched/Movies/The Family Plan",
"Emby/Emby-Server/jellyplex_watched/Movies/Five Nights at Freddy's",
"Emby/Emby-Server/jellyplex_watched/Movies/The Hunger Games: The Ballad of Songbirds & Snakes/5",
"Emby/Emby-Server/jellyplex_watched/Movies/Killers of the Flower Moon/4",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Rose",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The End of the World/5",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/The Unquiet Dead",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Doctor Who (2005)/Aliens of London (1)/4",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Departure/5",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/Secrets and Lies",
"Emby/Emby-Server/jellyplex_watched/TV Shows/Monarch: Legacy of Monsters/The Way Out",
"Jellyfin/Jellyfin-Server/JellyUser/Movies/Tears of Steel",
"Jellyfin/Jellyfin-Server/JellyUser/Shows/Monarch: Legacy of Monsters/Parallels and Interiors/4"
]
# Expected values for the mark.log file, dry-run is slightly different than write-run
@ -164,6 +164,12 @@ def main():
lines = read_marklog()
if not check_marklog(lines, expected_values):
print("Failed to validate marklog")
for line in lines:
# Remove the newline character
line = line.strip()
print(line)
exit(1)
print("Successfully validated marklog")