Skip to content

Commit

Permalink
Added playerctl module
Browse files Browse the repository at this point in the history
  • Loading branch information
mazei513 committed Jan 28, 2025
1 parent 6e81713 commit 28007f5
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 3 deletions.
17 changes: 14 additions & 3 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::{
menu::{menu_wrapper, MenuSize, MenuType},
modules::{
self, app_launcher::AppLauncher, clipboard::Clipboard, clock::Clock,
keyboard_layout::KeyboardLayout, keyboard_submap::KeyboardSubmap, privacy::Privacy,
settings::Settings, system_info::SystemInfo, tray::TrayModule, updates::Updates,
window_title::WindowTitle, workspaces::Workspaces,
keyboard_layout::KeyboardLayout, keyboard_submap::KeyboardSubmap, playerctl::Playerctl,
privacy::Privacy, settings::Settings, system_info::SystemInfo, tray::TrayModule,
updates::Updates, window_title::WindowTitle, workspaces::Workspaces,
},
outputs::{HasOutput, Outputs},
position_button::ButtonUIRef,
Expand Down Expand Up @@ -40,6 +40,7 @@ pub struct App {
pub clock: Clock,
pub privacy: Privacy,
pub settings: Settings,
pub playerctl: Playerctl,
}

#[derive(Debug, Clone)]
Expand All @@ -61,6 +62,7 @@ pub enum Message {
Privacy(modules::privacy::PrivacyMessage),
Settings(modules::settings::Message),
WaylandEvent(WaylandEvent),
Playerctl(modules::playerctl::Message),
}

impl App {
Expand All @@ -85,6 +87,7 @@ impl App {
clock: Clock::default(),
privacy: Privacy::default(),
settings: Settings::default(),
playerctl: Playerctl::default(),
},
task,
)
Expand Down Expand Up @@ -224,6 +227,7 @@ impl App {
},
_ => Task::none(),
},
Message::Playerctl(msg) => self.playerctl.update(msg),
}
}

Expand Down Expand Up @@ -266,6 +270,13 @@ impl App {
*button_ui_ref,
self.config.position,
),
Some((MenuType::Playerctl, button_ui_ref)) => menu_wrapper(
id,
self.playerctl.menu_view().map(Message::Playerctl),
MenuSize::Large,
*button_ui_ref,
self.config.position,
),
None => Row::new().into(),
},
None => Row::new().into(),
Expand Down
6 changes: 6 additions & 0 deletions src/components/icons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub enum Icons {
VerticalDots,
Airplane,
Webcam,
SkipPrevious,
PlayPause,
SkipNext,
}

impl From<Icons> for &'static str {
Expand Down Expand Up @@ -129,6 +132,9 @@ impl From<Icons> for &'static str {
Icons::VerticalDots => "󰇙",
Icons::Airplane => "󰀝",
Icons::Webcam => "",
Icons::SkipPrevious => "󰒮",
Icons::PlayPause => "󰐎",
Icons::SkipNext => "󰒭",
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ pub enum ModuleName {
Clock,
Privacy,
Settings,
Playerctl,
}

#[derive(Deserialize, Clone, Debug)]
Expand Down
1 change: 1 addition & 0 deletions src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum MenuType {
Updates,
Settings,
Tray(String),
Playerctl,
}

#[derive(Clone, Debug)]
Expand Down
3 changes: 3 additions & 0 deletions src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod clipboard;
pub mod clock;
pub mod keyboard_layout;
pub mod keyboard_submap;
pub mod playerctl;
pub mod privacy;
pub mod settings;
pub mod system_info;
Expand Down Expand Up @@ -223,6 +224,7 @@ impl App {
ModuleName::Clock => self.clock.view(&self.config.clock.format),
ModuleName::Privacy => self.privacy.view(()),
ModuleName::Settings => self.settings.view(()),
ModuleName::Playerctl => self.playerctl.view(()),
}
}

Expand All @@ -244,6 +246,7 @@ impl App {
ModuleName::Clock => self.clock.subscription(()),
ModuleName::Privacy => self.privacy.subscription(()),
ModuleName::Settings => self.settings.subscription(()),
ModuleName::Playerctl => self.playerctl.subscription(()),
}
}
}
163 changes: 163 additions & 0 deletions src/modules/playerctl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::{any::TypeId, process::Stdio, time::Duration};

