2022-03-25 03:05:20 +08:00
|
|
|
import { PureComponent } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import logger from '/imports/startup/client/logger';
|
2024-01-12 01:46:39 +08:00
|
|
|
import { throttle } from 'radash';
|
2022-03-25 03:05:20 +08:00
|
|
|
import Service from './service';
|
|
|
|
|
2024-01-12 01:46:39 +08:00
|
|
|
const THROTTLE_TIMEOUT = 200;
|
|
|
|
|
2022-03-25 03:05:20 +08:00
|
|
|
class Speech extends PureComponent {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
this.onEnd = this.onEnd.bind(this);
|
2022-03-25 03:05:20 +08:00
|
|
|
this.onError = this.onError.bind(this);
|
|
|
|
this.onResult = this.onResult.bind(this);
|
|
|
|
|
|
|
|
this.result = {
|
2022-04-01 03:40:07 +08:00
|
|
|
id: Service.generateId(),
|
2022-03-25 03:05:20 +08:00
|
|
|
transcript: '',
|
|
|
|
isFinal: true,
|
|
|
|
};
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
this.idle = true;
|
|
|
|
|
2023-12-08 03:23:36 +08:00
|
|
|
this.speechRecognition = Service.initSpeechRecognition(props.setUserSpeechLocale);
|
2022-03-25 03:05:20 +08:00
|
|
|
|
|
|
|
if (this.speechRecognition) {
|
2022-03-29 03:23:46 +08:00
|
|
|
this.speechRecognition.onend = () => this.onEnd();
|
2022-03-25 03:05:20 +08:00
|
|
|
this.speechRecognition.onerror = (event) => this.onError(event);
|
|
|
|
this.speechRecognition.onresult = (event) => this.onResult(event);
|
|
|
|
}
|
2024-01-12 01:46:39 +08:00
|
|
|
|
|
|
|
this.throttledTranscriptUpdate = throttle(
|
|
|
|
{ interval: THROTTLE_TIMEOUT },
|
|
|
|
props.captionSubmitText
|
|
|
|
);
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
const {
|
|
|
|
locale,
|
2022-03-29 03:23:46 +08:00
|
|
|
connected,
|
|
|
|
talking,
|
2022-03-25 03:05:20 +08:00
|
|
|
} = this.props;
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
// Connected
|
|
|
|
if (!prevProps.connected && connected) {
|
2022-03-25 03:05:20 +08:00
|
|
|
this.start(locale);
|
|
|
|
}
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
// Disconnected
|
|
|
|
if (prevProps.connected && !connected) {
|
|
|
|
this.stop();
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Switch locale
|
|
|
|
if (prevProps.locale !== locale) {
|
2022-03-29 03:23:46 +08:00
|
|
|
if (prevProps.connected && connected) {
|
|
|
|
this.stop();
|
2022-03-25 03:05:20 +08:00
|
|
|
this.start(locale);
|
|
|
|
}
|
|
|
|
}
|
2022-03-29 03:23:46 +08:00
|
|
|
|
|
|
|
// Recovery from idle
|
|
|
|
if (!prevProps.talking && talking) {
|
|
|
|
if (prevProps.connected && connected) {
|
|
|
|
if (this.idle) {
|
|
|
|
this.start(locale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2022-03-29 03:23:46 +08:00
|
|
|
this.stop();
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
onEnd() {
|
|
|
|
this.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
onError(event) {
|
|
|
|
this.stop();
|
2022-03-25 03:05:20 +08:00
|
|
|
|
|
|
|
logger.error({
|
|
|
|
logCode: 'captions_speech_recognition',
|
2022-03-29 03:23:46 +08:00
|
|
|
extraInfo: {
|
|
|
|
error: event.error,
|
|
|
|
message: event.message,
|
|
|
|
},
|
2022-03-25 03:05:20 +08:00
|
|
|
}, 'Captions speech recognition error');
|
|
|
|
}
|
|
|
|
|
|
|
|
onResult(event) {
|
|
|
|
const {
|
|
|
|
resultIndex,
|
|
|
|
results,
|
|
|
|
} = event;
|
|
|
|
|
2022-04-01 03:40:07 +08:00
|
|
|
const { id } = this.result;
|
2022-03-25 03:05:20 +08:00
|
|
|
const { transcript } = results[resultIndex][0];
|
|
|
|
const { isFinal } = results[resultIndex];
|
|
|
|
|
|
|
|
this.result.transcript = transcript;
|
|
|
|
this.result.isFinal = isFinal;
|
|
|
|
|
2024-01-12 01:46:39 +08:00
|
|
|
const { locale, captionSubmitText } = this.props;
|
2022-03-25 03:05:20 +08:00
|
|
|
if (isFinal) {
|
2024-01-12 01:46:39 +08:00
|
|
|
captionSubmitText(id, transcript, locale, true);
|
2022-04-01 03:40:07 +08:00
|
|
|
this.result.id = Service.generateId();
|
2022-03-25 03:05:20 +08:00
|
|
|
} else {
|
2024-01-12 01:46:39 +08:00
|
|
|
this.throttledTranscriptUpdate(id, transcript, locale, false);
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
start(locale) {
|
2022-04-12 04:48:01 +08:00
|
|
|
if (this.speechRecognition && Service.isLocaleValid(locale)) {
|
2022-03-25 03:05:20 +08:00
|
|
|
this.speechRecognition.lang = locale;
|
|
|
|
try {
|
2022-04-01 03:40:07 +08:00
|
|
|
this.result.id = Service.generateId();
|
2022-03-25 03:05:20 +08:00
|
|
|
this.speechRecognition.start();
|
2022-03-29 03:23:46 +08:00
|
|
|
this.idle = false;
|
2022-03-25 03:05:20 +08:00
|
|
|
} catch (event) {
|
2022-03-29 03:23:46 +08:00
|
|
|
this.onError(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
this.idle = true;
|
|
|
|
if (this.speechRecognition) {
|
|
|
|
const {
|
|
|
|
isFinal,
|
|
|
|
transcript,
|
|
|
|
} = this.result;
|
|
|
|
|
|
|
|
if (!isFinal) {
|
2022-04-01 03:40:07 +08:00
|
|
|
const { locale } = this.props;
|
|
|
|
const { id } = this.result;
|
|
|
|
Service.updateFinalTranscript(id, transcript, locale);
|
2022-03-29 03:23:46 +08:00
|
|
|
this.speechRecognition.abort();
|
|
|
|
} else {
|
|
|
|
this.speechRecognition.stop();
|
2022-03-25 03:05:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Speech.propTypes = {
|
|
|
|
locale: PropTypes.string.isRequired,
|
2022-03-29 03:23:46 +08:00
|
|
|
connected: PropTypes.bool.isRequired,
|
|
|
|
talking: PropTypes.bool.isRequired,
|
2023-12-08 03:23:36 +08:00
|
|
|
setUserSpeechLocale: PropTypes.func.isRequired,
|
2022-03-25 03:05:20 +08:00
|
|
|
};
|
|
|
|
|
2022-03-29 03:23:46 +08:00
|
|
|
export default Speech;
|