First working POC

This commit is contained in:
2026-01-26 22:21:54 +01:00
commit cfddc1edea
255 changed files with 77606 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
use serde::{Deserialize, Serialize};
use super::media::MediaItem;
/// Tracks why playback ended to determine autoplay behavior
///
/// @req: UR-005 - Control media playback (autoplay logic)
/// @req: DR-001 - Player state machine (end reason tracking)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EndReason {
/// Track played to completion (natural end) - trigger autoplay
Finished,
/// User pressed next/previous - already handled, don't autoplay
UserSkip,
/// User stopped playback - don't autoplay
UserStop,
/// Playback error - don't autoplay
Error,
/// User selected a different track - don't autoplay
NewTrackLoaded,
}
/// Player state machine (6 states: Idle, Loading, Playing, Paused, Seeking, Error)
///
/// @req: DR-001 - Player state machine (idle, loading, playing, paused, seeking, error)
/// @req: UR-005 - Control media playback (state tracking)
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum PlayerState {
#[default]
/// No media loaded
Idle,
/// Media is being loaded/buffered
Loading { media: MediaItem },
/// Media is playing
Playing {
media: MediaItem,
/// Current position in seconds
position: f64,
/// Total duration in seconds
duration: f64,
},
/// Media is paused
Paused {
media: MediaItem,
/// Current position in seconds
position: f64,
/// Total duration in seconds
duration: f64,
},
/// Seeking to a new position
Seeking {
media: MediaItem,
/// Target position in seconds
target: f64,
},
/// An error occurred
Error {
media: Option<MediaItem>,
error: String,
},
}
impl PlayerState {
/// Get the current playback position if available
pub fn position(&self) -> Option<f64> {
match self {
PlayerState::Playing { position, .. } => Some(*position),
PlayerState::Paused { position, .. } => Some(*position),
_ => None,
}
}
/// Check if the player is currently playing
pub fn is_playing(&self) -> bool {
matches!(self, PlayerState::Playing { .. })
}
/// Check if the player is currently paused
#[allow(dead_code)]
pub fn is_paused(&self) -> bool {
matches!(self, PlayerState::Paused { .. })
}
}