1#[cfg(feature = "mpris")]
2mod mpris;
3pub(crate) mod state;
4#[cfg(test)]
5mod tests;
6
7use std::{
8 path::PathBuf,
9 sync::{Arc, LazyLock, RwLock},
10 thread,
11 time::Duration,
12};
13
14use log::{debug, error, info, trace, warn};
15use rodio::Player;
16use serde::{Deserialize, Serialize};
17use walkdir::WalkDir;
18
19use crate::{
20 Error,
21 music_lib::{Track, tracks_from_m3u8, tracks_from_paths},
22 playback::state::{
23 PLAYER_STATE, background_save_state, restore_state_from_cache, unwrap_state_mut,
24 unwrap_state_ref,
25 },
26};
27
28pub use state::{Event, PlayerState, Queue, QueueSource};
29
30#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub enum PlaybackState {
33 Playing,
35 Paused,
37 #[default]
41 Stopped,
42}
43
44impl std::fmt::Display for PlaybackState {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::Playing => write!(f, "Playing"),
48 Self::Paused => write!(f, "Paused"),
49 Self::Stopped => write!(f, "Stopped"),
50 }
51 }
52}
53
54#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
56pub enum LoopState {
57 #[default]
59 Off,
60 Playlist,
62 Track,
64}
65
66impl LoopState {
67 pub fn cycle(&self) -> Self {
68 match self {
69 Self::Off => Self::Playlist,
70 Self::Playlist => Self::Track,
71 Self::Track => Self::Off,
72 }
73 }
74}
75#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub enum ShuffleState {
78 #[default]
82 Off,
83 On,
85}
86
87impl ShuffleState {
88 pub fn enabled(&self) -> bool {
89 self == &Self::On
90 }
91
92 pub fn toggle(self) -> Self {
93 match self {
94 Self::Off => Self::On,
95 Self::On => Self::Off,
96 }
97 }
98}
99
100impl std::fmt::Display for ShuffleState {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Self::Off => write!(f, "Off"),
104 Self::On => write!(f, "On"),
105 }
106 }
107}
108
109impl std::fmt::Display for LoopState {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 Self::Off => write!(f, "Off"),
113 Self::Track => write!(f, "Track"),
114 Self::Playlist => write!(f, "Playlist"),
115 }
116 }
117}
118
119#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
124pub enum SignedDuration {
125 Positive(Duration),
126 Negative(Duration),
127}
128
129impl SignedDuration {
130 pub fn from_secs<T: Into<i64> + Copy>(secs: T) -> SignedDuration {
131 if secs.into() < 0 {
132 SignedDuration::Negative(Duration::from_secs(secs.into().abs().try_into().unwrap()))
133 } else {
134 SignedDuration::Positive(Duration::from_secs(secs.into().abs().try_into().unwrap()))
135 }
136 }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
144pub struct PlayerVolume {
145 volume: f64,
146}
147
148impl Default for PlayerVolume {
149 fn default() -> Self {
150 Self { volume: 1.0 }
151 }
152}
153
154impl std::fmt::Display for PlayerVolume {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 write!(f, "Player volume: {}", self.volume)
157 }
158}
159
160impl PlayerVolume {
161 pub fn new(volume: f64) -> Self {
165 Self {
166 volume: volume.clamp(0.0, 1.0),
167 }
168 }
169
170 pub fn set(&mut self, volume: f64) {
174 self.volume = volume.clamp(0.0, 1.0);
175 }
176
177 pub fn get(&self) -> f64 {
179 self.volume
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
184pub struct Config {
185 pub skip_previous_threshold: Option<Duration>,
190}
191
192pub(crate) static PLAYER_HANDLE: LazyLock<Arc<RwLock<Option<rodio::Player>>>> =
193 LazyLock::new(|| Arc::new(RwLock::new(None)));
194
195pub(crate) fn unwrap_player(maybe_player: Option<&rodio::Player>) -> Result<&rodio::Player, Error> {
196 match maybe_player {
197 Some(player) => Ok(player),
198 None => Err(Error::StateNotInitialized),
199 }
200}
201
202pub fn play(index: Option<usize>) -> Result<(), Error> {
204 let player_guard = PLAYER_HANDLE.read().unwrap();
205 let player = unwrap_player(player_guard.as_ref())?;
206 let mut state_guard = PLAYER_STATE.write().unwrap();
207 let state = unwrap_state_mut(state_guard.as_mut())?;
208 if let Some(id) = index {
209 trace!("Playing a track at index {id}");
210 let track = match state.play_track(id) {
211 Some(track) => track,
212 None => {
213 error!("No track at index {id}");
214 return Err(Error::NoTrackAtIndex(id));
215 }
216 };
217 match track.open_source() {
218 Ok(source) => {
219 player.clear();
220 player.append(source);
221 player.play();
222 state.set_playback_state(PlaybackState::Playing);
223 }
224 Err(e) => {
225 error!("Could not open audio source: {e}");
226 player.stop();
227 state.set_playback_state(PlaybackState::Stopped);
228 return Err(Error::SourceError);
229 }
230 }
231 background_save_state(state.clone());
232 Ok(())
233 } else {
234 trace!("Playing the current media");
235 if !player.is_paused() && !player.empty() {
236 Err(Error::PlayerPlaying)
237 } else if player.empty() {
238 if let Some(track) = state.current() {
239 match track.open_source() {
240 Ok(source) => {
241 player.append(source);
242 state.set_playback_state(PlaybackState::Playing);
243 }
244 Err(e) => {
245 error!("Could not open audio source: {e}");
246 player.stop();
247 state.set_playback_state(PlaybackState::Stopped);
248 return Err(Error::SourceError);
249 }
250 }
251 background_save_state(state.clone());
252 Ok(())
253 } else {
254 Err(Error::QueueEmpty)
255 }
256 } else {
257 player.play();
258 state.set_playback_state(PlaybackState::Playing);
259 Ok(())
260 }
261 }
262}
263
264pub fn pause() -> Result<(), Error> {
265 trace!("Pausing the current media");
266 let player_guard = PLAYER_HANDLE.read().unwrap();
267 let player = player_guard.as_ref().unwrap();
268 if player.is_paused() {
269 Err(Error::PlayerPaused)
270 } else {
271 player.pause();
274 let mut state_guard = PLAYER_STATE.write().unwrap();
275 let state = unwrap_state_mut(state_guard.as_mut())?;
276 state.set_playback_state(PlaybackState::Paused);
277 Ok(())
278 }
279}
280
281pub fn stop() -> Result<(), Error> {
282 trace!("Stopping the playback");
283 let player_guard = PLAYER_HANDLE.read().unwrap();
284 let player = player_guard.as_ref().unwrap();
285 if player.empty() {
286 Err(Error::PlayerStopped)
287 } else {
288 player.stop();
289 let mut state_guard = PLAYER_STATE.write().unwrap();
290 let state = unwrap_state_mut(state_guard.as_mut())?;
291 state.set_playback_state(PlaybackState::Stopped);
292 Ok(())
293 }
294}
295
296pub fn toggle_playing() -> Result<(), Error> {
297 trace!("Toggling playback state");
298 let state_guard = PLAYER_STATE.read().unwrap();
299 let state = unwrap_state_ref(state_guard.as_ref())?;
300 let playback_state = state.playback_state;
301 match playback_state {
302 PlaybackState::Paused => {
303 drop(state_guard);
304 play(None)
305 }
306 PlaybackState::Playing => {
307 drop(state_guard);
308 pause()
309 }
310 PlaybackState::Stopped => {
311 if state.current().is_some() {
312 let pos = state.position;
313 drop(state_guard);
314 play(Some(pos))
315 } else {
316 Err(Error::QueueEmpty)
317 }
318 }
319 }
320}
321
322#[cfg(feature = "mpris")]
323pub(crate) fn get_playback_state() -> Result<PlaybackState, Error> {
324 let state_guard = PLAYER_STATE.read().unwrap();
325 let state = unwrap_state_ref(state_guard.as_ref())?;
326 Ok(state.playback_state)
327}
328
329#[cfg(feature = "mpris")]
330pub(crate) fn get_current_meta() -> Result<Option<mpris_server::Metadata>, Error> {
331 let state_guard = PLAYER_STATE.read().unwrap();
332 let state = unwrap_state_ref(state_guard.as_ref())?;
333 match state.current() {
334 Some(track) => Ok(Some(track.get_meta(state.position))),
335 None => Ok(None),
336 }
337}
338
339#[cfg(feature = "mpris")]
340pub(crate) fn get_loop_state() -> Result<LoopState, Error> {
341 let state_guard = PLAYER_STATE.read().unwrap();
342 let state = unwrap_state_ref(state_guard.as_ref())?;
343 Ok(state.loop_state)
344}
345
346#[cfg(feature = "mpris")]
347pub(crate) fn get_shuffle_state() -> Result<ShuffleState, Error> {
348 let state_guard = PLAYER_STATE.read().unwrap();
349 let state = unwrap_state_ref(state_guard.as_ref())?;
350 Ok(state.shuffle_state)
351}
352
353#[cfg(feature = "mpris")]
354pub(crate) fn get_player_position() -> Result<Duration, Error> {
355 let state_guard = PLAYER_STATE.read().unwrap();
356 let state = unwrap_state_ref(state_guard.as_ref())?;
357 Ok(state.player_position)
358}
359
360#[cfg(feature = "mpris")]
361pub(crate) fn get_player_volume() -> Result<PlayerVolume, Error> {
362 let player_guard = PLAYER_HANDLE.read().unwrap();
363 let player = unwrap_player(player_guard.as_ref())?;
364 Ok(PlayerVolume::new(player.volume()))
365}
366
367#[cfg(feature = "mpris")]
368pub(crate) fn can_play() -> Result<bool, Error> {
369 let state_guard = PLAYER_STATE.read().unwrap();
370 let state = unwrap_state_ref(state_guard.as_ref())?;
371 Ok(state.can_play())
372}
373
374#[cfg(feature = "mpris")]
375pub(crate) fn can_pause() -> Result<bool, Error> {
376 let state_guard = PLAYER_STATE.read().unwrap();
377 let state = unwrap_state_ref(state_guard.as_ref())?;
378 Ok(state.playback_state == PlaybackState::Playing)
379}
380
381#[cfg(feature = "mpris")]
382pub(crate) fn can_go_next() -> Result<bool, Error> {
383 let state_guard = PLAYER_STATE.read().unwrap();
384 let state = unwrap_state_ref(state_guard.as_ref())?;
385 Ok(state.can_go_next())
386}
387
388#[cfg(feature = "mpris")]
389pub(crate) fn can_go_previous() -> Result<bool, Error> {
390 let state_guard = PLAYER_STATE.read().unwrap();
391 let state = unwrap_state_ref(state_guard.as_ref())?;
392 Ok(state.can_go_previous())
393}
394
395pub fn open_uri(uri: PathBuf) -> Result<(), Error> {
398 trace!("Opening URI {uri:?}");
399 match uri.try_exists() {
400 Ok(exists) => {
401 if !exists {
402 return Err(Error::PathDoesNotExist);
403 }
404 }
405 Err(e) => {
406 error!("Could not check if the URI {uri:?} exists: {e}");
407 return Err(Error::PathInaccessible(uri));
408 }
409 }
410 if uri.is_dir() {
411 trace!("The provided URI {uri:?} is a directory, indexing it");
412
413 let mut files = Vec::new();
414 let label = uri.to_string_lossy().to_string();
415 for result in WalkDir::new(uri).into_iter() {
416 let entry = match result {
417 Ok(entry) => entry,
418 Err(e) => {
419 warn!("Error while trying to access the file: {e}");
420 continue;
421 }
422 };
423
424 match entry.metadata() {
425 Ok(meta) => {
426 if meta.is_file() {
427 files.push(PathBuf::from(entry.path()));
428 }
429 }
430 Err(e) => {
431 warn!("Could not get path metadata: {e}");
432 continue;
433 }
434 };
435 }
436 let tracks = tracks_from_paths(&files, true)?;
437 if tracks.is_empty() {
438 Err(Error::DirectoryWithNoTracks)
439 } else {
440 set_queue(Queue::Custom { label, tracks })
441 }
442 } else {
443 trace!(
444 "The provided URI {uri:?} is a file, so we're either opening an audio file or a playlist"
445 );
446 if let Ok(paths) = tracks_from_m3u8(&uri) {
447 trace!("Loaded M3U8 playlist {uri:?}");
448 let tracks = tracks_from_paths(&paths, true)?;
449 return set_queue(Queue::Custom {
450 label: uri.to_string_lossy().to_string(),
451 tracks,
452 });
453 } else {
454 trace!("The file does not appear to be an M3U8 playlist");
455 }
456 let label = uri.to_string_lossy().to_string();
457 let track = tracks_from_paths(&[uri], false)?;
458 set_queue(Queue::Custom {
459 label,
460 tracks: track,
461 })
462 }
463}
464
465pub fn play_new_queue(queue: Queue, index: Option<usize>) -> Result<(), Error> {
474 let mut state_guard = PLAYER_STATE.write().unwrap();
475 let state = unwrap_state_mut(state_guard.as_mut())?;
476 state.play_new_queue(queue, index);
477
478 let player_guard = PLAYER_HANDLE.read().unwrap();
479 let player = unwrap_player(player_guard.as_ref())?;
480 player.stop();
481 if let Some(track) = state.current() {
482 match track.open_source() {
483 Ok(source) => {
484 player.append(source);
485 player.play();
486 state.set_playback_state(PlaybackState::Playing);
487 }
488 Err(e) => {
489 error!("Could not open audio source: {e}");
490 state.set_playback_state(PlaybackState::Stopped);
491 player.stop();
492 return Err(Error::SourceError);
493 }
494 }
495 }
496 background_save_state(state.clone());
497 Ok(())
498}
499
500pub fn set_queue(queue: Queue) -> Result<(), Error> {
506 trace!("Setting a new queue");
507 let mut state_guard = PLAYER_STATE.write().unwrap();
508 let state = unwrap_state_mut(state_guard.as_mut())?;
509 state.set_queue(queue);
510 let player_guard = PLAYER_HANDLE.read().unwrap();
511 let player = unwrap_player(player_guard.as_ref())?;
512 player.stop();
513 if let Some(track) = state.current() {
514 match track.open_source() {
515 Ok(source) => {
516 player.append(source);
517 player.pause();
518 }
519 Err(e) => {
520 error!("Could not open audio source: {e}");
521 player.stop();
522 state.set_playback_state(PlaybackState::Stopped);
523 return Err(Error::SourceError);
524 }
525 }
526 }
527 background_save_state(state.clone());
528 Ok(())
529}
530
531pub fn append_to_queue(queue: &mut Vec<Arc<Track>>) -> Result<(), Error> {
532 trace!("Appending tracks to queue");
533 let mut state_guard = PLAYER_STATE.write().unwrap();
534 let state = unwrap_state_mut(state_guard.as_mut())?;
535 state.append_tracks(queue);
536 background_save_state(state.clone());
537 Ok(())
538}
539
540pub fn remove_from_queue(indices: Vec<usize>) -> Result<(), Error> {
541 trace!("Removing {} tracks from queue", indices.len());
542 let mut state_guard = PLAYER_STATE.write().unwrap();
543 let state = unwrap_state_mut(state_guard.as_mut())?;
544 let previous = state.current().cloned();
545 state.remove_tracks(indices)?;
546 let player_guard = PLAYER_HANDLE.read().unwrap();
547 let player = unwrap_player(player_guard.as_ref())?;
548 if previous.as_ref() != state.current() {
549 if let Some(track) = state.current() {
550 match track.open_source() {
551 Ok(source) => {
552 player.stop();
553 player.append(source);
554 state.set_playback_state(PlaybackState::Playing);
555 }
556 Err(e) => {
557 error!("Could not open audio source: {e}");
558 player.stop();
559 state.set_playback_state(PlaybackState::Stopped);
560 return Err(Error::SourceError);
561 }
562 }
563 } else {
564 player.stop();
565 state.set_playback_state(PlaybackState::Stopped);
566 }
567 }
568 background_save_state(state.clone());
569 Ok(())
570}
571
572pub fn skip_next() -> Result<(), Error> {
573 trace!("Skipping to the next track");
574 let mut state_guard = PLAYER_STATE.write().unwrap();
575 let state = unwrap_state_mut(state_guard.as_mut())?;
576 if let Some(track) = state.next_track() {
577 let player_guard = PLAYER_HANDLE.read().unwrap();
578 let player = unwrap_player(player_guard.as_ref())?;
579 match track.open_source() {
580 Ok(source) => {
581 state.set_player_position(Duration::default());
582 state.set_playback_state(PlaybackState::Playing);
583 player.clear();
584 player.append(source);
585 player.play();
586 }
587 Err(e) => {
588 error!("Could not open audio source: {e}");
589 state.set_playback_state(PlaybackState::Stopped);
590 player.stop();
591 return Err(Error::SourceError);
592 }
593 }
594 background_save_state(state.clone());
595 Ok(())
596 } else if state.queue_empty() {
597 info!("Cannot skip to the next track, queue is empty");
598 Err(Error::QueueEmpty)
599 } else {
600 info!("Cannot skip to the next track");
601 Err(Error::CannotGoNext)
602 }
603}
604
605pub fn skip_previous() -> Result<(), Error> {
606 trace!("Skipping to the previous track");
607 let mut state_guard = PLAYER_STATE.write().unwrap();
608 let state = unwrap_state_mut(state_guard.as_mut())?;
609
610 let guard = crate::CONFIG.read().unwrap();
611 let config = guard.as_ref().unwrap();
612 if let Some(threshold) = config.playback.skip_previous_threshold
613 && state.player_position > threshold
614 {
615 trace!("Player position above skip threshold, resetting player position instead");
616 drop(state_guard);
617 return set_player_position(Duration::default());
618 }
619
620 if let Some(track) = state.previous_track() {
621 let player_guard = PLAYER_HANDLE.read().unwrap();
622 let player = unwrap_player(player_guard.as_ref())?;
623 match track.open_source() {
624 Ok(source) => {
625 state.set_player_position(Duration::default());
626 state.set_playback_state(PlaybackState::Playing);
627 player.clear();
628 player.append(source);
629 player.play();
630 }
631 Err(e) => {
632 error!("Could not open audio source: {e}");
633 player.stop();
634 state.set_playback_state(PlaybackState::Stopped);
635 return Err(Error::SourceError);
636 }
637 };
638 background_save_state(state.clone());
639 Ok(())
640 } else if state.queue_empty() {
641 info!("Cannot go to the previous track, queue is empty");
642 Err(Error::QueueEmpty)
643 } else {
644 info!("Cannot go to the previous track");
645 Err(Error::CannotGoPrevious)
646 }
647}
648
649pub fn set_loop_state(loop_state: LoopState) -> Result<(), Error> {
650 trace!("Setting loop state to {loop_state:?}");
651 let mut state_guard = PLAYER_STATE.write().unwrap();
652 let state = unwrap_state_mut(state_guard.as_mut())?;
653 state.set_loop_state(loop_state);
654 background_save_state(state.clone());
655 Ok(())
656}
657
658pub fn cycle_loop_state() -> Result<(), Error> {
659 trace!("Cycling loop state");
660 let mut state_guard = PLAYER_STATE.write().unwrap();
661 let state = unwrap_state_mut(state_guard.as_mut())?;
662 state.set_loop_state(state.loop_state.cycle());
663 background_save_state(state.clone());
664 Ok(())
665}
666
667pub fn set_shuffle_state(shuffle_state: ShuffleState) -> Result<(), Error> {
668 trace!("Setting shuffle state to {shuffle_state:?}");
669 let mut state_guard = PLAYER_STATE.write().unwrap();
670 let state = unwrap_state_mut(state_guard.as_mut())?;
671 state.set_shuffle_state(shuffle_state);
672 background_save_state(state.clone());
673 Ok(())
674}
675
676pub fn toggle_shuffle_state() -> Result<(), Error> {
677 trace!("Toggling shuffle state");
678 let mut state_guard = PLAYER_STATE.write().unwrap();
679 let state = unwrap_state_mut(state_guard.as_mut())?;
680 let shuffle_state = state.shuffle_state.toggle();
681 trace!("Setting shuffle state to {shuffle_state:?}");
682 state.set_shuffle_state(shuffle_state);
683 background_save_state(state.clone());
684 Ok(())
685}
686
687pub fn set_player_position(position: Duration) -> Result<(), Error> {
688 trace!("Setting player position to {:?}", position.as_secs());
689 let player_guard = PLAYER_HANDLE.read().unwrap();
690 let mut state_guard = PLAYER_STATE.write().unwrap();
691 let state = unwrap_state_mut(state_guard.as_mut())?;
692 if state.playback_state == PlaybackState::Stopped {
693 return Err(Error::PlayerStopped);
694 }
695 let player = match player_guard.as_ref() {
696 Some(player) => player,
697 None => {
698 warn!("Cannot set player position, player is not connected");
699 return Err(Error::PlayerNotConnected);
700 }
701 };
702 if player.empty() {
703 Err(Error::QueueEmpty)
704 } else if let Some(track) = state.current()
705 && position > track.duration
706 {
707 drop(state_guard);
708 skip_next()
709 } else if let Err(e) = player.try_seek(position) {
710 error!("Could not set player position: {e}");
711 #[cfg(feature = "mpris")]
713 mpris::set_position(state.player_position);
714 Err(Error::SeekNotSupported)
715 } else {
716 state.set_player_position(position);
717 Ok(())
718 }
719}
720
721static SEEK_ROUND_THRESHOLD: LazyLock<Duration> = LazyLock::new(|| Duration::from_secs(1));
727
728#[cfg(feature = "mpris")]
735static SUPPORTED_MIME_TYPES: LazyLock<Vec<String>> = LazyLock::new(|| {
736 let arr = [
737 "audio/aac",
738 "audio/32kadpcm",
739 "audio/aiff",
740 "audio/x-aiff",
741 "audio/x-caf",
742 "audio/flac",
743 "audio/matroska",
744 "audio/mpeg",
745 "audio/mp4",
746 "audio/MPA",
747 "audio/mpa-robust",
748 "audio/ogg",
749 "audio/vorbis",
750 "audio/vorbis-config",
751 "audio/vnd.wave",
752 "audio/wav",
753 "audio/wave",
754 "audio/x-wav",
755 "audio/webm",
756 ];
757 arr.into_iter().map(|t| t.to_string()).collect()
758});
759
760pub fn seek(delta: SignedDuration) -> Result<(), Error> {
761 let player_guard = PLAYER_HANDLE.read().unwrap();
762 let player = match player_guard.as_ref() {
763 Some(player) => player,
764 None => {
765 warn!("Cannot seek, the player is not connected");
766 return Err(Error::PlayerNotConnected);
767 }
768 };
769 let mut state_guard = PLAYER_STATE.write().unwrap();
770 let state = unwrap_state_mut(state_guard.as_mut())?;
771 let pos = match delta {
772 SignedDuration::Positive(positive_dur) => {
773 if positive_dur == Duration::default() {
774 info!("Refusing to seek by 0s");
775 return Err(Error::InvalidDuration);
776 }
777 let sum = match positive_dur.checked_add(player.get_pos()) {
778 Some(sum) => sum,
779 None => {
780 error!("Overflow detected while seeking, aborting");
781 return Err(Error::DurationOverflow);
782 }
783 };
784 if let Some(track) = state.current()
785 && sum > track.duration - *SEEK_ROUND_THRESHOLD
786 {
787 drop(state_guard);
788 return skip_next();
789 } else {
790 trace!("Seeking player by {positive_dur:?}");
791 sum
792 }
793 }
794 SignedDuration::Negative(negative_dur) => {
795 if negative_dur == Duration::default() {
796 info!("Refusing to seek by 0s");
797 return Err(Error::InvalidDuration);
798 }
799 trace!("Seeking player by -{negative_dur:?}");
800 let sub = match player.get_pos().checked_sub(negative_dur) {
801 Some(sub) => sub,
802 None => {
803 error!("Overflow detected while seeking");
804 return Err(Error::DurationOverflow);
805 }
806 };
807 if sub < *SEEK_ROUND_THRESHOLD {
808 Duration::default()
809 } else {
810 sub
811 }
812 }
813 };
814 if let Err(e) = player.try_seek(pos) {
815 error!("Could not set player position: {e}");
816 Err(Error::SeekNotSupported)
817 } else {
818 state.set_player_position(pos);
819 Ok(())
820 }
821}
822
823pub fn set_player_volume(volume: PlayerVolume) -> Result<(), Error> {
824 trace!("Setting player volume to {:?}", volume);
825 let player_guard = PLAYER_HANDLE.read().unwrap();
826 let player = unwrap_player(player_guard.as_ref())?;
827 player.set_volume(volume.get());
828 let mut state_guard = PLAYER_STATE.write().unwrap();
829 let state = unwrap_state_mut(state_guard.as_mut())?;
830 state.set_player_volume(volume);
831 background_save_state(state.clone());
832 Ok(())
833}
834
835pub(crate) fn init(
836 #[cfg(feature = "mpris")] identity: String,
837 #[cfg(feature = "mpris")] identifier: String,
838) {
839 trace!("Initializing the playback module");
840
841 let state = match restore_state_from_cache() {
842 Ok(state) => {
843 debug!("Restored player state from cache");
844 state
845 }
846 Err(e) => {
847 error!("Could not restore player state from cache: {e}");
849 let state = PlayerState::default();
850 debug!("Creating a new state and attempting to save it in cache");
851 background_save_state(state.clone());
852 state
853 }
854 };
855 crate::send_event(crate::Event::Playback(Event::StateInitialized(
856 state.clone(),
857 )));
858 trace!("Player state ready!");
859
860 let handle = match rodio::DeviceSinkBuilder::open_default_sink() {
863 Ok(sink) => sink,
864 Err(e) => {
865 error!("Could not open the default sink, audio playback will not work! Error: {e}");
866 return;
867 }
868 };
869 let player = Player::connect_new(handle.mixer());
870 player.set_volume(state.player_volume.get());
871
872 *PLAYER_STATE.write().unwrap() = Some(state);
873 *PLAYER_HANDLE.write().unwrap() = Some(player);
874
875 let mut state_guard = PLAYER_STATE.write().unwrap();
876 let state = state_guard.as_mut().unwrap();
877 if let Some(track) = state.current() {
878 match track.open_source() {
879 Ok(source) => {
880 let player_guard = PLAYER_HANDLE.read().unwrap();
881 let player = player_guard.as_ref().unwrap();
882 player.append(source);
883 player.pause();
884 if let Err(e) = player.try_seek(state.player_position) {
885 error!("Couldn't set initial player position: {e}");
886 state.set_player_position(Duration::default());
887 }
888 drop(player_guard);
889 }
890 Err(e) => {
891 error!("Couldn't open the current track: {e}");
892 state.set_playback_state(PlaybackState::Stopped);
893 }
894 }
895 }
896 drop(state_guard);
897
898 #[cfg(feature = "mpris")]
899 mpris::launch_server(identity, identifier);
900
901 trace!("Playback module initialized");
902
903 let sleep_duration = Duration::from_millis(100);
904 loop {
905 thread::sleep(sleep_duration);
906 let mut state_guard = PLAYER_STATE.write().unwrap();
907 let state = state_guard.as_mut().unwrap();
908 let player_guard = PLAYER_HANDLE.read().unwrap();
909 let player = player_guard.as_ref().unwrap();
910 if !player.is_paused() && !player.empty() {
911 state.increment_player_position(sleep_duration);
912 } else if player.empty() && state.is_playing() {
913 state.set_player_position(Duration::default());
914 let track = match state.next_track() {
915 Some(track) => track,
916 None => {
917 state.set_playback_state(PlaybackState::Stopped);
918 continue;
919 }
920 };
921 match track.open_source() {
922 Ok(source) => {
923 player.append(source);
924 state.set_playback_state(PlaybackState::Playing);
925 }
926 Err(e) => {
927 error!("Could not open audio source: {e}");
928 player.stop();
929 state.set_playback_state(PlaybackState::Stopped);
930 continue;
931 }
932 }
933 }
934 drop(state_guard);
935 drop(player_guard);
936 }
937}