feat: queue videos in running player
This commit is contained in:
+30
-23
@@ -193,9 +193,10 @@ fn playback_checks_are_local_to_views_and_previews_and_pruned_with_collections()
|
||||
#[test]
|
||||
fn playback_checks_render_independently_and_live_hints_use_configured_keys() {
|
||||
let mut app = app(&["tubular"]);
|
||||
let document = "[keys.video.select]\ntoggle = 'f20'\nclear = 'f21'\n"
|
||||
.parse::<toml_edit::DocumentMut>()
|
||||
.unwrap();
|
||||
let document =
|
||||
"[keys.video.select]\ntoggle = 'f20'\nclear = 'f21'\n[keys.video]\nenqueue = 'f22'\n"
|
||||
.parse::<toml_edit::DocumentMut>()
|
||||
.unwrap();
|
||||
app.config.keys = keys::Bindings::from_toml(document.get("keys")).unwrap();
|
||||
app.key_hints = KeyHints::new(&app.config.keys);
|
||||
for context in [
|
||||
@@ -206,7 +207,7 @@ fn playback_checks_render_independently_and_live_hints_use_configured_keys() {
|
||||
Context::CollectionInfo,
|
||||
Context::CollectionDescription,
|
||||
] {
|
||||
for key in ["F20", "F21"] {
|
||||
for key in ["F20", "F21", "F22"] {
|
||||
assert!(app.key_hints.footer(context).contains(key));
|
||||
assert!(app.key_hints.preview.iter().all(|hint| hint.contains(key)));
|
||||
assert!(app.config.keys.help_text().to_string().contains(key));
|
||||
@@ -238,27 +239,33 @@ fn playback_checks_render_independently_and_live_hints_use_configured_keys() {
|
||||
|
||||
#[test]
|
||||
fn playback_checks_survive_spawn_failure_clear_after_spawn_and_fall_back_to_hover() {
|
||||
let mut app = app(&["tubular"]);
|
||||
app.views[0].videos = youtube::search_results(
|
||||
for modifiers in [
|
||||
KeyModifiers::NONE,
|
||||
KeyModifiers::SUPER,
|
||||
KeyModifiers::CONTROL,
|
||||
] {
|
||||
let mut app = app(&["tubular"]);
|
||||
app.views[0].videos = youtube::search_results(
|
||||
r#"{"entries":[{"id":"jNQXAC9IVRw","title":"First"},{"id":"rQ_J9WH6CGk","title":"Second"}]}"#,
|
||||
).unwrap().videos;
|
||||
app.views[0].selection.select(Some(1));
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "rQ_J9WH6CGk");
|
||||
app.views[0].toggle_checked();
|
||||
app.views[0].selection.select(Some(0));
|
||||
app.options.player = "/nonexistent/tubular-test-player".into();
|
||||
app.play();
|
||||
assert!(app.player_launch.is_none());
|
||||
assert!(app.views[0].checked.contains("rQ_J9WH6CGk"));
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "rQ_J9WH6CGk");
|
||||
app.options.player = "/usr/bin/true".into();
|
||||
app.play();
|
||||
assert!(app.player_launch.is_some());
|
||||
assert!(app.views[0].checked.is_empty());
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "jNQXAC9IVRw");
|
||||
app.player_launch = None;
|
||||
app.play();
|
||||
assert!(app.player_launch.is_some());
|
||||
app.views[0].selection.select(Some(1));
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "rQ_J9WH6CGk");
|
||||
app.views[0].toggle_checked();
|
||||
app.views[0].selection.select(Some(0));
|
||||
app.options.player = "/nonexistent/tubular-test-player".into();
|
||||
app.key(KeyEvent::new(KeyCode::Enter, modifiers));
|
||||
assert!(app.player_launch.is_none());
|
||||
assert!(app.views[0].checked.contains("rQ_J9WH6CGk"));
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "rQ_J9WH6CGk");
|
||||
app.options.player = "/usr/bin/true".into();
|
||||
app.key(KeyEvent::new(KeyCode::Enter, modifiers));
|
||||
assert!(app.player_launch.is_some());
|
||||
assert!(app.views[0].checked.is_empty());
|
||||
assert_eq!(app.views[0].playback_videos()[0].id, "jNQXAC9IVRw");
|
||||
app.player_launch = None;
|
||||
app.key(KeyEvent::new(KeyCode::Enter, modifiers));
|
||||
assert!(app.player_launch.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -60,6 +60,39 @@ fn playback_checks_are_configurable_in_views_without_stealing_modal_space() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enqueue_is_configurable_and_only_active_in_video_views() {
|
||||
let defaults = Bindings::default();
|
||||
let overrides = configured("[keys.video]\nenqueue = 'f22'\n").unwrap();
|
||||
for &context in ALL {
|
||||
let expected = VIEW.contains(&context).then_some(Action::VideoEnqueue);
|
||||
for modifiers in [KeyModifiers::SUPER, KeyModifiers::CONTROL] {
|
||||
assert_eq!(
|
||||
press(&defaults, context, KeyCode::Enter, modifiers),
|
||||
expected
|
||||
);
|
||||
assert_ne!(
|
||||
press(&overrides, context, KeyCode::Enter, modifiers),
|
||||
Some(Action::VideoEnqueue),
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
press(&overrides, context, KeyCode::F(22), KeyModifiers::NONE),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
press(
|
||||
&defaults,
|
||||
Context::Normal,
|
||||
KeyCode::Enter,
|
||||
KeyModifiers::NONE
|
||||
),
|
||||
Some(Action::VideoPlay),
|
||||
);
|
||||
assert!(configured("[keys.video]\nenqueue = 'enter'\n").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_choice_is_configurable_and_normal_only() {
|
||||
let defaults = Bindings::default();
|
||||
|
||||
+51
-35
@@ -647,20 +647,30 @@ fn catalog_and_saved_show_rows_cannot_be_checked() {
|
||||
let mut browser = directory.browser(Some("anime"));
|
||||
catalog_results(&mut browser);
|
||||
browser.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
let generation = browser.generation;
|
||||
browser.key(KeyEvent::new(KeyCode::Enter, KeyModifiers::CONTROL));
|
||||
assert_eq!(browser.generation, generation);
|
||||
assert!(matches!(browser.screen().content, Content::Catalog { .. }));
|
||||
assert!(browser.player.is_none());
|
||||
assert!(browser.screen().checked.is_empty());
|
||||
assert!(browser.screen().playback_videos().is_empty());
|
||||
assert!(
|
||||
!browser
|
||||
.bindings()
|
||||
.iter()
|
||||
.any(|(action, _)| { matches!(action, Action::VideoToggle | Action::VideoClear) })
|
||||
);
|
||||
assert!(!browser.bindings().iter().any(|(action, _)| {
|
||||
matches!(
|
||||
action,
|
||||
Action::VideoToggle | Action::VideoClear | Action::VideoEnqueue
|
||||
)
|
||||
}));
|
||||
browser
|
||||
.state
|
||||
.save_show(&media("first"), &BTreeSet::new())
|
||||
.unwrap();
|
||||
browser.library(BTreeSet::new());
|
||||
browser.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
let generation = browser.generation;
|
||||
browser.key(KeyEvent::new(KeyCode::Enter, KeyModifiers::SUPER));
|
||||
assert_eq!(browser.generation, generation);
|
||||
assert!(matches!(browser.screen().content, Content::Library { .. }));
|
||||
assert!(browser.player.is_none());
|
||||
assert!(browser.screen().checked.is_empty());
|
||||
assert!(browser.screen().playback_videos().is_empty());
|
||||
enter_series(&mut browser);
|
||||
@@ -726,33 +736,39 @@ fn collection_checks_prune_hidden_and_removed_episodes_without_resurrection() {
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn episode_launch_retains_checks_on_failure_and_clears_only_after_spawn() {
|
||||
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.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
browser.select(Action::VideoNext);
|
||||
let hovered = browser.screen().selected_video().unwrap();
|
||||
browser.save(CollectionKind::Saved);
|
||||
browser.set_watch_status(WatchStatus::Watching);
|
||||
assert!(!browser.state.contains(CollectionKind::Saved, &first.id));
|
||||
assert!(!browser.state.contains(CollectionKind::Watching, &first.id));
|
||||
assert!(browser.state.contains(CollectionKind::Saved, &hovered.id));
|
||||
assert!(
|
||||
browser
|
||||
.state
|
||||
.contains(CollectionKind::Watching, &hovered.id)
|
||||
);
|
||||
browser.open();
|
||||
assert!(browser.screen().error.is_some());
|
||||
assert_eq!(browser.screen().checked, BTreeSet::from([first.id.clone()]));
|
||||
assert_eq!(browser.screen().playback_videos()[0].id, first.id);
|
||||
browser.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
browser.options.player = "/usr/bin/true".into();
|
||||
browser.open();
|
||||
assert!(browser.screen().error.is_none());
|
||||
assert!(browser.player.is_some());
|
||||
assert!(browser.screen().checked.is_empty());
|
||||
assert_eq!(browser.screen().playback_videos()[0].id, hovered.id);
|
||||
for modifiers in [
|
||||
KeyModifiers::NONE,
|
||||
KeyModifiers::SUPER,
|
||||
KeyModifiers::CONTROL,
|
||||
] {
|
||||
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.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
browser.select(Action::VideoNext);
|
||||
let hovered = browser.screen().selected_video().unwrap();
|
||||
browser.save(CollectionKind::Saved);
|
||||
browser.set_watch_status(WatchStatus::Watching);
|
||||
assert!(!browser.state.contains(CollectionKind::Saved, &first.id));
|
||||
assert!(!browser.state.contains(CollectionKind::Watching, &first.id));
|
||||
assert!(browser.state.contains(CollectionKind::Saved, &hovered.id));
|
||||
assert!(
|
||||
browser
|
||||
.state
|
||||
.contains(CollectionKind::Watching, &hovered.id)
|
||||
);
|
||||
browser.key(KeyEvent::new(KeyCode::Enter, modifiers));
|
||||
assert!(browser.screen().error.is_some());
|
||||
assert_eq!(browser.screen().checked, BTreeSet::from([first.id.clone()]));
|
||||
assert_eq!(browser.screen().playback_videos()[0].id, first.id);
|
||||
browser.key(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE));
|
||||
browser.options.player = "/usr/bin/true".into();
|
||||
browser.key(KeyEvent::new(KeyCode::Enter, modifiers));
|
||||
assert!(browser.screen().error.is_none());
|
||||
assert!(browser.player.is_some());
|
||||
assert!(browser.screen().checked.is_empty());
|
||||
assert_eq!(browser.screen().playback_videos()[0].id, hovered.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1417,13 +1417,13 @@ fn player_launch_rejects_invalid_and_duplicate_batch_identities_before_creating_
|
||||
let mut invalid_video = video();
|
||||
invalid_video.id = id.into();
|
||||
assert!(matches!(
|
||||
PlayerLaunch::start(&[video(), invalid_video], OsStr::new("terebi"), &mut state),
|
||||
PlayerLaunch::start(&[video(), invalid_video], OsStr::new("terebi"), &mut state, false),
|
||||
Err(error) if error.kind() == io::ErrorKind::InvalidInput
|
||||
));
|
||||
}
|
||||
for videos in [vec![], vec![video(), video()]] {
|
||||
assert!(matches!(
|
||||
PlayerLaunch::start(&videos, OsStr::new("terebi"), &mut state),
|
||||
PlayerLaunch::start(&videos, OsStr::new("terebi"), &mut state, false),
|
||||
Err(error) if error.kind() == io::ErrorKind::InvalidInput
|
||||
));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user