From d03b86fa1c9bfb7aa504a88a0039dec7c249411d Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Sat, 18 Jul 2026 15:00:41 +0300 Subject: [PATCH] Sort home continue-watching by lastWatchedAt; stop bumping savedAt from watch history --- src/library_state.rs | 13 +++++++++++-- src/nuvio_sync.rs | 37 ++----------------------------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/src/library_state.rs b/src/library_state.rs index ef1b94e..551551b 100644 --- a/src/library_state.rs +++ b/src/library_state.rs @@ -209,9 +209,18 @@ pub(crate) fn filter_home_continue_watching_json( } true }) - .collect(); + .collect::>(); - serde_json::to_string(&filtered).ok() + let mut ranked = filtered; + ranked.sort_by_key(|item| { + std::cmp::Reverse( + item.get("lastWatchedAt") + .and_then(Value::as_i64) + .unwrap_or(0), + ) + }); + + serde_json::to_string(&ranked).ok() } pub(crate) fn watched_video_ids_json(items_json: &str, imdb_id: &str) -> Option { diff --git a/src/nuvio_sync.rs b/src/nuvio_sync.rs index de21645..3a560c7 100644 --- a/src/nuvio_sync.rs +++ b/src/nuvio_sync.rs @@ -20,12 +20,6 @@ fn iso_from_ms(ms: i64) -> String { .unwrap_or_default() } -fn ms_from_iso(value: &str) -> Option { - chrono::DateTime::parse_from_rfc3339(value) - .ok() - .map(|dt| dt.timestamp_millis()) -} - fn safe_id_part(value: &str) -> String { let cleaned: String = value .trim() @@ -349,7 +343,6 @@ pub(crate) fn import_merge_plan_json(args_json: &str) -> Option { } } - let mut latest_watched_at: Map = Map::new(); if let Some(watch_history) = args.get("watchHistory").and_then(Value::as_array) { for item in watch_history { let Some(content_id) = str_field(item, "content_id") else { @@ -363,12 +356,6 @@ pub(crate) fn import_merge_plan_json(args_json: &str) -> Option { ) { watched.insert(format!("{content_id}:{s}:{e}"), Value::Bool(true)); } - if let Some(at) = item.get("watched_at").and_then(Value::as_i64) { - let existing = latest_watched_at.get(content_id).and_then(Value::as_i64); - if existing.is_none_or(|prev| at > prev) { - latest_watched_at.insert(content_id.to_string(), json!(at)); - } - } } for id in &active_remote_ids { watched.remove(id); @@ -379,7 +366,6 @@ pub(crate) fn import_merge_plan_json(args_json: &str) -> Option { watched.get(key).and_then(Value::as_bool).unwrap_or(false) }; let mut to_remove: Vec = Vec::new(); - let mut saved_at_bumps: Vec<(String, String)> = Vec::new(); for (content_id, entry) in &progress { let video_watched = str_field(entry, "lastVideoId") .map(|id| is_watched(&watched, id)) @@ -393,30 +379,11 @@ pub(crate) fn import_merge_plan_json(args_json: &str) -> Option { }; if video_watched || episode_watched { to_remove.push(content_id.clone()); - continue; - } - let resolved = entry - .get("continueWatchingEpisodeResolved") - .and_then(Value::as_bool) - .unwrap_or(false); - if resolved { - let history_latest = latest_watched_at.get(content_id).and_then(Value::as_i64); - let saved_at = str_field(entry, "savedAt").and_then(ms_from_iso); - if let (Some(latest), Some(current)) = (history_latest, saved_at) { - if latest > current { - saved_at_bumps.push((content_id.clone(), iso_from_ms(latest))); - } - } } } for id in to_remove { progress.remove(&id); } - for (id, saved_at) in saved_at_bumps { - if let Some(entry) = progress.get_mut(&id).and_then(Value::as_object_mut) { - entry.insert("savedAt".into(), Value::String(saved_at)); - } - } Some( json!({ @@ -671,7 +638,7 @@ mod tests { } #[test] - fn resolved_up_next_saved_at_bumps_to_latest_history() { + fn resolved_up_next_saved_at_ignores_history_watched_at() { let result = merge(json!({ "progress": {}, "watched": {}, @@ -688,7 +655,7 @@ mod tests { })); let entry = &result["progress"]["tt1"]; assert_eq!(entry["continueWatchingBadge"], json!("upNext")); - assert_eq!(entry["savedAt"], json!(iso_from_ms(1_700_000_500_000))); + assert_eq!(entry["savedAt"], json!(iso_from_ms(1_700_000_000_000))); } #[test]