mirror of
https://github.com/NotAShelf/mpvrc.git
synced 2026-04-15 15:33:47 +00:00
Merge pull request #1 from NotAShelf/interactive-cli
cli: interactive mode
This commit is contained in:
commit
b7672a306b
3 changed files with 191 additions and 7 deletions
41
.github/workflows/doc.yml
vendored
Normal file
41
.github/workflows/doc.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
rustdoc:
|
||||
name: Build Rust API docs
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Build Documentation
|
||||
run: cargo doc
|
||||
|
||||
- name: Deploy Docs
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc
|
||||
publish_branch: gh-pages
|
||||
|
|
@ -146,7 +146,8 @@ impl MpvCommand {
|
|||
///
|
||||
/// # Returns
|
||||
/// A string slice representing the command.
|
||||
#[must_use] pub const fn as_str(&self) -> &str {
|
||||
#[must_use]
|
||||
pub const fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::SetProperty => "set_property",
|
||||
Self::PlaylistNext => "playlist-next",
|
||||
|
|
|
|||
154
src/main.rs
154
src/main.rs
|
|
@ -1,15 +1,14 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use serde_json::json;
|
||||
use std::io::{self};
|
||||
use std::path::PathBuf;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
use mrc::set_property;
|
||||
use mrc::SOCKET_PATH;
|
||||
use mrc::{
|
||||
get_property, loadfile, playlist_clear, playlist_move, playlist_next, playlist_prev,
|
||||
playlist_remove, quit, seek,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about)]
|
||||
|
|
@ -51,6 +50,7 @@ enum CommandOptions {
|
|||
Move {
|
||||
/// The index of the item to move
|
||||
index1: usize,
|
||||
|
||||
/// The index to move the item to
|
||||
index2: usize,
|
||||
},
|
||||
|
|
@ -89,6 +89,9 @@ enum CommandOptions {
|
|||
/// The properties to fetch
|
||||
properties: Vec<String>,
|
||||
},
|
||||
|
||||
/// Enter interactive mode to send commands to MPV IPC
|
||||
Interactive,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -108,7 +111,6 @@ async fn main() -> io::Result<()> {
|
|||
info!("Playing media at index: {}", idx);
|
||||
set_property("playlist-pos", &json!(idx), None).await?;
|
||||
}
|
||||
|
||||
info!("Unpausing playback");
|
||||
set_property("pause", &json!(false), None).await?;
|
||||
}
|
||||
|
|
@ -171,6 +173,7 @@ async fn main() -> io::Result<()> {
|
|||
error!("{}", e);
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidInput, e));
|
||||
}
|
||||
|
||||
info!("Adding {} files to the playlist", filenames.len());
|
||||
for filename in filenames {
|
||||
loadfile(&filename, true, None).await?;
|
||||
|
|
@ -195,6 +198,145 @@ async fn main() -> io::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
CommandOptions::Interactive => {
|
||||
println!("Entering interactive mode. Type 'exit' to quit.");
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
loop {
|
||||
print!("mpv> ");
|
||||
stdout.flush()?;
|
||||
let mut input = String::new();
|
||||
stdin.read_line(&mut input)?;
|
||||
let trimmed = input.trim();
|
||||
|
||||
if trimmed.eq_ignore_ascii_case("exit") {
|
||||
println!("Exiting interactive mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// I don't like this either, but it looks cleaner than a multi-line
|
||||
// print macro just cramped in here.
|
||||
let commands = vec![
|
||||
(
|
||||
"play [index]",
|
||||
"Play or unpause playback, optionally at the specified index",
|
||||
),
|
||||
("pause", "Pause playback"),
|
||||
("stop", "Stop playback and quit MPV"),
|
||||
("next", "Skip to the next item in the playlist"),
|
||||
("prev", "Skip to the previous item in the playlist"),
|
||||
("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"),
|
||||
("get <property>", "Get the specified property"),
|
||||
(
|
||||
"set <property> <value>",
|
||||
"Set the specified property to a value",
|
||||
),
|
||||
("exit", "Quit interactive mode"),
|
||||
];
|
||||
|
||||
if trimmed.eq_ignore_ascii_case("help") {
|
||||
println!("Valid commands:");
|
||||
for (command, description) in commands {
|
||||
println!(" {} - {}", command, description);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let parts: Vec<&str> = trimmed.split_whitespace().collect();
|
||||
match parts.as_slice() {
|
||||
["play"] => {
|
||||
info!("Unpausing playback");
|
||||
set_property("pause", &json!(false), None).await?;
|
||||
}
|
||||
|
||||
["play", index] => {
|
||||
if let Ok(idx) = index.parse::<usize>() {
|
||||
info!("Playing media at index: {}", idx);
|
||||
set_property("playlist-pos", &json!(idx), None).await?;
|
||||
set_property("pause", &json!(false), None).await?;
|
||||
} else {
|
||||
println!("Invalid index: {}", index);
|
||||
}
|
||||
}
|
||||
|
||||
["pause"] => {
|
||||
info!("Pausing playback");
|
||||
set_property("pause", &json!(true), None).await?;
|
||||
}
|
||||
|
||||
["stop"] => {
|
||||
info!("Pausing playback");
|
||||
quit(None).await?;
|
||||
}
|
||||
|
||||
["next"] => {
|
||||
info!("Skipping to next item in the playlist");
|
||||
playlist_next(None).await?;
|
||||
}
|
||||
|
||||
["prev"] => {
|
||||
info!("Skipping to previous item in the playlist");
|
||||
playlist_prev(None).await?;
|
||||
}
|
||||
|
||||
["seek", seconds] => {
|
||||
if let Ok(sec) = seconds.parse::<i32>() {
|
||||
info!("Seeking to {} seconds", sec);
|
||||
seek(sec.into(), None).await?;
|
||||
} else {
|
||||
println!("Invalid seconds: {}", seconds);
|
||||
}
|
||||
}
|
||||
|
||||
["clear"] => {
|
||||
info!("Clearing the playlist");
|
||||
playlist_clear(None).await?;
|
||||
}
|
||||
|
||||
["list"] => {
|
||||
info!("Listing playlist items");
|
||||
if let Some(data) = get_property("playlist", None).await? {
|
||||
println!("{}", serde_json::to_string_pretty(&data)?);
|
||||
}
|
||||
}
|
||||
|
||||
["add", files @ ..] => {
|
||||
if files.is_empty() {
|
||||
println!("No files provided to add to the playlist");
|
||||
} else {
|
||||
info!("Adding {} files to the playlist", files.len());
|
||||
for file in files {
|
||||
loadfile(file, true, None).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
["get", property] => {
|
||||
if let Some(data) = get_property(property, None).await? {
|
||||
println!("{property}: {data}");
|
||||
}
|
||||
}
|
||||
|
||||
["set", property, value] => {
|
||||
let json_value = serde_json::from_str::<serde_json::Value>(value)
|
||||
.unwrap_or_else(|_| json!(value));
|
||||
set_property(property, &json_value, None).await?;
|
||||
println!("Set {property} to {value}");
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Unknown command: {}", trimmed);
|
||||
println!("Valid commands: play <index>, pause, stop, next, prev, seek <seconds>, clear, list, add <files>, get <property>, set <property> <value>, exit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue