Skip to content

Commit

Permalink
Merge pull request #11 from luigi311/dev
Browse files Browse the repository at this point in the history
Fix plex login, Match plex marking logic to jellyfin
  • Loading branch information
luigi311 authored Jun 11, 2022
2 parents 6a95086 + ef48014 commit a6f95c7
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 79 deletions.
31 changes: 18 additions & 13 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
# Do not mark any shows/movies as played and instead just output to log if they would of been marked.
## Do not mark any shows/movies as played and instead just output to log if they would of been marked.
DRYRUN = "True"
# Additional logging information
## Additional logging information
DEBUG = "True"
# How often to run the script in seconds
## How often to run the script in seconds
SLEEP_DURATION = "3600"
# Log file where all output will be written to
## Log file where all output will be written to
LOGFILE = "log.log"
# Map usernames between plex and jellyfin in the event that they are different, order does not matter
## Map usernames between plex and jellyfin in the event that they are different, order does not matter
#USER_MAPPING = { "testuser2": "testuser3" }
# Map libraries between plex and jellyfin in the even that they are different, order does not matter
## Map libraries between plex and jellyfin in the even that they are different, order does not matter
#LIBRARY_MAPPING = { "Shows": "TV Shows" }

# URL of the plex server, use hostname or IP address if the hostname is not resolving correctly

## Recommended to use token as it is faster to connect as it is direct to the server instead of going through the plex servers
## URL of the plex server, use hostname or IP address if the hostname is not resolving correctly
PLEX_BASEURL = "http://localhost:32400"
# Plex token https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/
## Plex token https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/
PLEX_TOKEN = "SuperSecretToken"
# If not using plex token then use username and password of the server admin
## If not using plex token then use username and password of the server admin along with the servername
#PLEX_USERNAME = ""
#PLEX_PASSWORD = ""
#PLEX_SERVERNAME = "Plex Server"


# Jellyfin server URL, use hostname or IP address if the hostname is not resolving correctly
## Jellyfin server URL, use hostname or IP address if the hostname is not resolving correctly
JELLYFIN_BASEURL = "http://localhost:8096"
# Jellyfin api token, created manually by logging in to the jellyfin server admin dashboard and creating an api key
## Jellyfin api token, created manually by logging in to the jellyfin server admin dashboard and creating an api key
JELLYFIN_TOKEN = "SuperSecretToken"

# Blacklisting/Whitelisting libraries, library types such as Movies, TV Shows, and users. Mappings apply so if the mapping for the user or library exist then both will be excluded.

## Blacklisting/Whitelisting libraries, library types such as Movies/TV Shows, and users. Mappings apply so if the mapping for the user or library exist then both will be excluded.
#BLACKLIST_LIBRARY = ""
#WHITELIST_LIBRARY = ""
#BLACKLIST_LIBRARY_TYPE = ""
#WHITELIST_LIBRARY_TYPE = ""
#BLACKLIST_USERS = ""
WHITELIST_USERS = "testuser1,testuser2"
WHITELIST_USERS = "testuser1,testuser2"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# JellyPlex-Watched