use super::{Module, OnModulePress};
use crate::{
app,
components::icons::{icon, Icons},
menu::MenuType,
utils::launcher::execute_command,
};
use iced::{
stream::channel,
widget::{button, column, row, slider, text},
Alignment::Center,
Element, Subscription, Task,
};
use log::error;
use tokio::{process, time::sleep};

async fn get_current_song() -> String {
let get_current_song_cmd = process::Command::new("bash")
.arg("-c")
.arg("playerctl metadata --format \"{{ artist }} - {{ title }}\"")
.stdout(Stdio::piped())
.output()
.await;

match get_current_song_cmd {
Ok(get_current_song_cmd) => String::from_utf8_lossy(&get_current_song_cmd.stdout)
.trim()
.into(),
Err(e) => {
error!("Error: {:?}", e);
String::new()
}
}
}

fn get_volume() -> f64 {
let get_current_song_cmd = std::process::Command::new("bash")
.arg("-c")
.arg("playerctl volume")
.stdout(Stdio::piped())
.output();

match get_current_song_cmd {
Ok(check_update_cmd) => {
String::from_utf8_lossy(&check_update_cmd.stdout)
.trim()
.parse::<f64>()
.unwrap()
* 100.0
}
Err(e) => {
error!("Error: {:?}", e);
100.0
}
}
}

pub struct Playerctl {
song: String,
volume: f64,
}

impl Default for Playerctl {
fn default() -> Self {
Self {
song: String::default(),
volume: get_volume(),
}
}
}

#[derive(Debug, Clone)]
pub enum Message {
SetSong(String),
Prev,
Play,
Next,
SetVolume(f64),
}

impl Playerctl {
pub fn update(&mut self, message: Message) -> Task<crate::app::Message> {
match message {
Message::SetSong(song) => {
self.song = song;
Task::none()
}
Message::Prev => {
execute_command("playerctl previous".to_string());
Task::perform(async move { get_current_song().await }, move |song| {
app::Message::Playerctl(Message::SetSong(song))
})
}
Message::Play => {
execute_command("playerctl play-pause".to_string());
Task::perform(async move { get_current_song().await }, move |song| {
app::Message::Playerctl(Message::SetSong(song))
})
}
Message::Next => {
execute_command("playerctl next".to_string());
Task::perform(async move { get_current_song().await }, move |song| {
app::Message::Playerctl(Message::SetSong(song))
})
}
Message::SetVolume(v) => {
execute_command(format!("playerctl volume {}", v / 100.0));
self.volume = v;
Task::none()
}
}
}

pub fn menu_view(&self) -> Element<Message> {
column![
slider(0.0..=100.0, self.volume, |new_v| {
Message::SetVolume(new_v)
}),
row![
button(icon(Icons::SkipPrevious)).on_press(Message::Prev),
button(icon(Icons::PlayPause)).on_press(Message::Play),
button(icon(Icons::SkipNext)).on_press(Message::Next)
]
.spacing(8)
]
.spacing(8)
.align_x(Center)
.into()
}
}

impl Module for Playerctl {
type ViewData<'a> = ();
type SubscriptionData<'a> = ();

fn view(
&self,
(): Self::ViewData<'_>,
) -> Option<(Element<app::Message>, Option<OnModulePress>)> {
Some((
text(self.song.clone()).size(12).into(),
Some(OnModulePress::ToggleMenu(MenuType::Playerctl)),
))
}

fn subscription(&self, (): Self::SubscriptionData<'_>) -> Option<Subscription<app::Message>> {
let id = TypeId::of::<Self>();

Some(Subscription::batch(vec![Subscription::run_with_id(
id,
channel(10, |mut output| async move {
loop {
let song = get_current_song().await;
let _ = output.try_send(Message::SetSong(song));
sleep(Duration::from_secs(1)).await;
}
}),
)
.map(app::Message::Playerctl)]))
}
}

0 comments on commit 28007f5

Please sign in to comment.