diff --git a/src/components/views/audio_messages/AudioPlayer.tsx b/src/components/views/audio_messages/AudioPlayer.tsx index 748b1c9ffc..fb9270765e 100644 --- a/src/components/views/audio_messages/AudioPlayer.tsx +++ b/src/components/views/audio_messages/AudioPlayer.tsx @@ -36,6 +36,7 @@ interface IProps { interface IState { playbackPhase: PlaybackState; + error?: boolean; } @replaceableComponent("views.audio_messages.AudioPlayer") @@ -55,8 +56,10 @@ export default class AudioPlayer extends React.PureComponent { // Don't wait for the promise to complete - it will emit a progress update when it // is done, and it's not meant to take long anyhow. - // noinspection JSIgnoredPromiseFromCall - this.props.playback.prepare(); + this.props.playback.prepare().catch(e => { + console.error("Error processing audio file:", e); + this.setState({ error: true }); + }); } private onPlaybackUpdate = (ev: PlaybackState) => { @@ -91,34 +94,37 @@ export default class AudioPlayer extends React.PureComponent { public render(): ReactNode { // tabIndex=0 to ensure that the whole component becomes a tab stop, where we handle keyboard // events for accessibility - return
-
- -
- - { this.props.mediaName || _t("Unnamed audio") } - -
- -   { /* easiest way to introduce a gap between the components */ } - { this.renderFileSize() } + return <> +
+
+ +
+ + { this.props.mediaName || _t("Unnamed audio") } + +
+ +   { /* easiest way to introduce a gap between the components */ } + { this.renderFileSize() } +
+
+ + +
-
- - -
-
; + { this.state.error &&
{ _t("Error downloading audio") }
} + ; } } diff --git a/src/components/views/audio_messages/RecordingPlayback.tsx b/src/components/views/audio_messages/RecordingPlayback.tsx index 7d9312f369..ca0ed83d84 100644 --- a/src/components/views/audio_messages/RecordingPlayback.tsx +++ b/src/components/views/audio_messages/RecordingPlayback.tsx @@ -22,6 +22,7 @@ import PlaybackClock from "./PlaybackClock"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { TileShape } from "../rooms/EventTile"; import PlaybackWaveform from "./PlaybackWaveform"; +import { _t } from "../../../languageHandler"; interface IProps { // Playback instance to render. Cannot change during component lifecycle: create @@ -33,6 +34,7 @@ interface IProps { interface IState { playbackPhase: PlaybackState; + error?: boolean; } @replaceableComponent("views.audio_messages.RecordingPlayback") @@ -49,8 +51,10 @@ export default class RecordingPlayback extends React.PureComponent { + console.error("Error processing audio file:", e); + this.setState({ error: true }); + }); } private get isWaveformable(): boolean { @@ -65,10 +69,13 @@ export default class RecordingPlayback extends React.PureComponent - - - { this.isWaveformable && } -
; + return <> +
+ + + { this.isWaveformable && } +
+ { this.state.error &&
{ _t("Error downloading audio") }
} + ; } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1116e4cdc1..90b9767f7b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2601,6 +2601,7 @@ "Use email to optionally be discoverable by existing contacts.": "Use email to optionally be discoverable by existing contacts.", "Sign in with SSO": "Sign in with SSO", "Unnamed audio": "Unnamed audio", + "Error downloading audio": "Error downloading audio", "Pause": "Pause", "Play": "Play", "Couldn't load page": "Couldn't load page", diff --git a/src/voice/Playback.ts b/src/voice/Playback.ts index df0bf593fa..cb198e4ff7 100644 --- a/src/voice/Playback.ts +++ b/src/voice/Playback.ts @@ -135,18 +135,23 @@ export class Playback extends EventEmitter implements IDestroyable { // Safari compat: promise API not supported on this function this.audioBuf = await new Promise((resolve, reject) => { this.context.decodeAudioData(this.buf, b => resolve(b), async e => { - // This error handler is largely for Safari as well, which doesn't support Opus/Ogg - // very well. - console.error("Error decoding recording: ", e); - console.warn("Trying to re-encode to WAV instead..."); + try { + // This error handler is largely for Safari as well, which doesn't support Opus/Ogg + // very well. + console.error("Error decoding recording: ", e); + console.warn("Trying to re-encode to WAV instead..."); - const wav = await decodeOgg(this.buf); + const wav = await decodeOgg(this.buf); - // noinspection ES6MissingAwait - not needed when using callbacks - this.context.decodeAudioData(wav, b => resolve(b), e => { - console.error("Still failed to decode recording: ", e); + // noinspection ES6MissingAwait - not needed when using callbacks + this.context.decodeAudioData(wav, b => resolve(b), e => { + console.error("Still failed to decode recording: ", e); + reject(e); + }); + } catch (e) { + console.error("Caught decoding error:", e); reject(e); - }); + } }); });