mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-28 06:21:23 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 81da36f03c | |||
| 8bfa5daf78 | |||
| b816d2aa88 | |||
| 23ae562849 | |||
| e3bc1fd55f | |||
| 15964f205b | |||
| 6a0ac61033 | |||
| 4b802273f4 | |||
| baae7a1ccf | |||
| 654694cecf | |||
| 04ecf66beb | |||
| 0fe94f9112 |
@@ -0,0 +1 @@
|
||||
custom: ['https://boosty.to/arabian']
|
||||
Generated
+765
-117
File diff suppressed because it is too large
Load Diff
+57
-12
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "pwsp"
|
||||
version = "1.5.1"
|
||||
version = "1.6.0"
|
||||
edition = "2024"
|
||||
authors = ["arabian"]
|
||||
description = "PWSP lets you play audio files through your microphone. Has both CLI and GUI clients."
|
||||
@@ -18,16 +18,37 @@ async-trait = "0.1.89"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
|
||||
clap = { version = "4.5.55", default-features = false, features = ["std", "suggestions", "help", "usage", "error-context", "derive"] }
|
||||
clap = { version = "4.5.58", default-features = false, features = [
|
||||
"std",
|
||||
"suggestions",
|
||||
"help",
|
||||
"usage",
|
||||
"error-context",
|
||||
"derive",
|
||||
] }
|
||||
dirs = "6.0.0"
|
||||
itertools = "0.14.0"
|
||||
|
||||
rodio = { version = "0.21.1", default-features = false, features = ["symphonia-all", "playback"] }
|
||||
rodio = { version = "0.21.1", default-features = false, features = [
|
||||
"symphonia-all",
|
||||
"playback",
|
||||
] }
|
||||
pipewire = "0.9.2"
|
||||
rfd = { version = "0.17.2", default-features = false, features = ["xdg-portal"]}
|
||||
rfd = { version = "0.17.2", default-features = false, features = [
|
||||
"xdg-portal",
|
||||
] }
|
||||
opener = { version = "0.8.4", features = ["reveal"] }
|
||||
|
||||
egui = { version = "0.33.3", default-features = false, features = ["default_fonts", "rayon"] }
|
||||
eframe = { version = "0.33.3", default-features = false, features = ["default_fonts", "glow", "x11", "wayland"] }
|
||||
egui = { version = "0.33.3", default-features = false, features = [
|
||||
"default_fonts",
|
||||
"rayon",
|
||||
] }
|
||||
eframe = { version = "0.33.3", default-features = false, features = [
|
||||
"default_fonts",
|
||||
"glow",
|
||||
"x11",
|
||||
"wayland",
|
||||
] }
|
||||
egui_material_icons = "0.5.0"
|
||||
egui_dnd = "0.14.0"
|
||||
|
||||
@@ -52,10 +73,34 @@ panic = "abort"
|
||||
|
||||
[package.metadata.deb]
|
||||
assets = [
|
||||
["target/release/pwsp-daemon", "usr/bin/", "755"],
|
||||
["target/release/pwsp-cli", "usr/bin/", "755"],
|
||||
["target/release/pwsp-gui", "usr/bin/", "755"],
|
||||
["assets/pwsp-gui.desktop", "usr/share/applications/pwsp.desktop", "644"],
|
||||
["assets/icon.png", "usr/share/icons/hicolor/256x256/apps/pwsp.png", "644"],
|
||||
["assets/pwsp-daemon.service", "usr/lib/systemd/user/pwsp-daemon.service", "644"],
|
||||
[
|
||||
"target/release/pwsp-daemon",
|
||||
"usr/bin/",
|
||||
"755",
|
||||
],
|
||||
[
|
||||
"target/release/pwsp-cli",
|
||||
"usr/bin/",
|
||||
"755",
|
||||
],
|
||||
[
|
||||
"target/release/pwsp-gui",
|
||||
"usr/bin/",
|
||||
"755",
|
||||
],
|
||||
[
|
||||
"assets/pwsp-gui.desktop",
|
||||
"usr/share/applications/pwsp.desktop",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"assets/icon.png",
|
||||
"usr/share/icons/hicolor/256x256/apps/pwsp.png",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"assets/pwsp-daemon.service",
|
||||
"usr/lib/systemd/user/pwsp-daemon.service",
|
||||
"644",
|
||||
],
|
||||
]
|
||||
|
||||
@@ -208,3 +208,6 @@ a [pull request](https://github.com/arabianq/pipewire-soundpad/pulls).
|
||||
|
||||
This project is licensed under
|
||||
the [MIT License](https://github.com/arabianq/pipewire-soundpad/blob/main/LICENSE).
|
||||
|
||||
# **🤖 AI Wiki**
|
||||
[](https://deepwiki.com/arabianq/pipewire-soundpad)
|
||||
|
||||
+1
-1
Submodule packages/aur/bin updated: 446179bb91...534cbc33ba
+1
-1
Submodule packages/aur/standart updated: d122d0719f...b381a90a50
@@ -4,7 +4,7 @@
|
||||
%global cargo_install_lib 0
|
||||
|
||||
Name: pwsp
|
||||
Version: 1.5.1
|
||||
Version: 1.6.0
|
||||
Release: %autorelease
|
||||
Summary: Lets you play audio files through your microphone
|
||||
|
||||
|
||||
@@ -92,6 +92,8 @@ enum GetCommands {
|
||||
Input,
|
||||
/// All audio inputs
|
||||
Inputs,
|
||||
/// Version of the daemon
|
||||
DaemonVersion,
|
||||
/// Full player state
|
||||
FullState,
|
||||
}
|
||||
@@ -148,6 +150,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
GetCommands::Tracks => Request::get_tracks(),
|
||||
GetCommands::Input => Request::get_input(),
|
||||
GetCommands::Inputs => Request::get_inputs(),
|
||||
GetCommands::DaemonVersion => Request::get_daemon_version(),
|
||||
GetCommands::FullState => Request::get_full_state(),
|
||||
},
|
||||
Commands::Set { parameter } => match parameter {
|
||||
|
||||
+89
-19
@@ -4,13 +4,11 @@ use egui::{
|
||||
Layout, RichText, ScrollArea, Sense, Slider, TextEdit, Ui, Vec2,
|
||||
};
|
||||
use egui_dnd::dnd;
|
||||
use egui_material_icons::icons;
|
||||
use pwsp::types::audio_player::TrackInfo;
|
||||
use egui_material_icons::icons::*;
|
||||
use pwsp::types::{audio_player::TrackInfo, gui::AppState};
|
||||
use pwsp::utils::gui::format_time_pair;
|
||||
use std::{error::Error, time::Instant};
|
||||
|
||||
use pwsp::types::gui::AppState;
|
||||
|
||||
enum TrackAction {
|
||||
Pause(u32),
|
||||
Resume(u32),
|
||||
@@ -21,13 +19,13 @@ enum TrackAction {
|
||||
impl SoundpadGui {
|
||||
fn get_volume_icon(volume: f32) -> &'static str {
|
||||
if volume > 0.7 {
|
||||
icons::ICON_VOLUME_UP
|
||||
ICON_VOLUME_UP
|
||||
} else if volume <= 0.0 {
|
||||
icons::ICON_VOLUME_OFF
|
||||
ICON_VOLUME_OFF
|
||||
} else if volume < 0.3 {
|
||||
icons::ICON_VOLUME_MUTE
|
||||
ICON_VOLUME_MUTE
|
||||
} else {
|
||||
icons::ICON_VOLUME_DOWN
|
||||
ICON_VOLUME_DOWN
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +44,7 @@ impl SoundpadGui {
|
||||
ui.spacing_mut().item_spacing.y = 5.0;
|
||||
// --------- Back Button and Title ----------
|
||||
ui.horizontal_top(|ui| {
|
||||
let back_button = Button::new(icons::ICON_ARROW_BACK).frame(false);
|
||||
let back_button = Button::new(ICON_ARROW_BACK).frame(false);
|
||||
let back_button_response = ui.add(back_button);
|
||||
if back_button_response.clicked() {
|
||||
self.app_state.show_settings = false;
|
||||
@@ -83,6 +81,10 @@ impl SoundpadGui {
|
||||
self.config.save_to_file().ok();
|
||||
}
|
||||
// --------------------------------
|
||||
|
||||
ui.with_layout(Layout::bottom_up(Align::Min), |ui| {
|
||||
ui.label(format!("GUI version: {}", env!("CARGO_PKG_VERSION")));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -169,9 +171,9 @@ impl SoundpadGui {
|
||||
ui.horizontal_top(|ui| {
|
||||
// ---------- Play Button ----------
|
||||
let play_button = Button::new(if track.paused {
|
||||
icons::ICON_PLAY_ARROW
|
||||
ICON_PLAY_ARROW
|
||||
} else {
|
||||
icons::ICON_PAUSE
|
||||
ICON_PAUSE
|
||||
})
|
||||
.corner_radius(15.0);
|
||||
|
||||
@@ -188,9 +190,9 @@ impl SoundpadGui {
|
||||
// ---------- Loop Button ----------
|
||||
let loop_button = Button::new(
|
||||
RichText::new(if track.looped {
|
||||
icons::ICON_REPEAT_ONE
|
||||
ICON_REPEAT_ONE
|
||||
} else {
|
||||
icons::ICON_REPEAT
|
||||
ICON_REPEAT
|
||||
})
|
||||
.size(18.0),
|
||||
)
|
||||
@@ -248,7 +250,7 @@ impl SoundpadGui {
|
||||
// --------------------------------
|
||||
|
||||
// ---------- Stop Button ---------
|
||||
let stop_button = Button::new(icons::ICON_CLOSE).frame(false);
|
||||
let stop_button = Button::new(ICON_CLOSE).frame(false);
|
||||
let stop_button_response = ui.add_sized([30.0, 30.0], stop_button);
|
||||
if stop_button_response.clicked() {
|
||||
action = Some(TrackAction::Stop(track.id));
|
||||
@@ -311,7 +313,7 @@ impl SoundpadGui {
|
||||
let path = item.clone();
|
||||
ui.horizontal(|ui| {
|
||||
handle.ui(ui, |ui| {
|
||||
ui.label(icons::ICON_DRAG_INDICATOR);
|
||||
ui.label(ICON_DRAG_INDICATOR);
|
||||
});
|
||||
let name = path
|
||||
.file_name()
|
||||
@@ -333,18 +335,46 @@ impl SoundpadGui {
|
||||
self.open_dir(&path);
|
||||
}
|
||||
|
||||
let delete_dir_button = Button::new(icons::ICON_DELETE).frame(false);
|
||||
let delete_dir_button = Button::new(ICON_DELETE).frame(false);
|
||||
let delete_dir_button_response =
|
||||
ui.add_sized([18.0, 18.0], delete_dir_button);
|
||||
if delete_dir_button_response.clicked() {
|
||||
self.app_state.dirs_to_remove.insert(path.clone());
|
||||
}
|
||||
|
||||
// Context menu
|
||||
dir_button_response.context_menu(|ui| {
|
||||
if ui
|
||||
.button(format!("{} {}", ICON_OPEN_IN_NEW, "Show"))
|
||||
.clicked()
|
||||
{
|
||||
self.open_dir(&path);
|
||||
}
|
||||
|
||||
if ui
|
||||
.button(format!(
|
||||
"{} {}",
|
||||
ICON_OPEN_IN_BROWSER, "Open in File Manager"
|
||||
))
|
||||
.clicked()
|
||||
{
|
||||
if let Err(e) = opener::open(&path) {
|
||||
eprintln!("Failed to open file manager: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button(format!("{} {}", ICON_DELETE, "Remove")).clicked() {
|
||||
self.app_state.dirs_to_remove.insert(path.clone());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
self.app_state.dirs = dirs;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
let add_dirs_button = Button::new(icons::ICON_ADD).frame(false);
|
||||
let add_dirs_button = Button::new(ICON_ADD).frame(false);
|
||||
let add_dirs_button_response = ui.add_sized([18.0, 18.0], add_dirs_button);
|
||||
if add_dirs_button_response.clicked() {
|
||||
self.add_dirs();
|
||||
@@ -416,8 +446,48 @@ impl SoundpadGui {
|
||||
self.play_file(&entry_path, false);
|
||||
}
|
||||
});
|
||||
self.app_state.selected_file = Some(entry_path);
|
||||
self.app_state.selected_file = Some(entry_path.clone());
|
||||
}
|
||||
|
||||
// Context menu
|
||||
file_button_response.context_menu(|ui| {
|
||||
if ui
|
||||
.button(format!("{} {}", ICON_BOLT, "Play Solo"))
|
||||
.clicked()
|
||||
{
|
||||
self.play_file(&entry_path, false);
|
||||
self.app_state.selected_file = Some(entry_path.clone());
|
||||
}
|
||||
|
||||
if ui.button(format!("{} {}", ICON_ADD, "Add New")).clicked() {
|
||||
self.play_file(&entry_path, true);
|
||||
self.app_state.selected_file = Some(entry_path.clone());
|
||||
}
|
||||
|
||||
if ui
|
||||
.button(format!("{} {}", ICON_SWAP_HORIZ, "Replace Last"))
|
||||
.clicked()
|
||||
&& let Some(last_track) = self.audio_player_state.tracks.last()
|
||||
{
|
||||
self.stop(Some(last_track.id));
|
||||
self.play_file(&entry_path, true);
|
||||
self.app_state.selected_file = Some(entry_path.clone());
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui
|
||||
.button(format!(
|
||||
"{} {}",
|
||||
ICON_OPEN_IN_BROWSER, "Show in File Manager"
|
||||
))
|
||||
.clicked()
|
||||
{
|
||||
if let Err(e) = opener::reveal(&entry_path) {
|
||||
eprintln!("Failed to open file manager: {}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -486,7 +556,7 @@ impl SoundpadGui {
|
||||
|
||||
// ---------- Settings button ----------
|
||||
let settings_button =
|
||||
Button::new(icons::ICON_SETTINGS.atom_size(Vec2::new(18.0, 18.0))).frame(false);
|
||||
Button::new(ICON_SETTINGS.atom_size(Vec2::new(18.0, 18.0))).frame(false);
|
||||
let settings_button_response = ui.add_sized([18.0, 18.0], settings_button);
|
||||
if settings_button_response.clicked() {
|
||||
self.app_state.show_settings = true;
|
||||
|
||||
+10
-1
@@ -82,6 +82,8 @@ pub struct ToggleLoopCommand {
|
||||
pub id: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct GetDaemonVersionCommand {}
|
||||
|
||||
pub struct GetFullStateCommand {}
|
||||
|
||||
#[async_trait]
|
||||
@@ -347,6 +349,13 @@ impl Executable for ToggleLoopCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetDaemonVersionCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
Response::new(true, env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Executable for GetFullStateCommand {
|
||||
async fn execute(&self) -> Response {
|
||||
@@ -374,7 +383,7 @@ impl Executable for GetFullStateCommand {
|
||||
tracks: audio_player.get_tracks(),
|
||||
volume: audio_player.volume,
|
||||
current_input: current_input_nick,
|
||||
all_inputs,
|
||||
all_inputs: all_inputs,
|
||||
};
|
||||
|
||||
Response::new(true, serde_json::to_string(&full_state).unwrap())
|
||||
|
||||
@@ -156,6 +156,10 @@ impl Request {
|
||||
Request::new("toggle_loop", args)
|
||||
}
|
||||
|
||||
pub fn get_daemon_version() -> Self {
|
||||
Request::new("get_daemon_version", vec![])
|
||||
}
|
||||
|
||||
pub fn get_full_state() -> Self {
|
||||
Request::new("get_full_state", vec![])
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ pub fn parse_command(request: &Request) -> Option<Box<dyn Executable + Send>> {
|
||||
Some(Box::new(SetLoopCommand { enabled, id }))
|
||||
}
|
||||
"toggle_loop" => Some(Box::new(ToggleLoopCommand { id })),
|
||||
"get_daemon_version" => Some(Box::new(GetDaemonVersionCommand {})),
|
||||
"get_full_state" => Some(Box::new(GetFullStateCommand {})),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user