Skip to content

Commit

Permalink
chore(Release): 3.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
skick1234 committed Dec 28, 2021
2 parents db93616 + 24100ad commit 24bad9f
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 55 deletions.
42 changes: 22 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "distube",
"version": "3.0.4",
"version": "3.0.5",
"description": "A Discord.js module to simplify your music commands and play songs with audio filters on Discord without any API key. Support YouTube, SoundCloud, Bandcamp, Facebook, and 700+ more sites",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -46,36 +46,38 @@
"homepage": "https://distube.js.org/",
"dependencies": {
"@distube/youtube-dl": "^2.2.4",
"@distube/ytdl-core": "^4.9.3",
"@distube/ytdl-core": "^4.9.4",
"@distube/ytpl": "^1.1.1",
"@distube/ytsr": "^1.1.5",
"prism-media": "https://codeload.github.com/distubejs/prism-media/tar.gz/main",
"tiny-typed-emitter": "^2.1.0"
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/plugin-proposal-class-properties": "^7.16.0",
"@babel/plugin-proposal-object-rest-spread": "^7.16.0",
"@babel/preset-env": "^7.16.0",
"@babel/preset-typescript": "^7.16.0",
"@commitlint/cli": "^14.1.0",
"@commitlint/config-conventional": "^14.1.0",
"@discordjs/voice": "^0.7.2",
"@babel/core": "^7.16.5",
"@babel/plugin-proposal-class-properties": "^7.16.5",
"@babel/plugin-proposal-object-rest-spread": "^7.16.5",
"@babel/preset-env": "^7.16.5",
"@babel/preset-typescript": "^7.16.5",
"@commitlint/cli": "^16.0.1",
"@commitlint/config-conventional": "^16.0.0",
"@discordjs/voice": "^0.7.5",
"@distube/docgen": "github:distubejs/docgen",
"@types/jest": "^27.0.2",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.7",
"babel-jest": "^27.3.1",
"discord.js": "^13.3.1",
"eslint": "^7.32.0",
"eslint-config-distube": "^1.4.0",
"@typescript-eslint/eslint-plugin": "^5.8.1",
"babel-jest": "^27.4.5",
"discord.js": "^13.4.0",
"eslint": "^8.5.0",
"eslint-config-distube": "^1.5.0",
"eslint-plugin-jsdoc": "^37.4.0",
"husky": "^7.0.4",
"jest": "^27.3.1",
"jest": "^27.4.5",
"jsdoc-babel": "^0.5.0",
"lint-staged": "^11.2.6",
"npm-check-updates": "^12.0.2",
"lint-staged": "^12.1.4",
"npm-check-updates": "^12.0.5",
"pinst": "^2.1.6",
"prettier": "^2.4.1",
"typescript": "^4.4.4"
"prettier": "^2.5.1",
"typescript": "^4.5.4"
},
"peerDependencies": {
"@discordjs/opus": "*",
Expand Down
2 changes: 2 additions & 0 deletions src/__test__/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ client.user = new ClientUser(client, rawClientUser);
const guild = new Guild(client, rawGuild);
const textChannel = guild.channels.cache.get("737499503384461325") as TextChannel;
const voiceChannel = guild.channels.cache.get("853225781604646933") as VoiceChannel;
Object.defineProperty(voiceChannel, "joinable", { value: true, writable: false });
const stageChannel = guild.channels.cache.get("835876864458489857") as StageChannel;
Object.defineProperty(stageChannel, "joinable", { value: false, writable: false });
const botVoiceState = new VoiceState(guild, rawBotVoiceState);
const userVoiceState = new VoiceState(guild, rawUserVoiceState);
const message = new Message(client, rawMessage);
Expand Down
11 changes: 3 additions & 8 deletions src/core/voice/DisTubeVoice.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { TypedEmitter } from "tiny-typed-emitter";
import { DisTubeError, createDiscordJSAdapter, isSupportedVoiceChannel } from "../..";
import { DisTubeError, createDiscordJSAdapter, entersState, isSupportedVoiceChannel } from "../..";
import {
AudioPlayerStatus,
VoiceConnectionDisconnectReason,
VoiceConnectionStatus,
createAudioPlayer,
createAudioResource,
entersState,
joinVoiceChannel,
} from "@discordjs/voice";
import type { DisTubeStream, DisTubeVoiceEvents, DisTubeVoiceManager } from "../..";
Expand Down Expand Up @@ -125,16 +124,12 @@ export class DisTubeVoice extends TypedEmitter<DisTubeVoiceEvents> {
* @param {Error} [error] Optional, an error to emit with 'error' event.
*/
leave(error?: Error) {
this.stop();
this.stop(true);
if (!this.isDisconnected) {
this.emit("disconnect", error);
this.isDisconnected = true;
}
entersState(this.audioPlayer, AudioPlayerStatus.Idle, (this.audioResource?.silencePaddingFrames ?? 5) * 20)
.catch(() => this.stop(true))
.finally(() => {
if (this.connection.state.status !== VoiceConnectionStatus.Destroyed) this.connection.destroy();
});
if (this.connection.state.status !== VoiceConnectionStatus.Destroyed) this.connection.destroy();
this.voices.delete(this.id);
}
/**
Expand Down
36 changes: 10 additions & 26 deletions src/core/voice/__test__/DisTubeVoice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ jest.mock("@discordjs/voice");
const Util = _Util as unknown as jest.Mocked<typeof _Util>;
const DiscordVoice = _DiscordVoice as unknown as jest.Mocked<typeof _DiscordVoice>;

const flushPromises = () => new Promise(jest.requireActual("timers").setImmediate);

const voiceManager = {
add: jest.fn(),
delete: jest.fn(),
Expand Down Expand Up @@ -128,7 +126,7 @@ describe("Constructor", () => {
(voice.emit as jest.Mock).mockClear();
expect(connection.on).nthCalledWith(1, DiscordVoice.VoiceConnectionStatus.Disconnected, expect.any(Function));
const catchFn = jest.fn();
DiscordVoice.entersState.mockReturnValue({ catch: catchFn } as any);
Util.entersState.mockReturnValue({ catch: catchFn } as any);
connection.on.mock.calls[0][1](
{},
{ reason: DiscordVoice.VoiceConnectionDisconnectReason.WebSocketClose, closeCode: 4014 },
Expand Down Expand Up @@ -241,17 +239,17 @@ describe("Methods", () => {
const TIMEOUT = 30e3;

test("Timeout when signalling connection", async () => {
DiscordVoice.entersState.mockRejectedValue(undefined);
Util.entersState.mockRejectedValue(undefined);
await expect(voice.join()).rejects.toThrow(new DisTubeError("VOICE_CONNECT_FAILED", TIMEOUT / 1e3));
expect(DiscordVoice.entersState).toBeCalledWith(connection, DiscordVoice.VoiceConnectionStatus.Ready, TIMEOUT);
expect(Util.entersState).toBeCalledWith(connection, DiscordVoice.VoiceConnectionStatus.Ready, TIMEOUT);
expect(connection.destroy).toBeCalledTimes(1);
expect(voiceManager.delete).toBeCalledWith(voice.id);
});

test("Timeout when connection destroyed", async () => {
const newVC = { guild: { id: 2 } };
Util.isSupportedVoiceChannel.mockReturnValue(true);
DiscordVoice.entersState.mockRejectedValue(undefined);
Util.entersState.mockRejectedValue(undefined);
connection.state.status = DiscordVoice.VoiceConnectionStatus.Destroyed;
await expect(voice.join(newVC as any)).rejects.toThrow(new DisTubeError("VOICE_CONNECT_FAILED", TIMEOUT / 1e3));
expect(voice.channel).toBe(newVC);
Expand All @@ -262,9 +260,9 @@ describe("Methods", () => {

test("Joined a voice channel", async () => {
Util.isSupportedVoiceChannel.mockReturnValue(true);
DiscordVoice.entersState.mockResolvedValue(undefined);
Util.entersState.mockResolvedValue(undefined);
await expect(voice.join(voiceChannel as any)).resolves.toBe(voice);
expect(DiscordVoice.entersState).toBeCalledWith(connection, DiscordVoice.VoiceConnectionStatus.Ready, TIMEOUT);
expect(Util.entersState).toBeCalledWith(connection, DiscordVoice.VoiceConnectionStatus.Ready, TIMEOUT);
expect(connection.destroy).not.toBeCalled();
expect(voiceManager.delete).not.toBeCalled();
expect(voice.channel).toBe(voiceChannel);
Expand All @@ -274,35 +272,21 @@ describe("Methods", () => {

describe("DisTubeVoice#leave()", () => {
describe("Destroy the connection", () => {
test("Without error", async () => {
DiscordVoice.entersState.mockResolvedValueOnce(voice.audioPlayer);
test("Without error", () => {
expect(voice.leave()).toBeUndefined();
await flushPromises();
expect(audioPlayer.stop).toBeCalledTimes(1);
expect(audioPlayer.stop).toBeCalledWith(true);
expect(connection.destroy).toBeCalledTimes(1);
expect(voice.emit).toBeCalledWith("disconnect", undefined);
expect(voiceManager.delete).toBeCalledWith(voice.id);
});
test("With error", async () => {
DiscordVoice.entersState.mockRejectedValueOnce(undefined);
voice.isDisconnected = false;
const err: any = {};
expect(voice.leave(err)).toBeUndefined();
await flushPromises();
expect(audioPlayer.stop).toBeCalledTimes(2);
expect(audioPlayer.stop).nthCalledWith(2, true);
expect(connection.destroy).toBeCalledTimes(1);
expect(voice.emit).toBeCalledWith("disconnect", err);
expect(voiceManager.delete).toBeCalledWith(voice.id);
});
});

test("Leave the destroyed connection", async () => {
DiscordVoice.entersState.mockResolvedValueOnce(voice.audioPlayer);
test("Leave the destroyed connection", () => {
connection.state.status = DiscordVoice.VoiceConnectionStatus.Destroyed;
expect(voice.leave()).toBeUndefined();
await flushPromises();
expect(audioPlayer.stop).toBeCalledTimes(1);
expect(audioPlayer.stop).toBeCalledWith(true);
expect(voice.emit).not.toBeCalled();
expect(connection.destroy).not.toBeCalled();
expect(voiceManager.delete).toBeCalledWith(voice.id);
Expand Down
32 changes: 31 additions & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { URL } from "url";
import { DisTubeError, DisTubeVoice, Queue } from ".";
import { Intents, SnowflakeUtil } from "discord.js";
import type { GuildIDResolvable } from ".";
import type { EventEmitter } from "node:events";
import type { AudioPlayer, AudioPlayerStatus, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice";
import type {
BitFieldResolvable,
Client,
Expand Down Expand Up @@ -146,7 +148,7 @@ export function isMessageInstance(message: any): message is Message {
export function isSupportedVoiceChannel(channel: any): channel is VoiceChannel | StageChannel {
return (
!!channel &&
channel.deleted === false &&
typeof channel.joinable === "boolean" &&
isSnowflake(channel.id) &&
isSnowflake(channel.guild?.id) &&
typeof channel.full === "boolean" &&
Expand Down Expand Up @@ -194,3 +196,31 @@ export function checkInvalidKey(
const invalidKey = Object.keys(target).find(key => !sourceKeys.includes(key));
if (invalidKey) throw new DisTubeError("INVALID_KEY", sourceName, invalidKey);
}

async function waitEvent(target: EventEmitter, status: string, maxTime: number) {
let cleanup = () => undefined as any;
try {
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error(`Didn't trigger ${status} within ${maxTime}ms`)), maxTime);
target.once(status, resolve);
target.once("error", reject);
cleanup = () => {
clearTimeout(timeout);
target.off(status, resolve);
target.off("error", reject);
};
});
return target;
} finally {
cleanup();
}
}

export async function entersState<T extends VoiceConnection | AudioPlayer>(
target: T,
status: T extends VoiceConnection ? VoiceConnectionStatus : AudioPlayerStatus,
maxTime: number,
) {
if (target.state.status === status) return target;
return waitEvent(target, status, maxTime) as Promise<T>;
}

0 comments on commit 24bad9f

Please sign in to comment.