feat: add AniList browsing with on-demand ZokoAnime playback

This commit is contained in:
2026-10-25 15:44:41 +01:00
parent a281c6730a
commit e328ff3883
20 changed files with 1062 additions and 235 deletions
+63 -2
View File
@@ -59,7 +59,7 @@ fn record(id: &str, event: &str, eligible: bool, position: f64) -> String {
fn miruro_video(episode: u32) -> Video {
Video {
id: Identity::new("opaque_Show-1".into(), episode)
id: Identity::miruro("opaque_Show-1".into(), episode)
.unwrap()
.key(),
title: format!("Example show · Episode {episode}"),
@@ -1313,7 +1313,7 @@ fn miruro_sessions_recover_the_maximum_identity_marker() {
let directory = TestDirectory::new();
let mut state = State::open(&directory.config()).unwrap();
let mut video = miruro_video(i32::MAX as u32);
video.id = Identity::new("a".repeat(512), i32::MAX as u32)
video.id = Identity::miruro("a".repeat(512), i32::MAX as u32)
.unwrap()
.key();
let report = state.progress_file(&video).unwrap();
@@ -1449,3 +1449,64 @@ fn progress_batch_prevalidates_snapshots_and_session_capacity() {
assert!(!state.sessions.exists());
assert_eq!(state.streams.len(), MAX_STREAMS - 1);
}
#[test]
fn anilist_library_and_progress_are_isolated_from_opaque_miruro_ids() {
let directory = TestDirectory::new();
let mut state = State::open(&directory.config()).unwrap();
let mut legacy_show = show();
legacy_show.id = "1".into();
legacy_show.metadata["id"] = serde_json::json!("1");
state
.save_show(&legacy_show, &["Legacy".into()].into())
.unwrap();
let legacy_bytes = fs::read(directory.0.join("miruro-library.toml")).unwrap();
state.select_catalog(Provider::AniList);
assert!(state.shows().is_empty());
assert!(state.save_show(&legacy_show, &BTreeSet::new()).is_err());
let mut ani_show = legacy_show.clone();
ani_show.metadata["catalog_provider"] = serde_json::json!("anilist");
state.save_show(&ani_show, &["New".into()].into()).unwrap();
let mut ani = miruro_video(1);
ani.id = Identity::anilist("1".into(), 1).unwrap().key();
ani.channel = Some("AniList".into());
let mut legacy = miruro_video(1);
legacy.id = Identity::miruro("1".into(), 1).unwrap().key();
for item in [&ani, &legacy, &video()] {
state.save(CollectionKind::Saved, item).unwrap();
}
let report = state.progress_file(&ani).unwrap();
register_identity(&report, &ani.id).unwrap();
append(
&report,
miruro_record(&legacy, "ended", true, 100.0, Some(100.0)).as_bytes(),
);
append(
&report,
miruro_record(&ani, "progress", false, 20.0, Some(100.0)).as_bytes(),
);
drop(state);
let mut state = State::open(&directory.config()).unwrap();
assert!(state.poll_progress().unwrap());
assert!(state.contains(CollectionKind::Watching, &ani.id));
assert!(!state.is_watched(&ani.id));
assert!(!state.is_watched(&legacy.id));
append(
&report,
miruro_record(&ani, "ended", true, 95.0, Some(100.0)).as_bytes(),
);
assert!(state.poll_progress().unwrap());
assert!(state.is_watched(&ani.id));
assert!(!state.is_watched(&legacy.id));
assert!(state.contains(CollectionKind::Saved, &legacy.id));
assert_eq!(state.shows()[0].tags, ["Legacy".into()].into());
state.select_catalog(Provider::AniList);
assert_eq!(state.shows()[0].tags, ["New".into()].into());
assert_eq!(
fs::read(directory.0.join("miruro-library.toml")).unwrap(),
legacy_bytes
);
let mut forged = ani.clone();
forged.channel = Some("Miruro".into());
assert!(state.progress_file(&forged).is_err());
}