diff --git a/example/music-bot/index.js b/example/music-bot/index.js index adad69f..d02659d 100644 --- a/example/music-bot/index.js +++ b/example/music-bot/index.js @@ -1,7 +1,6 @@ require("dotenv").config({ path: __dirname+"/.env" }); -require("discord-player/smoothVolume"); const { Client, GuildMember, Intents } = require("discord.js"); const config = require("./config"); const { Player, QueryType, QueueRepeatMode } = require("discord-player"); diff --git a/package.json b/package.json index 142b768..970b8b6 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "require": "./dist/index.js", "import": "./dist/index.mjs" }, - "./smoothVolume": "./dist/smoothVolume.js" + "./smoothVolume": "./dist/smoothVolume.js", + "./src/*": "./dist/*", + "./dist/*": "./dist/*" }, "scripts": { "dev": "cd example/test && ts-node index.ts", @@ -86,7 +88,7 @@ "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "discord-api-types": "^0.24.0", - "discord.js": "^13.3.1", + "discord.js": "^13.6.0", "eslint": "^8.3.0", "gen-esm-wrapper": "^1.1.3", "husky": "^7.0.4", diff --git a/src/Player.ts b/src/Player.ts index 0703894..92d6b20 100644 --- a/src/Player.ts +++ b/src/Player.ts @@ -45,7 +45,7 @@ class Player extends EventEmitter { */ this.client = client; - if (!new Intents(this.client.options.intents).has(Intents.FLAGS.GUILD_VOICE_STATES)) { + if (this.client?.options?.intents && !new Intents(this.client?.options?.intents).has(Intents.FLAGS.GUILD_VOICE_STATES)) { throw new PlayerError('client is missing "GUILD_VOICE_STATES" intent'); } @@ -153,7 +153,7 @@ class Player extends EventEmitter { const _meta = queueInitOptions.metadata; delete queueInitOptions["metadata"]; - queueInitOptions.volumeSmoothness ??= 0.1; + queueInitOptions.volumeSmoothness ??= 0.08; queueInitOptions.ytdlOptions ??= this.options.ytdlOptions; const queue = new Queue(this, guild, queueInitOptions); queue.metadata = _meta; diff --git a/src/Structures/Queue.ts b/src/Structures/Queue.ts index be23ac9..b546e6f 100644 --- a/src/Structures/Queue.ts +++ b/src/Structures/Queue.ts @@ -10,6 +10,7 @@ import YouTube from "youtube-sr"; import AudioFilters from "../utils/AudioFilters"; import { PlayerError, ErrorStatusCode } from "./PlayerError"; import type { Readable } from "stream"; +import { VolumeTransformer } from "../VoiceInterface/VolumeTransformer"; class Queue { public readonly guild: Guild; @@ -154,8 +155,7 @@ class Queue { if (!["GUILD_STAGE_VOICE", "GUILD_VOICE"].includes(_channel?.type)) throw new PlayerError(`Channel type must be GUILD_VOICE or GUILD_STAGE_VOICE, got ${_channel?.type}!`, ErrorStatusCode.INVALID_ARG_TYPE); const connection = await this.player.voiceUtils.connect(_channel, { - deaf: this.options.autoSelfDeaf, - maxTime: this.player.options.connectionTimeout || 20000 + deaf: this.options.autoSelfDeaf }); this.connection = connection; @@ -206,6 +206,10 @@ class Queue { } }); + await this.player.voiceUtils.enterReady(this.connection.voiceConnection, { + maxTime: this.player.options.connectionTimeout || 30_000 + }); + return this; } @@ -703,8 +707,9 @@ class Queue { if (options.seek) this._streamTime = options.seek; this._filtersUpdate = options.filtersUpdate; - if (resource.volume && typeof this.options.volumeSmoothness === "number") { - Reflect.set(resource.volume, "_smoothing", this.options.volumeSmoothness || 0); + const volumeTransformer = resource.volume as VolumeTransformer; + if (volumeTransformer?.hasSmoothness && typeof this.options.volumeSmoothness === "number") { + if (typeof volumeTransformer.setSmoothness === "function") volumeTransformer.setSmoothness(this.options.volumeSmoothness || 0); } this.setVolume(this.options.initialVolume); diff --git a/src/VoiceInterface/VoiceUtils.ts b/src/VoiceInterface/VoiceUtils.ts index 7de4433..f926c0a 100644 --- a/src/VoiceInterface/VoiceUtils.ts +++ b/src/VoiceInterface/VoiceUtils.ts @@ -49,13 +49,17 @@ class VoiceUtils { maxTime?: number; } ) { - let conn = joinVoiceChannel({ + const conn = joinVoiceChannel({ guildId: channel.guild.id, channelId: channel.id, adapterCreator: channel.guild.voiceAdapterCreator as unknown as DiscordGatewayAdapterCreator, selfDeaf: Boolean(options.deaf) }); + return conn; + } + + public async enterReady(conn: VoiceConnection, options: { maxTime?: number } = {}) { try { conn = await entersState(conn, VoiceConnectionStatus.Ready, options?.maxTime ?? 20000); return conn; diff --git a/src/VolumeTransformer.ts b/src/VoiceInterface/VolumeTransformer.ts similarity index 73% rename from src/VolumeTransformer.ts rename to src/VoiceInterface/VolumeTransformer.ts index c619c80..b493dd1 100644 --- a/src/VolumeTransformer.ts +++ b/src/VoiceInterface/VolumeTransformer.ts @@ -16,6 +16,7 @@ export class VolumeTransformer extends Transform { private _chunk: Buffer; public volume: number; private _targetVolume: number; + public type: "s16le" | "s32le" | "s16be" | "s32be"; constructor(options: VolumeTransformerOptions = {}) { super(options); switch (options.type) { @@ -42,9 +43,11 @@ export class VolumeTransformer extends Transform { default: throw new Error("VolumeTransformer type should be one of s16le, s16be, s32le, s32be"); } + this.type = options.type; this._bytes = this._bits / 8; this._extremum = Math.pow(2, this._bits - 1); - this.volume = typeof options.volume === "undefined" ? 1 : options.volume; + this.volume = Number.isNaN(options.volume) ? 1 : Number(options.volume); + if (!Number.isFinite(this.volume)) this.volume = 1; this._targetVolume = this.volume; this._chunk = Buffer.alloc(0); this._smoothing = options.smoothness || 0; @@ -57,14 +60,16 @@ export class VolumeTransformer extends Transform { return index; } - _transform(chunk: Buffer, encoding: BufferEncoding, done: () => unknown) { - if (this._smoothing > 0 && this.volume !== this._targetVolume) { - if (this.volume < this._targetVolume) { - this.volume = this.volume + this._smoothing >= this._targetVolume ? this._targetVolume : this.volume + this._smoothing; - } else if (this.volume > this._targetVolume) { - this.volume = this.volume - this._smoothing <= this._targetVolume ? this._targetVolume : this.volume - this._smoothing; - } + _applySmoothness() { + if (this.volume < this._targetVolume) { + this.volume = this.volume + this._smoothing >= this._targetVolume ? this._targetVolume : this.volume + this._smoothing; + } else if (this.volume > this._targetVolume) { + this.volume = this.volume - this._smoothing <= this._targetVolume ? this._targetVolume : this.volume - this._smoothing; } + } + + _transform(chunk: Buffer, encoding: BufferEncoding, done: () => unknown) { + if (this.smoothingEnabled() && this.volume !== this._targetVolume) this._applySmoothness(); if (this.volume === 1) { this.push(chunk); @@ -94,6 +99,9 @@ export class VolumeTransformer extends Transform { } setVolume(volume: number) { + if (Number.isNaN(volume)) volume = 1; + if (typeof volume !== "number") volume = Number(volume); + if (!Number.isFinite(volume)) volume = volume < 0 ? 0 : 1; this._targetVolume = volume; if (this._smoothing <= 0) this.volume = volume; } @@ -113,4 +121,24 @@ export class VolumeTransformer extends Transform { get volumeLogarithmic() { return Math.pow(this.volume, 1 / 1.660964); } + + get smoothness() { + return this._smoothing; + } + + setSmoothness(smoothness: number) { + this._smoothing = smoothness; + } + + smoothingEnabled() { + return Number.isFinite(this._smoothing) && this._smoothing > 0; + } + + get hasSmoothness() { + return true; + } + + static get hasSmoothing() { + return true; + } } diff --git a/src/index.ts b/src/index.ts index 0e7198f..12a5300 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,6 @@ +// try applying smooth volume patch on load +import "./smoothVolume"; + export { AudioFilters } from "./utils/AudioFilters"; export { ExtractorModel } from "./Structures/ExtractorModel"; export { Playlist } from "./Structures/Playlist"; diff --git a/src/smoothVolume.ts b/src/smoothVolume.ts index 7267a1f..d7c6af7 100644 --- a/src/smoothVolume.ts +++ b/src/smoothVolume.ts @@ -1,4 +1,12 @@ -import { VolumeTransformer } from "./VolumeTransformer"; +import { VolumeTransformer as VolumeTransformerMock } from "./VoiceInterface/VolumeTransformer"; -// eslint-disable-next-line -(require("prism-media") as typeof import("prism-media")).VolumeTransformer = VolumeTransformer; +try { + // eslint-disable-next-line + const mod = require("prism-media") as typeof import("prism-media") & { VolumeTransformer: typeof VolumeTransformerMock }; + + if (typeof mod.VolumeTransformer.hasSmoothing !== "boolean") { + Reflect.set(mod, "VolumeTransformer", VolumeTransformerMock); + } +} catch { + /* do nothing */ +} diff --git a/yarn.lock b/yarn.lock index e05e2cf..100d029 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1990,7 +1990,7 @@ discord-ytdl-core@^5.0.4: dependencies: prism-media "^1.2.9" -discord.js@^13.3.1: +discord.js@^13.6.0: version "13.6.0" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.6.0.tgz#d8a8a591dbf25cbcf9c783d5ddf22c4694860475" integrity sha512-tXNR8zgsEPxPBvGk3AQjJ9ljIIC6/LOPjzKwpwz8Y1Q2X66Vi3ZqFgRHYwnHKC0jC0F+l4LzxlhmOJsBZDNg9g==