Skip to content

Commit

Permalink
Jellyfin/Emby: Check partial sync support
Browse files Browse the repository at this point in the history
- add `is_partial_update_supported` method to each class to validate given version against earliest known supported version
- add `get_server_version` to get server version number
- add `update_partial` parameter to user update function, deciding whether or not to allow partial updates
  • Loading branch information
awakenedhaggis authored and luigi311 committed Jul 15, 2024
1 parent 679d353 commit b1639ea
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 68 deletions.
6 changes: 5 additions & 1 deletion src/emby.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from src.jellyfin_emby import JellyfinEmby
from packaging import version


class Emby(JellyfinEmby):
Expand All @@ -8,7 +9,7 @@ def __init__(self, baseurl, token):
'Client="JellyPlex-Watched", '
'Device="script", '
'DeviceId="script", '
'Version="0.0.0"'
'Version="6.0.2"'
)
headers = {
"Accept": "application/json",
Expand All @@ -19,3 +20,6 @@ def __init__(self, baseurl, token):
super().__init__(
server_type="Emby", baseurl=baseurl, token=token, headers=headers
)

def is_partial_update_supported(self, server_version):
return server_version > version.parse("4.4")
7 changes: 5 additions & 2 deletions src/jellyfin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from src.jellyfin_emby import JellyfinEmby

from packaging import version

class Jellyfin(JellyfinEmby):
def __init__(self, baseurl, token):
Expand All @@ -8,7 +8,7 @@ def __init__(self, baseurl, token):
'Client="JellyPlex-Watched", '
'Device="script", '
'DeviceId="script", '
'Version="5.2.0", '
'Version="6.0.2", '
f'Token="{token}"'
)
headers = {
Expand All @@ -19,3 +19,6 @@ def __init__(self, baseurl, token):
super().__init__(
server_type="Jellyfin", baseurl=baseurl, token=token, headers=headers
)

def is_partial_update_supported(self, server_version):
return server_version >= version.parse("10.9.0")
145 changes: 80 additions & 65 deletions src/jellyfin_emby.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ def __init__(self, server_type, baseurl, token, headers):
raise Exception(f"{self.server_type} token not set")

self.session = requests.Session()
self.version = version.parse(self.info(version=True))
self.users = self.get_users()

def query(self, query, query_type, identifiers=None, json=None):
Expand Down Expand Up @@ -178,17 +177,13 @@ def query(self, query, query_type, identifiers=None, json=None):
)
raise Exception(e)

def info(self, version=False) -> str:
def info(self) -> str:
try:
query_string = "/System/Info/Public"

response = self.query(query_string, "get")

if response:
# Return version only if requested
if version:
return response['Version']

return f"{self.server_type} {response['ServerName']}: {response['Version']}"
else:
return None
Expand All @@ -197,6 +192,19 @@ def info(self, version=False) -> str:
logger(f"{self.server_type}: Get server name failed {e}", 2)
raise Exception(e)

def get_server_version(self):
try:
response = self.query('/System/Info/Public', 'get')

if response:
return version.parse(response['Version'])
else:
return None

except Exception as e:
logger(f"{self.server_type}: Get server version failed: {e}", 2)
raise Exception(e)

