Krolyxon
Commit a459eec

rmptuisrcconnection.rs

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