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 packaging import version
class Emby(JellyfinEmby):
@ -8,7 +9,7 @@ class Emby(JellyfinEmby):
'Client="JellyPlex-Watched", '
'Device="script", '
'DeviceId="script", '
'Version="0.0.0"'
'Version="6.0.2"'
)
headers = {
"Accept": "application/json",
@ -19,3 +20,6 @@ class Emby(JellyfinEmby):
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")

View File

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

View File

@ -125,7 +125,6 @@ class JellyfinEmby:
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):
@ -178,17 +177,13 @@ class JellyfinEmby:
)
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
@ -197,6 +192,19 @@ class JellyfinEmby:
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 = {}
@ -505,7 +513,7 @@ class JellyfinEmby:
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(
@ -565,33 +573,29 @@ class JellyfinEmby:
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}"
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),
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}",
@ -698,39 +702,35 @@ class JellyfinEmby:
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)
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:
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}"
)
logger(msg, 6)
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),
)
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}",
@ -754,6 +754,15 @@ class JellyfinEmby:
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
@ -826,7 +835,13 @@ class JellyfinEmby:
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: