Krolyxon
Tag v0.1.6

rmptuisrcconnection.rs

235 lines · 6.7 KB · Rust

1use crate::app::AppResult;
2use crate::utils::is_installed;
3use mpd::song::Song;
4use mpd::{Client, State};
5use simple_dmenu::dmenu;
6use std::time::Duration;
7
8/// Defines the current status of volume (Muted or UnMuted)
9#[derive(Debug)]
10pub enum VolumeStatus {
11 Muted(i8),
12 Unmuted,
13}
14
15#[derive(Debug)]
16/// struct storing the mpd Client related stuff
17pub struct Connection {
18 pub conn: Client,
19 pub songs_filenames: Vec<String>,
20 pub state: String,
21 pub elapsed: Duration,
22 pub total_duration: Duration,
23 pub current_song: Song,
24 pub stats: mpd::Stats,
25 pub status: mpd::Status,
26 pub volume_status: VolumeStatus,
27}
28
29impl Connection {
30 /// Create a new connection
31 pub fn builder(addrs: &str) -> AppResult<Self> {
32 let mut conn = Client::connect(addrs).unwrap_or_else(|_| {
33 eprintln!("Error connecting to mpd server, Make sure mpd is running");
34 std::process::exit(1);
35 });
36
37 let empty_song = Song {
38 file: "No Song playing or in Queue".to_string(),
39 ..Default::default()
40 };
41
42 let songs_filenames: Vec<String> = conn.listall()?.into_iter().map(|x| x.file).collect();
43
44 let status = conn.status().unwrap();
45 let (elapsed, total) = status.time.unwrap_or_default();
46 let stats = conn.stats().unwrap_or_default();
47
48 let current_song = conn
49 .currentsong()
50 .unwrap_or_else(|_| Some(empty_song.clone()))
51 .unwrap_or(empty_song);
52
53 let volume_status = VolumeStatus::Unmuted;
54
55 Ok(Self {
56 conn,
57 songs_filenames,
58 state: "Stopped".to_string(),
59 elapsed,
60 total_duration: total,
61 current_song,
62 stats,
63 status,
64 volume_status,
65 })
66 }
67
68 /// Dmenu prompt for selecting songs
69 pub fn play_dmenu(&mut self) -> AppResult<()> {
70 if is_installed("dmenu") {
71 let ss: Vec<&str> = self.songs_filenames.iter().map(|x| x.as_str()).collect();
72 let op = dmenu!(iter &ss; args "-p", "Choose a song: ", "-l", "30");
73 let index = ss.iter().position(|s| s == &op);
74 if let Some(i) = index {
75 let song = self.get_song_with_only_filename(ss.get(i).unwrap());
76 self.push(&song)?;
77 }
78 }
79
80 Ok(())
81 }
82
83 /// Update status
84 pub fn update_status(&mut self) {
85 let status = self.conn.status().unwrap();
86 let stats = self.conn.stats().unwrap_or_default();
87
88 let empty_song = self.get_song_with_only_filename("No Song playing or in Queue");
89
90 let current_song = self
91 .conn
92 .currentsong()
93 .unwrap_or_else(|_| Some(empty_song.clone()))
94 .unwrap_or(empty_song);
95
96 // Status
97 self.status = status.clone();
98
99 // Playback State
100 self.state = match status.state {
101 State::Stop => "Stopped".to_string(),
102 State::Play => "Playing".to_string(),
103 State::Pause => "Paused".to_string(),
104 };
105
106 // Progress
107 let (elapsed, total) = status.time.unwrap_or_default();
108 self.elapsed = elapsed;
109 self.total_duration = total;
110
111 // Current song
112 self.current_song = current_song;
113
114 // Stats
115 self.stats = stats;
116 }
117
118 /// Get progress ratio of current playing song
119 pub fn get_progress_ratio(&self) -> f64 {
120 let total = self.total_duration.as_secs_f64();
121 if total == 0.0 {
122 0.0
123 } else {
124 let ratio = self.elapsed.as_secs_f64() / self.total_duration.as_secs_f64();
125 if ratio > 1.0 || ratio == 0.0 {
126 0.0
127 } else {
128 ratio
129 }
130 }
131 }
132
133 /// push the given song to queue
134 pub fn push(&mut self, song: &Song) -> AppResult<()> {
135 if self.conn.queue().unwrap().is_empty() {
136 self.conn.push(song).unwrap();
137 self.conn.play().unwrap();
138 } else {
139 self.conn.push(song)?;
140 let len: u32 = (self.conn.queue().unwrap().len() - 1).try_into().unwrap();
141 self.conn.switch(len)?;
142 if self.conn.status()?.state == State::Stop {
143 self.conn.play()?;
144 }
145 }
146
147 Ok(())
148 }
149
150 /// Push all songs of a playlist into queue
151 pub fn load_playlist(&mut self, playlist: &str) -> AppResult<()> {
152 self.conn.load(playlist, ..)?;
153 self.conn.play()?;
154 Ok(())
155 }
156
157 /// Add given song to playlist
158 pub fn add_to_playlist(&mut self, playlist: &str, song: &Song) -> AppResult<()> {
159 self.conn.pl_push(playlist, song)?;
160 Ok(())
161 }
162
163 /// Given a filename, get instance of Song with only filename
164 pub fn get_song_with_only_filename(&self, filename: &str) -> Song {
165 Song {
166 file: filename.to_string(),
167 ..Default::default()
168 }
169 }
170
171 /// Given a song name from a directory, it returns the full path of the song in the database
172 pub fn get_full_path(&self, short_path: &str) -> Option<&str> {
173 for (i, f) in self.songs_filenames.iter().enumerate() {
174 if f.contains(short_path) {
175 return Some(self.songs_filenames.get(i).unwrap());
176 }
177 }
178 None
179 }
180
181 /// Gives title of current playing song
182 pub fn now_playing(&mut self) -> AppResult<Option<String>> {
183 if let Some(s) = &self.current_song.title {
184 if let Some(a) = &self.current_song.artist {
185 Ok(Some(format!("\"{}\" By {}", s, a)))
186 } else {
187 Ok(Some(s.to_string()))
188 }
189 } else {
190 Ok(Some(self.current_song.file.clone()))
191 }
192 }
193
194 // Playback controls
195 /// Pause playback
196 pub fn pause(&mut self) {
197 self.conn.pause(true).unwrap();
198 }
199
200 /// Toggles playback
201 pub fn toggle_pause(&mut self) {
202 self.conn.toggle_pause().unwrap();
203 }
204
205 /// Toggle Repeat mode
206 pub fn toggle_repeat(&mut self) {
207 let mode = self.status.repeat;
208 self.conn.repeat(!mode).unwrap();
209 }
210
211 /// Toggle random mode
212 pub fn toggle_random(&mut self) {
213 let mode = self.status.random;
214 self.conn.random(!mode).unwrap();
215 }
216
217 // Volume controls
218 /// Increase Volume
219 pub fn inc_volume(&mut self, v: i8) {
220 let cur = self.status.volume;
221 if cur + v <= 100 {
222 self.volume_status = VolumeStatus::Unmuted;
223 self.conn.volume(cur + v).unwrap();
224 }
225 }
226
227 /// Decrease volume
228 pub fn dec_volume(&mut self, v: i8) {
229 let cur = self.status.volume;
230 if cur - v >= 0 {
231 self.volume_status = VolumeStatus::Unmuted;
232 self.conn.volume(cur - v).unwrap();
233 }
234 }
235}