refactor: delegate Miruro playback resolution to Terebi
This commit is contained in:
+21
-128
@@ -1,12 +1,11 @@
|
||||
use super::*;
|
||||
use ratatui::crossterm::event::KeyModifiers;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs,
|
||||
io::Write,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
use tubular::miruro::{Detail, SourceDocument};
|
||||
use tubular::miruro::Detail;
|
||||
|
||||
struct Directory(PathBuf);
|
||||
impl Directory {
|
||||
@@ -53,16 +52,6 @@ fn series(media: &Media) -> miruro_browser::Series {
|
||||
serde_json::json!({"episode_number":7, "absolute_number":53, "title":null, "duration_seconds":null}),
|
||||
] }).unwrap()
|
||||
}
|
||||
fn source(video: &Video, token: &str) -> SourceDocument {
|
||||
SourceDocument {
|
||||
version: 1,
|
||||
url: format!("https://vault.keeply.top/master.m3u8?token={token}"),
|
||||
headers: BTreeMap::new(),
|
||||
identity: Identity::from_key(&video.id).unwrap(),
|
||||
title: Some(video.title.clone()),
|
||||
subtitles: Vec::new(),
|
||||
}
|
||||
}
|
||||
fn catalog_results(browser: &mut Browser) {
|
||||
browser.accept(
|
||||
browser.generation,
|
||||
@@ -77,19 +66,6 @@ fn enter_series(browser: &mut Browser) {
|
||||
let result = series(media);
|
||||
browser.accept(browser.generation, Ok(Response::Series(result)));
|
||||
}
|
||||
fn sources(directory: &Directory) -> Vec<Value> {
|
||||
let path = directory.0.join("playback-sessions");
|
||||
if !path.exists() {
|
||||
return Vec::new();
|
||||
}
|
||||
fs::read_dir(path)
|
||||
.unwrap()
|
||||
.map(|entry| {
|
||||
serde_json::from_slice(&fs::read(entry.unwrap().path().join("source.json")).unwrap())
|
||||
.unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn metadata_popup_scrolls_and_closes_without_moving_the_episode_selection() {
|
||||
@@ -188,7 +164,7 @@ fn catalog_series_back_forward_keep_native_query_selection_and_actual_episodes()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn latest_intent_and_navigation_reject_stale_catalog_series_and_sources() {
|
||||
fn latest_intent_and_navigation_reject_stale_catalog_and_series() {
|
||||
let directory = Directory::new();
|
||||
let mut browser = directory.browser(Some("old"));
|
||||
let old = browser.generation;
|
||||
@@ -214,30 +190,10 @@ fn latest_intent_and_navigation_reject_stale_catalog_series_and_sources() {
|
||||
};
|
||||
let data = series(media);
|
||||
browser.accept(browser.generation, Ok(Response::Series(data)));
|
||||
let video = browser.screen().selected_video().unwrap();
|
||||
browser.open();
|
||||
let stale = browser.generation;
|
||||
browser.history(false);
|
||||
browser.accept(
|
||||
stale,
|
||||
Ok(Response::Source(
|
||||
video.clone(),
|
||||
Box::new(source(&video, "stale")),
|
||||
)),
|
||||
assert_eq!(
|
||||
browser.screen().selected_video().unwrap().id,
|
||||
"miruro:first:3"
|
||||
);
|
||||
assert!(sources(&directory).is_empty());
|
||||
browser.history(true);
|
||||
browser.open();
|
||||
let stale = browser.generation;
|
||||
browser.refresh();
|
||||
browser.accept(
|
||||
stale,
|
||||
Ok(Response::Source(
|
||||
video.clone(),
|
||||
Box::new(source(&video, "refreshed-away")),
|
||||
)),
|
||||
);
|
||||
assert!(sources(&directory).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -274,12 +230,9 @@ fn manual_watch_status_refreshes_filters_and_preserves_other_providers() {
|
||||
assert!(!browser.state.is_watched(&episode.id));
|
||||
browser.preview = None;
|
||||
browser.collection(CollectionKind::Watching);
|
||||
browser.open();
|
||||
assert!(browser.pending.is_some());
|
||||
browser.key(KeyEvent::new(KeyCode::Char('M'), KeyModifiers::NONE));
|
||||
assert!(browser.state.is_watched(&episode.id));
|
||||
assert_eq!(browser.screen().len(), 0);
|
||||
assert!(browser.pending.is_none());
|
||||
browser.collection(CollectionKind::WatchLater);
|
||||
browser.key(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::NONE));
|
||||
assert_eq!(browser.screen().len(), 0);
|
||||
@@ -336,8 +289,6 @@ fn offline_collections_and_filtered_bulk_removal_never_touch_youtube() {
|
||||
}
|
||||
browser.collection(CollectionKind::Saved);
|
||||
assert_eq!(browser.screen().len(), 1);
|
||||
browser.open();
|
||||
assert!(matches!(&browser.pending, Some(Request::Source(video)) if video.id == episode.id));
|
||||
browser.remove(true);
|
||||
assert_eq!(
|
||||
browser.removal.as_ref().unwrap().1.as_slice(),
|
||||
@@ -351,10 +302,8 @@ fn offline_collections_and_filtered_bulk_removal_never_touch_youtube() {
|
||||
.state
|
||||
.contains(CollectionKind::WatchLater, &episode.id)
|
||||
);
|
||||
let (_, report) = browser
|
||||
.state
|
||||
.miruro_session(&episode, &source(&episode, "progress"))
|
||||
.unwrap();
|
||||
let report = browser.state.progress_file(&episode).unwrap();
|
||||
crate::video_state::register_identity(&report, &episode.id).unwrap();
|
||||
let mut output = fs::OpenOptions::new();
|
||||
output.write(true).create_new(true);
|
||||
#[cfg(unix)]
|
||||
@@ -391,89 +340,33 @@ fn offline_collections_and_filtered_bulk_removal_never_touch_youtube() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selected_episode_and_offline_reopen_write_fresh_validated_source_sessions() {
|
||||
fn launcher_failure_does_not_mark_series_or_offline_episodes_as_started() {
|
||||
let directory = Directory::new();
|
||||
let mut browser = directory.browser(Some("anime"));
|
||||
catalog_results(&mut browser);
|
||||
enter_series(&mut browser);
|
||||
let first = browser.screen().selected_video().unwrap();
|
||||
browser.open();
|
||||
let stale = browser.generation;
|
||||
browser.select(Action::VideoNext);
|
||||
browser.accept(
|
||||
stale,
|
||||
Ok(Response::Source(
|
||||
first.clone(),
|
||||
Box::new(source(&first, "not-selected")),
|
||||
)),
|
||||
);
|
||||
assert!(sources(&directory).is_empty());
|
||||
let selected = browser.screen().selected_video().unwrap();
|
||||
assert_eq!(Identity::from_key(&selected.id).unwrap().episode, 7);
|
||||
browser.save(CollectionKind::Saved);
|
||||
browser.collection(CollectionKind::Saved);
|
||||
browser.open();
|
||||
browser.pending = None;
|
||||
browser.accept(
|
||||
browser.generation,
|
||||
Ok(Response::Source(
|
||||
selected.clone(),
|
||||
Box::new(source(&first, "wrong-identity")),
|
||||
)),
|
||||
);
|
||||
assert!(sources(&directory).is_empty());
|
||||
browser.open();
|
||||
browser.pending = None;
|
||||
let mut invalid = source(&selected, "private-token");
|
||||
invalid
|
||||
.headers
|
||||
.insert("X-Invalid".into(), "private-header\nvalue".into());
|
||||
browser.accept(
|
||||
browser.generation,
|
||||
Ok(Response::Source(selected.clone(), Box::new(invalid))),
|
||||
);
|
||||
assert!(sources(&directory).is_empty());
|
||||
assert!(!browser.status.contains("private-token"));
|
||||
assert!(!browser.status.contains("private-header"));
|
||||
for token in ["fresh-one", "fresh-two"] {
|
||||
browser.open();
|
||||
assert!(
|
||||
matches!(&browser.pending, Some(Request::Source(video)) if video.id == selected.id && video.title == selected.title)
|
||||
);
|
||||
browser.pending = None; // Deliver the real source response without a network request.
|
||||
browser.accept(
|
||||
browser.generation,
|
||||
Ok(Response::Source(
|
||||
selected.clone(),
|
||||
Box::new(source(&selected, token)),
|
||||
)),
|
||||
);
|
||||
}
|
||||
let written = sources(&directory);
|
||||
assert_eq!(written.len(), 2);
|
||||
assert!(
|
||||
written
|
||||
.iter()
|
||||
.all(|source| source["identity"]["episode"] == 7
|
||||
&& source["identity"]["media_id"] == "first")
|
||||
);
|
||||
assert!(
|
||||
written
|
||||
.iter()
|
||||
.any(|source| source["url"].as_str().unwrap().ends_with("fresh-one"))
|
||||
);
|
||||
assert!(
|
||||
written
|
||||
.iter()
|
||||
.any(|source| source["url"].as_str().unwrap().ends_with("fresh-two"))
|
||||
);
|
||||
assert!(!browser.state.is_watched(&selected.id)); // Spawn failure/CLI completion is not progress.
|
||||
assert!(browser.screen().error.is_some());
|
||||
assert!(!browser.state.is_watched(&selected.id));
|
||||
assert!(
|
||||
!browser
|
||||
.state
|
||||
.contains(CollectionKind::Watching, &selected.id)
|
||||
);
|
||||
assert!(browser.screen().error.is_some()); // Native launch errors remain visible.
|
||||
browser.save(CollectionKind::Saved);
|
||||
browser.collection(CollectionKind::Saved);
|
||||
browser.open();
|
||||
assert!(browser.screen().error.is_some());
|
||||
assert!(!browser.state.is_watched(&selected.id));
|
||||
assert!(
|
||||
!browser
|
||||
.state
|
||||
.contains(CollectionKind::Watching, &selected.id)
|
||||
);
|
||||
assert!(browser.state.contains(CollectionKind::Saved, &selected.id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user