⬆️ ureq v2→v3, add continuous stream detection
- Updateurequest dependency from v2 to latest major (v3.14) - Add ureq as optional dependency in pmoaudio-ext - Introduce is_continuous flag to UriSource and PlayerState for proper StreamType handling (Finite vs Continuous) - Implement detect_continuous_stream() with URL pattern matching and HTTP HEAD header inspection (ICY, chunked encoding) - Update send_track_boundary() to accept StreamType parameter
This commit is contained in:
@@ -165,6 +165,7 @@ impl NodeLogic for PlayerSourceLogic {
|
||||
let mut current_uri: Option<String> = None;
|
||||
let mut next_uri: Option<String> = None;
|
||||
let mut paused_at_sec: f64 = 0.0;
|
||||
let mut is_continuous: bool = false;
|
||||
|
||||
info!("PlayerSource: started");
|
||||
|
||||
@@ -182,7 +183,7 @@ impl NodeLogic for PlayerSourceLogic {
|
||||
None => break,
|
||||
Some(cmd) => {
|
||||
self.handle_command(
|
||||
cmd, &mut state, &mut current_uri,
|
||||
cmd, is_continuous, &mut state, &mut current_uri,
|
||||
&mut next_uri, &mut paused_at_sec,
|
||||
&output, &stop_token,
|
||||
).await?;
|
||||
@@ -213,15 +214,17 @@ impl NodeLogic for PlayerSourceLogic {
|
||||
};
|
||||
|
||||
let duration_sec = source.duration_sec();
|
||||
let is_continuous = source.is_continuous();
|
||||
let _ = self.event_tx.send(PlayerEvent::Playing {
|
||||
uri: uri.clone(),
|
||||
duration_sec,
|
||||
});
|
||||
info!("PlayerSource: playing {:?} from {:.1}s", uri, paused_at_sec);
|
||||
info!("PlayerSource: playing {:?} from {:.1}s continuous={}", uri, paused_at_sec, is_continuous);
|
||||
|
||||
// Pompe audio — s'arrête sur EOF, Pause, Stop, ou commande
|
||||
let result = self.pump(
|
||||
source,
|
||||
is_continuous,
|
||||
&mut state,
|
||||
&mut current_uri,
|
||||
&mut next_uri,
|
||||
@@ -256,6 +259,7 @@ impl PlayerSourceLogic {
|
||||
async fn handle_command(
|
||||
&mut self,
|
||||
cmd: PlayerCommand,
|
||||
is_continuous: bool,
|
||||
state: &mut TransportState,
|
||||
current_uri: &mut Option<String>,
|
||||
next_uri: &mut Option<String>,
|
||||
@@ -282,14 +286,16 @@ impl PlayerSourceLogic {
|
||||
TransportState::Paused => {
|
||||
info!("PlayerSource: Play (resume from {:.1}s)", paused_at_sec);
|
||||
// Injecter un TrackBoundary pour EOS + nouveau BOS OGG propre
|
||||
send_track_boundary(current_uri.as_deref(), output, *paused_at_sec, stop_token).await?;
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
send_track_boundary(current_uri.as_deref(), output, *paused_at_sec, stream_type, stop_token).await?;
|
||||
*state = TransportState::Playing;
|
||||
}
|
||||
TransportState::Loaded => {
|
||||
info!("PlayerSource: Play (start)");
|
||||
*paused_at_sec = 0.0;
|
||||
// TrackBoundary initial pour le premier BOS OGG
|
||||
send_track_boundary(current_uri.as_deref(), output, 0.0, stop_token).await?;
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
send_track_boundary(current_uri.as_deref(), output, 0.0, stream_type, stop_token).await?;
|
||||
*state = TransportState::Playing;
|
||||
}
|
||||
TransportState::Idle => {
|
||||
@@ -321,7 +327,8 @@ impl PlayerSourceLogic {
|
||||
if current_uri.is_some() {
|
||||
info!("PlayerSource: Seek to {:.1}s", pos);
|
||||
*paused_at_sec = pos;
|
||||
send_track_boundary(current_uri.as_deref(), output, pos, stop_token).await?;
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
send_track_boundary(current_uri.as_deref(), output, pos, stream_type, stop_token).await?;
|
||||
*state = TransportState::Playing;
|
||||
}
|
||||
}
|
||||
@@ -336,6 +343,7 @@ impl PlayerSourceLogic {
|
||||
async fn pump(
|
||||
&mut self,
|
||||
source: UriSource,
|
||||
is_continuous: bool,
|
||||
state: &mut TransportState,
|
||||
current_uri: &mut Option<String>,
|
||||
next_uri: &mut Option<String>,
|
||||
@@ -395,7 +403,8 @@ impl PlayerSourceLogic {
|
||||
*next_uri = None;
|
||||
*paused_at_sec = 0.0;
|
||||
// TrackBoundary pour clore le bitstream OGG proprement
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, 0.0, stop_token).await {
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, 0.0, stream_type, stop_token).await {
|
||||
result = Err(e);
|
||||
}
|
||||
*state = TransportState::Playing;
|
||||
@@ -409,7 +418,8 @@ impl PlayerSourceLogic {
|
||||
info!("PlayerSource: Seek to {:.1}s", pos);
|
||||
source_stop.cancel();
|
||||
*paused_at_sec = pos;
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, pos, stop_token).await {
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, pos, stream_type, stop_token).await {
|
||||
result = Err(e);
|
||||
break;
|
||||
}
|
||||
@@ -435,7 +445,8 @@ impl PlayerSourceLogic {
|
||||
info!("PlayerSource: gapless transition to {:?}", next);
|
||||
*current_uri = Some(next);
|
||||
*paused_at_sec = 0.0;
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, 0.0, stop_token).await {
|
||||
let stream_type = if is_continuous { StreamType::Continuous } else { StreamType::Finite };
|
||||
if let Err(e) = send_track_boundary(current_uri.as_deref(), output, 0.0, stream_type, stop_token).await {
|
||||
result = Err(e);
|
||||
}
|
||||
*state = TransportState::Playing;
|
||||
@@ -495,6 +506,7 @@ async fn send_track_boundary(
|
||||
uri: Option<&str>,
|
||||
output: &[mpsc::Sender<Arc<AudioSegment>>],
|
||||
timestamp_sec: f64,
|
||||
stream_type: StreamType,
|
||||
stop_token: &CancellationToken,
|
||||
) -> Result<(), AudioError> {
|
||||
if output.is_empty() || stop_token.is_cancelled() {
|
||||
@@ -506,7 +518,7 @@ async fn send_track_boundary(
|
||||
let _ = meta.set_title(Some(u.to_string())).await;
|
||||
}
|
||||
let meta_arc = Arc::new(tokio::sync::RwLock::new(meta));
|
||||
let boundary = AudioSegment::new_track_boundary(0, timestamp_sec, meta_arc, StreamType::Finite);
|
||||
let boundary = AudioSegment::new_track_boundary(0, timestamp_sec, meta_arc, stream_type);
|
||||
|
||||
send_to_children("PlayerSource", output, boundary).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user