Jellyfin/Emby: Check partial sync support

- 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
pull/185/head
awakenedhaggis 2024-07-15 11:12:29 -06:00 committed by Luis Garcia
parent 679d3535b1
commit b1639eab0f
3 changed files with 89 additions and 67 deletions

View File

@ -1,4 +1,5 @@
from src.jellyfin_emby import JellyfinEmby from src.jellyfin_emby import JellyfinEmby
from packaging import version
class Emby(JellyfinEmby): class Emby(JellyfinEmby):
@ -8,7 +9,7 @@ class Emby(JellyfinEmby):
'Client="JellyPlex-Watched", ' 'Client="JellyPlex-Watched", '
'Device="script", ' 'Device="script", '
'DeviceId="script", ' 'DeviceId="script", '
'Version="0.0.0"' 'Version="6.0.2"'
) )
headers = { headers = {
"Accept": "application/json", "Accept": "application/json",
@ -19,3 +20,6 @@ class Emby(JellyfinEmby):
super().__init__( super().__init__(
server_type="Emby", baseurl=baseurl, token=token, headers=headers server_type="Emby", baseurl=baseurl, token=token, headers=headers
) )
def is_partial_update_supported(self, server_version):
return server_version > version.parse("4.4")

View File

@ -1,5 +1,5 @@
from src.jellyfin_emby import JellyfinEmby from src.jellyfin_emby import JellyfinEmby
from packaging import version
class Jellyfin(JellyfinEmby): class Jellyfin(JellyfinEmby):
def __init__(self, baseurl, token): def __init__(self, baseurl, token):
@ -8,7 +8,7 @@ class Jellyfin(JellyfinEmby):
'Client="JellyPlex-Watched", ' 'Client="JellyPlex-Watched", '
'Device="script", ' 'Device="script", '
'DeviceId="script", ' 'DeviceId="script", '
'Version="5.2.0", ' 'Version="6.0.2", '
f'Token="{token}"' f'Token="{token}"'
) )
headers = { headers = {
@ -19,3 +19,6 @@ class Jellyfin(JellyfinEmby):
super().__init__( super().__init__(
server_type="Jellyfin", baseurl=baseurl, token=token, headers=headers 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")

View File

@ -125,7 +125,6 @@ class JellyfinEmby:
raise Exception(f"{self.server_type} token not set") raise Exception(f"{self.server_type} token not set")
self.session = requests.Session() self.session = requests.Session()
self.version = version.parse(self.info(version=True))
self.users = self.get_users() self.users = self.get_users()
def query(self, query, query_type, identifiers=None, json=None): def query(self, query, query_type, identifiers=None, json=None):
@ -178,17 +177,13 @@ class JellyfinEmby:
) )
raise Exception(e) raise Exception(e)
def info(self, version=False) -> str: def info(self) -> str:
try: try:
query_string = "/System/Info/Public" query_string = "/System/Info/Public"
response = self.query(query_string, "get") response = self.query(query_string, "get")
if response: if response:
# Return version only if requested
if version:
return response['Version']
return f"{self.server_type} {response['ServerName']}: {response['Version']}" return f"{self.server_type} {response['ServerName']}: {response['Version']}"
else: else:
return None return None
@ -197,6 +192,19 @@ class JellyfinEmby:
logger(f"{self.server_type}: Get server name failed {e}", 2) logger(f"{self.server_type}: Get server name failed {e}", 2)
raise Exception(e) 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): def get_users(self):
try: try:
users = {} users = {}
@ -505,7 +513,7 @@ class JellyfinEmby:
raise Exception(e) raise Exception(e)
def update_user_watched( 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: try:
logger( logger(
@ -565,33 +573,29 @@ class JellyfinEmby:
library, library,
jellyfin_video.get("Name"), jellyfin_video.get("Name"),
) )
else: elif update_partial:
# Handle partially watched movies not supported in jellyfin < 10.9.0 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 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: if not dryrun:
logger(msg, 5) logger(msg, 5)
playback_position_payload = { playback_position_payload = {
"PlaybackPositionTicks": movie_status["time"] "PlaybackPositionTicks": movie_status["time"]
* 10_000, * 10_000,
} }
self.query( self.query(
f"/Users/{user_id}/Items/{jellyfin_video_id}/UserData", f"/Users/{user_id}/Items/{jellyfin_video_id}/UserData",
"post", "post",
json=playback_position_payload, 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(msg, 6)
log_marked(
user_name,
library,
jellyfin_video.get("Name"),
duration=floor(movie_status["time"] / 60_000),
)
else: else:
logger( logger(
f"{self.server_type}: Skipping movie {jellyfin_video.get('Name')} as it is not in mark list for {user_name}", f"{self.server_type}: Skipping movie {jellyfin_video.get('Name')} as it is not in mark list for {user_name}",
@ -698,39 +702,35 @@ class JellyfinEmby:
jellyfin_episode.get("SeriesName"), jellyfin_episode.get("SeriesName"),
jellyfin_episode.get("Name"), jellyfin_episode.get("Name"),
) )
else: elif update_partial:
# Handle partially watched episodes not supported in jellyfin < 10.9.0 msg = (
if self.server_type == "Jellyfin" and self.version < version.parse("10.9.0"): f"{self.server_type}: {jellyfin_episode['SeriesName']} {jellyfin_episode['SeasonName']} Episode {jellyfin_episode.get('IndexNumber')} {jellyfin_episode.get('Name')}"
logger(f"{self.server_type}: Skipping episode {jellyfin_episode.get('Name')} as partially watched not supported in Jellyfin < 10.9.0", 4) + 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: else:
msg = ( logger(msg, 6)
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: log_marked(
logger(msg, 5) user_name,
playback_position_payload = { library,
"PlaybackPositionTicks": episode_status[ jellyfin_episode.get("SeriesName"),
"time" jellyfin_episode.get("Name"),
] duration=floor(episode_status["time"] / 60_000),
* 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: else:
logger( logger(
f"{self.server_type}: Skipping episode {jellyfin_episode.get('Name')} as it is not in mark list for {user_name}", f"{self.server_type}: Skipping episode {jellyfin_episode.get('Name')} as it is not in mark list for {user_name}",
@ -754,6 +754,15 @@ class JellyfinEmby:
self, watched_list, user_mapping=None, library_mapping=None, dryrun=False self, watched_list, user_mapping=None, library_mapping=None, dryrun=False
): ):
try: 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(): for user, libraries in watched_list.items():
logger(f"{self.server_type}: Updating for entry {user}, {libraries}", 1) logger(f"{self.server_type}: Updating for entry {user}, {libraries}", 1)
user_other = None user_other = None
@ -826,7 +835,13 @@ class JellyfinEmby:
if library_id: if library_id:
self.update_user_watched( 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: except Exception as e: