Playlist supports video tracks (full-screen video + audio through the analyser)

Audio and video files can both be added to the playlist; video tracks play
full-screen with their audio routed through the analyser (visuals/waveform
react), have duration/progress, and are included in recordings. Persisted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.8 committed 2026-06-19 16:46:44 +02:00
1 parent 1ae8fa81a7
commit 0f0accc67d
6 files changed
+74 -15

No files matched your search

+25
View File
@@ -34,6 +34,9 @@ class AudioEngine {
this.stream = null; // MediaStream when using live input
this.mode = 'none';
this.onEnded = null; // callback fired when a (non-looping) track ends
this._videoSrc = null; // persistent source for the playlist video element
this._videoGain = null;
this._videoSrcEl = null;
// Smoothed band values and beat state.
this.gain = 1.0; // master reactivity multiplier
@@ -145,6 +148,28 @@ class AudioEngine {
this.mode = 'input';
}
// Play a playlist video track: route the given <video> element's audio
// through the analyser/output so visuals react and it's audible/recordable.
attachVideo(videoEl) {
this.resume();
this._disconnectSource();
if (this._videoSrcEl !== videoEl) {
// createMediaElementSource may only be called once per element.
this._videoSrc = this.ctx.createMediaElementSource(videoEl);
this._videoGain = this.ctx.createGain();
this._videoSrc.connect(this._videoGain);
this._videoGain.connect(this.analyser);
this._videoGain.connect(this.outGain);
this._videoGain.connect(this.recordDest);
this._videoSrcEl = videoEl;
}
videoEl.onended = () => { if (this.onEnded) this.onEnded(); };
this.mediaEl = videoEl;
this.sourceNode = null;
this.sourceGain = null;
this.mode = 'video';
}
async listInputDevices() {
// Labels are only populated after permission is granted.
try { await navigator.mediaDevices.getUserMedia({ audio: true }).then(s => s.getTracks().forEach(t => t.stop())); } catch (e) {}