From 1fcf26ff358097dc50240b5b99aa6d77695d9783 Mon Sep 17 00:00:00 2001 From: DevAndromeda <46562212+DevAndromeda@users.noreply.github.com> Date: Sat, 23 Jul 2022 11:21:03 +0545 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=83=20Remove=20discord-ytdl-core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - src/Structures/Queue.ts | 77 +++++++++++++++------------------------ src/index.ts | 1 + src/utils/FFmpegStream.ts | 59 ++++++++++++++++++++++++++++++ src/utils/Util.ts | 8 +++- yarn.lock | 12 ------ 6 files changed, 96 insertions(+), 62 deletions(-) create mode 100644 src/utils/FFmpegStream.ts diff --git a/package.json b/package.json index ecd2ff3..3f654be 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "homepage": "https://discord-player.js.org", "dependencies": { "@discordjs/voice": "^0.11.0", - "discord-ytdl-core": "^5.0.4", "libsodium-wrappers": "^0.7.10", "soundcloud-scraper": "^5.0.3", "spotify-url-info": "^3.1.2", diff --git a/src/Structures/Queue.ts b/src/Structures/Queue.ts index 53602bf..b6dcccf 100644 --- a/src/Structures/Queue.ts +++ b/src/Structures/Queue.ts @@ -3,7 +3,7 @@ import { Player } from "../Player"; import { StreamDispatcher } from "../VoiceInterface/StreamDispatcher"; import Track from "./Track"; import { PlayerOptions, PlayerProgressbarOptions, PlayOptions, QueueFilters, QueueRepeatMode, TrackSource } from "../types/types"; -import ytdl from "discord-ytdl-core"; +import ytdl from "ytdl-core"; import { AudioResource, StreamType } from "@discordjs/voice"; import { Util } from "../utils/Util"; import YouTube from "youtube-sr"; @@ -11,6 +11,7 @@ import AudioFilters from "../utils/AudioFilters"; import { PlayerError, ErrorStatusCode } from "./PlayerError"; import type { Readable } from "stream"; import { VolumeTransformer } from "../VoiceInterface/VolumeTransformer"; +import { createFFmpegStream } from "../utils/FFmpegStream"; class Queue { public readonly guild: Guild; @@ -635,66 +636,46 @@ class Queue { } let stream = null; - const customDownloader = typeof this.onBeforeCreateStream === "function"; + const hasCustomDownloader = typeof this.onBeforeCreateStream === "function"; if (["youtube", "spotify"].includes(track.raw.source)) { let spotifyResolved = false; if (this.options.spotifyBridge && track.raw.source === "spotify" && !track.raw.engine) { track.raw.engine = await YouTube.search(`${track.author} ${track.title}`, { type: "video" }) - .then((x) => x[0].url) + .then((res) => res[0].url) .catch(() => null); spotifyResolved = true; } - const link = track.raw.source === "spotify" ? track.raw.engine : track.url; - if (!link) return void this.play(this.tracks.shift(), { immediate: true }); - if (customDownloader) { - stream = (await this.onBeforeCreateStream(track, spotifyResolved ? "youtube" : track.raw.source, this)) ?? null; - if (stream) - stream = ytdl - .arbitraryStream(stream, { - opusEncoded: false, - fmt: "s16le", - encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [], - seek: options.seek ? options.seek / 1000 : 0 - }) - .on("error", (err) => { - return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err); - }); - } else { - stream = ytdl(link, { - ...this.options.ytdlOptions, - // discord-ytdl-core - opusEncoded: false, - fmt: "s16le", - encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [], - seek: options.seek ? options.seek / 1000 : 0 - }).on("error", (err) => { - return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err); - }); + const url = track.raw.source === "spotify" ? track.raw.engine : track.url; + if (!url) return void this.play(this.tracks.shift(), { immediate: true }); + + if (hasCustomDownloader) { + stream = (await this.onBeforeCreateStream(track, spotifyResolved ? "youtube" : track.raw.source, this)) || null; + } + + if (!stream) { + stream = ytdl(url, this.options.ytdlOptions); } } else { - const tryArb = (customDownloader && (await this.onBeforeCreateStream(track, track.raw.source || track.raw.engine, this))) || null; - const arbitrarySource = tryArb - ? tryArb - : track.raw.source === "soundcloud" - ? await track.raw.engine.downloadProgressive() - : typeof track.raw.engine === "function" - ? await track.raw.engine() - : track.raw.engine; - stream = ytdl - .arbitraryStream(arbitrarySource, { - opusEncoded: false, - fmt: "s16le", - encoderArgs: options.encoderArgs ?? this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [], - seek: options.seek ? options.seek / 1000 : 0 - }) - .on("error", (err) => { - return err.message.toLowerCase().includes("premature close") ? null : this.player.emit("error", this, err); - }); + const arbitraryStream = (hasCustomDownloader && (await this.onBeforeCreateStream(track, track.raw.source || track.raw.engine, this))) || null; + stream = + arbitraryStream || track.raw.source === "soundcloud" + ? await track.raw.engine.downloadProgressive() + : typeof track.raw.engine === "function" + ? await track.raw.engine + : track.raw.engine; } - const resource: AudioResource = this.connection.createStream(stream, { + const ffmpegStream = createFFmpegStream(stream, { + encoderArgs: options.encoderArgs || this._activeFilters.length ? ["-af", AudioFilters.create(this._activeFilters)] : [], + seek: options.seek ? options.seek / 1000 : 0, + fmt: "s16le" + }).on("error", (err) => { + if (!`${err}`.toLowerCase().includes("premature close")) this.player.emit("error", this, err); + }); + + const resource: AudioResource = this.connection.createStream(ffmpegStream, { type: StreamType.Raw, data: track, disableVolume: Boolean(this.options.disableVolume) diff --git a/src/index.ts b/src/index.ts index 12a5300..c3c1d1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,7 @@ export { VoiceUtils } from "./VoiceInterface/VoiceUtils"; export { VoiceEvents, StreamDispatcher } from "./VoiceInterface/StreamDispatcher"; export { Util } from "./utils/Util"; export * from "./types/types"; +export * from "./utils/FFmpegStream"; // eslint-disable-next-line @typescript-eslint/no-var-requires export const version: string = require(`${__dirname}/../package.json`).version; diff --git a/src/utils/FFmpegStream.ts b/src/utils/FFmpegStream.ts new file mode 100644 index 0000000..9f49b28 --- /dev/null +++ b/src/utils/FFmpegStream.ts @@ -0,0 +1,59 @@ +import { FFmpeg } from "prism-media"; +import type { Duplex, Readable } from "stream"; + +export interface FFmpegStreamOptions { + fmt?: string; + encoderArgs?: string[]; + seek?: number; + skip?: boolean; +} + +export function FFMPEG_ARGS_STRING(stream: string, fmt?: string) { + // prettier-ignore + return [ + "-reconnect", "1", + "-reconnect_streamed", "1", + "-reconnect_delay_max", "5", + "-i", stream, + "-analyzeduration", "0", + "-loglevel", "0", + "-f", `${typeof fmt === "string" ? fmt : "s16le"}`, + "-ar", "48000", + "-ac", "2" + ]; +} + +export function FFMPEG_ARGS_PIPED(fmt?: string) { + // prettier-ignore + return [ + "-analyzeduration", "0", + "-loglevel", "0", + "-f", `${typeof fmt === "string" ? fmt : "s16le"}`, + "-ar", "48000", + "-ac", "2" + ]; +} + +/** + * Creates FFmpeg stream + * @param stream The source stream + * @param options FFmpeg stream options + */ +export function createFFmpegStream(stream: Readable | Duplex | string, options?: FFmpegStreamOptions) { + if (options.skip && typeof stream !== "string") return stream; + options ??= {}; + const args = typeof stream === "string" ? FFMPEG_ARGS_STRING(stream, options.fmt) : FFMPEG_ARGS_PIPED(options.fmt); + + if (!Number.isNaN(options.seek)) args.unshift("-ss", String(options.seek)); + if (Array.isArray(options.encoderArgs)) args.push(...options.encoderArgs); + + const transcoder = new FFmpeg({ shell: false, args }); + transcoder.on("close", () => transcoder.destroy()); + + if (typeof stream !== "string") { + stream.on("error", () => transcoder.destroy()); + stream.pipe(transcoder); + } + + return transcoder; +} diff --git a/src/utils/Util.ts b/src/utils/Util.ts index d976a12..f173fee 100644 --- a/src/utils/Util.ts +++ b/src/utils/Util.ts @@ -102,7 +102,13 @@ class Util { try { return await import(lib).then((res) => res.fetch || res.default?.fetch || res.default); } catch { - // uh? + try { + // eslint-disable-next-line + const res = require(lib); + if (res) return res.fetch || res.default?.fetch || res.default; + } catch { + // no? + } } } } diff --git a/yarn.lock b/yarn.lock index 046b5d6..9ccfb53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -566,13 +566,6 @@ discord-api-types@^0.36.2: resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.36.2.tgz#2362bc544837be965ec99a5919f900c9699a7028" integrity sha512-TunPAvzwneK/m5fr4hxH3bMsrtI22nr9yjfHyo5NBGMjpsAauGNiGCmwoFf0oO3jSd2mZiKUvZwCKDaB166u2Q== -discord-ytdl-core@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/discord-ytdl-core/-/discord-ytdl-core-5.0.4.tgz#84a2af1a8e8c235b4fc109b23350d12782ab66cd" - integrity sha512-O+G9wuCw5TERR9iHZFMYnYQbs/ZGudDc9cxa1OKSV5TdcBYrHGS1JqvE90ZvSQ6SmS4XvqtOf/Okls6yiGJ3sg== - dependencies: - prism-media "^1.2.9" - discord.js@^14.0.2: version "14.0.2" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.0.2.tgz#c327582d893c6b3e3e1c8fb36e656f51a3008400" @@ -1249,11 +1242,6 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== -prism-media@^1.2.9: - version "1.3.2" - resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b" - integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g== - prism-media@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.4.tgz#7951f26a9186b791dc8c820ff07310ec46a8a5f1"