Handle broadcast chunk errors (#9970)
* Use strings for broadcast playback states * Handle broadcast decode errors
This commit is contained in:
parent
60edb85a1a
commit
533b250bb6
10 changed files with 483 additions and 275 deletions
|
@ -43,12 +43,14 @@ import {
|
|||
import { RelationsHelper, RelationsHelperEvent } from "../../events/RelationsHelper";
|
||||
import { VoiceBroadcastChunkEvents } from "../utils/VoiceBroadcastChunkEvents";
|
||||
import { determineVoiceBroadcastLiveness } from "../utils/determineVoiceBroadcastLiveness";
|
||||
import { _t } from "../../languageHandler";
|
||||
|
||||
export enum VoiceBroadcastPlaybackState {
|
||||
Paused,
|
||||
Playing,
|
||||
Stopped,
|
||||
Buffering,
|
||||
Paused = "pause",
|
||||
Playing = "playing",
|
||||
Stopped = "stopped",
|
||||
Buffering = "buffering",
|
||||
Error = "error",
|
||||
}
|
||||
|
||||
export enum VoiceBroadcastPlaybackEvent {
|
||||
|
@ -205,12 +207,24 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
};
|
||||
|
||||
private async tryLoadPlayback(chunkEvent: MatrixEvent): Promise<void> {
|
||||
try {
|
||||
return await this.loadPlayback(chunkEvent);
|
||||
} catch (err) {
|
||||
logger.warn("Unable to load broadcast playback", {
|
||||
message: err.message,
|
||||
broadcastId: this.infoEvent.getId(),
|
||||
chunkId: chunkEvent.getId(),
|
||||
});
|
||||
this.setError();
|
||||
}
|
||||
}
|
||||
|
||||
private async loadPlayback(chunkEvent: MatrixEvent): Promise<void> {
|
||||
const eventId = chunkEvent.getId();
|
||||
|
||||
if (!eventId) {
|
||||
logger.warn("got voice broadcast chunk event without ID", this.infoEvent, chunkEvent);
|
||||
return;
|
||||
throw new Error("Broadcast chunk event without Id occurred");
|
||||
}
|
||||
|
||||
const helper = new MediaEventHelper(chunkEvent);
|
||||
|
@ -311,16 +325,28 @@ export class VoiceBroadcastPlayback
|
|||
private async playEvent(event: MatrixEvent): Promise<void> {
|
||||
this.setState(VoiceBroadcastPlaybackState.Playing);
|
||||
this.currentlyPlaying = event;
|
||||
const playback = await this.getOrLoadPlaybackForEvent(event);
|
||||
const playback = await this.tryGetOrLoadPlaybackForEvent(event);
|
||||
playback?.play();
|
||||
}
|
||||
|
||||
private async tryGetOrLoadPlaybackForEvent(event: MatrixEvent): Promise<Playback | undefined> {
|
||||
try {
|
||||
return await this.getOrLoadPlaybackForEvent(event);
|
||||
} catch (err) {
|
||||
logger.warn("Unable to load broadcast playback", {
|
||||
message: err.message,
|
||||
broadcastId: this.infoEvent.getId(),
|
||||
chunkId: event.getId(),
|
||||
});
|
||||
this.setError();
|
||||
}
|
||||
}
|
||||
|
||||
private async getOrLoadPlaybackForEvent(event: MatrixEvent): Promise<Playback | undefined> {
|
||||
const eventId = event.getId();
|
||||
|
||||
if (!eventId) {
|
||||
logger.warn("event without id occurred");
|
||||
return;
|
||||
throw new Error("Broadcast chunk event without Id occurred");
|
||||
}
|
||||
|
||||
if (!this.playbacks.has(eventId)) {
|
||||
|
@ -330,13 +356,12 @@ export class VoiceBroadcastPlayback
|
|||
const playback = this.playbacks.get(eventId);
|
||||
|
||||
if (!playback) {
|
||||
// logging error, because this should not happen
|
||||
logger.warn("unable to find playback for event", event);
|
||||
throw new Error(`Unable to find playback for event ${event.getId()}`);
|
||||
}
|
||||
|
||||
// try to load the playback for the next event for a smooth(er) playback
|
||||
const nextEvent = this.chunkEvents.getNext(event);
|
||||
if (nextEvent) this.loadPlayback(nextEvent);
|
||||
if (nextEvent) this.tryLoadPlayback(nextEvent);
|
||||
|
||||
return playback;
|
||||
}
|
||||
|
@ -405,8 +430,8 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
|
||||
const currentPlayback = this.getCurrentPlayback();
|
||||
const skipToPlayback = await this.tryGetOrLoadPlaybackForEvent(event);
|
||||
const currentPlaybackEvent = this.currentlyPlaying;
|
||||
const skipToPlayback = await this.getOrLoadPlaybackForEvent(event);
|
||||
|
||||
if (!skipToPlayback) {
|
||||
logger.warn("voice broadcast chunk to skip to not found", event);
|
||||
|
@ -464,6 +489,9 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
|
||||
public stop(): void {
|
||||
// error is a final state
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Error) return;
|
||||
|
||||
this.setState(VoiceBroadcastPlaybackState.Stopped);
|
||||
this.getCurrentPlayback()?.stop();
|
||||
this.currentlyPlaying = null;
|
||||
|
@ -471,6 +499,9 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
|
||||
public pause(): void {
|
||||
// error is a final state
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Error) return;
|
||||
|
||||
// stopped voice broadcasts cannot be paused
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Stopped) return;
|
||||
|
||||
|
@ -479,6 +510,9 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
|
||||
public resume(): void {
|
||||
// error is a final state
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Error) return;
|
||||
|
||||
if (!this.currentlyPlaying) {
|
||||
// no playback to resume, start from the beginning
|
||||
this.start();
|
||||
|
@ -496,6 +530,9 @@ export class VoiceBroadcastPlayback
|
|||
* paused → playing
|
||||
*/
|
||||
public async toggle(): Promise<void> {
|
||||
// error is a final state
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Error) return;
|
||||
|
||||
if (this.state === VoiceBroadcastPlaybackState.Stopped) {
|
||||
await this.start();
|
||||
return;
|
||||
|
@ -514,6 +551,9 @@ export class VoiceBroadcastPlayback
|
|||
}
|
||||
|
||||
private setState(state: VoiceBroadcastPlaybackState): void {
|
||||
// error is a final state
|
||||
if (this.getState() === VoiceBroadcastPlaybackState.Error) return;
|
||||
|
||||
if (this.state === state) {
|
||||
return;
|
||||
}
|
||||
|
@ -522,6 +562,16 @@ export class VoiceBroadcastPlayback
|
|||
this.emit(VoiceBroadcastPlaybackEvent.StateChanged, state, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set error state. Stop current playback, if any.
|
||||
*/
|
||||
private setError(): void {
|
||||
this.setState(VoiceBroadcastPlaybackState.Error);
|
||||
this.getCurrentPlayback()?.stop();
|
||||
this.currentlyPlaying = null;
|
||||
this.setPosition(0);
|
||||
}
|
||||
|
||||
public getInfoState(): VoiceBroadcastInfoState {
|
||||
return this.infoState;
|
||||
}
|
||||
|
@ -536,6 +586,10 @@ export class VoiceBroadcastPlayback
|
|||
this.setLiveness(determineVoiceBroadcastLiveness(this.infoState));
|
||||
}
|
||||
|
||||
public get errorMessage(): string {
|
||||
return this.getState() === VoiceBroadcastPlaybackState.Error ? _t("Unable to play this voice broadcast") : "";
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.chunkRelationHelper.destroy();
|
||||
this.infoRelationHelper.destroy();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue