diff options
| author | salmonik <raisersoft@proton.me> | 2026-05-05 21:12:32 +0200 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-05-05 21:12:32 +0200 |
| commit | bd29978b2a0d0d6def486fcdaa63bc8934d68045 (patch) | |
| tree | 5df0efb17d350ae7f095eb9c0940b1bbf568de96 /src/playback.rs | |
| download | rsplayer-bd29978b2a0d0d6def486fcdaa63bc8934d68045.tar.gz rsplayer-bd29978b2a0d0d6def486fcdaa63bc8934d68045.zip | |
fix(daemon): robust auto-advance; ensure next track starts even if playback fails
Diffstat (limited to 'src/playback.rs')
| -rw-r--r-- | src/playback.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/playback.rs b/src/playback.rs new file mode 100644 index 0000000..76c0f44 --- /dev/null +++ b/src/playback.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | |||
| 3 | #[derive(Debug, Clone, PartialEq)] | ||
| 4 | #[allow(dead_code)] | ||
| 5 | pub enum PlaybackState { | ||
| 6 | Stopped, | ||
| 7 | Playing, | ||
| 8 | Paused, | ||
| 9 | } | ||
| 10 | |||
| 11 | #[derive(Debug, Clone)] | ||
| 12 | #[allow(dead_code)] | ||
| 13 | pub struct Track { | ||
| 14 | pub path: PathBuf, | ||
| 15 | pub title: String, | ||
| 16 | pub artist: String, | ||
| 17 | pub album: String, | ||
| 18 | pub duration: Option<u64>, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(Debug, Clone)] | ||
| 22 | #[allow(dead_code)] | ||
| 23 | pub enum ShuffleMode { | ||
| 24 | None, | ||
| 25 | All, | ||
| 26 | Album, | ||
| 27 | } | ||
| 28 | |||
| 29 | #[allow(dead_code)] | ||
| 30 | pub struct Player { | ||
| 31 | state: PlaybackState, | ||
| 32 | current_track: Option<Track>, | ||
| 33 | queue: Vec<Track>, | ||
| 34 | volume: u8, | ||
| 35 | shuffle: ShuffleMode, | ||
| 36 | } | ||
| 37 | |||
| 38 | #[allow(dead_code)] | ||
| 39 | impl Player { | ||
| 40 | pub fn new() -> Self { | ||
| 41 | Player { | ||
| 42 | state: PlaybackState::Stopped, | ||
| 43 | current_track: None, | ||
| 44 | queue: Vec::new(), | ||
| 45 | volume: 70, | ||
| 46 | shuffle: ShuffleMode::None, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn state(&self) -> &PlaybackState { | ||
| 51 | &self.state | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn volume(&self) -> u8 { | ||
| 55 | self.volume | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn play(&mut self) { | ||
| 59 | if self.state == PlaybackState::Paused { | ||
| 60 | self.resume(); | ||
| 61 | } else if self.current_track.is_none() && !self.queue.is_empty() { | ||
| 62 | self.current_track = Some(self.queue.remove(0)); | ||
| 63 | self.state = PlaybackState::Playing; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub fn play_track(&mut self, track: Track) { | ||
| 68 | self.current_track = Some(track); | ||
| 69 | self.state = PlaybackState::Playing; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn pause(&mut self) { | ||
| 73 | if self.state == PlaybackState::Playing { | ||
| 74 | self.state = PlaybackState::Paused; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn resume(&mut self) { | ||
| 79 | if self.state == PlaybackState::Paused { | ||
| 80 | self.state = PlaybackState::Playing; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn stop(&mut self) { | ||
| 85 | self.state = PlaybackState::Stopped; | ||
| 86 | self.current_track = None; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn next(&mut self) { | ||
| 90 | if !self.queue.is_empty() { | ||
| 91 | self.current_track = Some(self.queue.remove(0)); | ||
| 92 | } else { | ||
| 93 | self.stop(); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | pub fn previous(&mut self) { | ||
| 98 | if (self.state == PlaybackState::Playing || self.state == PlaybackState::Paused) | ||
| 99 | && self.current_track.is_some() | ||
| 100 | { | ||
| 101 | self.state = PlaybackState::Playing; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn set_volume(&mut self, level: u8) { | ||
| 106 | self.volume = level.min(100); | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn set_shuffle(&mut self, mode: ShuffleMode) { | ||
| 110 | self.shuffle = mode; | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn queue_track(&mut self, track: Track, position: &str) { | ||
| 114 | match position { | ||
| 115 | "now" => self.queue.insert(0, track), | ||
| 116 | "next" => { | ||
| 117 | let idx = 1.min(self.queue.len()); | ||
| 118 | self.queue.insert(idx, track); | ||
| 119 | } | ||
| 120 | _ => self.queue.push(track), | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | pub fn dequeue_next(&mut self) -> Option<Track> { | ||
| 125 | if !self.queue.is_empty() { | ||
| 126 | Some(self.queue.remove(0)) | ||
| 127 | } else { | ||
| 128 | None | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
