Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
chamilo-lms/assets/vue/components/AudioRecorder.vue

223 lines
5.2 KiB

<template>
<div v-if="!microphoneError">
<div class="flex flex-row">
<div
v-if="recorderState.isRecording"
class="flex rounded-md mr-2 mb-2 py-2 px-3 border border-error"
>
<BaseIcon
class="self-center mr-2 text-error motion-safe:animate-pulse"
icon="microphone"
/>
<p class="self-center font-semibold text-error">
{{ recordedTime }}
</p>
</div>
<BaseButton
v-if="showButtons && recorderState.isRecording"
:label="t('Stop recording')"
class="mr-2 mb-2"
icon="stop"
type="danger"
@click="stop"
/>
<BaseButton
v-else-if="showButtons"
:label="t('Start recording')"
class="mr-2 mb-2"
icon="microphone"
type="primary"
@click="record"
/>
</div>
<div v-if="showRecordedAudios">
<div
v-for="(audio, index) in recorderState.audioList"
:key="index"
class="py-2"
>
<audio
class="max-w-full"
controls
>
<source :src="window.URL.createObjectURL(audio)" />
</audio>
<BaseButton
:label="$t('Attach')"
class="my-1"
icon="attachment"
size="small"
type="success"
@click="attachAudio(audio)"
/>
</div>
</div>
</div>
<div v-else>
<p>
{{ microphoneError }}
</p>
</div>
</template>
<script setup>
import BaseButton from "./basecomponents/BaseButton.vue"
import { computed, onMounted, reactive, ref } from "vue"
import { RecordRTCPromisesHandler, StereoAudioRecorder } from "recordrtc"
import { useI18n } from "vue-i18n"
import BaseIcon from "./basecomponents/BaseIcon.vue"
const { t } = useI18n()
const props = defineProps({
multiple: {
type: Boolean,
required: false,
default: true,
},
showButtons: {
type: Boolean,
default: true,
},
showRecordedAudios: {
type: Boolean,
default: true,
},
})
const emit = defineEmits(["attach-audio", "recorded-audio"])
defineExpose({
record,
stop,
})
const recorderState = reactive({
isRecording: false,
audioList: [],
seconds: 0,
minutes: 0,
hours: 0,
})
const microphoneError = ref("")
const recordedTime = computed(() => {
let hours = timeComponentToString(recorderState.hours)
let minutes = timeComponentToString(recorderState.minutes)
let seconds = timeComponentToString(recorderState.seconds)
return `${hours} : ${minutes} : ${seconds}`
})
onMounted(() => {
let isMediaDevicesSupported = navigator.mediaDevices && navigator.mediaDevices.getUserMedia
if (!isMediaDevicesSupported) {
console.warn(
"Either your browser does not support microphone or your are serving your site from not secure " +
"context, check https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia for more information",
)
microphoneError.value = t("We're sorry, your browser does not support using a microphone")
}
})
let recorder = null
async function record() {
if (microphoneError.value) {
return
}
try {
let stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true })
recorder = new RecordRTCPromisesHandler(stream, {
recorderType: StereoAudioRecorder,
type: "audio",
mimeType: "audio/wav",
numberOfAudioChannels: 2,
})
recorder.startRecording()
startTimer()
recorderState.isRecording = true
} catch (error) {
console.warn(
"Either the user denied permission or microphone is not available, " +
"check https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia",
)
if (error.name === "NotAllowedError") {
microphoneError.value = t(
"Permission to use the microphone is not enabled, please enable it in your browser to record audio",
)
} else {
microphoneError.value = t("We're sorry, your browser does not support using a microphone")
}
}
}
async function stop() {
if (!recorder) {
return
}
if (false === props.multiple && recorderState.audioList.length > 0) {
recorderState.audioList.shift()
}
await recorder.stopRecording()
const audioBlob = await recorder.getBlob()
recorderState.audioList.push(audioBlob)
emit("recorded-audio", audioBlob)
recorderState.isRecording = false
stopTimer()
}
function attachAudio(audio) {
emit("attach-audio", audio)
const index = recorderState.audioList.indexOf(audio)
if (index >= 0) {
recorderState.audioList.splice(index, 1)
}
}
let timer = null
function startTimer() {
recorderState.seconds = 0
recorderState.minutes = 0
recorderState.hours = 0
timer = setInterval(() => {
recorderState.seconds = recorderState.seconds + 1
if (recorderState.seconds > 59) {
recorderState.minutes = recorderState.minutes + 1
recorderState.seconds = 0
}
if (recorderState.minutes > 59) {
recorderState.hours = recorderState.hours + 1
recorderState.minutes = 0
}
}, 1000)
}
function stopTimer() {
recorderState.seconds = 0
recorderState.minutes = 0
recorderState.hours = 0
clearInterval(timer)
timer = null
}
function timeComponentToString(value) {
return value.toString().padStart(2, "0")
}
</script>