1.0.0 rewrite

This commit is contained in:
2025-09-24 22:51:34 +03:00
parent 4f8681a8c6
commit dee908a347
32 changed files with 6973 additions and 7 deletions
+113
View File
@@ -0,0 +1,113 @@
use clap::{Parser, Subcommand};
use pwsp::{
types::socket::Request,
utils::daemon::{make_request, wait_for_daemon},
};
use std::{error::Error, path::PathBuf};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Perform an action (ping, pause, resume, stop, play)
Action {
#[clap(subcommand)]
action: Actions,
},
/// Get information from the player (is paused, volume, position, state)
Get {
#[clap(subcommand)]
parameter: GetCommands,
},
/// Set information in the player (volume, position)
Set {
#[clap(subcommand)]
parameter: SetCommands,
},
}
#[derive(Subcommand, Debug)]
enum Actions {
/// Ping the daemon
Ping,
/// Pause audio playback
Pause,
/// Resume audio playback
Resume,
/// Stop audio playback and clear the queue
Stop,
/// Play a file
Play { file_path: PathBuf },
}
#[derive(Subcommand, Debug)]
enum GetCommands {
/// Check if the player is paused
IsPaused,
/// Playback volume
Volume,
/// Playback position
Position,
/// Duration of the current file
Duration,
/// Player state
State,
/// Current playing file path
CurrentFilePath,
/// Current audio input
Input,
/// All audio inputs
Inputs,
}
#[derive(Subcommand, Debug)]
enum SetCommands {
/// Playback volume
Volume { volume: f32 },
/// Playback position
Position { position: f32 },
/// Input
Input { id: u32 },
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let cli = Cli::parse();
wait_for_daemon().await?;
let request = match cli.command {
Commands::Action { action } => match action {
Actions::Ping => Request::ping(),
Actions::Pause => Request::pause(),
Actions::Resume => Request::resume(),
Actions::Stop => Request::stop(),
Actions::Play { file_path } => Request::play(file_path.to_str().unwrap()),
},
Commands::Get { parameter } => match parameter {
GetCommands::IsPaused => Request::get_is_paused(),
GetCommands::Volume => Request::get_volume(),
GetCommands::Position => Request::get_position(),
GetCommands::Duration => Request::get_duration(),
GetCommands::State => Request::get_state(),
GetCommands::CurrentFilePath => Request::get_current_file_path(),
GetCommands::Input => Request::get_input(),
GetCommands::Inputs => Request::get_inputs(),
},
Commands::Set { parameter } => match parameter {
SetCommands::Volume { volume } => Request::set_volume(volume),
SetCommands::Position { position } => Request::seek(position),
SetCommands::Input { id } => Request::set_input(id),
},
};
let response = make_request(request).await?;
println!("{} : {}", response.status, response.message);
Ok(())
}
+96
View File
@@ -0,0 +1,96 @@
use pwsp::{
types::socket::{Request, Response},
utils::{
commands::parse_command,
daemon::{
create_runtime_dir, get_audio_player, get_daemon_config, get_runtime_dir,
is_daemon_running, link_player_to_virtual_mic,
},
pipewire::create_virtual_mic,
},
};
use std::{error::Error, fs};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::UnixListener,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
create_runtime_dir()?;
if is_daemon_running()? {
return Err("Another instance is already running.".into());
}
get_daemon_config(); // Initialize daemon config
create_virtual_mic()?;
get_audio_player().await; // Initialize audio player
link_player_to_virtual_mic().await?;
let runtime_dir = get_runtime_dir();
let lock_file = fs::File::create(runtime_dir.join("daemon.lock"))?;
lock_file.lock()?;
let socket_path = runtime_dir.join("daemon.sock");
if fs::metadata(&socket_path).is_ok() {
fs::remove_file(&socket_path)?;
}
let listener = UnixListener::bind(&socket_path)?;
println!(
"Daemon started. Listening on {}",
socket_path.to_str().unwrap_or_default()
);
loop {
let (mut stream, _addr) = listener.accept().await?;
tokio::spawn(async move {
// ---------- Read request (start) ----------
let mut len_bytes = [0u8; 4];
if stream.read_exact(&mut len_bytes).await.is_err() {
eprintln!("Failed to read message length from client!");
return;
}
let request_len = u32::from_le_bytes(len_bytes) as usize;
let mut buffer = vec![0u8; request_len];
if stream.read_exact(&mut buffer).await.is_err() {
eprintln!("Failed to read message from client!");
return;
}
let request: Request = serde_json::from_slice(&buffer).unwrap();
println!("Received request: {:?}", request);
// ---------- Read request (end) ----------
// ---------- Generate response (start) ----------
let command = parse_command(&request);
let response: Response;
if let Some(command) = command {
response = command.execute().await;
} else {
response = Response::new(false, "Unknown command");
}
// ---------- Generate response (end) ----------
// ---------- Send response (start) ----------
let response_data = serde_json::to_vec(&response).unwrap();
let response_len = response_data.len() as u32;
if stream.write_all(&response_len.to_le_bytes()).await.is_err() {
eprintln!("Failed to write response length to client!");
return;
}
if stream.write_all(&response_data).await.is_err() {
eprintln!("Failed to write response to client!");
return;
}
println!("Sent response: {:?}", response);
// ---------- Send response (end) ----------
});
}
}