feat: implement media echo fix and YouTube screenshare design
- Introduced a new `ScreenShareController` to manage YouTube screenshare functionality. - Updated `DiscordPlayer` to track ownership of audio streams, preventing conflicts between music playback and screenshare. - Added error handling for various states including voice connection checks and media busy states. - Created unit tests for `ScreenShareController` and `DiscordPlayer` ownership rules to ensure correct functionality. - Added documentation for the new media echo fix and screenshare design.
This commit is contained in:
@@ -7,10 +7,12 @@ import {
|
||||
StreamType,
|
||||
VoiceConnection,
|
||||
} from "@discordjs/voice";
|
||||
import type { DiscordPlayerOwner } from "./media/mediaTypes";
|
||||
|
||||
export class DiscordPlayer {
|
||||
private player: AudioPlayer;
|
||||
private connection: VoiceConnection | null = null;
|
||||
private owner: DiscordPlayerOwner = "none";
|
||||
|
||||
constructor() {
|
||||
this.player = createAudioPlayer();
|
||||
@@ -21,6 +23,7 @@ export class DiscordPlayer {
|
||||
|
||||
this.player.on("error", (error) => {
|
||||
console.error(`[player] Error: ${error.message}`);
|
||||
this.owner = "none";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,17 +32,28 @@ export class DiscordPlayer {
|
||||
this.connection.subscribe(this.player);
|
||||
}
|
||||
|
||||
public getOwner(): DiscordPlayerOwner {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public isConnected(): boolean {
|
||||
return this.connection !== null;
|
||||
}
|
||||
|
||||
public playStream(stream: Readable) {
|
||||
console.log("[player] Starting new audio stream...");
|
||||
public playStream(stream: Readable, owner: DiscordPlayerOwner) {
|
||||
if (owner === "none") {
|
||||
throw new Error("Discord audio player owner is required");
|
||||
}
|
||||
this.assertOwnerAvailable(owner);
|
||||
|
||||
const resource = createAudioResource(stream, {
|
||||
inputType: StreamType.OggOpus,
|
||||
});
|
||||
|
||||
if (this.owner === owner) {
|
||||
this.player.stop();
|
||||
}
|
||||
this.owner = owner;
|
||||
this.player.play(resource);
|
||||
this.connection?.subscribe(this.player);
|
||||
}
|
||||
@@ -48,16 +62,30 @@ export class DiscordPlayer {
|
||||
return this.player.state.status;
|
||||
}
|
||||
|
||||
public pause() {
|
||||
public pause(owner?: DiscordPlayerOwner) {
|
||||
if (!this.canControl(owner)) return;
|
||||
this.player.pause(true);
|
||||
}
|
||||
|
||||
public unpause(): boolean {
|
||||
public unpause(owner?: DiscordPlayerOwner): boolean {
|
||||
if (!this.canControl(owner)) return false;
|
||||
return this.player.unpause();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
public stop(owner?: DiscordPlayerOwner) {
|
||||
if (!this.canControl(owner)) return;
|
||||
this.player.stop();
|
||||
this.owner = "none";
|
||||
}
|
||||
|
||||
private assertOwnerAvailable(owner: DiscordPlayerOwner): void {
|
||||
if (this.owner !== "none" && this.owner !== owner) {
|
||||
throw new Error(`Discord audio player is owned by ${this.owner}`);
|
||||
}
|
||||
}
|
||||
|
||||
private canControl(owner?: DiscordPlayerOwner): boolean {
|
||||
return !owner || this.owner === "none" || this.owner === owner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user