Migrate instance ID storage from localStorage to sessionStorage
Switch instance ID storage from localStorage to sessionStorage in useWebRenderer composable to ensure better session management and prevent data persistence across browser sessions. Also update the DirectOggFlacSink implementation to improve backpressure handling, streamline the OGG chaining logic, and add proper Safari range header support for the stream endpoint.
This commit is contained in:
@@ -116,15 +116,16 @@ impl RendererRegistry {
|
||||
Ok((stream_url, udn))
|
||||
}
|
||||
|
||||
/// Retourne le DirectOggFlacHandle pour l'endpoint /stream (clonable).
|
||||
pub fn get_flac_handle(
|
||||
/// Retourne un DirectOggFlacStream pour l'endpoint /stream.
|
||||
/// Chaque appel retourne un wrapper sur le même reader persistant.
|
||||
pub fn get_stream(
|
||||
&self,
|
||||
instance_id: &str,
|
||||
) -> Option<pmoaudio_ext::sinks::DirectOggFlacHandle> {
|
||||
) -> Option<pmoaudio_ext::sinks::DirectOggFlacStream> {
|
||||
self.instances
|
||||
.read()
|
||||
.get(instance_id)
|
||||
.map(|i| i.flac_handle.clone())
|
||||
.map(|i| i.flac_handle.get_stream())
|
||||
}
|
||||
|
||||
/// Retourne le PipelineHandle par UDN (pour les handlers UPnP)
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
//! Handler HTTP GET /api/webrenderer/{id}/stream
|
||||
//!
|
||||
//! Sert le flux FLAC d'une instance WebRenderer via DirectFlacSink.
|
||||
//! Sert le flux OGG-FLAC d'une instance WebRenderer.
|
||||
//!
|
||||
//! Reconnectable : appelé à chaque Play, crée un nouveau pipe + encodeur.
|
||||
//! Safari fait systématiquement une requête Range: bytes=0-1 avant de jouer.
|
||||
//! On répond 206 avec 2 octets factices pour satisfaire la sonde,
|
||||
//! puis la vraie requête (sans Range) reçoit le stream persistant.
|
||||
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{Path, State},
|
||||
http::{
|
||||
StatusCode,
|
||||
header::{CACHE_CONTROL, CONNECTION, CONTENT_TYPE},
|
||||
HeaderMap, StatusCode,
|
||||
header::{CACHE_CONTROL, CONNECTION, CONTENT_TYPE, CONTENT_RANGE, CONTENT_LENGTH, ACCEPT_RANGES},
|
||||
},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
@@ -20,21 +22,31 @@ use tracing::info;
|
||||
use crate::registry::RendererRegistry;
|
||||
|
||||
/// GET /api/webrenderer/{id}/stream
|
||||
///
|
||||
/// Crée un nouveau pipe FLAC à chaque connexion (chaque Play).
|
||||
/// Le flux reste ouvert jusqu'à ce que le client se déconnecte (Stop).
|
||||
/// Les morceaux s'enchaînent en gapless dans le même flux.
|
||||
///
|
||||
/// Safari envoie un Range header (bytes=0-) et exige Accept-Ranges: bytes.
|
||||
/// On répond 206 Partial Content si un Range header est présent, sinon 200.
|
||||
pub async fn stream_handler(
|
||||
State(registry): State<Arc<RendererRegistry>>,
|
||||
Path(instance_id): Path<String>,
|
||||
headers: HeaderMap,
|
||||
) -> impl IntoResponse {
|
||||
info!(instance_id = %instance_id, "FLAC stream client connecting");
|
||||
|
||||
let handle = match registry.get_flac_handle(&instance_id) {
|
||||
Some(h) => h,
|
||||
// Détecter la sonde Range: bytes=0-1 de Safari
|
||||
if let Some(range) = headers.get("range") {
|
||||
if range.as_bytes() == b"bytes=0-1" {
|
||||
info!(instance_id = %instance_id, "Safari range probe — responding 206");
|
||||
return Response::builder()
|
||||
.status(StatusCode::PARTIAL_CONTENT)
|
||||
.header(CONTENT_TYPE, "audio/ogg; codecs=flac")
|
||||
.header(ACCEPT_RANGES, "bytes")
|
||||
.header(CONTENT_RANGE, "bytes 0-1/*")
|
||||
.header(CONTENT_LENGTH, "2")
|
||||
.body(Body::from(vec![0u8, 0u8]))
|
||||
.unwrap()
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
|
||||
let stream = match registry.get_stream(&instance_id) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return (
|
||||
StatusCode::NOT_FOUND,
|
||||
@@ -44,18 +56,14 @@ pub async fn stream_handler(
|
||||
}
|
||||
};
|
||||
|
||||
let stream = handle.connect().await;
|
||||
|
||||
info!(instance_id = %instance_id, "FLAC stream started");
|
||||
|
||||
// Toujours 200 OK pour un flux live de taille inconnue.
|
||||
// Les navigateurs (Safari inclus) acceptent 200 pour l'audio streaming.
|
||||
// Un Content-Range invalide (bytes 0-*/*) ferait rejeter le flux.
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header(CONTENT_TYPE, "audio/ogg; codecs=flac")
|
||||
.header(CACHE_CONTROL, "no-store, no-transform")
|
||||
.header(CONNECTION, "keep-alive")
|
||||
.header(ACCEPT_RANGES, "bytes")
|
||||
.header("X-Content-Type-Options", "nosniff")
|
||||
.body(Body::from_stream(ReaderStream::new(stream)))
|
||||
.unwrap()
|
||||
|
||||
Reference in New Issue
Block a user