JaBa/helpers/Music/Voice/streamdiscpatcher.js
2022-09-03 18:13:20 +05:00

156 lines
No EOL
4.4 KiB
JavaScript

const { AudioPlayer,
AudioPlayerStatus,
createAudioPlayer,
createAudioResource,
entersState,
StreamType,
VoiceConnection,
VoiceConnectionStatus,
VoiceConnectionDisconnectReason } = require("@discordjs/voice");
const TypedEmitter = require("tiny-typed-emitter");
const { Utilities } = require("../Util/Utilities.js")
class StreamDispatcher extends TypedEmitter {
voiceConnection = null;
audioPlayer = null;
channel = null;
audioResource = null;
readyLock = false;
paused = null;
constructor(voiceConnection, channel, connectionTimeout = 20000) {
super();
this.voiceConnection = voiceConnection;
this.audioPlayer = createAudioPlayer();
this.channel = channel;
this.paused = false;
this.voiceConnection.on("stateChange", async (_, newState) => {
if (newState.status === VoiceConnectionStatus.Disconnected) {
if (
newState.reason === VoiceConnectionDisconnectReason.WebSocketClose &&
newState.closeCode === 4014
) {
try {
await entersState(
this.voiceConnection,
VoiceConnectionStatus.Connecting,
connectionTimeout
);
} catch {
this.voiceConnection.destroy();
}
} else if (this.voiceConnection.rejoinAttempts < 5) {
await Utilities.wait(
(this.voiceConnection.rejoinAttempts + 1) * 5000
);
this.voiceConnection.rejoin();
} else {
this.voiceConnection.destroy();
}
} else if (newState.status === VoiceConnectionStatus.Destroyed) {
this.end();
} else if (
!this.readyLock &&
(newState.status === VoiceConnectionStatus.Connecting ||
newState.status === VoiceConnectionStatus.Signalling)
) {
this.readyLock = true;
try {
await entersState(
this.voiceConnection,
VoiceConnectionStatus.Ready,
connectionTimeout
);
} catch {
if (
this.voiceConnection.state.status !==
VoiceConnectionStatus.Destroyed
)
this.voiceConnection.destroy();
} finally {
this.readyLock = false;
}
}
});
this.audioPlayer.on("stateChange", (oldState, newState) => {
if (newState.status === AudioPlayerStatus.Playing) {
if (!this.paused) return void this.emit("start", this.audioResource);
} else if (
newState.status === AudioPlayerStatus.Idle &&
oldState.status !== AudioPlayerStatus.Idle
) {
if (!this.paused) {
void this.emit("finish", this.audioResource);
this.audioResource = null;
}
}
});
this.audioPlayer.on("debug", (m) => void this.emit("debug", m));
this.audioPlayer.on("error", (error) => void this.emit("error", error));
this.voiceConnection.subscribe(this.audioPlayer);
}
createStream(src, ops = { type?: "", data?: "" }) {
this.audioResource = createAudioResource(src, {
inputType: ops?.type ?? StreamType.Arbitrary,
metadata: ops?.data,
inlineVolume: true,
});
return this.audioResource;
}
status() {
return this.audioPlayer.state.status;
}
disconnect() {
try {
this.audioPlayer.stop(true);
this.voiceConnection.destroy();
} catch { }
}
end() {
this.audioPlayer.stop();
}
pause(interpolateSilence) {
const success = this.audioPlayer.pause(interpolateSilence);
this.paused = success;
return success;
}
resume() {
const success = this.audioPlayer.unpause();
this.paused = !success;
return success;
}
async playStream(resource = this.audioResource) {
if (!resource) throw new PlayerError("Audio resource is not available!", ErrorStatusCode.NO_AUDIO_RESOURCE);
if (resource.ended) return void this.emit("error", new PlayerError("Cannot play a resource that has already ended."));
if (!this.audioResource) this.audioResource = resource;
if (this.voiceConnection.state.status !== VoiceConnectionStatus.Ready) {
try {
await entersState(this.voiceConnection, VoiceConnectionStatus.Ready, this.connectionTimeout);
} catch (err) {
return void this.emit("error", err);
}
}
try {
this.audioPlayer.play(resource);
} catch (e) {
this.emit("error", e);
}
return this;
}
}
module.exports = { StreamDispatcher }