From 21b7df991bd10da6a1089a8135929e490722a8c6 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Thu, 12 Dec 2024 23:34:23 +0300 Subject: [PATCH] begin refactoring common logic into lib target --- src/lib.rs | 54 ++++++++++++++++++++++++++++++ src/main.rs | 95 +++++++++++++---------------------------------------- 2 files changed, 76 insertions(+), 73 deletions(-) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..083e1904 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,54 @@ +use serde_json::{Value, json}; +use std::io::{self}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::UnixStream; +use tracing::{debug, error}; + +pub const SOCKET_PATH: &str = "/tmp/mpvsocket"; + +/// Sends an IPC command to the MPV socket and returns the parsed response data. +pub async fn send_ipc_command(command: &str, args: &[Value]) -> io::Result> { + debug!( + "Sending IPC command: {} with arguments: {:?}", + command, args + ); + + match UnixStream::connect(SOCKET_PATH).await { + Ok(mut socket) => { + debug!("Found MPV socket at {}", SOCKET_PATH); + + let mut command_array = vec![json!(command)]; + command_array.extend_from_slice(args); + + let message = json!({ "command": command_array }); + let message_str = format!("{}\n", serde_json::to_string(&message)?); + debug!("Serialized message to send with newline: {}", message_str); + + socket.write_all(message_str.as_bytes()).await?; + socket.flush().await?; + debug!("Message sent and flushed"); + + let mut response = vec![0; 1024]; + let n = socket.read(&mut response).await?; + let response_str = String::from_utf8_lossy(&response[..n]); + debug!("Raw response: {}", response_str); + + match serde_json::from_str::(&response_str) { + Ok(json_response) => { + debug!("Parsed IPC response: {:?}", json_response); + Ok(json_response.get("data").cloned()) + } + + Err(e) => { + error!("Failed to parse response: {}", e); + Ok(None) + } + } + } + + Err(e) => { + error!("Failed to connect to MPV socket: {}", e); + Err(e) + } + } +} diff --git a/src/main.rs b/src/main.rs index a00bad19..3920f5ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ use clap::{Parser, Subcommand}; -use serde_json::json; +use serde_json::{Value, json}; use std::io::{self}; use std::path::PathBuf; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::UnixStream; use tracing::{debug, error, info}; +// TODO: SOCKET_PATH should be an argument to send_ipc_command +// and in the CLI, it should be expected as a command line argument +// or/and environment variable. +use mrc::{SOCKET_PATH, send_ipc_command}; + #[derive(Parser)] #[command(author, version, about)] struct Cli { @@ -86,61 +89,8 @@ enum CommandOptions { }, } -const SOCKET_PATH: &str = "/tmp/mpvsocket"; -async fn send_ipc_command( - command: &str, - args: &[serde_json::Value], -) -> io::Result> { - debug!( - "Sending IPC command: {} with arguments: {:?}", - command, args - ); - match UnixStream::connect(SOCKET_PATH).await { - Ok(mut socket) => { - debug!("Connected to MPV socket successfully"); - let mut command_array = vec![json!(command)]; - command_array.extend_from_slice(args); - let message = json!({ "command": command_array }); - let message_str = format!("{}\n", serde_json::to_string(&message)?); - debug!("Serialized message to send with newline: {}", message_str); - socket.write_all(message_str.as_bytes()).await?; - socket.flush().await?; - debug!("Message sent and flushed"); - - let mut response = vec![0; 1024]; - let n = socket.read(&mut response).await?; - let response_str = String::from_utf8_lossy(&response[..n]); - debug!("Raw response: {}", response_str); - - match serde_json::from_str::(&response_str) { - Ok(json_response) => { - debug!("Parsed IPC response: {:?}", json_response); - Ok(json_response.get("data").cloned()) - } - - Err(e) => { - error!("Failed to parse response: {}", e); - Ok(None) - } - } - } - - Err(e) => { - error!("Failed to connect to MPV socket: {}", e); - Err(e) - } - } -} - -async fn ipc_command(command: &str, args: &[serde_json::Value]) -> io::Result<()> { - if (send_ipc_command(command, args).await?).is_some() { - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::Other, - "Failed to execute command", - )) - } +pub async fn execute_command(command: &str, args: &[Value]) -> io::Result> { + send_ipc_command(command, args).await } #[tokio::main] @@ -158,55 +108,55 @@ async fn main() -> io::Result<()> { CommandOptions::Play { index } => { if let Some(idx) = index { info!("Playing media at index: {}", idx); - ipc_command("set_property", &[json!("playlist-pos"), json!(idx)]).await?; + execute_command("set_property", &[json!("playlist-pos"), json!(idx)]).await?; } info!("Unpausing playback"); - ipc_command("set_property", &[json!("pause"), json!(false)]).await?; + execute_command("set_property", &[json!("pause"), json!(false)]).await?; } CommandOptions::Pause => { info!("Pausing playback"); - ipc_command("set_property", &[json!("pause"), json!(true)]).await?; + execute_command("set_property", &[json!("pause"), json!(true)]).await?; } CommandOptions::Stop => { info!("Stopping playback and quitting MPV"); - ipc_command("quit", &[]).await?; + execute_command("quit", &[]).await?; } CommandOptions::Next => { info!("Skipping to next item in the playlist"); - ipc_command("playlist-next", &[]).await?; + execute_command("playlist-next", &[]).await?; } CommandOptions::Prev => { info!("Skipping to previous item in the playlist"); - ipc_command("playlist-prev", &[]).await?; + execute_command("playlist-prev", &[]).await?; } CommandOptions::Seek { seconds } => { info!("Seeking to {} seconds", seconds); - ipc_command("seek", &[json!(seconds)]).await?; + execute_command("seek", &[json!(seconds)]).await?; } CommandOptions::Move { index1, index2 } => { info!("Moving item from index {} to {}", index1, index2); - ipc_command("playlist-move", &[json!(index1), json!(index2)]).await?; + execute_command("playlist-move", &[json!(index1), json!(index2)]).await?; } CommandOptions::Remove { index } => { if let Some(idx) = index { info!("Removing item at index {}", idx); - ipc_command("playlist-remove", &[json!(idx)]).await?; + execute_command("playlist-remove", &[json!(idx)]).await?; } else { info!("Removing current item from playlist"); - ipc_command("playlist-remove", &[json!("current")]).await?; + execute_command("playlist-remove", &[json!("current")]).await?; } } CommandOptions::Clear => { info!("Clearing the playlist"); - ipc_command("playlist-clear", &[]).await?; + execute_command("playlist-clear", &[]).await?; } CommandOptions::List => { @@ -222,19 +172,18 @@ 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 { - ipc_command("loadfile", &[json!(filename), json!("append-play")]).await?; + execute_command("loadfile", &[json!(filename), json!("append-play")]).await?; } } CommandOptions::Replace { filenames } => { info!("Replacing current playlist with {} files", filenames.len()); if let Some(first_file) = filenames.first() { - ipc_command("loadfile", &[json!(first_file), json!("replace")]).await?; + execute_command("loadfile", &[json!(first_file), json!("replace")]).await?; for filename in &filenames[1..] { - ipc_command("loadfile", &[json!(filename), json!("append-play")]).await?; + execute_command("loadfile", &[json!(filename), json!("append-play")]).await?; } } }