mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 14:31:23 +00:00
1.0.0 rewrite
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
use crate::{
|
||||
types::pipewire::{AudioDevice, DeviceType, Terminate},
|
||||
utils::{
|
||||
daemon::get_daemon_config,
|
||||
pipewire::{create_link, get_all_devices, get_device},
|
||||
},
|
||||
};
|
||||
use rodio::{Decoder, OutputStream, OutputStreamBuilder, Sink, Source};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
error::Error,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Default, Clone, Serialize, Deserialize)]
|
||||
pub enum PlayerState {
|
||||
#[default]
|
||||
Stopped,
|
||||
Paused,
|
||||
Playing,
|
||||
}
|
||||
|
||||
pub struct AudioPlayer {
|
||||
_stream_handle: OutputStream,
|
||||
sink: Sink,
|
||||
|
||||
input_link_sender: Option<pipewire::channel::Sender<Terminate>>,
|
||||
pub current_input_device: Option<AudioDevice>,
|
||||
|
||||
pub volume: f32,
|
||||
pub duration: Option<f32>,
|
||||
|
||||
pub current_file_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl AudioPlayer {
|
||||
pub async fn new() -> Result<Self, Box<dyn Error>> {
|
||||
let daemon_config = get_daemon_config();
|
||||
let default_volume = daemon_config.default_volume.unwrap_or(1.0);
|
||||
let mut default_input_device: Option<AudioDevice> = None;
|
||||
if let Some(id) = daemon_config.default_input_id
|
||||
&& let Ok(device) = get_device(id).await
|
||||
&& device.device_type == DeviceType::Input
|
||||
{
|
||||
default_input_device = Some(device);
|
||||
}
|
||||
|
||||
let stream_handle = OutputStreamBuilder::open_default_stream()?;
|
||||
let sink = Sink::connect_new(stream_handle.mixer());
|
||||
sink.set_volume(default_volume);
|
||||
|
||||
let mut audio_player = AudioPlayer {
|
||||
_stream_handle: stream_handle,
|
||||
sink,
|
||||
|
||||
input_link_sender: None,
|
||||
current_input_device: default_input_device.clone(),
|
||||
|
||||
volume: default_volume,
|
||||
duration: None,
|
||||
|
||||
current_file_path: None,
|
||||
};
|
||||
|
||||
if default_input_device.is_some() {
|
||||
audio_player.link_devices().await?;
|
||||
}
|
||||
|
||||
Ok(audio_player)
|
||||
}
|
||||
|
||||
fn abort_link_thread(&mut self) {
|
||||
if let Some(sender) = &self.input_link_sender {
|
||||
match sender.send(Terminate {}) {
|
||||
Ok(_) => println!("Sent terminate signal to link thread"),
|
||||
Err(_) => println!("Failed to send terminate signal to link thread"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn link_devices(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
self.abort_link_thread();
|
||||
|
||||
if self.current_input_device.is_none() {
|
||||
println!("No input device selected, skipping device linking");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (input_devices, _) = get_all_devices().await?;
|
||||
|
||||
let mut pwsp_daemon_input: Option<AudioDevice> = None;
|
||||
for input_device in input_devices {
|
||||
if input_device.name == "pwsp-virtual-mic" {
|
||||
pwsp_daemon_input = Some(input_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if pwsp_daemon_input.is_none() {
|
||||
println!("Could not find pwsp-daemon input device, skipping device linking");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pwsp_daemon_input = pwsp_daemon_input.unwrap();
|
||||
|
||||
let current_input_device = self.current_input_device.clone().unwrap();
|
||||
let output_fl = current_input_device
|
||||
.clone()
|
||||
.output_fl
|
||||
.expect("Failed to get pwsp-daemon output_fl");
|
||||
let output_fr = current_input_device
|
||||
.clone()
|
||||
.output_fr
|
||||
.expect("Failed to get pwsp-daemon output_fl");
|
||||
let input_fl = pwsp_daemon_input
|
||||
.clone()
|
||||
.input_fl
|
||||
.expect("Failed to get pwsp-daemon input_fl");
|
||||
let input_fr = pwsp_daemon_input
|
||||
.clone()
|
||||
.input_fr
|
||||
.expect("Failed to get pwsp-daemon input_fr");
|
||||
self.input_link_sender = Some(create_link(output_fl, output_fr, input_fl, input_fr)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) {
|
||||
if self.get_state() == PlayerState::Playing {
|
||||
self.sink.pause();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resume(&mut self) {
|
||||
if self.get_state() == PlayerState::Paused {
|
||||
self.sink.play();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.sink.stop();
|
||||
}
|
||||
|
||||
pub fn is_paused(&self) -> bool {
|
||||
self.sink.is_paused()
|
||||
}
|
||||
|
||||
pub fn get_state(&mut self) -> PlayerState {
|
||||
if self.sink.len() == 0 {
|
||||
return PlayerState::Stopped;
|
||||
}
|
||||
|
||||
if self.sink.is_paused() {
|
||||
return PlayerState::Paused;
|
||||
}
|
||||
|
||||
PlayerState::Playing
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
self.volume = volume;
|
||||
self.sink.set_volume(volume);
|
||||
}
|
||||
|
||||
pub fn get_position(&mut self) -> f32 {
|
||||
if self.get_state() == PlayerState::Stopped {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
self.sink.get_pos().as_secs_f32()
|
||||
}
|
||||
|
||||
pub fn seek(&mut self, position: f32) -> Result<(), Box<dyn Error>> {
|
||||
match self.sink.try_seek(Duration::from_secs_f32(position)) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_duration(&mut self) -> Result<f32, Box<dyn Error>> {
|
||||
if self.get_state() == PlayerState::Stopped {
|
||||
Err("Nothing is playing right now".into())
|
||||
} else {
|
||||
match self.duration {
|
||||
Some(duration) => Ok(duration),
|
||||
None => Err("Couldn't determine duration for current file".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn play(&mut self, file_path: &Path) -> Result<(), Box<dyn Error>> {
|
||||
if !file_path.exists() {
|
||||
return Err(format!("File does not exist: {}", file_path.display()).into());
|
||||
}
|
||||
|
||||
let file = fs::File::open(file_path)?;
|
||||
match Decoder::try_from(file) {
|
||||
Ok(source) => {
|
||||
self.current_file_path = Some(file_path.to_path_buf());
|
||||
|
||||
if let Some(duration) = source.total_duration() {
|
||||
self.duration = Some(duration.as_secs_f32());
|
||||
} else {
|
||||
self.duration = None;
|
||||
}
|
||||
|
||||
self.sink.stop();
|
||||
self.sink.append(source);
|
||||
self.sink.play();
|
||||
self.link_devices().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_current_file_path(&mut self) -> &Option<PathBuf> {
|
||||
if self.get_state() == PlayerState::Stopped {
|
||||
self.current_file_path = None;
|
||||
}
|
||||
&self.current_file_path
|
||||
}
|
||||
|
||||
pub async fn set_current_input_device(&mut self, id: u32) -> Result<(), Box<dyn Error>> {
|
||||
let input_device = get_device(id).await?;
|
||||
|
||||
if input_device.device_type != DeviceType::Input {
|
||||
return Err("Selected device is not an input device".into());
|
||||
}
|
||||
|
||||
self.current_input_device = Some(input_device);
|
||||
|
||||
self.link_devices().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
use crate::{
|
||||
types::socket::Response,
|
||||
utils::{daemon::get_audio_player, pipewire::get_all_devices},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Executable {
|
||||
async fn execute(&self) -> Response;
|
||||
}
|
||||
|
||||
pub struct PingCommand {}
|
||||
|
||||
pub struct PauseCommand {}
|
||||
|
||||
pub struct ResumeCommand {}
|
||||
|
||||
pub struct StopCommand {}
|
||||
|
||||
pub struct IsPausedCommand {}
|
||||
|
||||
pub struct GetStateCommand {}
|
||||
|
||||
pub struct GetVolumeCommand {}
|
||||
|
||||
pub struct SetVolumeCommand {
|
||||
pub volume: Option<f32>,
|
||||
}
|
||||
|
||||
pub struct GetPositionCommand {}
|
||||
|
||||
pub struct SeekCommand {
|
||||
pub position: Option<f32>,
|
||||
}
|
||||
|
||||
pub struct GetDurationCommand {}
|
||||
|
||||
pub struct PlayCommand {
|
||||
pub file_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub struct GetCurrentFilePathCommand {}
|
||||
|
||||
pub struct GetCurrentInputCommand {}
|
||||
|
||||
pub struct GetAllInputsCommand {}
|
||||
|
||||
pub struct SetCurrentInputCommand {
|
||||
pub id: Option<u32>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for PingCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
Response::new(true, "pong")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for PauseCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
audio_player.pause();
|
||||
Response::new(true, "Audio was paused")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for ResumeCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
audio_player.resume();
|
||||
Response::new(true, "Audio was resumed")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for StopCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
audio_player.stop();
|
||||
Response::new(true, "Audio was stopped")
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for IsPausedCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let audio_player = get_audio_player().await.lock().await;
|
||||
let is_paused = audio_player.is_paused().to_string();
|
||||
Response::new(true, is_paused)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetStateCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
let state = audio_player.get_state();
|
||||
Response::new(true, serde_json::to_string(&state).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetVolumeCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let audio_player = get_audio_player().await.lock().await;
|
||||
let volume = audio_player.volume;
|
||||
Response::new(true, volume.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for SetVolumeCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
if let Some(volume) = self.volume {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
audio_player.set_volume(volume);
|
||||
Response::new(true, format!("Audio volume was set to {}", volume))
|
||||
} else {
|
||||
Response::new(false, "Invalid volume value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetPositionCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
let position = audio_player.get_position();
|
||||
Response::new(true, position.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for SeekCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
if let Some(position) = self.position {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
match audio_player.seek(position) {
|
||||
Ok(_) => Response::new(true, format!("Audio position was set to {}", position)),
|
||||
Err(err) => Response::new(false, err.to_string()),
|
||||
}
|
||||
} else {
|
||||
Response::new(false, "Invalid position value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetDurationCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
match audio_player.get_duration() {
|
||||
Ok(duration) => Response::new(true, duration.to_string()),
|
||||
Err(err) => Response::new(false, err.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for PlayCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
if let Some(file_path) = &self.file_path {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
match audio_player.play(file_path).await {
|
||||
Ok(_) => Response::new(true, format!("Now playing {}", file_path.display())),
|
||||
Err(err) => Response::new(false, err.to_string()),
|
||||
}
|
||||
} else {
|
||||
Response::new(false, "Invalid file path")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetCurrentFilePathCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
let current_file_path = audio_player.get_current_file_path();
|
||||
if let Some(current_file_path) = current_file_path {
|
||||
Response::new(true, current_file_path.to_str().unwrap())
|
||||
} else {
|
||||
Response::new(false, "No file is playing")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetCurrentInputCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let audio_player = get_audio_player().await.lock().await;
|
||||
if let Some(input_device) = &audio_player.current_input_device {
|
||||
Response::new(true, format!("{} - {}", input_device.id, input_device.nick))
|
||||
} else {
|
||||
Response::new(false, "No input device selected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetAllInputsCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
let (input_devices, _output_devices) = get_all_devices().await.unwrap();
|
||||
let mut input_devices_strings = vec![];
|
||||
for device in input_devices {
|
||||
if device.name == "pwsp-virtual-mic" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let string = format!("{} - {}", device.id, device.nick);
|
||||
input_devices_strings.push(string);
|
||||
}
|
||||
let response_message = input_devices_strings.join("; ");
|
||||
|
||||
Response::new(true, response_message)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for SetCurrentInputCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
if let Some(id) = self.id {
|
||||
let mut audio_player = get_audio_player().await.lock().await;
|
||||
match audio_player.set_current_input_device(id).await {
|
||||
Ok(_) => Response::new(true, "Input device was set"),
|
||||
Err(err) => Response::new(false, err.to_string()),
|
||||
}
|
||||
} else {
|
||||
Response::new(false, "Invalid index value")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use crate::utils::config::get_config_path;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, error::Error, fs, path::PathBuf};
|
||||
|
||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
||||
pub struct DaemonConfig {
|
||||
pub default_input_id: Option<u32>,
|
||||
pub default_volume: Option<f32>,
|
||||
}
|
||||
|
||||
impl DaemonConfig {
|
||||
pub fn save_to_file(&self) -> Result<(), Box<dyn Error>> {
|
||||
let config_path = get_config_path()?.join("daemon.json");
|
||||
let config_dir = config_path.parent().unwrap();
|
||||
|
||||
if !config_path.exists() {
|
||||
fs::create_dir_all(config_dir)?;
|
||||
}
|
||||
|
||||
let config_json = serde_json::to_string_pretty(self)?;
|
||||
fs::write(config_path, config_json.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_from_file() -> Result<DaemonConfig, Box<dyn Error>> {
|
||||
let config_path = get_config_path()?.join("daemon.json");
|
||||
let bytes = fs::read(config_path)?;
|
||||
Ok(serde_json::from_slice::<DaemonConfig>(&bytes)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct GuiConfig {
|
||||
pub scale_factor: f32,
|
||||
|
||||
pub save_volume: bool,
|
||||
pub save_input: bool,
|
||||
pub save_scale_factor: bool,
|
||||
|
||||
pub dirs: HashSet<PathBuf>,
|
||||
}
|
||||
|
||||
impl Default for GuiConfig {
|
||||
fn default() -> Self {
|
||||
GuiConfig {
|
||||
scale_factor: 1.0,
|
||||
|
||||
save_volume: false,
|
||||
save_input: false,
|
||||
save_scale_factor: false,
|
||||
|
||||
dirs: HashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiConfig {
|
||||
pub fn save_to_file(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
let config_path = get_config_path()?.join("gui.json");
|
||||
let config_dir = config_path.parent().unwrap();
|
||||
|
||||
if !config_path.exists() {
|
||||
fs::create_dir_all(config_dir)?;
|
||||
}
|
||||
|
||||
// Do not save scale factor if user does not want to
|
||||
if !self.save_scale_factor {
|
||||
self.scale_factor = 1.0;
|
||||
}
|
||||
|
||||
let config_json = serde_json::to_string_pretty(self)?;
|
||||
fs::write(config_path, config_json.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_from_file() -> Result<GuiConfig, Box<dyn Error>> {
|
||||
let config_path = get_config_path()?.join("gui.json");
|
||||
let bytes = fs::read(config_path)?;
|
||||
Ok(serde_json::from_slice::<GuiConfig>(&bytes)?)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use crate::types::audio_player::PlayerState;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct AppState {
|
||||
pub search_query: String,
|
||||
|
||||
pub position_slider_value: f32,
|
||||
pub volume_slider_value: f32,
|
||||
|
||||
pub position_dragged: bool,
|
||||
pub volume_dragged: bool,
|
||||
|
||||
pub show_settings: bool,
|
||||
|
||||
pub current_dir: Option<PathBuf>,
|
||||
pub dirs: HashSet<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct AudioPlayerState {
|
||||
pub state: PlayerState,
|
||||
pub new_state: Option<PlayerState>,
|
||||
pub current_file_path: PathBuf,
|
||||
|
||||
pub is_paused: bool,
|
||||
|
||||
pub volume: f32,
|
||||
pub new_volume: Option<f32>,
|
||||
pub position: f32,
|
||||
pub new_position: Option<f32>,
|
||||
pub duration: f32,
|
||||
|
||||
pub current_input: u32,
|
||||
pub all_inputs: HashMap<u32, String>,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod audio_player;
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
pub mod gui;
|
||||
pub mod pipewire;
|
||||
pub mod socket;
|
||||
@@ -0,0 +1,31 @@
|
||||
#[derive(Debug)]
|
||||
pub struct Terminate {}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct Port {
|
||||
pub node_id: u32,
|
||||
pub port_id: u32,
|
||||
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum DeviceType {
|
||||
Input,
|
||||
Output,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct AudioDevice {
|
||||
pub id: u32,
|
||||
|
||||
pub nick: String,
|
||||
pub name: String,
|
||||
|
||||
pub device_type: DeviceType,
|
||||
|
||||
pub input_fl: Option<Port>,
|
||||
pub input_fr: Option<Port>,
|
||||
pub output_fl: Option<Port>,
|
||||
pub output_fr: Option<Port>,
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Request {
|
||||
pub name: String,
|
||||
pub args: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new<T: AsRef<str>>(function_name: T, data: Vec<(T, T)>) -> Self {
|
||||
let hashmap_data: HashMap<String, String> = data
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.as_ref().to_string(), value.as_ref().to_string()))
|
||||
.collect();
|
||||
|
||||
Request {
|
||||
name: function_name.as_ref().to_string(),
|
||||
args: hashmap_data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ping() -> Self {
|
||||
Request::new("ping", vec![])
|
||||
}
|
||||
|
||||
pub fn pause() -> Self {
|
||||
Request::new("pause", vec![])
|
||||
}
|
||||
|
||||
pub fn resume() -> Self {
|
||||
Request::new("resume", vec![])
|
||||
}
|
||||
|
||||
pub fn stop() -> Self {
|
||||
Request::new("stop", vec![])
|
||||
}
|
||||
|
||||
pub fn play(file_path: &str) -> Self {
|
||||
Request::new("play", vec![("file_path", file_path)])
|
||||
}
|
||||
|
||||
pub fn get_is_paused() -> Self {
|
||||
Request::new("is_paused", vec![])
|
||||
}
|
||||
|
||||
pub fn get_volume() -> Self {
|
||||
Request::new("get_volume", vec![])
|
||||
}
|
||||
|
||||
pub fn get_position() -> Self {
|
||||
Request::new("get_position", vec![])
|
||||
}
|
||||
|
||||
pub fn get_duration() -> Self {
|
||||
Request::new("get_duration", vec![])
|
||||
}
|
||||
|
||||
pub fn get_state() -> Self {
|
||||
Request::new("get_state", vec![])
|
||||
}
|
||||
|
||||
pub fn get_current_file_path() -> Self {
|
||||
Request::new("get_current_file_path", vec![])
|
||||
}
|
||||
|
||||
pub fn get_input() -> Self {
|
||||
Request::new("get_input", vec![])
|
||||
}
|
||||
|
||||
pub fn get_inputs() -> Self {
|
||||
Request::new("get_inputs", vec![])
|
||||
}
|
||||
|
||||
pub fn set_volume(volume: f32) -> Self {
|
||||
Request::new("set_volume", vec![("volume", &volume.to_string())])
|
||||
}
|
||||
|
||||
pub fn seek(position: f32) -> Self {
|
||||
Request::new("seek", vec![("position", &position.to_string())])
|
||||
}
|
||||
|
||||
pub fn set_input(id: u32) -> Self {
|
||||
Request::new("set_input", vec![("input_id", &id.to_string())])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Response {
|
||||
pub status: bool,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn new<T: AsRef<str>>(status: bool, message: T) -> Self {
|
||||
Response {
|
||||
status,
|
||||
message: message.as_ref().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user