[![Codacy Badge](https://app.codacy.com/project/badge/Grade/26b47c5db63942f28f02f207f692dc85)](https://www.codacy.com/gh/luigi311/JellyPlex-Watched/dashboard?utm_source=github.com&utm_medium=referral&utm_content=luigi311/JellyPlex-Watched&utm_campaign=Badge_Grade)

Sync watched between jellyfin and plex

## Description
Expand Down
42 changes: 21 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_mapping=None):
modified_watched_list_1 = copy.deepcopy(watched_list_1)

# remove entries from plex_watched that are in jellyfin_watched
for user_1 in watched_list_1:
user_other = None
Expand Down Expand Up @@ -47,7 +47,7 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
if watch_list_1_key == watch_list_2_item_key and watch_list_1_value == watch_list_2_item_value:
if item in modified_watched_list_1[user_1][library_1]:
modified_watched_list_1[user_1][library_1].remove(item)

# TV Shows
elif isinstance(watched_list_1[user_1][library_1], dict):
if item in watched_list_2[user_2][library_2]:
Expand All @@ -60,22 +60,22 @@ def cleanup_watched(watched_list_1, watched_list_2, user_mapping=None, library_m
if watch_list_1_episode_key == watch_list_2_episode_key and watch_list_1_episode_value == watch_list_2_episode_value:
if episode in modified_watched_list_1[user_1][library_1][item][season]:
modified_watched_list_1[user_1][library_1][item][season].remove(episode)

# If season is empty, remove season
if len(modified_watched_list_1[user_1][library_1][item][season]) == 0:
if season in modified_watched_list_1[user_1][library_1][item]:
del modified_watched_list_1[user_1][library_1][item][season]

# If the show is empty, remove the show
# If the show is empty, remove the show
if len(modified_watched_list_1[user_1][library_1][item]) == 0:
if item in modified_watched_list_1[user_1][library_1]:
del modified_watched_list_1[user_1][library_1][item]

# If library is empty then remove it
if len(modified_watched_list_1[user_1][library_1]) == 0:
if library_1 in modified_watched_list_1[user_1]:
del modified_watched_list_1[user_1][library_1]

# If user is empty delete user
if len(modified_watched_list_1[user_1]) == 0:
del modified_watched_list_1[user_1]
Expand All @@ -95,10 +95,10 @@ def setup_black_white_lists(library_mapping=None):
if library_other:
temp_library.append(library_other)

blacklist_library = blacklist_library + temp_library
blacklist_library = blacklist_library + temp_library
else:
blacklist_library = []

logger(f"Blacklist Library: {blacklist_library}", 1)

whitelist_library = os.getenv("WHITELIST_LIBRARY")
Expand Down Expand Up @@ -140,11 +140,11 @@ def setup_black_white_lists(library_mapping=None):
if blacklist_users:
if len(blacklist_users) > 0:
blacklist_users = blacklist_users.split(",")
blacklist_users = [x.lower().strip() for x in blacklist_users]
blacklist_users = [x.lower().strip() for x in blacklist_users]
else:
blacklist_users = []
logger(f"Blacklist Users: {blacklist_users}", 1)

whitelist_users = os.getenv("WHITELIST_USERS")
if whitelist_users:
if len(whitelist_users) > 0:
Expand All @@ -159,11 +159,11 @@ def setup_black_white_lists(library_mapping=None):
return blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, blacklist_users, whitelist_users

def setup_users(plex, jellyfin, blacklist_users, whitelist_users, user_mapping=None):

# generate list of users from plex.users
plex_users = [ x.title.lower() for x in plex.users ]
jellyfin_users = [ key.lower() for key in jellyfin.users.keys() ]

# combined list of overlapping users from plex and jellyfin
users = {}

Expand All @@ -173,10 +173,10 @@ def setup_users(plex, jellyfin, blacklist_users, whitelist_users, user_mapping=N
if jellyfin_plex_mapped_user:
users[plex_user] = jellyfin_plex_mapped_user
continue

if plex_user in jellyfin_users:
users[plex_user] = plex_user

for jellyfin_user in jellyfin_users:
if user_mapping:
plex_jellyfin_mapped_user = search_mapping(user_mapping, jellyfin_user)
Expand All @@ -186,27 +186,27 @@ def setup_users(plex, jellyfin, blacklist_users, whitelist_users, user_mapping=N

if jellyfin_user in plex_users:
users[jellyfin_user] = jellyfin_user

logger(f"User list that exist on both servers {users}", 1)

users_filtered = {}
for user in users:
# whitelist_user is not empty and user lowercase is not in whitelist lowercase
if len(whitelist_users) > 0:
if user not in whitelist_users and users[user] not in whitelist_users:
logger(f"{user} or {users[user]} is not in whitelist", 1)
continue

if user not in blacklist_users and users[user] not in blacklist_users:
users_filtered[user] = users[user]

logger(f"Filtered user list {users_filtered}", 1)

plex_users = []
for plex_user in plex.users:
if plex_user.title.lower() in users_filtered.keys() or plex_user.title.lower() in users_filtered.values():
plex_users.append(plex_user)

jellyfin_users = {}
for jellyfin_user, jellyfin_id in jellyfin.users.items():
if jellyfin_user.lower() in users_filtered.keys() or jellyfin_user.lower() in users_filtered.values():
Expand Down Expand Up @@ -267,11 +267,11 @@ def main():
# Update watched status
plex.update_watched(jellyfin_watched, user_mapping, library_mapping, dryrun)
jellyfin.update_watched(plex_watched, user_mapping, library_mapping, dryrun)


if __name__ == "__main__":
sleep_timer = float(os.getenv("SLEEP_TIMER", "3600"))

while(True):
try:
main()
Expand Down
2 changes: 1 addition & 1 deletion src/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def check_skip_logic(library_title, library_type, blacklist_library, whitelist_l
if len(whitelist_library) > 0:
if library_title.lower() not in [x.lower() for x in whitelist_library]:
skip_reason = "is not whitelist_library"

if library_other:
if library_other.lower() not in [x.lower() for x in whitelist_library]:
skip_reason = "is not whitelist_library"
Expand Down
30 changes: 15 additions & 15 deletions src/jellyfin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self):

if not self.baseurl:
raise Exception("Jellyfin baseurl not set")

if not self.token:
raise Exception("Jellyfin token not set")

Expand All @@ -27,7 +27,7 @@ def query(self, query, query_type):

if query_type == "get":
response = requests.get(self.baseurl + query, headers={"accept":"application/json", "X-Emby-Token": self.token})

elif query_type == "post":
authorization = (
'MediaBrowser , '
Expand All @@ -42,19 +42,19 @@ def query(self, query, query_type):
except Exception as e:
logger(e, 2)
logger(response, 2)

def get_users(self):
users = {}

query = "/Users"
response = self.query(query, "get")

# If reponse is not empty
if response:
for user in response:
users[user["Name"]] = user["Id"]

return users
return users

def get_jellyfin_watched(self, users, blacklist_library, whitelist_library, blacklist_library_type, whitelist_library_type, library_mapping=None):
users_watched = {}
Expand All @@ -64,12 +64,12 @@ def get_jellyfin_watched(self, users, blacklist_library, whitelist_library, blac
user_name = user_name.lower()

libraries = self.query(f"/Users/{user_id}/Views", "get")["Items"]

for library in libraries:
library_title = library["Name"]
library_id = library["Id"]
watched = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&Filters=IsPlayed&limit=1", "get")

if len(watched["Items"]) == 0:
logger(f"Jellyfin: No watched items found in library {library_title}", 1)
continue
Expand Down Expand Up @@ -123,7 +123,7 @@ def get_jellyfin_watched(self, users, blacklist_library, whitelist_library, blac
# Lowercase episode["ProviderIds"] keys
episode["ProviderIds"] = {k.lower(): v for k, v in episode["ProviderIds"].items()}
users_watched[user_name][library_title][show["Name"]][season["Name"]].append(episode["ProviderIds"])

return users_watched

def update_watched(self, watched_list, user_mapping=None, library_mapping=None, dryrun=False):
Expand All @@ -135,7 +135,7 @@ def update_watched(self, watched_list, user_mapping=None, library_mapping=None,
user_other = user_mapping[user]
elif user in user_mapping.values():
user_other = search_mapping(user_mapping, user)

if user_other:
logger(f"Swapping user {user} with {user_other}", 1)
user = user_other
Expand All @@ -145,13 +145,13 @@ def update_watched(self, watched_list, user_mapping=None, library_mapping=None,
if user.lower() == key.lower():
user_id = self.users[key]
break

if not user_id:
logger(f"{user} not found in Jellyfin", 2)
break

jellyfin_libraries = self.query(f"/Users/{user_id}/Views", "get")["Items"]

for library, videos in libraries.items():
if library_mapping:
library_other = None
Expand All @@ -160,7 +160,7 @@ def update_watched(self, watched_list, user_mapping=None, library_mapping=None,
library_other = library_mapping[library]
elif library in library_mapping.values():
library_other = search_mapping(library_mapping, library)

if library_other:
logger(f"Swapping library {library} with {library_other}", 1)
library = library_other
Expand All @@ -174,7 +174,7 @@ def update_watched(self, watched_list, user_mapping=None, library_mapping=None,
if jellyfin_library["Name"] == library:
library_id = jellyfin_library["Id"]
continue

if library_id:
logger(f"Jellyfin: Updating watched for {user} in library {library}", 1)
library_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&limit=1", "get")
Expand All @@ -196,7 +196,7 @@ def update_watched(self, watched_list, user_mapping=None, library_mapping=None,
else:
logger(f"Dryrun {msg}", 0)
break

# TV Shows
if library_type == "Episode":
jellyfin_search = self.query(f"/Users/{user_id}/Items?SortBy=SortName&SortOrder=Ascending&Recursive=true&ParentId={library_id}&isPlayed=false", "get")
Expand Down
Loading

0 comments on commit a6f95c7

Please sign in to comment.