Krolyxon
Commit e500c52

rmptuisrcconnection.rs

261 lines · 7.5 KB · Rust

1use mpd::song::Song;
2use mpd::{Client, State};
3use simple_dmenu::dmenu;
4use std::process::Command;
5use std::time::Duration;
6
7pub type Result<T> = core::result::Result<T, Error>;
8pub type Error = Box<dyn std::error::Error>;
9
10#[derive(Debug)]
11pub struct Connection {
12 pub conn: Client,
13 pub songs_filenames: Vec<String>,
14 pub state: String,
15 pub elapsed: Duration,
16 pub total_duration: Duration,
17 pub volume: u8,
18 pub repeat: bool,
19 pub random: bool,
20}
21
22impl Connection {
23 /// Create a new connection
24 pub fn new(addrs: &str) -> Result<Self> {
25 let mut conn = Client::connect(addrs).unwrap();
26 let songs_filenames: Vec<String> = conn
27 .listall()
28 .unwrap()
29 .into_iter()
30 .map(|x| x.file)
31 .collect();
32 let status = conn.status().unwrap();
33 let (elapsed, total) = status.time.unwrap_or_default();
34 let volume: u8 = status.volume as u8;
35 let repeat = status.repeat;
36 let random = status.random;
37
38 Ok(Self {
39 conn,
40 songs_filenames,
41 state: "Stopped".to_string(),
42 elapsed,
43 total_duration: total,
44 volume,
45 repeat,
46 random,
47 })
48 }
49
50 /// Fzf prompt for selecting song
51 pub fn play_fzf(&mut self) -> Result<()> {
52 is_installed("fzf").map_err(|ex| ex)?;
53
54 let ss = &self.songs_filenames;
55 let fzf_choice = rust_fzf::select(ss.clone(), Vec::new()).unwrap();
56 let index = get_choice_index(&self.songs_filenames, fzf_choice.get(0).unwrap());
57 let song = self.get_song_with_only_filename(ss.get(index).unwrap());
58 self.push(&song)?;
59
60 Ok(())
61 }
62
63 /// Dmenu prompt for selecting songs
64 pub fn play_dmenu(&mut self) -> Result<()> {
65 is_installed("dmenu").map_err(|ex| ex)?;
66 let ss: Vec<&str> = self.songs_filenames.iter().map(|x| x.as_str()).collect();
67 let op = dmenu!(iter &ss; args "-p", "Choose a song: ", "-l", "30");
68 let index = get_choice_index(&self.songs_filenames, &op);
69 let song = self.get_song_with_only_filename(ss.get(index).unwrap());
70 self.push(&song)?;
71 Ok(())
72 }
73
74 /// Update status
75 pub fn update_status(&mut self) {
76 let status = self.conn.status().unwrap();
77
78 // Playback State
79 match status.state {
80 State::Stop => self.state = "Stopped".to_string(),
81 State::Play => self.state = "Playing".to_string(),
82 State::Pause => self.state = "Paused".to_string(),
83 }
84
85 // Progress
86 let (elapsed, total) = status.time.unwrap_or_default();
87 self.elapsed = elapsed;
88 self.total_duration = total;
89
90 // Volume
91 self.volume = status.volume as u8;
92
93 // Repeat mode
94 self.repeat = status.repeat;
95
96 // Random mode
97 self.random = status.random;
98 }
99
100 /// Get progress ratio of current playing song
101 pub fn get_progress_ratio(&self) -> f64 {
102 let total = self.total_duration.as_secs_f64();
103 if total == 0.0 {
104 0.0
105 } else {
106 let ratio = self.elapsed.as_secs_f64() / self.total_duration.as_secs_f64();
107 if ratio > 1.0 || ratio == 0.0 {
108 0.0
109 } else {
110 ratio
111 }
112 }
113 }
114
115 /// push the given song to queue
116 pub fn push(&mut self, song: &Song) -> Result<()> {
117 if self.conn.queue().unwrap().is_empty() {
118 self.conn.push(song).unwrap();
119 self.conn.play().unwrap();
120 } else {
121 self.conn.push(song)?;
122 let len: u32 = (self.conn.queue().unwrap().len() - 1).try_into().unwrap();
123 self.conn.switch(len)?;
124 if self.conn.status()?.state == State::Stop {
125 self.conn.play()?;
126 }
127 }
128
129 Ok(())
130 }
131
132 /// Push all songs of a playlist into queue
133 pub fn push_playlist(&mut self, playlist: &str) -> Result<()> {
134 let songs: Vec<Song> = self.conn.playlist(playlist)?;
135
136 for song in songs {
137 if self.songs_filenames.contains(&song.file) {
138 let song = self.get_song_with_only_filename(&song.file);
139 self.conn.push(&song)?;
140 self.conn.play()?;
141 }
142 }
143 Ok(())
144 }
145
146 /// Given a filename, get instance of Song with only filename
147 pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
148 Song {
149 file: filename.to_string(),
150 artist: None,
151 title: None,
152 duration: None,
153 last_mod: None,
154 name: None,
155 place: None,
156 range: None,
157 tags: vec![("".to_string(), "".to_string())],
158 }
159 }
160
161 /// Print status to stdout
162 pub fn status(&mut self) {
163 let current_song = self.conn.currentsong();
164 let status = self.conn.status().unwrap();
165
166 if current_song.is_ok() && status.state != State::Stop {
167 let song = current_song.unwrap();
168 if let Some(s) = song {
169 println!("{} - {}", s.artist.unwrap(), s.title.unwrap());
170 }
171 }
172 println!(
173 "volume: {}\trepeat: {}\trandom: {}\tsingle: {}\tconsume: {}",
174 status.volume, status.repeat, status.random, status.single, status.consume
175 );
176 }
177
178 /// Gives title of current playing song
179 pub fn now_playing(&mut self) -> Result<Option<String>> {
180 let song = self.conn.currentsong()?.unwrap_or_default();
181 if let Some(s) = song.title {
182 if let Some(a) = song.artist {
183 return Ok(Some(format!("\"{}\" By {}", s, a)));
184 } else {
185 return Ok(Some(s));
186 }
187 } else {
188 return Ok(Some(song.file));
189 }
190 }
191
192 // Playback controls
193 /// Pause playback
194 pub fn pause(&mut self) {
195 self.conn.pause(true).unwrap();
196 }
197
198 /// Toggles playback
199 pub fn toggle_pause(&mut self) {
200 self.conn.toggle_pause().unwrap();
201 }
202
203 /// Toggle Repeat mode
204 pub fn toggle_repeat(&mut self) {
205 if self.conn.status().unwrap().repeat {
206 self.conn.repeat(false).unwrap();
207 } else {
208 self.conn.repeat(true).unwrap();
209 }
210 }
211
212 /// Toggle random mode
213 pub fn toggle_random(&mut self) {
214 if self.conn.status().unwrap().random {
215 self.conn.random(false).unwrap();
216 } else {
217 self.conn.random(true).unwrap();
218 }
219 }
220
221 // Volume controls
222 /// Increase Volume
223 pub fn inc_volume(&mut self, v: i8) {
224 let cur = self.conn.status().unwrap().volume;
225 if cur + v <= 100 {
226 self.conn.volume(cur + v).unwrap();
227 }
228 }
229
230 /// Decrease volume
231 pub fn dec_volume(&mut self, v: i8) {
232 let cur = self.conn.status().unwrap().volume;
233 if cur - v >= 0 {
234 self.conn.volume(cur - v).unwrap();
235 }
236 }
237}
238
239/// Gets the index of the string from the Vector
240fn get_choice_index(ss: &Vec<String>, selection: &str) -> usize {
241 let mut choice: usize = 0;
242 if let Some(index) = ss.iter().position(|s| s == selection) {
243 choice = index;
244 }
245
246 choice
247}
248
249/// Checks if given program is installed in your system
250fn is_installed(ss: &str) -> Result<()> {
251 let output = Command::new("which")
252 .arg(ss)
253 .output()
254 .expect("Failed to execute command");
255 if output.status.success() {
256 Ok(())
257 } else {
258 let err = format!("{} not installed", ss);
259 Err(err.into())
260 }
261}