Files
SoulStream/crates/player/examples/preview_probe.rs
T
KeelrLeek a667b4b28e Baseline: full tree snapshot after git repository loss
The previous .git directory was found gutted (only info/ remained), so
history is unrecoverable locally. This commit restores version control
with the tree exactly as it stands: the working app, the preview-tracks
launcher on the artist hero, the scrollable-track-previews design bundle
(docs + approved mockups), and the Phase 0 validation artifacts.
2026-07-22 17:57:00 -05:00

101 lines
3.6 KiB
Rust

//! Phase 0.3 — native transport probe (isolated diagnostic, not feature code).
//!
//! Answers the native column's core questions with the existing crate, no new
//! dependencies:
//!
//! 1. Can a SECOND `Player` instance coexist with a "main" one — two sinks,
//! two event channels, no shared state?
//! 2. Does a real Deezer preview (MPEG-1 Layer III, 128k CBR, ~480 KB)
//! decode and play through rodio/symphonia?
//! 3. Do pause / resume / stop behave on the second instance while the
//! first is mid-track, without cross-talk on either event channel?
//!
//! The preview file is downloaded OUTSIDE this probe (curl) and passed as
//! argv[1]; argv[2] is any long local track for the "main" side. Run:
//!
//! cargo run -p player --example preview_probe -- preview.mp3 main.mp3
use std::sync::mpsc::RecvTimeoutError;
use std::time::{Duration, Instant};
use player::{Player, PlayerEvent};
fn drain(
label: &str,
rx: &std::sync::mpsc::Receiver<PlayerEvent>,
for_ms: u64,
log: &mut Vec<String>,
) {
let end = Instant::now() + Duration::from_millis(for_ms);
loop {
let left = end.saturating_duration_since(Instant::now());
if left.is_zero() {
break;
}
match rx.recv_timeout(left) {
Ok(ev) => log.push(format!("{label}: {ev:?}")),
Err(RecvTimeoutError::Timeout) => break,
Err(RecvTimeoutError::Disconnected) => {
log.push(format!("{label}: channel closed"));
break;
}
}
}
}
fn main() {
let mut args = std::env::args().skip(1);
let preview = args.next().expect("argv[1]: downloaded preview mp3");
let main_track = args.next();
let mut log = Vec::new();
// "Main" player — only if a second file was provided; the coexistence
// half of the probe is skipped otherwise.
let main_pair = main_track.map(|p| {
let (main, main_rx) = Player::spawn();
main.play_local(&p, None);
(main, main_rx, p)
});
// Preview player: a wholly separate instance and event channel.
let (prev, prev_rx) = Player::spawn();
let t0 = Instant::now();
prev.play_local(&preview, Some(30.0));
drain("preview", &prev_rx, 2500, &mut log);
let started = log.iter().any(|l| l.contains("Playing"));
println!("PROBE preview-starts: {started} ({} ms to first events)", t0.elapsed().as_millis());
prev.pause();
drain("preview", &prev_rx, 600, &mut log);
let paused = log.iter().any(|l| l.contains("Paused"));
println!("PROBE preview-pauses: {paused}");
prev.resume();
drain("preview", &prev_rx, 900, &mut log);
if let Some((_main, main_rx, ref path)) = main_pair {
let mut main_log = Vec::new();
drain("main", &main_rx, 1200, &mut main_log);
let main_alive = main_log.iter().any(|l| l.contains("Playing") || l.contains("Position"));
println!("PROBE main-coexists: {main_alive} ({path})");
// cross-talk check: no preview-channel event may appear on main's
// channel or vice versa — separate receivers make this structural,
// so what we actually assert is that both channels stayed live.
println!("PROBE channels-independent: true (separate mpsc receivers by construction)");
} else {
println!("PROBE main-coexists: skipped (no second file)");
}
prev.stop();
drain("preview", &prev_rx, 600, &mut log);
let stopped = log.iter().any(|l| l.contains("Stopped"));
println!("PROBE preview-stops: {stopped}");
println!("--- event log ---");
for l in &log {
println!("{l}");
}
}