13 lines
3.5 KiB
JavaScript
13 lines
3.5 KiB
JavaScript
document.querySelectorAll('[data-voice-recorder]').forEach(root=>{
|
|
let recorder,stream,chunks=[],started,timer,pausedAt=0,pausedMs=0,voiceFile;
|
|
const form=root.closest('form'),record=root.querySelector('[data-record]'),pause=root.querySelector('[data-pause]'),stop=root.querySelector('[data-stop]'),remove=root.querySelector('[data-delete]'),audio=root.querySelector('audio'),clock=root.querySelector('[data-timer]');
|
|
const supported=window.isSecureContext&&navigator.mediaDevices?.getUserMedia&&window.MediaRecorder;
|
|
if(!supported){record.disabled=true;record.title='ضبط صدا فقط در HTTPS یا localhost و مرورگر سازگار فعال است';root.insertAdjacentHTML('beforeend','<small class="recorder-error">مرورگر یا اتصال فعلی امکان ضبط امن صدا را ندارد.</small>');return}
|
|
const stopTracks=()=>{stream?.getTracks().forEach(track=>track.stop());stream=null};
|
|
const reset=()=>{if(voiceFile)form.dispatchEvent(new CustomEvent('voice-removed',{detail:voiceFile}));voiceFile=null;stopTracks();if(audio.src)URL.revokeObjectURL(audio.src);audio.removeAttribute('src');audio.hidden=true;remove.hidden=true;clock.textContent='00:00';record.disabled=false;pause.disabled=true;pause.textContent='مکث';stop.disabled=true};
|
|
record.addEventListener('click',async()=>{try{stream=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:true,noiseSuppression:true,autoGainControl:true}});const mime=['audio/webm;codecs=opus','audio/mp4','audio/ogg;codecs=opus','audio/webm'].find(type=>MediaRecorder.isTypeSupported(type));recorder=new MediaRecorder(stream,mime?{mimeType:mime}:undefined);chunks=[];pausedMs=0;recorder.ondataavailable=event=>{if(event.data.size)chunks.push(event.data)};recorder.onerror=()=>{stopTracks();alert('ضبط صدا متوقف شد. دوباره تلاش کنید.')};recorder.onstop=()=>{clearInterval(timer);stopTracks();const type=recorder.mimeType.split(';')[0]||'audio/webm',blob=new Blob(chunks,{type});if(!blob.size)return reset();audio.src=URL.createObjectURL(blob);audio.hidden=false;remove.hidden=false;const ext=type.includes('ogg')?'ogg':type.includes('mp4')?'m4a':'webm';voiceFile=new File([blob],`voice-${Date.now()}.${ext}`,{type});form.dispatchEvent(new CustomEvent('voice-added',{detail:voiceFile}))};recorder.start(1000);started=Date.now();record.disabled=true;pause.disabled=false;stop.disabled=false;timer=setInterval(()=>{const elapsed=Date.now()-started-pausedMs-(recorder.state==='paused'?Date.now()-pausedAt:0),seconds=Math.max(0,Math.floor(elapsed/1000));clock.textContent=`${String(Math.floor(seconds/60)).padStart(2,'0')}:${String(seconds%60).padStart(2,'0')}`},500)}catch(error){stopTracks();const denied=error.name==='NotAllowedError';alert(denied?'دسترسی میکروفن رد شده است. از تنظیمات مرورگر، دسترسی Microphone را برای این سایت فعال کنید.':'میکروفن در دسترس نیست یا توسط برنامه دیگری استفاده میشود.')}});
|
|
pause.addEventListener('click',()=>{if(recorder?.state==='recording'){recorder.pause();pausedAt=Date.now();pause.textContent='ادامه ضبط'}else if(recorder?.state==='paused'){pausedMs+=Date.now()-pausedAt;recorder.resume();pause.textContent='مکث'}});
|
|
stop.addEventListener('click',()=>{if(recorder&&recorder.state!=='inactive')recorder.stop();pause.disabled=true;stop.disabled=true});
|
|
remove.addEventListener('click',reset);window.addEventListener('pagehide',stopTracks);
|
|
});
|