8 Commits

Author SHA1 Message Date
Luis Garcia
629f50ecdc Tag version 8.2.0
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-14 19:10:36 +00:00
Luigi311
3e2450b5fd Merge pull request #296 from luigi311/fix_emby
Jellyfin/Emby: Add fallback for played percentage if missing
2025-07-14 13:09:58 -06:00
Luis Garcia
0de5e86837 Jellyfin/Emby: Add fallback for played percentage if missing
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-14 17:21:55 +00:00
Luis Garcia
33a719f693 Tag version 8.1.0
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-12 00:17:46 +00:00
Luigi311
9ff985a848 Merge pull request #292 from luigi311/sync_timestamps
Jellyfin/Emby: Sync across the view times
2025-07-11 18:16:56 -06:00
Luis Garcia
5501e21aa8 Jellyfin/Emby: Use the same endpoint for marking as for partials to fix emby
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-12 00:14:27 +00:00
Luis Garcia
2208d91d07 README: Add sync view dates
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-11 23:48:33 +00:00
Luis Garcia
75f7f576ac Jellyfin/Emby: Sync across the view times
Signed-off-by: Luis Garcia <git@luigi311.com>
2025-07-11 23:48:23 +00:00
7 changed files with 155 additions and 56 deletions

View File

@@ -19,6 +19,7 @@ Keep in sync all your users watched history between jellyfin, plex and emby serv
- \[x] One way/multi way sync - \[x] One way/multi way sync
- \[x] Sync watched - \[x] Sync watched
- \[x] Sync in progress - \[x] Sync in progress
- \[ ] Sync view dates
### Jellyfin ### Jellyfin
@@ -29,6 +30,8 @@ Keep in sync all your users watched history between jellyfin, plex and emby serv
- \[x] One way/multi way sync - \[x] One way/multi way sync
- \[x] Sync watched - \[x] Sync watched
- \[x] Sync in progress - \[x] Sync in progress
- \[x] Sync view dates
### Emby ### Emby
@@ -39,6 +42,8 @@ Keep in sync all your users watched history between jellyfin, plex and emby serv
- \[x] One way/multi way sync - \[x] One way/multi way sync
- \[x] Sync watched - \[x] Sync watched
- \[x] Sync in progress - \[x] Sync in progress
- \[x] Sync view dates
## Configuration ## Configuration

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "jellyplex-watched" name = "jellyplex-watched"
version = "8.0.0" version = "8.2.0"
description = "Sync watched between media servers locally" description = "Sync watched between media servers locally"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"

View File

