MediareCorder застрял на iOS в качестве приложения PWA, как очистить все треки?Javascript

Форум по Javascript
Ответить
Anonymous
 MediareCorder застрял на iOS в качестве приложения PWA, как очистить все треки?

Сообщение Anonymous »

Очень странная проблема у меня есть в iOS, когда общая сеть в качестве приложения (PWA) на домашний экран.
всякий раз, когда я использую его через браузер Safari на iPhone, он работает на 100% каждый раз. Однако, когда я помещаю его в качестве приложения на главном экране, в первый раз я открываю его, он работает нормально, когда я закрываю его и снова открываю, он просто не начинает записывать. Я должен перезагрузить телефон, чтобы он работал. Так что это работает один раз, я думаю, каким -то образом он не закончится потоком или что -то в этом роде, но в коде я попробовал все возможные способы закрыть и очистить трек. Пробовал GPT, Claude, Gemini Solutions. Ничего не сработало, это просто работает 1 раз как PWA. Моя последняя надежда - кто -то еще столкнулся с этой проблемой и может попытаться помочь мне?
p.s. Android работает нормально. < /P>

import { ref, computed } from 'vue'
import axios from 'axios'

const captureState = ref('idle') // idle | recording | analyzing
const showReviewModal = ref(false)
const transcript = ref('')
const showTranscript = ref(false)
const tasks = ref([]) // {id, title, selected}
const recordingId = ref(null)
const errorMessage = ref('')
const isSending = ref(false)

const statusText = computed(() => {
switch (captureState.value) {
case 'recording': return 'Recording... Tap to stop'
case 'analyzing': return 'Analyzing your thoughts...'
default: return 'Tap to start recording'
}
})
const selectedCount = computed(() => tasks.value.filter(t => t.selected).length)

let mediaStream = null
let mediaRecorder = null
let chunks = []

async function handleCapture () {
if (captureState.value === 'idle') {
await startRecording()
} else if (captureState.value === 'recording') {
await stopRecording()
}
}

async function startRecording () {
errorMessage.value = ''
try {
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
throw new Error('Audio recording requires HTTPS')
}
if (!navigator.mediaDevices?.getUserMedia) {
throw new Error('Audio recording not supported in this browser')
}

mediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
},
video: false
})

const mimeType = getSupportedAudioType()
mediaRecorder = new MediaRecorder(mediaStream, { mimeType })
chunks = []

mediaRecorder.ondataavailable = (e) => {
if (e.data && e.data.size > 0) chunks.push(e.data)
}

mediaRecorder.onstop = async () => {
try {
const blob = new Blob(chunks, { type: mimeType })
if (!blob || blob.size === 0) throw new Error('No audio data recorded')

captureState.value = 'analyzing'
await sendRecording(blob)
} catch (err) {
errorMessage.value = err?.message || String(err)
captureState.value = 'idle'
} finally {
cleanupMedia()
}
}

mediaRecorder.start()
captureState.value = 'recording'
} catch (err) {
errorMessage.value = err?.message || String(err)
cleanupMedia()
captureState.value = 'idle'
}
}

async function stopRecording () {
try {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop()
} else {
cleanupMedia()
captureState.value = 'idle'
}
} catch (err) {
errorMessage.value = 'Error stopping recording: ' + (err?.message || String(err))
cleanupMedia()
captureState.value = 'idle'
}
}

function cleanupMedia () {
try {
mediaRecorder?.stop?.()
} catch {}
mediaRecorder = null
chunks = []

if (mediaStream) {
try {
mediaStream.getTracks().forEach(t => {
try { t.stop() } catch {}
})
} catch {}
}
mediaStream = null
}

function getSupportedAudioType () {
const types = [
'audio/webm;codecs=opus',
'audio/webm',
'audio/ogg;codecs=opus',
'audio/ogg',
'audio/mp4',
'audio/mpeg'
]
for (const t of types) {
if (MediaRecorder.isTypeSupported?.(t)) return t
}
return ''
}

async function sendRecording (blob) {
if (!blob) return
isSending.value = true
try {
const fileName = pickFileNameFromType(blob.type)
const formData = new FormData()
const file = new File([blob], fileName, { type: blob.type || 'application/octet-stream' })
formData.append('audio', file)

const res = await axios.post('/recordings', formData)
recordingId.value = res.data.recording_id
transcript.value = res.data.transcript
showTranscript.value = false
tasks.value = (res.data.tasks || []).map(t => ({ ...t, selected: true }))
showReviewModal.value = true
} catch (e) {
errorMessage.value = e?.response?.data?.message || e?.message || 'Upload failed'
} finally {
isSending.value = false
captureState.value = 'idle'
}
}

function pickFileNameFromType (type) {
if (type.includes('webm')) return 'recording.webm'
if (type.includes('ogg')) return 'recording.ogg'
if (type.includes('mp4') || type.includes('mpeg')) return 'recording.m4a'
return 'recording.bin'
}

async function addTasks () {
if (!recordingId.value) return
isSending.value = true
try {
await axios.post(`/recordings/${recordingId.value}/tasks`, { tasks: tasks.value })
window.location.href = '/tasks'
} catch (e) {
errorMessage.value = e.response?.data?.message || e.message
} finally {
isSending.value = false
}
}

function resetRecording () {
showReviewModal.value = false
transcript.value = ''
tasks.value = []
recordingId.value = null
errorMessage.value = ''
showTranscript.value = false
}

document.addEventListener('visibilitychange', () => {
if (document.hidden && captureState.value === 'recording') {
stopRecording()
}
})
window.addEventListener('pagehide', () => {
if (captureState.value === 'recording') {
stopRecording()
} else {
cleanupMedia()
}
})



Подробнее здесь: https://stackoverflow.com/questions/797 ... all-tracks
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»