Vendor tested Lesavka observer assets and numeric receipt schema from 6b97ceb1. Verified 65 JS tests, 10 service tests, kustomize render and Kubernetes client dry-run. No media upload, capture activation or changes to the homepage and private OCR.
28 lines
1.2 KiB
JavaScript
28 lines
1.2 KiB
JavaScript
// Independent cleanup attempts: a broken optional recorder or audio node must
|
|
// never keep the camera/microphone open or prevent the remaining owners closing.
|
|
const attempt=operation=>{try{operation();}catch{/* Continue releasing other owners. */}};
|
|
|
|
export function stopTracks(stream) {
|
|
if(!stream)return;
|
|
for(const track of stream.getTracks())attempt(()=>{track.onended=null;track.stop();});
|
|
}
|
|
|
|
export function releaseCapture(run,preview,finishRecording) {
|
|
if(run.released)return;
|
|
run.released=true;
|
|
attempt(()=>stopTracks(run.stream));
|
|
for(const timer of [run.timer,run.setupTimer,run.signalTimer])clearTimeout(timer);
|
|
clearInterval(run.updateTimer);
|
|
if(run.callback!=null)attempt(()=>preview.cancelVideoFrameCallback(run.callback));
|
|
if(run.recording)attempt(finishRecording);
|
|
if(run.node) {
|
|
run.node.onprocessorerror=null;
|
|
run.node.port.onmessage=null;
|
|
attempt(()=>run.node.port.postMessage('stop'));
|
|
attempt(()=>run.node.disconnect());
|
|
}
|
|
attempt(()=>run.source?.disconnect());
|
|
// A rejected or unresolved AudioContext.close never postpones track release.
|
|
attempt(()=>run.context?.close().catch(()=>{}));
|
|
}
|