@@ -1,8 +1,8 @@
# Functions for Jellyfin and Emby # Functions for Jellyfin and Emby
from datetime import datetime
import requests import requests
import traceback import traceback
import os
from math import floor from math import floor
from typing import Any, Literal from typing import Any, Literal
from packaging.version import parse, Version from packaging.version import parse, Version
@@ -76,15 +76,21 @@ def get_mediaitem(
generate_guids: bool, generate_guids: bool,
generate_locations: bool, generate_locations: bool,
) -> MediaItem: ) -> MediaItem:
user_data = item.get("UserData", {})
last_played_date = user_data.get("LastPlayedDate")
viewed_date = datetime.today()
if last_played_date:
viewed_date = datetime.fromisoformat(last_played_date.replace("Z", "+00:00"))
return MediaItem( return MediaItem(
identifiers=extract_identifiers_from_item( identifiers=extract_identifiers_from_item(
server_type, item, generate_guids, generate_locations server_type, item, generate_guids, generate_locations
), ),
status=WatchedStatus( status=WatchedStatus(
completed=item.get("UserData", {}).get("Played"), completed=user_data.get("Played"),
time=floor( time=floor(user_data.get("PlaybackPositionTicks", 0) / 10000),
item.get("UserData", {}).get("PlaybackPositionTicks", 0) / 10000 viewed_date=viewed_date,
),
), ),
) )
@@ -311,7 +317,7 @@ class JellyfinEmby:
movie_items = [] movie_items = []
watched_items = self.query( watched_items = self.query(
f"/Users/{user_id}/Items" f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsPlayed&IncludeItemTypes=Movie&Recursive=True&Fields=ItemCounts,ProviderIds,MediaSources", + f"?ParentId={library_id}&Filters=IsPlayed&IncludeItemTypes=Movie&Recursive=True&Fields=ItemCounts,ProviderIds,MediaSources,UserDataLastPlayedDate",
"get", "get",
) )
@@ -320,7 +326,7 @@ class JellyfinEmby:
in_progress_items = self.query( in_progress_items = self.query(
f"/Users/{user_id}/Items" f"/Users/{user_id}/Items"
+ f"?ParentId={library_id}&Filters=IsResumable&IncludeItemTypes=Movie&Recursive=True&Fields=ItemCounts,ProviderIds,MediaSources", + f"?ParentId={library_id}&Filters=IsResumable&IncludeItemTypes=Movie&Recursive=True&Fields=ItemCounts,ProviderIds,MediaSources,UserDataLastPlayedDate",
"get", "get",
) )
@@ -371,7 +377,23 @@ class JellyfinEmby:
if not show.get("UserData"): if not show.get("UserData"):
continue continue
if show["UserData"].get("PlayedPercentage", 0) > 0: played_percentage = show["UserData"].get("PlayedPercentage")
if played_percentage is None:
# Emby no longer shows PlayedPercentage
total_episodes = show.get("RecursiveItemCount")
unplayed_episodes = show["UserData"].get("UnplayedItemCount")
if total_episodes is None:
# Failed to get total count of episodes
continue
if (
unplayed_episodes is not None
and unplayed_episodes < total_episodes
):
watched_shows_filtered.append(show)
else:
if played_percentage > 0:
watched_shows_filtered.append(show) watched_shows_filtered.append(show)
# Retrieve the watched/partially watched list of episodes of each watched show # Retrieve the watched/partially watched list of episodes of each watched show
@@ -388,7 +410,7 @@ class JellyfinEmby:
show_episodes = self.query( show_episodes = self.query(
f"/Shows/{show.get('Id')}/Episodes" f"/Shows/{show.get('Id')}/Episodes"
+ f"?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,MediaSources", + f"?userId={user_id}&isPlaceHolder=false&Fields=ProviderIds,MediaSources,UserDataLastPlayedDate",
"get", "get",
) )
@@ -558,12 +580,28 @@ class JellyfinEmby:
jelly_identifiers, stored_movie.identifiers jelly_identifiers, stored_movie.identifiers
): ):
jellyfin_video_id = jellyfin_video.get("Id") jellyfin_video_id = jellyfin_video.get("Id")
viewed_date: str = (
stored_movie.status.viewed_date.isoformat(
timespec="milliseconds"
).replace("+00:00", "Z")
)
if stored_movie.status.completed: if stored_movie.status.completed:
msg = f"{self.server_type}: {jellyfin_video.get('Name')} as watched for {user_name} in {library_name}" msg = f"{self.server_type}: {jellyfin_video.get('Name')} as watched for {user_name} in {library_name}"
if not dryrun: if not dryrun:
user_data_payload: dict[
str, float | bool | datetime
] = {
"PlayCount": 1,
"Played": True,
"PlaybackPositionTicks": 0,
"LastPlayedDate": viewed_date,
}
self.query( self.query(
f"/Users/{user_id}/PlayedItems/{jellyfin_video_id}", f"/Users/{user_id}/Items/{jellyfin_video_id}/UserData",
"post", "post",
json=user_data_payload,
) )
logger.success(f"{'[DRYRUN] ' if dryrun else ''}{msg}") logger.success(f"{'[DRYRUN] ' if dryrun else ''}{msg}")
@@ -581,14 +619,19 @@ class JellyfinEmby:
msg = f"{self.server_type}: {jellyfin_video.get('Name')} as partially watched for {floor(stored_movie.status.time / 60_000)} minutes for {user_name} in {library_name}" msg = f"{self.server_type}: {jellyfin_video.get('Name')} as partially watched for {floor(stored_movie.status.time / 60_000)} minutes for {user_name} in {library_name}"
if not dryrun: if not dryrun:
playback_position_payload: dict[str, float] = { user_data_payload: dict[
str, float | bool | datetime
] = {
"PlayCount": 0,
"Played": False,
"PlaybackPositionTicks": stored_movie.status.time "PlaybackPositionTicks": stored_movie.status.time
* 10_000, * 10_000,
"LastPlayedDate": viewed_date,
} }
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=user_data_payload,
) )
logger.success(f"{'[DRYRUN] ' if dryrun else ''}{msg}") logger.success(f"{'[DRYRUN] ' if dryrun else ''}{msg}")
@@ -671,15 +714,31 @@ class JellyfinEmby:
stored_ep.identifiers, stored_ep.identifiers,
): ):
jellyfin_episode_id = jellyfin_episode.get("Id") jellyfin_episode_id = jellyfin_episode.get("Id")
viewed_date: str = (
stored_ep.status.viewed_date.isoformat(
timespec="milliseconds"
).replace("+00:00", "Z")
)
if stored_ep.status.completed: if stored_ep.status.completed:
msg = ( msg = (
f"{self.server_type}: {jellyfin_episode.get('SeriesName')} {jellyfin_episode.get('SeasonName')} Episode {jellyfin_episode.get('IndexNumber')} {jellyfin_episode.get('Name')}" f"{self.server_type}: {jellyfin_episode.get('SeriesName')} {jellyfin_episode.get('SeasonName')} Episode {jellyfin_episode.get('IndexNumber')} {jellyfin_episode.get('Name')}"
+ f" as watched for {user_name} in {library_name}" + f" as watched for {user_name} in {library_name}"
) )
if not dryrun: if not dryrun:
user_data_payload: dict[
str, float | bool | datetime
] = {
"PlayCount": 1,
"Played": True,
"PlaybackPositionTicks": 0,
"LastPlayedDate": viewed_date,
}
self.query( self.query(
f"/Users/{user_id}/PlayedItems/{jellyfin_episode_id}", f"/Users/{user_id}/Items/{jellyfin_episode_id}/UserData",
"post", "post",
json=user_data_payload,
) )
logger.success( logger.success(
@@ -703,14 +762,19 @@ class JellyfinEmby:
) )
if not dryrun: if not dryrun:
playback_position_payload = { user_data_payload: dict[
str, float | bool | datetime
] = {
"PlayCount": 0,
"Played": False,
"PlaybackPositionTicks": stored_ep.status.time "PlaybackPositionTicks": stored_ep.status.time
* 10_000, * 10_000,
"LastPlayedDate": viewed_date,
} }
self.query( self.query(
f"/Users/{user_id}/Items/{jellyfin_episode_id}/UserData", f"/Users/{user_id}/Items/{jellyfin_episode_id}/UserData",
"post", "post",
json=playback_position_payload, json=user_data_payload,
) )
logger.success( logger.success(

View File

@@ -1,3 +1,4 @@
from datetime import datetime, timezone
import requests import requests
from loguru import logger from loguru import logger
@@ -83,11 +84,19 @@ def get_mediaitem(
generate_guids: bool = True, generate_guids: bool = True,
generate_locations: bool = True, generate_locations: bool = True,
) -> MediaItem: ) -> MediaItem:
last_viewed_at = item.lastViewedAt
viewed_date = datetime.today()
if last_viewed_at:
viewed_date = last_viewed_at.replace(tzinfo=timezone.utc)
return MediaItem( return MediaItem(
identifiers=extract_identifiers_from_item( identifiers=extract_identifiers_from_item(
item, generate_guids, generate_locations item, generate_guids, generate_locations
), ),
status=WatchedStatus(completed=completed, time=item.viewOffset), status=WatchedStatus(
completed=completed, time=item.viewOffset, viewed_date=viewed_date
),
) )

View File

@@ -1,4 +1,5 @@
import copy import copy
from datetime import datetime
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from loguru import logger from loguru import logger
from typing import Any from typing import Any
@@ -21,6 +22,7 @@ class MediaIdentifiers(BaseModel):
class WatchedStatus(BaseModel): class WatchedStatus(BaseModel):
completed: bool completed: bool
time: int time: int
viewed_date: datetime
class MediaItem(BaseModel): class MediaItem(BaseModel):

View File

@@ -1,3 +1,4 @@
from datetime import datetime
import sys import sys
import os import os
@@ -23,6 +24,8 @@ from src.watched import (
cleanup_watched, cleanup_watched,
) )
viewed_date = datetime.today()
tv_shows_watched_list_1: list[Series] = [ tv_shows_watched_list_1: list[Series] = [
Series( Series(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -41,7 +44,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="968589", tmdb_id="968589",
tvdb_id="295296", tvdb_id="295296",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -51,7 +54,9 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="968590", tmdb_id="968590",
tvdb_id="295297", tvdb_id="295297",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(
completed=False, time=240000, viewed_date=viewed_date
),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -61,7 +66,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="968592", tmdb_id="968592",
tvdb_id="295298", tvdb_id="295298",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -82,7 +87,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="4661246", tmdb_id="4661246",
tvdb_id="10009418", tvdb_id="10009418",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -92,7 +97,9 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="4712059", tmdb_id="4712059",
tvdb_id="10009419", tvdb_id="10009419",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(
completed=False, time=240000, viewed_date=viewed_date
),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -102,7 +109,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="4712061", tmdb_id="4712061",
tvdb_id="10009420", tvdb_id="10009420",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -123,7 +130,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="3070048", tmdb_id="3070048",
tvdb_id="8438181", tvdb_id="8438181",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -133,7 +140,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="4568681", tmdb_id="4568681",
tvdb_id="9829910", tvdb_id="9829910",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -143,7 +150,7 @@ tv_shows_watched_list_1: list[Series] = [
tmdb_id="4497012", tmdb_id="4497012",
tvdb_id="9870382", tvdb_id="9870382",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -170,7 +177,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="295294", tvdb_id="295294",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -180,7 +187,9 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="295295", tvdb_id="295295",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=False, time=300670), status=WatchedStatus(
completed=False, time=300670, viewed_date=viewed_date
),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -190,7 +199,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="295298", tvdb_id="295298",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -211,7 +220,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="9959300", tvdb_id="9959300",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -221,7 +230,9 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="10009417", tvdb_id="10009417",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=False, time=300741), status=WatchedStatus(
completed=False, time=300741, viewed_date=viewed_date
),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -231,7 +242,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="10009420", tvdb_id="10009420",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -252,7 +263,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="8438181", tvdb_id="8438181",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -262,7 +273,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="9829910", tvdb_id="9829910",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -272,7 +283,7 @@ tv_shows_watched_list_2: list[Series] = [
tvdb_id="9870382", tvdb_id="9870382",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
], ],
), ),
@@ -299,7 +310,7 @@ expected_tv_show_watched_list_1: list[Series] = [
tmdb_id="968589", tmdb_id="968589",
tvdb_id="295296", tvdb_id="295296",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -309,7 +320,9 @@ expected_tv_show_watched_list_1: list[Series] = [
tmdb_id="968590", tmdb_id="968590",
tvdb_id="295297", tvdb_id="295297",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(
completed=False, time=240000, viewed_date=viewed_date
),
), ),
], ],
), ),
@@ -330,7 +343,7 @@ expected_tv_show_watched_list_1: list[Series] = [
tmdb_id="4661246", tmdb_id="4661246",
tvdb_id="10009418", tvdb_id="10009418",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -340,7 +353,9 @@ expected_tv_show_watched_list_1: list[Series] = [
tmdb_id="4712059", tmdb_id="4712059",
tvdb_id="10009419", tvdb_id="10009419",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(
completed=False, time=240000, viewed_date=viewed_date
),
), ),
], ],
), ),
@@ -367,7 +382,7 @@ expected_tv_show_watched_list_2: list[Series] = [
tvdb_id="295294", tvdb_id="295294",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -377,7 +392,9 @@ expected_tv_show_watched_list_2: list[Series] = [
tvdb_id="295295", tvdb_id="295295",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=False, time=300670), status=WatchedStatus(
completed=False, time=300670, viewed_date=viewed_date
),
), ),
], ],
), ),
@@ -398,7 +415,7 @@ expected_tv_show_watched_list_2: list[Series] = [
tvdb_id="9959300", tvdb_id="9959300",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -408,7 +425,9 @@ expected_tv_show_watched_list_2: list[Series] = [
tvdb_id="10009417", tvdb_id="10009417",
tmdb_id=None, tmdb_id=None,
), ),
status=WatchedStatus(completed=False, time=300741), status=WatchedStatus(
completed=False, time=300741, viewed_date=viewed_date
),
), ),
], ],
), ),
@@ -426,7 +445,7 @@ movies_watched_list_1: list[MediaItem] = [
tmdb_id="10378", tmdb_id="10378",
tvdb_id="12352", tvdb_id="12352",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -436,7 +455,7 @@ movies_watched_list_1: list[MediaItem] = [
tmdb_id="1029575", tmdb_id="1029575",
tvdb_id="351194", tvdb_id="351194",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -446,7 +465,7 @@ movies_watched_list_1: list[MediaItem] = [
tmdb_id="466420", tmdb_id="466420",
tvdb_id="135852", tvdb_id="135852",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(completed=False, time=240000, viewed_date=viewed_date),
), ),
] ]
@@ -462,7 +481,7 @@ movies_watched_list_2: list[MediaItem] = [
tmdb_id="1029575", tmdb_id="1029575",
tvdb_id=None, tvdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -472,7 +491,7 @@ movies_watched_list_2: list[MediaItem] = [
tmdb_id="507089", tmdb_id="507089",
tvdb_id=None, tvdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -482,7 +501,7 @@ movies_watched_list_2: list[MediaItem] = [
tmdb_id="695721", tmdb_id="695721",
tvdb_id=None, tvdb_id=None,
), ),
status=WatchedStatus(completed=False, time=301215), status=WatchedStatus(completed=False, time=301215, viewed_date=viewed_date),
), ),
] ]
@@ -498,7 +517,7 @@ expected_movie_watched_list_1: list[MediaItem] = [
tmdb_id="10378", tmdb_id="10378",
tvdb_id="12352", tvdb_id="12352",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -508,7 +527,7 @@ expected_movie_watched_list_1: list[MediaItem] = [
tmdb_id="466420", tmdb_id="466420",
tvdb_id="135852", tvdb_id="135852",
), ),
status=WatchedStatus(completed=False, time=240000), status=WatchedStatus(completed=False, time=240000, viewed_date=viewed_date),
), ),
] ]
@@ -524,7 +543,7 @@ expected_movie_watched_list_2: list[MediaItem] = [
tmdb_id="507089", tmdb_id="507089",
tvdb_id=None, tvdb_id=None,
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
), ),
MediaItem( MediaItem(
identifiers=MediaIdentifiers( identifiers=MediaIdentifiers(
@@ -534,7 +553,7 @@ expected_movie_watched_list_2: list[MediaItem] = [
tmdb_id="695721", tmdb_id="695721",
tvdb_id=None, tvdb_id=None,
), ),
status=WatchedStatus(completed=False, time=301215), status=WatchedStatus(completed=False, time=301215, viewed_date=viewed_date),
), ),
] ]
@@ -562,7 +581,7 @@ tv_shows_2_watched_list_1: list[Series] = [
tmdb_id="282843", tmdb_id="282843",
tvdb_id="176357", tvdb_id="176357",
), ),
status=WatchedStatus(completed=True, time=0), status=WatchedStatus(completed=True, time=0, viewed_date=viewed_date),
) )
], ],
) )

2
uv.lock generated
View File

@@ -84,7 +84,7 @@ wheels = [
[[package]] [[package]]
name = "jellyplex-watched" name = "jellyplex-watched"
version = "8.0.0" version = "8.2.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "loguru" }, { name = "loguru" },