@joewinke/jatui 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -224,30 +224,46 @@
224
224
  } catch {
225
225
  secondaryStream = null
226
226
  }
227
- const AudioCtxCls =
228
- window.AudioContext ||
229
- (window as unknown as { webkitAudioContext: typeof AudioContext })
230
- .webkitAudioContext
231
- audioCtx = new AudioCtxCls()
232
- const dest = audioCtx.createMediaStreamDestination()
233
- for (const s of [primaryStream, secondaryStream]) {
234
- const tracks = s?.getAudioTracks() ?? []
235
- if (tracks.length) {
236
- audioCtx.createMediaStreamSource(new MediaStream(tracks)).connect(dest)
227
+ const displayAudio = primaryStream.getAudioTracks()
228
+ const micAudio = secondaryStream?.getAudioTracks() ?? []
229
+ const hasLiveAudio = displayAudio.length > 0 || micAudio.length > 0
230
+
231
+ // Only build the mixed audio track when there is a REAL source to
232
+ // feed it. A `MediaStreamAudioDestinationNode` with nothing
233
+ // connected — no display audio (a window share, or a tab with no
234
+ // sound) and no mic (denied, or blocked at the Permissions-Policy
235
+ // layer) — still produces a "live" track, but one that carries no
236
+ // samples at all. Pairing that dead track with the video track in
237
+ // one MediaRecorder stalls the muxer: every `ondataavailable` fires
238
+ // with `size === 0`, the whole clip comes back empty, and the
239
+ // caller's `blob.size === 0` guard silently drops it — a clean
240
+ // "recording…" UI that attaches nothing on Stop, no error shown
241
+ // (mos-oq8b1.27). Recording video-only when there is no audio
242
+ // avoids ever constructing that dead track.
243
+ let audioTracks: MediaStreamTrack[] = []
244
+ if (hasLiveAudio) {
245
+ const AudioCtxCls =
246
+ window.AudioContext ||
247
+ (window as unknown as { webkitAudioContext: typeof AudioContext })
248
+ .webkitAudioContext
249
+ audioCtx = new AudioCtxCls()
250
+ const dest = audioCtx.createMediaStreamDestination()
251
+ for (const tracks of [displayAudio, micAudio]) {
252
+ if (tracks.length) {
253
+ audioCtx.createMediaStreamSource(new MediaStream(tracks)).connect(dest)
254
+ }
237
255
  }
256
+ audioTracks = dest.stream.getAudioTracks()
238
257
  }
239
- stream = new MediaStream([
240
- ...primaryStream.getVideoTracks(),
241
- ...dest.stream.getAudioTracks()
242
- ])
258
+ stream = new MediaStream([...primaryStream.getVideoTracks(), ...audioTracks])
243
259
  primaryStream.getVideoTracks()[0]?.addEventListener('ended', () => {
244
260
  if (phase === 'recording') stop()
245
261
  })
246
- mimeUsed = pickMime([
247
- 'video/webm;codecs=vp9,opus',
248
- 'video/webm;codecs=vp8,opus',
249
- 'video/webm'
250
- ])
262
+ mimeUsed = pickMime(
263
+ hasLiveAudio
264
+ ? ['video/webm;codecs=vp9,opus', 'video/webm;codecs=vp8,opus', 'video/webm']
265
+ : ['video/webm;codecs=vp9', 'video/webm;codecs=vp8', 'video/webm']
266
+ )
251
267
  }
252
268
  recordedStream = stream
253
269
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",