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(mpris::get_meta(track, 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: impl Into<Queue>, index: Option<usize>) -> Result<(), Error> {
474 let queue = queue.into();
475 let mut state_guard = PLAYER_STATE.write().unwrap();
476 let state = unwrap_state_mut(state_guard.as_mut())?;
477 state.play_new_queue(queue, index);
478
479 let player_guard = PLAYER_HANDLE.read().unwrap();
480 let player = unwrap_player(player_guard.as_ref())?;
481 player.stop();
482 if let Some(track) = state.current() {
483 match track.open_source() {
484 Ok(source) => {
485 player.append(source);
486 player.play();
487 state.set_playback_state(PlaybackState::Playing);
488 }
489 Err(e) => {
490 error!("Could not open audio source: {e}");
491 state.set_playback_state(PlaybackState::Stopped);
492 player.stop();
493 return Err(Error::SourceError);
494 }
495 }
496 }
497 background_save_state(state.clone());
498 Ok(())
499}
500
501pub fn set_queue(queue: impl Into<Queue>) -> Result<(), Error> {
507 trace!("Setting a new queue");
508 let queue = queue.into();
509 let mut state_guard = PLAYER_STATE.write().unwrap();
510 let state = unwrap_state_mut(state_guard.as_mut())?;
511 state.set_queue(queue);
512 let player_guard = PLAYER_HANDLE.read().unwrap();
513 let player = unwrap_player(player_guard.as_ref())?;
514 player.stop();
515 if let Some(track) = state.current() {
516 match track.open_source() {
517 Ok(source) => {
518 player.append(source);
519 player.pause();
520 }
521 Err(e) => {
522 error!("Could not open audio source: {e}");
523 player.stop();
524 state.set_playback_state(PlaybackState::Stopped);
525 return Err(Error::SourceError);
526 }
527 }
528 }
529 background_save_state(state.clone());
530 Ok(())
531}
532
533pub fn append_to_queue(queue: &mut Vec<Arc<Track>>) -> Result<(), Error> {
534 trace!("Appending tracks to queue");
535 let mut state_guard = PLAYER_STATE.write().unwrap();
536 let state = unwrap_state_mut(state_guard.as_mut())?;
537 state.append_tracks(queue);
538 background_save_state(state.clone());
539 Ok(())
540}
541
542pub fn remove_from_queue(indices: Vec<usize>) -> Result<(), Error> {
543 trace!("Removing {} tracks from queue", indices.len());
544 let mut state_guard = PLAYER_STATE.write().unwrap();
545 let state = unwrap_state_mut(state_guard.as_mut())?;
546 let previous = state.current().cloned();
547 state.remove_tracks(indices)?;
548 let player_guard = PLAYER_HANDLE.read().unwrap();
549 let player = unwrap_player(player_guard.as_ref())?;
550 if previous.as_ref() != state.current() {
551 if let Some(track) = state.current() {
552 match track.open_source() {
553 Ok(source) => {
554 player.stop();
555 player.append(source);
556 state.set_playback_state(PlaybackState::Playing);
557 }
558 Err(e) => {
559 error!("Could not open audio source: {e}");
560 player.stop();
561 state.set_playback_state(PlaybackState::Stopped);
562 return Err(Error::SourceError);
563 }
564 }
565 } else {
566 player.stop();
567 state.set_playback_state(PlaybackState::Stopped);
568 }
569 }
570 background_save_state(state.clone());
571 Ok(())
572}
573
574pub fn skip_next() -> Result<(), Error> {
575 trace!("Skipping to the next track");
576 let mut state_guard = PLAYER_STATE.write().unwrap();
577 let state = unwrap_state_mut(state_guard.as_mut())?;
578 if let Some(track) = state.next_track() {
579 let player_guard = PLAYER_HANDLE.read().unwrap();
580 let player = unwrap_player(player_guard.as_ref())?;
581 match track.open_source() {
582 Ok(source) => {
583 state.set_player_position(Duration::default());
584 state.set_playback_state(PlaybackState::Playing);
585 player.clear();
586 player.append(source);
587 player.play();
588 }
589 Err(e) => {
590 error!("Could not open audio source: {e}");
591 state.set_playback_state(PlaybackState::Stopped);
592 player.stop();
593 return Err(Error::SourceError);
594 }
595 }
596 background_save_state(state.clone());
597 Ok(())
598 } else if state.queue_empty() {
599 info!("Cannot skip to the next track, queue is empty");
600 Err(Error::QueueEmpty)
601 } else {
602 info!("Cannot skip to the next track");
603 Err(Error::CannotGoNext)
604 }
605}
606
607pub fn skip_previous() -> Result<(), Error> {
608 trace!("Skipping to the previous track");
609 let mut state_guard = PLAYER_STATE.write().unwrap();
610 let state = unwrap_state_mut(state_guard.as_mut())?;
611
612 let guard = crate::CONFIG.read().unwrap();
613 let config = guard.as_ref().unwrap();
614 if let Some(threshold) = config.playback.skip_previous_threshold
615 && state.player_position > threshold
616 {
617 trace!("Player position above skip threshold, resetting player position instead");
618 drop(state_guard);
619 return set_player_position(Duration::default());
620 }
621
622 if let Some(track) = state.previous_track() {
623 let player_guard = PLAYER_HANDLE.read().unwrap();
624 let player = unwrap_player(player_guard.as_ref())?;
625 match track.open_source() {
626 Ok(source) => {
627 state.set_player_position(Duration::default());
628 state.set_playback_state(PlaybackState::Playing);
629 player.clear();
630 player.append(source);
631 player.play();
632 }
633 Err(e) => {
634 error!("Could not open audio source: {e}");
635 player.stop();
636 state.set_playback_state(PlaybackState::Stopped);
637 return Err(Error::SourceError);
638 }
639 };
640 background_save_state(state.clone());
641 Ok(())
642 } else if state.queue_empty() {
643 info!("Cannot go to the previous track, queue is empty");
644 Err(Error::QueueEmpty)
645 } else {
646 info!("Cannot go to the previous track");
647 Err(Error::CannotGoPrevious)
648 }
649}
650
651pub fn set_loop_state(loop_state: LoopState) -> Result<(), Error> {
652 trace!("Setting loop state to {loop_state:?}");
653 let mut state_guard = PLAYER_STATE.write().unwrap();
654 let state = unwrap_state_mut(state_guard.as_mut())?;
655 state.set_loop_state(loop_state);
656 background_save_state(state.clone());
657 Ok(())
658}
659
660pub fn cycle_loop_state() -> Result<(), Error> {
661 trace!("Cycling loop state");
662 let mut state_guard = PLAYER_STATE.write().unwrap();
663 let state = unwrap_state_mut(state_guard.as_mut())?;
664 state.set_loop_state(state.loop_state.cycle());
665 background_save_state(state.clone());
666 Ok(())
667}
668
669pub fn set_shuffle_state(shuffle_state: ShuffleState) -> Result<(), Error> {
670 trace!("Setting shuffle state to {shuffle_state:?}");
671 let mut state_guard = PLAYER_STATE.write().unwrap();
672 let state = unwrap_state_mut(state_guard.as_mut())?;
673 state.set_shuffle_state(shuffle_state);
674 background_save_state(state.clone());
675 Ok(())
676}
677
678pub fn toggle_shuffle_state() -> Result<(), Error> {
679 trace!("Toggling shuffle state");
680 let mut state_guard = PLAYER_STATE.write().unwrap();
681 let state = unwrap_state_mut(state_guard.as_mut())?;
682 let shuffle_state = state.shuffle_state.toggle();
683 trace!("Setting shuffle state to {shuffle_state:?}");
684 state.set_shuffle_state(shuffle_state);
685 background_save_state(state.clone());
686 Ok(())
687}
688
689pub fn set_player_position(position: Duration) -> Result<(), Error> {
690 trace!("Setting player position to {:?}", position.as_secs());
691 let player_guard = PLAYER_HANDLE.read().unwrap();
692 let mut state_guard = PLAYER_STATE.write().unwrap();
693 let state = unwrap_state_mut(state_guard.as_mut())?;
694 if state.playback_state == PlaybackState::Stopped {
695 return Err(Error::PlayerStopped);
696 }
697 let player = match player_guard.as_ref() {
698 Some(player) => player,
699 None => {
700 warn!("Cannot set player position, player is not connected");
701 return Err(Error::PlayerNotConnected);
702 }
703 };
704 if player.empty() {
705 Err(Error::QueueEmpty)
706 } else if let Some(track) = state.current()
707 && position > track.duration
708 {
709 drop(state_guard);
710 skip_next()
711 } else if let Err(e) = player.try_seek(position) {
712 error!("Could not set player position: {e}");
713 #[cfg(feature = "mpris")]
715 mpris::set_position(state.player_position);
716 Err(Error::SeekNotSupported)
717 } else {
718 state.set_player_position(position);
719 Ok(())
720 }
721}
722
723static SEEK_ROUND_THRESHOLD: LazyLock<Duration> = LazyLock::new(|| Duration::from_secs(1));
729
730#[cfg(feature = "mpris")]
737static SUPPORTED_MIME_TYPES: LazyLock<Vec<String>> = LazyLock::new(|| {
738 let arr = [
739 "audio/aac",
740 "audio/32kadpcm",
741 "audio/aiff",
742 "audio/x-aiff",
743 "audio/x-caf",
744 "audio/flac",
745 "audio/matroska",
746 "audio/mpeg",
747 "audio/mp4",
748 "audio/MPA",
749 "audio/mpa-robust",
750 "audio/ogg",
751 "audio/vorbis",
752 "audio/vorbis-config",
753 "audio/vnd.wave",
754 "audio/wav",
755 "audio/wave",
756 "audio/x-wav",
757 "audio/webm",
758 ];
759 arr.into_iter().map(|t| t.to_string()).collect()
760});
761
762pub fn seek(delta: SignedDuration) -> Result<(), Error> {
763 let player_guard = PLAYER_HANDLE.read().unwrap();
764 let player = match player_guard.as_ref() {
765 Some(player) => player,
766 None => {
767 warn!("Cannot seek, the player is not connected");
768 return Err(Error::PlayerNotConnected);
769 }
770 };
771 let mut state_guard = PLAYER_STATE.write().unwrap();
772 let state = unwrap_state_mut(state_guard.as_mut())?;
773 let pos = match delta {
774 SignedDuration::Positive(positive_dur) => {
775 if positive_dur == Duration::default() {
776 info!("Refusing to seek by 0s");
777 return Err(Error::InvalidDuration);
778 }
779 let sum = match positive_dur.checked_add(player.get_pos()) {
780 Some(sum) => sum,
781 None => {
782 error!("Overflow detected while seeking, aborting");
783 return Err(Error::DurationOverflow);
784 }
785 };
786 if let Some(track) = state.current()
787 && sum > track.duration - *SEEK_ROUND_THRESHOLD
788 {
789 drop(state_guard);
790 return skip_next();
791 } else {
792 trace!("Seeking player by {positive_dur:?}");
793 sum
794 }
795 }
796 SignedDuration::Negative(negative_dur) => {
797 if negative_dur == Duration::default() {
798 info!("Refusing to seek by 0s");
799 return Err(Error::InvalidDuration);
800 }
801 trace!("Seeking player by -{negative_dur:?}");
802 let sub = match player.get_pos().checked_sub(negative_dur) {
803 Some(sub) => sub,
804 None => {
805 error!("Overflow detected while seeking");
806 return Err(Error::DurationOverflow);
807 }
808 };
809 if sub < *SEEK_ROUND_THRESHOLD {
810 Duration::default()
811 } else {
812 sub
813 }
814 }
815 };
816 if let Err(e) = player.try_seek(pos) {
817 error!("Could not set player position: {e}");
818 Err(Error::SeekNotSupported)
819 } else {
820 state.set_player_position(pos);
821 Ok(())
822 }
823}
824
825pub fn set_player_volume(volume: PlayerVolume) -> Result<(), Error> {
826 trace!("Setting player volume to {:?}", volume);
827 let player_guard = PLAYER_HANDLE.read().unwrap();
828 let player = unwrap_player(player_guard.as_ref())?;
829 player.set_volume(volume.get());
830 let mut state_guard = PLAYER_STATE.write().unwrap();
831 let state = unwrap_state_mut(state_guard.as_mut())?;
832 state.set_player_volume(volume);
833 background_save_state(state.clone());
834 Ok(())
835}
836
837pub(crate) fn init(
838 #[cfg(feature = "mpris")] identity: String,
839 #[cfg(feature = "mpris")] identifier: String,
840) {
841 trace!("Initializing the playback module");
842
843 let state = match restore_state_from_cache() {
844 Ok(state) => {
845 debug!("Restored player state from cache");
846 state
847 }
848 Err(e) => {
849 error!("Could not restore player state from cache: {e}");
851 let state = PlayerState::default();
852 debug!("Creating a new state and attempting to save it in cache");
853 background_save_state(state.clone());
854 state
855 }
856 };
857 crate::send_event(crate::Event::Playback(Event::StateInitialized(
858 state.clone(),
859 )));
860 trace!("Player state ready!");
861
862 let handle = match rodio::DeviceSinkBuilder::open_default_sink() {
865 Ok(sink) => sink,
866 Err(e) => {
867 error!("Could not open the default sink, audio playback will not work: {e}");
868 return;
869 }
870 };
871 let player = Player::connect_new(handle.mixer());
872 player.set_volume(state.player_volume.get());
873 trace!("Player handle initialized, audio playback should work fine!");
874
875 *PLAYER_STATE.write().unwrap() = Some(state);
876 *PLAYER_HANDLE.write().unwrap() = Some(player);
877
878 let mut state_guard = PLAYER_STATE.write().unwrap();
879 let state = state_guard.as_mut().unwrap();
880 if let Some(track) = state.current() {
881 match track.open_source() {
882 Ok(source) => {
883 let player_guard = PLAYER_HANDLE.read().unwrap();
884 let player = player_guard.as_ref().unwrap();
885 player.append(source);
886 player.pause();
887 if let Err(e) = player.try_seek(state.player_position) {
888 error!("Couldn't set initial player position: {e}");
889 state.set_player_position(Duration::default());
890 }
891 drop(player_guard);
892 }
893 Err(e) => {
894 error!("Couldn't open the current track: {e}");
895 state.set_playback_state(PlaybackState::Stopped);
896 }
897 }
898 }
899 drop(state_guard);
900
901 #[cfg(feature = "mpris")]
902 mpris::launch_server(identity, identifier);
903
904 trace!("Playback module initialized");
905
906 let sleep_duration = Duration::from_millis(100);
907 loop {
908 thread::sleep(sleep_duration);
909 let mut state_guard = PLAYER_STATE.write().unwrap();
910 let state = state_guard.as_mut().unwrap();
911 let player_guard = PLAYER_HANDLE.read().unwrap();
912 let player = player_guard.as_ref().unwrap();
913 if !player.is_paused() && !player.empty() {
914 state.increment_player_position(sleep_duration);
915 } else if player.empty() && state.is_playing() {
916 state.set_player_position(Duration::default());
917 let track = match state.next_track() {
918 Some(track) => track,
919 None => {
920 state.set_playback_state(PlaybackState::Stopped);
921 continue;
922 }
923 };
924 match track.open_source() {
925 Ok(source) => {
926 player.append(source);
927 state.set_playback_state(PlaybackState::Playing);
928 }
929 Err(e) => {
930 error!("Could not open audio source: {e}");
931 player.stop();
932 state.set_playback_state(PlaybackState::Stopped);
933 continue;
934 }
935 }
936 }
937 drop(state_guard);
938 drop(player_guard);
939 }
940}