From 4500dd6f7598fd4a1ee2837c891a30a17c396ae5 Mon Sep 17 00:00:00 2001 From: Jack Goodall Date: Mon, 29 Jun 2026 01:32:49 +0200 Subject: [PATCH] fix: recompute Season/Episode stable UUID after meta enrichment Co-Authored-By: Claude Opus 4.8 --- crates/remux-server/src/addons/mod.rs | 28 +++++----- crates/remux-server/src/db/media.rs | 77 +++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 14 deletions(-) diff --git a/crates/remux-server/src/addons/mod.rs b/crates/remux-server/src/addons/mod.rs index dd04e280..eaadffb1 100644 --- a/crates/remux-server/src/addons/mod.rs +++ b/crates/remux-server/src/addons/mod.rs @@ -1416,22 +1416,22 @@ impl AddonService { } } - // Recompute stable UUID for Movie/Series once the canonical external ID + // Recompute stable UUID for Movie/Series/Season/Episode once the canonical external ID // (IMDB or custom stremio) is resolved by meta enrichment. Catalog stubs // arrive with a TMDB-keyed UUID; validate() expects the canonical one. - if matches!(media.kind, db::MediaKind::Movie | db::MediaKind::Series) { - let raw = db::MediaIdRaw { - kind: media - .kind - .clone(), - external_ids: media - .external_ids - .clone(), - season: None, - episode: None, - }; - if let Some(canonical) = raw.canonical() { - media.id = crate::common::stable_media_uuid(&media.kind, &canonical); + if matches!( + media.kind, + db::MediaKind::Movie + | db::MediaKind::Series + | db::MediaKind::Season + | db::MediaKind::Episode + ) { + let raw = media.media_id_raw(); + if raw + .canonical() + .is_some() + { + media.id = uuid::Uuid::from(&raw); } } diff --git a/crates/remux-server/src/db/media.rs b/crates/remux-server/src/db/media.rs index 9d4a1794..345e18fc 100644 --- a/crates/remux-server/src/db/media.rs +++ b/crates/remux-server/src/db/media.rs @@ -5741,6 +5741,83 @@ mod tests { use super::*; use crate::db::MediaIdRaw; + #[test] + fn stale_episode_id_recomputes_to_canonical_and_validates() { + let series_imdb = NonEmptyString::try_new("tt1844624".to_string()).unwrap(); + let mut ep = Media { + kind: MediaKind::Episode, + title: "S0E1 - Behind the Fright".to_string(), + idx: Some(1), + parent_idx: Some(0), + external_ids: ExternalIds { + series_imdb: Some(series_imdb.clone()), + ..Default::default() + }, + id: crate::common::stable_media_uuid(&MediaKind::Episode, "tt1844624:1:1"), + ..Default::default() + }; + + assert!( + ep.validate() + .is_err() + ); + + let raw = ep.media_id_raw(); + assert!( + raw.canonical() + .is_some() + ); + ep.id = Uuid::from(&raw); + + assert_eq!( + ep.id, + crate::common::stable_media_uuid(&MediaKind::Episode, "tt1844624:0:1") + ); + assert!( + ep.validate() + .is_ok() + ); + } + + #[test] + fn stale_season_id_recomputes_to_canonical_and_validates() { + let series_imdb = NonEmptyString::try_new("tt1844624".to_string()).unwrap(); + let mut season = Media { + kind: MediaKind::Season, + title: "Specials".to_string(), + idx: Some(0), + external_ids: ExternalIds { + series_imdb: Some(series_imdb.clone()), + ..Default::default() + }, + id: crate::common::stable_media_uuid(&MediaKind::Season, "tt1844624:1"), + ..Default::default() + }; + + assert!( + season + .validate() + .is_err() + ); + + let raw = season.media_id_raw(); + assert!( + raw.canonical() + .is_some() + ); + season.id = Uuid::from(&raw); + + assert_eq!( + season.id, + crate::common::stable_media_uuid(&MediaKind::Season, "tt1844624:0") + ); + assert!( + season + .validate() + .is_ok() + ); + } + #[test] fn from_path_tmdb_in_directory() { let ids = ExternalIds::from_path(