mirror of
https://github.com/NotAShelf/mpvrc.git
synced 2026-04-15 15:33:47 +00:00
interactive: improve general usage with vi mode & history; add shortcuts
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I5bf85f6ad656ddc3c8246f07630658806a6a6964
This commit is contained in:
parent
ec229485d4
commit
1768c14a33
1 changed files with 77 additions and 40 deletions
|
|
@ -1,41 +1,72 @@
|
|||
use crate::commands::Commands;
|
||||
use crate::{MrcError, Result};
|
||||
use rustyline::config::EditMode;
|
||||
use rustyline::{Cmd, Config, Editor, KeyEvent};
|
||||
use serde_json::json;
|
||||
use std::io::{self, Write};
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct InteractiveMode;
|
||||
|
||||
impl InteractiveMode {
|
||||
pub async fn run() -> Result<()> {
|
||||
pub async fn run(socket_path: Option<&str>) -> Result<()> {
|
||||
let hist_path = dirs::data_local_dir()
|
||||
.map(|p| p.join("mrc").join("history.txt"))
|
||||
.unwrap_or_else(|| PathBuf::from("history.txt"));
|
||||
|
||||
let config = Config::builder().edit_mode(EditMode::Vi).build();
|
||||
|
||||
let mut rl: Editor<(), _> = Editor::with_config(config)
|
||||
.map_err(|e| MrcError::ConnectionError(Error::other(e.to_string())))?;
|
||||
|
||||
rl.bind_sequence(KeyEvent::ctrl('c'), Cmd::Interrupt);
|
||||
rl.bind_sequence(KeyEvent::ctrl('l'), Cmd::ClearScreen);
|
||||
|
||||
let _ = rl.load_history(&hist_path);
|
||||
|
||||
println!("Entering interactive mode. Type 'help' for commands or 'exit' to quit.");
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
println!("Socket: {}", socket_path.unwrap_or("/tmp/mpvsocket"));
|
||||
|
||||
loop {
|
||||
print!("mpv> ");
|
||||
stdout.flush().map_err(MrcError::ConnectionError)?;
|
||||
let readline = rl.readline("mpv> ");
|
||||
match readline {
|
||||
Ok(input) => {
|
||||
let trimmed = input.trim();
|
||||
if trimmed.is_empty() {
|
||||
continue;
|
||||
}
|
||||
rl.add_history_entry(trimmed).ok();
|
||||
|
||||
let mut input = String::new();
|
||||
stdin
|
||||
.read_line(&mut input)
|
||||
.map_err(MrcError::ConnectionError)?;
|
||||
let trimmed = input.trim();
|
||||
if trimmed.eq_ignore_ascii_case("exit") {
|
||||
println!("Exiting interactive mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
if trimmed.eq_ignore_ascii_case("exit") {
|
||||
println!("Exiting interactive mode.");
|
||||
break;
|
||||
}
|
||||
if trimmed.eq_ignore_ascii_case("help") {
|
||||
Self::show_help();
|
||||
continue;
|
||||
}
|
||||
|
||||
if trimmed.eq_ignore_ascii_case("help") {
|
||||
Self::show_help();
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(e) = Self::process_command(trimmed).await {
|
||||
eprintln!("Error: {}", e);
|
||||
if let Err(e) = Self::process_command(trimmed, socket_path).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
}
|
||||
Err(rustyline::error::ReadlineError::Interrupted) => {
|
||||
println!("(interrupted)");
|
||||
continue;
|
||||
}
|
||||
Err(rustyline::error::ReadlineError::Eof) => {
|
||||
println!("Exiting interactive mode.");
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rl.save_history(&hist_path).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +84,7 @@ impl InteractiveMode {
|
|||
("seek <seconds>", "Seek to the specified position"),
|
||||
("clear", "Clear the playlist"),
|
||||
("list", "List all items in the playlist"),
|
||||
("add <files>", "Add files to the playlist"),
|
||||
("add <files...>", "Add files to the playlist"),
|
||||
("get <property>", "Get the specified property"),
|
||||
(
|
||||
"set <property> <value>",
|
||||
|
|
@ -64,56 +95,61 @@ impl InteractiveMode {
|
|||
];
|
||||
|
||||
for (command, description) in commands {
|
||||
println!(" {} - {}", command, description);
|
||||
println!(" {:<22} - {}", command, description);
|
||||
}
|
||||
|
||||
println!("\nKeyboard shortcuts:");
|
||||
println!(" Ctrl+C - Interrupt current command");
|
||||
println!(" Ctrl+L - Clear screen");
|
||||
println!(" Ctrl+D - Exit");
|
||||
}
|
||||
|
||||
async fn process_command(input: &str) -> Result<()> {
|
||||
async fn process_command(input: &str, socket_path: Option<&str>) -> Result<()> {
|
||||
let parts: Vec<&str> = input.split_whitespace().collect();
|
||||
|
||||
match parts.as_slice() {
|
||||
["play"] => {
|
||||
Commands::play(None).await?;
|
||||
Commands::play(None, socket_path).await?;
|
||||
}
|
||||
|
||||
["play", index] => {
|
||||
if let Ok(idx) = index.parse::<usize>() {
|
||||
Commands::play(Some(idx)).await?;
|
||||
Commands::play(Some(idx), socket_path).await?;
|
||||
} else {
|
||||
println!("Invalid index: {}", index);
|
||||
}
|
||||
}
|
||||
|
||||
["pause"] => {
|
||||
Commands::pause().await?;
|
||||
Commands::pause(socket_path).await?;
|
||||
}
|
||||
|
||||
["stop"] => {
|
||||
Commands::stop().await?;
|
||||
Commands::stop(socket_path).await?;
|
||||
}
|
||||
|
||||
["next"] => {
|
||||
Commands::next().await?;
|
||||
Commands::next(socket_path).await?;
|
||||
}
|
||||
|
||||
["prev"] => {
|
||||
Commands::prev().await?;
|
||||
Commands::prev(socket_path).await?;
|
||||
}
|
||||
|
||||
["seek", seconds] => {
|
||||
if let Ok(sec) = seconds.parse::<i32>() {
|
||||
Commands::seek_to(sec.into()).await?;
|
||||
Commands::seek_to(sec.into(), socket_path).await?;
|
||||
} else {
|
||||
println!("Invalid seconds: {}", seconds);
|
||||
}
|
||||
}
|
||||
|
||||
["clear"] => {
|
||||
Commands::clear_playlist().await?;
|
||||
Commands::clear_playlist(socket_path).await?;
|
||||
}
|
||||
|
||||
["list"] => {
|
||||
Commands::list_playlist().await?;
|
||||
Commands::list_playlist(socket_path).await?;
|
||||
}
|
||||
|
||||
["add", files @ ..] => {
|
||||
|
|
@ -121,18 +157,18 @@ impl InteractiveMode {
|
|||
if file_strings.is_empty() {
|
||||
println!("No files provided to add to the playlist");
|
||||
} else {
|
||||
Commands::add_files(&file_strings).await?;
|
||||
Commands::add_files(&file_strings, socket_path).await?;
|
||||
}
|
||||
}
|
||||
|
||||
["get", property] => {
|
||||
Commands::get_single_property(property).await?;
|
||||
Commands::get_single_property(property, socket_path).await?;
|
||||
}
|
||||
|
||||
["set", property, value] => {
|
||||
let json_value = serde_json::from_str::<serde_json::Value>(value)
|
||||
.unwrap_or_else(|_| json!(value));
|
||||
Commands::set_single_property(property, &json_value).await?;
|
||||
Commands::set_single_property(property, &json_value, socket_path).await?;
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
|
@ -144,3 +180,4 @@ impl InteractiveMode {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue