1use std::time::Duration;
22
23use super::backend::{PlayerBackend, PlayerError};
24use super::media_player::{
25 duration_from_secs, Capabilities, MediaPlayer, OpenRequest, Phase, PlaybackSnapshot,
26};
27use super::state::PlayerState;
28
29pub struct LegacyPlayer<B: PlayerBackend> {
30 inner: B,
31 capabilities: Capabilities,
36 has_item: bool,
40}
41
42impl<B: PlayerBackend> LegacyPlayer<B> {
43 pub fn new(inner: B, capabilities: Capabilities) -> Self {
44 Self {
45 inner,
46 capabilities,
47 has_item: false,
48 }
49 }
50}
51
52impl<B: PlayerBackend + Send> MediaPlayer for LegacyPlayer<B> {
53 fn open(&mut self, req: OpenRequest) -> Result<(), PlayerError> {
59 self.inner.load(&req.media)?;
60 self.has_item = true;
61 if req.autoplay {
62 self.inner.play()?;
63 }
64 if !req.start.is_zero() {
65 let _ = self.inner.seek(req.start.as_secs_f64());
67 }
68 Ok(())
69 }
70
71 fn play(&mut self) -> Result<(), PlayerError> {
72 self.inner.play()
73 }
74
75 fn pause(&mut self) -> Result<(), PlayerError> {
76 self.inner.pause()
77 }
78
79 fn close(&mut self) -> Result<(), PlayerError> {
80 self.has_item = false;
81 self.inner.stop()
82 }
83
84 fn seek(&mut self, to: Duration) -> Result<(), PlayerError> {
85 self.inner.seek(to.as_secs_f64())
86 }
87
88 fn set_volume(&mut self, volume: f32) -> Result<(), PlayerError> {
89 self.inner.set_volume(volume)
90 }
91
92 fn set_muted(&mut self, _muted: bool) -> Result<(), PlayerError> {
95 Err(PlayerError {
96 message: "mute is not supported by this backend".to_string(),
97 })
98 }
99
100 fn set_rate(&mut self, _rate: f64) -> Result<(), PlayerError> {
101 Err(PlayerError {
102 message: "playback rate is not supported by this backend".to_string(),
103 })
104 }
105
106 fn select_audio_track(&mut self, index: Option<i32>) -> Result<(), PlayerError> {
107 self.inner.set_audio_track(index.unwrap_or(-1))
108 }
109
110 fn select_subtitle_track(&mut self, index: Option<i32>) -> Result<(), PlayerError> {
111 self.inner.set_subtitle_track(index)
112 }
113
114 fn snapshot(&self) -> PlaybackSnapshot {
115 let phase = match self.inner.state() {
116 _ if !self.has_item => Phase::Idle,
117 PlayerState::Playing { .. } => Phase::Playing,
118 PlayerState::Paused { .. } => Phase::Paused,
119 PlayerState::Idle => Phase::Idle,
120 PlayerState::Error { error, .. } => Phase::Failed(error),
121 PlayerState::Loading { .. } | PlayerState::Seeking { .. } => Phase::Ready,
126 };
127 PlaybackSnapshot {
128 phase,
129 position: duration_from_secs(self.inner.position()).unwrap_or(Duration::ZERO),
130 duration: self.inner.duration().and_then(duration_from_secs),
131 seekable: true,
132 volume: self.inner.volume(),
133 muted: false,
134 rate: 1.0,
135 audio_track: None,
136 subtitle_track: None,
137 }
138 }
139
140 fn set_audio_settings(
141 &mut self,
142 settings: &crate::settings::AudioSettings,
143 ) -> Result<(), PlayerError> {
144 self.inner.set_audio_settings(settings)
145 }
146
147 fn audio_settings(&self) -> crate::settings::AudioSettings {
148 self.inner.audio_settings()
149 }
150
151 fn capabilities(&self) -> Capabilities {
152 self.capabilities
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use crate::player::media::MediaItem;
160 use crate::settings::AudioSettings;
161
162 struct HostileBackend {
174 duration: f64,
175 position: f64,
176 }
177
178 impl PlayerBackend for HostileBackend {
179 fn load(&mut self, _media: &MediaItem) -> Result<(), PlayerError> {
180 Ok(())
181 }
182 fn play(&mut self) -> Result<(), PlayerError> {
183 Ok(())
184 }
185 fn pause(&mut self) -> Result<(), PlayerError> {
186 Ok(())
187 }
188 fn stop(&mut self) -> Result<(), PlayerError> {
189 Ok(())
190 }
191 fn seek(&mut self, _position: f64) -> Result<(), PlayerError> {
192 Ok(())
193 }
194 fn set_volume(&mut self, _volume: f32) -> Result<(), PlayerError> {
195 Ok(())
196 }
197 fn position(&self) -> f64 {
198 self.position
199 }
200 fn duration(&self) -> Option<f64> {
201 Some(self.duration)
202 }
203 fn state(&self) -> PlayerState {
204 PlayerState::Idle
205 }
206 fn volume(&self) -> f32 {
207 1.0
208 }
209 fn set_audio_settings(&mut self, _s: &AudioSettings) -> Result<(), PlayerError> {
210 Ok(())
211 }
212 fn audio_settings(&self) -> AudioSettings {
213 AudioSettings::default()
214 }
215 fn set_audio_track(&mut self, _i: i32) -> Result<(), PlayerError> {
216 Ok(())
217 }
218 fn set_subtitle_track(&mut self, _i: Option<i32>) -> Result<(), PlayerError> {
219 Ok(())
220 }
221 }
222
223 fn hostile(duration: f64, position: f64) -> LegacyPlayer<HostileBackend> {
224 LegacyPlayer::new(
225 HostileBackend { duration, position },
226 crate::player::media_player::Capabilities::mpv(),
227 )
228 }
229
230 #[test]
238 fn test_snapshot_survives_an_engine_that_answers_badly() {
239 let s = hostile(-9_223_372_036_854_776.0, 0.0).snapshot();
241 assert_eq!(s.duration, None, "a negative duration is not a duration");
242
243 for bad in [f64::NAN, f64::NEG_INFINITY, f64::INFINITY, -1.0, 0.0] {
244 let s = hostile(bad, bad).snapshot();
245 assert_eq!(s.duration, None, "{bad} should not become a duration");
246 assert_eq!(
247 s.position,
248 Duration::ZERO,
249 "{bad} should not become a position"
250 );
251 }
252
253 let s = hostile(6997.024, 540.0).snapshot();
255 assert_eq!(s.duration, Some(Duration::from_secs_f64(6997.024)));
256 assert_eq!(s.position, Duration::from_secs_f64(540.0));
257 }
258}