Krolyxon
Commit 05c4b17

rmptuisrcconnection.rs

179 lines · 5.2 KB · Rust

1use mpd::song::Song;
2use mpd::{Client, State};
3use simple_dmenu::dmenu;
4use std::process::Command;
5
6pub type Result<T> = core::result::Result<T, Error>;
7pub type Error = Box<dyn std::error::Error>;
8
9#[derive(Debug)]
10pub struct Connection {
11 pub conn: Client,
12 pub songs_filenames: Vec<String>,
13 // pub state: String,
14}
15
16impl Connection {
17 pub fn new(addrs: &str) -> Result<Self> {
18 let mut conn = Client::connect(addrs).unwrap();
19 let songs_filenames: Vec<String> = conn
20 .listall()
21 .unwrap()
22 .into_iter()
23 .map(|x| x.file)
24 .collect();
25
26 Ok(Self {
27 conn,
28 songs_filenames,
29 // state: "Stopped".to_string(),
30 })
31 }
32
33 pub fn play_fzf(&mut self) -> Result<()> {
34 is_installed("fzf").map_err(|ex| ex)?;
35
36 let ss = &self.songs_filenames;
37 let fzf_choice = rust_fzf::select(ss.clone(), Vec::new()).unwrap();
38 let index = get_choice_index(&self.songs_filenames, fzf_choice.get(0).unwrap());
39 let song = self.get_song_with_only_filename(ss.get(index).unwrap());
40 self.push(&song)?;
41
42 Ok(())
43 }
44
45 pub fn play_dmenu(&mut self) -> Result<()> {
46 is_installed("dmenu").map_err(|ex| ex)?;
47 let ss: Vec<&str> = self.songs_filenames.iter().map(|x| x.as_str()).collect();
48 let op = dmenu!(iter &ss; args "-l", "30");
49 let index = get_choice_index(&self.songs_filenames, &op);
50 let song = self.get_song_with_only_filename(ss.get(index).unwrap());
51 self.push(&song)?;
52 Ok(())
53 }
54
55 // pub fn update_state(&mut self) {
56 // match self.conn.status().unwrap().state {
57 // State::Stop => self.state = "Stopped".to_string(),
58 // State::Play => self.state = "Playing".to_string(),
59 // State::Pause => self.state = "Paused".to_string(),
60 // }
61 // }
62
63 pub fn push(&mut self, song: &Song) -> Result<()> {
64 if self.conn.queue().unwrap().is_empty() {
65 self.conn.push(song).unwrap();
66 self.conn.play().unwrap();
67 } else {
68 self.conn.push(song)?;
69 if self.conn.status()?.state == State::Stop {
70 self.conn.play()?;
71 }
72 self.conn.next().unwrap();
73 }
74
75 Ok(())
76 }
77
78 pub fn push_playlist(&mut self, playlist: &str) -> Result<()> {
79 let songs: Vec<Song> = self.conn.playlist(playlist)?;
80
81 for song in songs {
82 if self.songs_filenames.contains(&song.file) {
83 let song = self.get_song_with_only_filename(&song.file);
84 self.conn.push(&song)?;
85 self.conn.play()?;
86 }
87 }
88 Ok(())
89 }
90
91 pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
92 Song {
93 file: filename.to_string(),
94 artist: None,
95 title: None,
96 duration: None,
97 last_mod: None,
98 name: None,
99 place: None,
100 range: None,
101 tags: vec![("".to_string(), "".to_string())],
102 }
103 }
104
105 pub fn get_current_song(&mut self) -> Option<String> {
106 self.conn.currentsong().unwrap().unwrap_or_default().title
107 }
108 pub fn status(&mut self) {
109 let current_song = self.conn.currentsong();
110 let status = self.conn.status().unwrap();
111
112 if current_song.is_ok() && status.state != State::Stop {
113 let song = current_song.unwrap();
114 if let Some(s) = song {
115 println!("{} - {}", s.artist.unwrap(), s.title.unwrap());
116 }
117 }
118 println!(
119 "volume: {}\trepeat: {}\trandom: {}\tsingle: {}\tconsume: {}",
120 status.volume, status.repeat, status.random, status.single, status.consume
121 );
122 }
123
124 pub fn now_playing(&mut self) -> Option<String> {
125 let song = self.conn.currentsong().unwrap().unwrap_or_default();
126 if let Some(s) = song.title {
127 if let Some(a) = song.artist {
128 Some(format!("{} - {}", s, a))
129 } else {
130 Some(s)
131 }
132 } else {
133 None
134 }
135 }
136
137 // Playback controls
138 pub fn pause(&mut self) {
139 self.conn.pause(true).unwrap();
140 }
141
142 pub fn toggle_pause(&mut self) {
143 self.conn.toggle_pause().unwrap();
144 }
145
146 // Volume controls
147 pub fn set_volume(&mut self, u: String) {
148 let cur = self.conn.status().unwrap().volume;
149 let sym = u.get(0..1).unwrap();
150 let u: i8 = u.parse::<i8>().unwrap();
151 if sym == "+" || sym == "-" {
152 self.conn.volume(cur + u).unwrap();
153 } else {
154 self.conn.volume(u).unwrap();
155 }
156 }
157}
158
159fn get_choice_index(ss: &Vec<String>, selection: &str) -> usize {
160 let mut choice: usize = 0;
161 if let Some(index) = ss.iter().position(|s| s == selection) {
162 choice = index;
163 }
164
165 choice
166}
167
168fn is_installed(ss: &str) -> Result<()> {
169 let output = Command::new("which")
170 .arg(ss)
171 .output()
172 .expect("Failed to execute command");
173 if output.status.success() {
174 Ok(())
175 } else {
176 let err = format!("{} not installed", ss);
177 Err(err.into())
178 }
179}