sync: Fix already on list handling by using new add watched func AND no longer auto restore soft deleted records

no longer auto restoring soft deleted records means if the user (soft) deletes a show (default behavior), the next sync job wont add it back.
This commit is contained in:
IRHM
2026-03-06 16:49:52 +00:00
committed by momi
parent 5c4c7be115
commit 0e54a8c44a
2 changed files with 126 additions and 40 deletions
+71 -21
View File
@@ -41,7 +41,7 @@ type JellyfinSyncResponse struct {
}
type WatchedProvider interface {
AddWatched(userId uint, ar domain.WatchedAddRequest, at entity.ActivityType) (entity.Watched, error)
AddWatched(userId uint, ar domain.WatchedAddRequest, extraProps domain.WatchedAddExtraProps) (entity.Watched, error)
}
type WatchedSeasonProvider interface {
@@ -132,18 +132,44 @@ func (s *SyncService) startJellyfinSync(
job.UpdateJobCurrentTask(jobId, userId, "syncing "+v.Name)
// 2. Imported watched movie
w, err := s.wp.AddWatched(userId, domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaMovie,
TMDBID: tmdbId,
WatchedDate: v.UserData.LastPlayedDate,
}, entity.IMPORTED_WATCHED_JF)
w, err := s.wp.AddWatched(
userId,
domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaMovie,
TMDBID: tmdbId,
WatchedDate: v.UserData.LastPlayedDate,
}, domain.WatchedAddExtraProps{
ActivityType: entity.IMPORTED_WATCHED_JF,
DontRestore: true,
})
if err != nil {
if err.Error() == "content already on watched list" {
slog.Error("jellyfinSyncWatched: Unique constraint hit.. content must already be on watch list.", "movie_name", v.Name, "movie_ids", v.ProviderIds, "user_id", userId)
if errors.Is(err, domain.ErrWatchedExists) {
slog.Info("jellyfinSyncWatched: Content already exists on list.",
"movie_name", v.Name,
"movie_ids", v.ProviderIds,
"user_id", userId)
} else if errors.Is(err, domain.ErrWatchedExistsSoftDeleted) {
slog.Warn("jellyfinSyncWatched: Movie exists on list soft deleted.")
job.AddJobError(
jobId,
userId,
"failed to add movie "+
v.Name+
". You have previously deleted it from your list!")
// We don't continue as it was manually removed as is still
// soft deleted. We don't want to re-add it (user should un-delete themselves).
continue
} else {
slog.Error("jellyfinSyncWatched: Movie failed to import.", "movie_name", v.Name, "movie_ids", v.ProviderIds, "user_id", userId)
job.AddJobError(jobId, userId, "movie could not be imported (failed when adding to watched list): "+v.Name)
slog.Error("jellyfinSyncWatched: Movie failed to import.",
"movie_name", v.Name,
"movie_ids", v.ProviderIds,
"user_id", userId,
"error", err)
job.AddJobError(
jobId,
userId,
"movie could not be imported (failed when adding to watched list): "+v.Name)
}
} else {
// 3. Add IMPORTED_ADDED_WATCHED_JF activity
@@ -210,18 +236,42 @@ func (s *SyncService) startJellyfinSync(
job.UpdateJobCurrentTask(jobId, userId, "syncing serie "+v.Name)
// 2. Imported watched series
w, err := s.wp.AddWatched(userId, domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaShow,
TMDBID: tmdbId,
WatchedDate: v.UserData.LastPlayedDate,
}, entity.IMPORTED_WATCHED_JF)
w, err := s.wp.AddWatched(
userId,
domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaShow,
TMDBID: tmdbId,
WatchedDate: v.UserData.LastPlayedDate,
}, domain.WatchedAddExtraProps{
ActivityType: entity.IMPORTED_WATCHED_JF,
DontRestore: true,
})
if err != nil {
if err.Error() == "content already on watched list" {
slog.Info("jellyfinSyncWatched: Unique constraint hit.. content must already be on watch list.",
"series_name", v.Name, "series_ids", v.ProviderIds, "user_id", userId, "watched_id", w.ID)
if errors.Is(err, domain.ErrWatchedExists) {
slog.Info("jellyfinSyncWatched: Content already exists on list.",
"series_name", v.Name,
"series_ids", v.ProviderIds,
"user_id", userId,
"watched_id", w.ID)
// In this case, we allow continuing below to start syncing seasons/episodes
} else if errors.Is(err, domain.ErrWatchedExistsSoftDeleted) {
slog.Warn("jellyfinSyncWatched: Show exists on list soft deleted.")
job.AddJobError(
jobId,
userId,
"failed to add show "+
v.Name+
". You have previously deleted it from your list!")
// We don't continue as it was manually removed as is still
// soft deleted. We don't want to re-add it (user should un-delete themselves).
continue
} else {
slog.Error("jellyfinSyncWatched: Series failed to import.", "series_name", v.Name, "series_ids", v.ProviderIds, "user_id", userId)
slog.Error("jellyfinSyncWatched: Series failed to import.",
"series_name", v.Name,
"series_ids", v.ProviderIds,
"user_id", userId,
"error", err)
job.AddJobError(jobId, userId, "series could not be imported (failed when adding to watched list): "+v.Name)
}
} else {
+55 -19
View File
@@ -20,7 +20,7 @@ type PlexSyncResponse struct {
}
type WatchedProvider interface {
AddWatched(userId uint, ar domain.WatchedAddRequest, at entity.ActivityType) (entity.Watched, error)
AddWatched(userId uint, ar domain.WatchedAddRequest, extraProps domain.WatchedAddExtraProps) (entity.Watched, error)
}
type WatchedSeasonProvider interface {
@@ -115,16 +115,34 @@ func (s *SyncService) startPlexSync(
}
lastViewedAt := time.Unix(movie.LastViewedAt, 0)
w, err := s.wp.AddWatched(userId, domain.WatchedAddRequest{
Status: entity.FINISHED,
TMDBID: tmdbId,
ContentType: util.SupportedMediaMovie,
Rating: float64(movie.UserRating),
WatchedDate: lastViewedAt,
}, entity.IMPORTED_WATCHED_PLEX)
w, err := s.wp.AddWatched(
userId,
domain.WatchedAddRequest{
Status: entity.FINISHED,
TMDBID: tmdbId,
ContentType: util.SupportedMediaMovie,
Rating: float64(movie.UserRating),
WatchedDate: lastViewedAt,
},
domain.WatchedAddExtraProps{
ActivityType: entity.IMPORTED_WATCHED_PLEX,
DontRestore: true,
})
if err != nil {
if err.Error() == "content already on watched list" {
slog.Error("plexSyncWatched: unique constraint hit. movie must already be on watch list", "error", err)
if errors.Is(err, domain.ErrWatchedExists) {
slog.Info("plexSyncWatched: Movie already exists on list.")
continue
}
if errors.Is(err, domain.ErrWatchedExistsSoftDeleted) {
slog.Warn("plexSyncWatched: Movie exists on list soft deleted.")
job.AddJobError(
jobId,
userId,
"failed to add movie "+
movie.Title+
". You have previously deleted it from your list!")
// We don't continue as it was manually removed as is still
// soft deleted. We don't want to re-add it (user should un-delete themselves).
continue
}
slog.Error("plexSyncWatched: Failed to add movie as watched", "error", err)
@@ -182,16 +200,34 @@ func (s *SyncService) startPlexSync(
}
lastViewedAt := time.Unix(show.LastViewedAt, 0)
w, err := s.wp.AddWatched(userId, domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaShow,
TMDBID: tmdbId,
Rating: float64(show.UserRating),
WatchedDate: lastViewedAt,
}, entity.IMPORTED_WATCHED_PLEX)
w, err := s.wp.AddWatched(
userId,
domain.WatchedAddRequest{
Status: entity.FINISHED,
ContentType: util.SupportedMediaShow,
TMDBID: tmdbId,
Rating: float64(show.UserRating),
WatchedDate: lastViewedAt,
},
domain.WatchedAddExtraProps{
ActivityType: entity.IMPORTED_WATCHED_PLEX,
DontRestore: true,
})
if err != nil {
if err.Error() == "content already on watched list" {
slog.Info("plexSyncWatched: unique constraint hit. show must already be on watch list", "error", err)
if errors.Is(err, domain.ErrWatchedExists) {
slog.Info("plexSyncWatched: Show already exists on list.")
// In this case, we allow continuing below to start syncing seasons/episodes
} else if errors.Is(err, domain.ErrWatchedExistsSoftDeleted) {
slog.Warn("plexSyncWatched: Show exists on list soft deleted.")
job.AddJobError(
jobId,
userId,
"failed to add show "+
show.Title+
". You have previously deleted it from your list!")
// We don't continue as it was manually removed as is still
// soft deleted. We don't want to re-add it (user should un-delete themselves).
continue
} else {
slog.Error("plexSyncWatched: Failed to add show as watched", "error", err)
job.AddJobError(jobId, userId, "failed to add show "+show.Title)