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 updatespull/185/head
parent
679d3535b1
commit
b1639eab0f
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue