Ajout de la gestion de la position en temps réel et amélioration de la gestion de la durée
Ajout de l'événement Position pour envoyer la position courante ~1/s pendant la lecture. Modification de la gestion de la durée dans le registry pour ne pas écraser une durée déjà connue par la source (priorité à la source pour les flux radio). Mise à jour du handler position_update pour utiliser update_duration au lieu de update_position, et suppression de la gestion de position_sec qui est maintenant gérée par PlayerEvent::Position. Le serveur gère maintenant la position via les événements Position émis par PlayerSource, et la durée est uniquement utilisée comme fallback pour les sources sans durée connue.
This commit is contained in:
@@ -79,6 +79,8 @@ pub enum PlayerEvent {
|
||||
Stopped,
|
||||
/// Fin de piste (pour que le ControlPoint avance la queue)
|
||||
TrackEnded,
|
||||
/// Position courante (émise ~1/s pendant la lecture)
|
||||
Position { position_sec: f64 },
|
||||
/// Erreur lors de l'ouverture ou de la lecture
|
||||
Error(String),
|
||||
}
|
||||
@@ -345,6 +347,9 @@ impl PlayerSourceLogic {
|
||||
let source_stop = stop_token.child_token();
|
||||
let source_stop_clone = source_stop.clone();
|
||||
|
||||
// Dernière seconde entière pour laquelle on a émis un Position
|
||||
let mut last_reported_sec: i64 = -1;
|
||||
|
||||
// Spawner l'émission de la source dans une tâche séparée
|
||||
let emit_task = tokio::spawn(async move {
|
||||
source.emit_to_channel(&chunk_tx, &source_stop_clone).await
|
||||
@@ -443,6 +448,14 @@ impl PlayerSourceLogic {
|
||||
// Mettre à jour la position courante
|
||||
if seg.is_audio_chunk() {
|
||||
*paused_at_sec = seg.timestamp_sec;
|
||||
// Émettre Position ~1/s
|
||||
let sec = paused_at_sec.floor() as i64;
|
||||
if sec != last_reported_sec {
|
||||
last_reported_sec = sec;
|
||||
let _ = self.event_tx.send(PlayerEvent::Position {
|
||||
position_sec: *paused_at_sec,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Envoyer au pipeline en aval
|
||||
if let Err(e) = send_to_children("PlayerSource", output, seg).await {
|
||||
|
||||
@@ -162,6 +162,9 @@ async fn run_event_listener(
|
||||
s.playback_state = PlaybackState::Stopped;
|
||||
s.position = None;
|
||||
}
|
||||
PlayerEvent::Position { position_sec } => {
|
||||
state.write().position = Some(seconds_to_upnp_time(position_sec));
|
||||
}
|
||||
PlayerEvent::TrackEnded => {
|
||||
#[cfg(feature = "pmoserver")]
|
||||
{
|
||||
|
||||
@@ -68,12 +68,14 @@ pub struct PositionUpdateRequest {
|
||||
}
|
||||
|
||||
/// POST /api/webrenderer/{id}/position
|
||||
/// position_sec est ignoré (géré par PlayerEvent::Position côté serveur).
|
||||
/// duration_sec est utilisé comme fallback si la source ne connaît pas la durée (flux radio).
|
||||
pub async fn position_update_handler(
|
||||
State(registry): State<Arc<RendererRegistry>>,
|
||||
Path(instance_id): Path<String>,
|
||||
Json(req): Json<PositionUpdateRequest>,
|
||||
) -> impl IntoResponse {
|
||||
registry.update_position(&instance_id, req.position_sec, req.duration_sec);
|
||||
registry.update_duration(&instance_id, req.duration_sec);
|
||||
StatusCode::NO_CONTENT
|
||||
}
|
||||
|
||||
|
||||
@@ -152,15 +152,19 @@ impl RendererRegistry {
|
||||
.map(|i| i.device_instance.clone())
|
||||
}
|
||||
|
||||
/// Met à jour la position et la durée depuis le navigateur (audio.currentTime).
|
||||
pub fn update_position(&self, instance_id: &str, position_sec: f64, duration_sec: Option<f64>) {
|
||||
/// Met à jour la durée depuis le navigateur.
|
||||
/// La position est gérée par PlayerSource via PlayerEvent::Position.
|
||||
/// On n'utilise duration_sec que si la source ne la connaît pas (flux radio sans durée).
|
||||
pub fn update_duration(&self, instance_id: &str, duration_sec: Option<f64>) {
|
||||
let instances = self.instances.read();
|
||||
if let Some(instance) = instances.get(instance_id) {
|
||||
let mut s = instance.state.write();
|
||||
s.position = Some(crate::pipeline::seconds_to_upnp_time(position_sec));
|
||||
if let Some(dur) = duration_sec {
|
||||
if dur > 0.0 {
|
||||
s.duration = Some(crate::pipeline::seconds_to_upnp_time(dur));
|
||||
// N'écraser la durée que si elle n'est pas déjà connue (la source est prioritaire)
|
||||
if s.duration.is_none() {
|
||||
if let Some(dur) = duration_sec {
|
||||
if dur > 0.0 {
|
||||
s.duration = Some(crate::pipeline::seconds_to_upnp_time(dur));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user