def get_users(self):
try:
users = {}
Expand Down Expand Up @@ -505,7 +513,7 @@ def get_watched(
raise Exception(e)

def update_user_watched(
self, user_name, user_id, library, library_id, videos, dryrun
self, user_name, user_id, library, library_id, videos, update_partial, dryrun
):
try:
logger(
Expand Down Expand Up @@ -565,33 +573,29 @@ def update_user_watched(
library,
jellyfin_video.get("Name"),
)
else:
# Handle partially watched movies not supported in jellyfin < 10.9.0
if self.server_type == "Jellyfin" and self.version < version.parse("10.9.0"):
logger(f"{self.server_type}: Skipping movie {jellyfin_video.get('Name')} as partially watched not supported in Jellyfin < 10.9.0", 4)
else:
msg = f"{self.server_type}: {jellyfin_video.get('Name')} as partially watched for {floor(movie_status['time'] / 60_000)} minutes for {user_name} in {library}"

if not dryrun:
logger(msg, 5)
playback_position_payload = {
"PlaybackPositionTicks": movie_status["time"]
* 10_000,
}
self.query(
f"/Users/{user_id}/Items/{jellyfin_video_id}/UserData",
"post",
json=playback_position_payload,
)
else:
logger(msg, 6)

log_marked(
user_name,
library,
jellyfin_video.get("Name"),
duration=floor(movie_status["time"] / 60_000),
elif update_partial:
msg = f"{self.server_type}: {jellyfin_video.get('Name')} as partially watched for {floor(movie_status['time'] / 60_000)} minutes for {user_name} in {library}"

if not dryrun:
logger(msg, 5)
playback_position_payload = {
"PlaybackPositionTicks": movie_status["time"]
* 10_000,
}
self.query(
f"/Users/{user_id}/Items/{jellyfin_video_id}/UserData",
"post",
json=playback_position_payload,
)
else:
logger(msg, 6)

log_marked(
user_name,
library,
jellyfin_video.get("Name"),
duration=floor(movie_status["time"] / 60_000),
)
else:
logger(
f"{self.server_type}: Skipping movie {jellyfin_video.get('Name')} as it is not in mark list for {user_name}",
Expand Down Expand Up @@ -698,39 +702,35 @@ def update_user_watched(
jellyfin_episode.get("SeriesName"),
jellyfin_episode.get("Name"),
)
else:
# Handle partially watched episodes not supported in jellyfin < 10.9.0
if self.server_type == "Jellyfin" and self.version < version.parse("10.9.0"):
logger(f"{self.server_type}: Skipping episode {jellyfin_episode.get('Name')} as partially watched not supported in Jellyfin < 10.9.0", 4)
else:
msg = (
f"{self.server_type}: {jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode.get('IndexNumber')} {jellyfin_episode.get('Name')}"
+ f" as partially watched for {floor(episode_status['time'] / 60_000)} minutes for {user_name} in {library}"
)
elif update_partial:
msg = (
f"{self.server_type}: {jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode.get('IndexNumber')} {jellyfin_episode.get('Name')}"
+ f" as partially watched for {floor(episode_status['time'] / 60_000)} minutes for {user_name} in {library}"
)

if not dryrun:
logger(msg, 5)
playback_position_payload = {
"PlaybackPositionTicks": episode_status[
"time"
]
* 10_000,
}
self.query(
f"/Users/{user_id}/Items/{jellyfin_episode_id}/UserData",
"post",
json=playback_position_payload,
)
else:
logger(msg, 6)

log_marked(
user_name,
library,
jellyfin_episode.get("SeriesName"),
jellyfin_episode.get("Name"),
duration=floor(episode_status["time"] / 60_000),
if not dryrun:
logger(msg, 5)
playback_position_payload = {
"PlaybackPositionTicks": episode_status[
"time"
]
* 10_000,
}
self.query(
f"/Users/{user_id}/Items/{jellyfin_episode_id}/UserData",
"post",
json=playback_position_payload,
)
else:
logger(msg, 6)

log_marked(
user_name,
library,
jellyfin_episode.get("SeriesName"),
jellyfin_episode.get("Name"),
duration=floor(episode_status["time"] / 60_000),
)
else:
logger(
f"{self.server_type}: Skipping episode {jellyfin_episode.get('Name')} as it is not in mark list for {user_name}",
Expand All @@ -754,6 +754,15 @@ def update_watched(
self, watched_list, user_mapping=None, library_mapping=None, dryrun=False
):
try:
server_version = self.get_server_version()
update_partial = self.is_partial_update_supported(server_version)

if not update_partial:
logger(
f"{self.server_type}: Server version {server_version} does not support updating playback position.",
2,
)

for user, libraries in watched_list.items():
logger(f"{self.server_type}: Updating for entry {user}, {libraries}", 1)
user_other = None
Expand Down Expand Up @@ -826,7 +835,13 @@ def update_watched(

if library_id:
self.update_user_watched(
user_name, user_id, library, library_id, videos, dryrun
user_name,
user_id,
library,
library_id,
videos,
update_partial,
dryrun
)

except Exception as e:
Expand Down

0 comments on commit b1639ea

Please sign in to comment.