From 06893e6b8ec3a1dd1936bc78e32a9c762d947225 Mon Sep 17 00:00:00 2001 From: hzmi Date: Mon, 13 Jul 2020 05:26:15 +0700 Subject: [PATCH] chore(release): Release version 2.7.2 (#96) * chore(deps): bump winston from 3.3.2 to 3.3.3 Bumps [winston](https://github.com/winstonjs/winston) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/winstonjs/winston/releases) - [Changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md) - [Commits](https://github.com/winstonjs/winston/compare/v3.3.2...v3.3.3) Signed-off-by: dependabot[bot] * chore(deps-dev): bump eslint from 7.3.0 to 7.3.1 Bumps [eslint](https://github.com/eslint/eslint) from 7.3.0 to 7.3.1. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v7.3.0...v7.3.1) Signed-off-by: dependabot[bot] * chore(deps-dev): bump @types/node from 14.0.13 to 14.0.14 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.0.13 to 14.0.14. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot[bot] * chore(deps): Update ytdl-core to 3.1.2 * chore(docker): Cleanup Dockerfile * chore(docker): Cleanup Dockerfile * fix(HelpCommand): fix grammar * fix: Pass token from build function instead * feat(docker): Add volumes for cache * feat(caching): Some improvements * Add song length limiter for cache * Fix #81 by using a finish marker * feat: Make song titles clickable by using Markdown link Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .env.example | 3 +- .env.schema | 3 +- .gitignore | 2 +- Dockerfile | 23 +---- app.json | 3 + docker-compose.yml | 17 +-- package.json | 8 +- src/bot.ts | 6 +- src/commands/HelpCommand.ts | 2 +- src/commands/NowPlayingCommand.ts | 2 +- src/commands/PlayCommand.ts | 16 +-- src/commands/QueueCommand.ts | 2 +- src/commands/SkipCommand.ts | 2 +- src/config.ts | 3 +- src/structures/Jukebox.ts | 14 +-- src/utils/YoutubeDownload.ts | 39 ++++--- yarn.lock | 166 +++++++++++++++++------------- 17 files changed, 159 insertions(+), 152 deletions(-) diff --git a/.env.example b/.env.example index 77ad7e99..9c311a18 100644 --- a/.env.example +++ b/.env.example @@ -11,4 +11,5 @@ CONFIG_MAX_VOLUME=100 # Max volume allowed for music (default: 100) CONFIG_DEFAULT_VOLUME=50 # Default volume for music (default: 50) CONFIG_ALLOW_DUPLICATE=no # Whether to to all allow users to add duplicate of songs (yes or no) (default: no) CONFIG_DELETE_QUEUE_TIMEOUT=180 # Timeout in seconds before queue is deleted when no one is in the voice channel (default: 180 <3 minutes>) -CONFIG_CACHE_YOUTUBE_DOWNLOADS=no # (EXPERIMENTAL) Whether to download and cache musics from youtube (yes or no) (default: no) \ No newline at end of file +CONFIG_CACHE_YOUTUBE_DOWNLOADS=no # (EXPERIMENTAL) Whether to download and cache musics from youtube (yes or no) (default: no) +CONFIG_CACHE_MAX_LENGTH=5400 # (EXPERIMENTAL) Max song length in seconds that are allowed to cached (default: 5400 <1 hour 30 minutes>) \ No newline at end of file diff --git a/.env.schema b/.env.schema index 3a198aae..80b19ddb 100644 --- a/.env.schema +++ b/.env.schema @@ -9,4 +9,5 @@ CONFIG_MAX_VOLUME= CONFIG_DEFAULT_VOLUME= CONFIG_ALLOW_DUPLICATE= CONFIG_DELETE_QUEUE_TIMEOUT= -CONFIG_CACHE_YOUTUBE_DOWNLOADS= \ No newline at end of file +CONFIG_CACHE_YOUTUBE_DOWNLOADS= +CONFIG_CACHE_MAX_LENGTH= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5c384c3a..9cb2601d 100644 --- a/.gitignore +++ b/.gitignore @@ -100,4 +100,4 @@ dist .tern-port # YTDL Caches -*.webm \ No newline at end of file +cache/** \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 86300642..9157deea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,28 +6,11 @@ LABEL maintainer "Hazmi35 " WORKDIR /usr/Jukebox COPY . . -RUN echo [INFO] Starting to build Docker image... \ -&& echo [INFO] Installing build deps... \ -&& apk add --no-cache --virtual .build-deps python g++ make git curl \ -&& echo [INFO] Build deps installed! \ -&& echo [INFO] Installing 3rd party packages... \ +RUN apk add --no-cache --virtual .build-deps python3 build-base git curl \ && apk add --no-cache --virtual .third-party ffmpeg \ -&& echo [INFO] 3rd party packages installed! \ -&& echo [INFO] Node version: $(node --version) \ -&& echo [INFO] npm version: $(npm --version) \ -&& echo [INFO] Yarn version: $(yarn --version) \ -&& echo [INFO] Git version: $(git --version) \ -&& echo [INFO] Installing npm packages... \ && yarn install \ -&& echo [INFO] All npm packages installed! \ -&& echo [INFO] Everything looks okay. \ -&& echo [INFO] Building TypeScript project... \ -&& echo Using TypeScript version: $(node -p "require('typescript').version") \ -&& npm run build \ -&& echo [INFO] Done building TypeScript project! \ -&& echo [INFO] Pruning devDependencies... \ +&& yarn run build \ && yarn install --production \ -&& apk del .build-deps \ -&& echo [INFO] Done! Starting bot with node +&& apk del .build-deps CMD ["node", "dist/main.js"] \ No newline at end of file diff --git a/app.json b/app.json index 6f8a32a7..0561cc5f 100644 --- a/app.json +++ b/app.json @@ -35,6 +35,9 @@ }, "CONFIG_CACHE_YOUTUBE_DOWNLOADS": { "description": "(EXPERIMENTAL) Whether to download and cache musics from youtube (yes or no) (default: no)" + }, + "CONFIG_CACHE_MAX_LENGTH": { + "description": "(EXPERIMENTAL) Max song length in seconds that are allowed to cached (default: 5400 <1 hour 30 minutes>)" } }, "repository": "https://github.com/Hazmi35/jukebox", diff --git a/docker-compose.yml b/docker-compose.yml index 975f22dc..2ca49b02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,12 @@ -version: '2.4' - -services: - bot: - container_name: "jukebox-bot" - build: +services: + bot: + build: context: ./ dockerfile: Dockerfile - restart: unless-stopped - env_file: + container_name: jukebox-bot + env_file: - .env + restart: unless-stopped + volumes: + - "./cache:/usr/Jukebox/cache" +version: "2.4" \ No newline at end of file diff --git a/package.json b/package.json index fac6185a..66931e8b 100644 --- a/package.json +++ b/package.json @@ -29,14 +29,14 @@ "html-entities": "^1.3.1", "opusscript": "^0.0.7", "simple-youtube-api": "^5.2.1", - "winston": "^3.3.2", - "ytdl-core": "^2.1.7" + "winston": "^3.3.3", + "ytdl-core": "^3.1.2" }, "devDependencies": { - "@types/node": "^14.0.13", + "@types/node": "^14.0.14", "@typescript-eslint/eslint-plugin": "^3.4.0", "@typescript-eslint/parser": "^3.4.0", - "eslint": "^7.3.0", + "eslint": "^7.3.1", "rimraf": "^3.0.2", "ts-node": "^8.10.2", "typescript": "^3.9.5" diff --git a/src/bot.ts b/src/bot.ts index 35d31638..11e4088e 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,9 +1,7 @@ import "dotenv/config"; import Client from "./structures/Jukebox"; -const client = new Client({ disableMentions: "everyone", messageCacheMaxSize: Infinity, messageCacheLifetime: 540, messageSweepInterval: 180 }) - .setToken(process.env.DISCORD_TOKEN!); - +const client = new Client({ disableMentions: "everyone", messageCacheMaxSize: Infinity, messageCacheLifetime: 540, messageSweepInterval: 180 }); process.on("unhandledRejection", (e) => { client.log.error("UNHANDLED_REJECTION: ", e); @@ -15,4 +13,4 @@ process.on("uncaughtException", (e) => { process.exit(1); }); -client.build(); +client.build(process.env.DISCORD_TOKEN!); diff --git a/src/commands/HelpCommand.ts b/src/commands/HelpCommand.ts index d5c3902c..6d99239f 100644 --- a/src/commands/HelpCommand.ts +++ b/src/commands/HelpCommand.ts @@ -20,7 +20,7 @@ export default class PlayCommand extends BaseCommand { .setThumbnail(message.client.user!.displayAvatarURL()) .setDescription(message.client.commandsHandler.commands.map(c => `\`${c.help.name}\``).join(" ")) .setTimestamp() - .setFooter(`Use ${message.client.config.prefix}help to get more info on specific command!`, "https://hzmi.xyz/assets/images/390511462361202688.png")); + .setFooter(`Use ${message.client.config.prefix}help to get more info on a specific command!`, "https://hzmi.xyz/assets/images/390511462361202688.png")); } else { message.channel.send(new MessageEmbed() .setTitle(`Help for ${command.help.name} command`) diff --git a/src/commands/NowPlayingCommand.ts b/src/commands/NowPlayingCommand.ts index 50de4377..93a6c8b5 100644 --- a/src/commands/NowPlayingCommand.ts +++ b/src/commands/NowPlayingCommand.ts @@ -16,6 +16,6 @@ export default class NowPlayingCommand extends BaseCommand { public execute(message: IMessage): any { if (!message.guild!.queue) return message.channel.send(new MessageEmbed().setDescription("There is nothing playing.").setColor("#FFFF00")); - return message.channel.send(new MessageEmbed().setDescription(`▶ Now playing: **${message.guild!.queue.songs.first()!.title}**`).setColor("#00FF00")); + return message.channel.send(new MessageEmbed().setDescription(`▶ Now playing: **[${message.guild!.queue.songs.first()!.title}](${message.guild!.queue.songs.first()!.url})**`).setColor("#00FF00")); } } \ No newline at end of file diff --git a/src/commands/PlayCommand.ts b/src/commands/PlayCommand.ts index 9b279c54..388396d3 100644 --- a/src/commands/PlayCommand.ts +++ b/src/commands/PlayCommand.ts @@ -41,7 +41,7 @@ export default class PlayCommand extends BaseCommand { const playlist = await this.client.youtube.getPlaylist(url); const videos = await playlist.getVideos(); let skikppedVideos = 0; - message.channel.send(new MessageEmbed().setDescription(`Adding all videos in playlist: **${playlist.title}**, Hang on...`).setColor("#00FF00")); + message.channel.send(new MessageEmbed().setDescription(`Adding all videos in playlist: **[${playlist.title}](${playlist.url})**, Hang on...`).setColor("#00FF00")); for (const video of Object.values(videos)) { if ((video as any).raw.status.privacyStatus === "private") { skikppedVideos++; @@ -55,7 +55,7 @@ export default class PlayCommand extends BaseCommand { new MessageEmbed() .setDescription(`${skikppedVideos >= 2 ? `${skikppedVideos} videos` : `${skikppedVideos} video`} are skipped because it's a private video`) .setColor("#FFFF00")); - return message.channel.send(new MessageEmbed().setDescription(`All videos in playlist: **${playlist.title}**, has been added to the queue!`).setColor("#00FF00")); + return message.channel.send(new MessageEmbed().setDescription(`All videos in playlist: **[${playlist.title}](${playlist.url})**, has been added to the queue!`).setColor("#00FF00")); } else { try { // eslint-disable-next-line no-var @@ -67,7 +67,7 @@ export default class PlayCommand extends BaseCommand { let index = 0; const msg = await message.channel.send(new MessageEmbed() .setAuthor("Song Selection") // TODO: Find or create typings for simple-youtube-api or wait for v6 released - .setDescription(`${videos.map((video: any) => `**${++index} -** ${this.cleanTitle(video.title)}`).join("\n")}\n` + + .setDescription(`${videos.map((video: any) => `**${++index} -** ${this.cleanTitle(video.title)}${video.url}`).join("\n")}\n` + "*Type `cancel` or `c` to cancel song selection*") .setThumbnail(message.client.user!.displayAvatarURL()) .setColor("#00FF00") @@ -134,11 +134,11 @@ export default class PlayCommand extends BaseCommand { if (!this.client.config.allowDuplicate && message.guild!.queue.songs.find(s => s.id === song.id)) return message.channel.send(new MessageEmbed() .setTitle("Already queued.") .setColor("#FFFF00") - .setDescription(`Song: ${song.title} is already queued, and this bot configuration disallow duplicated song in queue, ` + .setDescription(`Song: **[${song.title}](${song.id})** is already queued, and this bot configuration disallow duplicated song in queue, ` + `please use \`${this.client.config.prefix}repeat\` instead`)); message.guild!.queue.songs.addSong(song); if (playlist) return; - return message.channel.send(new MessageEmbed().setDescription(`✅ Song **${song.title}** has been added to the queue`).setColor("#00FF00")); + return message.channel.send(new MessageEmbed().setDescription(`✅ Song **[${song.title}](${song.id})** has been added to the queue`).setColor("#00FF00")); } return message; } @@ -154,7 +154,7 @@ export default class PlayCommand extends BaseCommand { } serverQueue.connection!.voice.setSelfDeaf(true); - const SongData = await ytdl(song.url, { cache: this.client.config.cacheYoutubeDownloads }); + const SongData = await ytdl(song.url, { cache: this.client.config.cacheYoutubeDownloads, cacheMaxLength: this.client.config.cacheMaxLengthAllowed }); if (SongData.cache) this.client.log.info(`${this.client.shard ? `[Shard #${this.client.shard.ids}]` : ""} Using cache for song "${song.title}" on ${guild.name}`); @@ -165,12 +165,12 @@ export default class PlayCommand extends BaseCommand { }).on("start", () => { serverQueue.playing = true; this.client.log.info(`${this.client.shard ? `[Shard #${this.client.shard.ids}]` : ""} Song: "${song.title}" on ${guild.name} started`); - serverQueue.textChannel!.send(new MessageEmbed().setDescription(`▶ Start playing: **${song.title}**`).setColor("#00FF00")); + serverQueue.textChannel!.send(new MessageEmbed().setDescription(`▶ Start playing: **[${song.title}](${song.url})**`).setColor("#00FF00")); }).on("finish", () => { this.client.log.info(`${this.client.shard ? `[Shard #${this.client.shard.ids}]` : ""} Song: "${song.title}" on ${guild.name} ended`); if (serverQueue.loopMode === 0) serverQueue.songs.deleteFirst(); else if (serverQueue.loopMode === 2) { serverQueue.songs.deleteFirst(); serverQueue.songs.addSong(song); } - serverQueue.textChannel!.send(new MessageEmbed().setDescription(`⏹ Stop playing: **${song.title}**`).setColor("#00FF00")); + serverQueue.textChannel!.send(new MessageEmbed().setDescription(`⏹ Stop playing: **[${song.title}](${song.url})**`).setColor("#00FF00")); this.play(guild).catch(e => { serverQueue.textChannel!.send(new MessageEmbed().setDescription(`Error while trying to play music:\n\`${e}\``).setColor("#FF0000")); serverQueue.connection!.dispatcher.end(); diff --git a/src/commands/QueueCommand.ts b/src/commands/QueueCommand.ts index 7fd33582..f33e8389 100644 --- a/src/commands/QueueCommand.ts +++ b/src/commands/QueueCommand.ts @@ -16,7 +16,7 @@ export default class VolumeCommand extends BaseCommand { else { const embed = new MessageEmbed().setTitle("**Song Queue**").setColor("#00FF00").setThumbnail(message.client.user!.avatarURL()!); let num = 1; - const songs = message.guild!.queue.songs.map(s => `**${num++}.** **${s.title}**`); + const songs = message.guild!.queue.songs.map(s => `**${num++}.** [**${s.title}**](${s.url})`); if (message.guild!.queue.songs.size > 12) { const indexes: string[] = this.chunk(songs, 12); let index = 0; diff --git a/src/commands/SkipCommand.ts b/src/commands/SkipCommand.ts index 5ff8682c..e330b834 100644 --- a/src/commands/SkipCommand.ts +++ b/src/commands/SkipCommand.ts @@ -17,7 +17,7 @@ export default class PlayCommand extends BaseCommand { if (message.member!.voice.channel.id !== message.guild!.queue.voiceChannel!.id) return message.channel.send( new MessageEmbed().setDescription("❗ You need to be in the same voice channel as mine").setColor("#FF0000")); - message.channel.send(new MessageEmbed().setDescription(`⏭ Skipped ${message.guild!.queue.songs.first()!.title}`).setColor("#00FF00")); + message.channel.send(new MessageEmbed().setDescription(`⏭ Skipped [**${message.guild!.queue.songs.first()!.title}**](${message.guild!.queue.songs.first()!.url}})`).setColor("#00FF00")); message.guild!.queue.connection!.dispatcher.end(); } } \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 39ae5f1e..1d9634fe 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,5 +7,6 @@ export const maxVolume = Number(process.env.CONFIG_MAX_VOLUME) || 100; export const allowDuplicate: boolean = process.env.CONFIG_ALLOW_DUPLICATE! === "yes" || false; export const deleteQueueTimeout: number = Number(process.env.CONFIG_DELETE_QUEUE_TIMEOUT) * 1000 || 180 * 1000; export const cacheYoutubeDownloads: boolean = process.env.CONFIG_CACHE_YOUTUBE_DOWNLOADS! === "yes" || false; +export const cacheMaxLengthAllowed = Number(process.env.CONFIG_CACHE_MAX_LENGTH) || 5400; -export default { name, prefix, owners, totalShards, defaultVolume, maxVolume, allowDuplicate, deleteQueueTimeout, cacheYoutubeDownloads }; \ No newline at end of file +export default { name, prefix, owners, totalShards, defaultVolume, maxVolume, allowDuplicate, deleteQueueTimeout, cacheYoutubeDownloads, cacheMaxLengthAllowed }; \ No newline at end of file diff --git a/src/structures/Jukebox.ts b/src/structures/Jukebox.ts index e52057cd..b0042b94 100644 --- a/src/structures/Jukebox.ts +++ b/src/structures/Jukebox.ts @@ -13,7 +13,6 @@ import YouTube from "simple-youtube-api"; import "./Guild"; export default class Jukebox extends Client { - private _token = "n/a"; readonly config = config; readonly log = new LogWrapper(config.name).logger; readonly youtube = new YouTube(process.env.YT_API_KEY!, { cache: false, fetchAll: true }); @@ -21,24 +20,15 @@ export default class Jukebox extends Client { readonly eventsLaoder = new ClientEventsLoader(this, resolve(__dirname, "..", "events")); constructor(opt: ClientOptions) { super(opt); } - public async build(): Promise { + public async build(token: string): Promise { // NOTE: Will be removed when caching is not a experimental feature anymore if (this.config.cacheYoutubeDownloads) this.log.warn(this.constructor.name, { stack: "cacheYoutubeDownloads is still a experimental feature"}); this.on("ready", () => this.commandsHandler.load()); this.eventsLaoder.load(); - await this.login(this.getToken()); + await this.login(token); return this; } - public setToken(token: string): Jukebox { - this._token = token; - return this; - } - - public getToken(): string { - return this._token; - } - public async getGuildsCount(): Promise { if (!this.shard) return this.guilds.cache.size; const size = await this.shard.broadcastEval("this.guilds.cache.size"); diff --git a/src/utils/YoutubeDownload.ts b/src/utils/YoutubeDownload.ts index bdf13979..55e58715 100644 --- a/src/utils/YoutubeDownload.ts +++ b/src/utils/YoutubeDownload.ts @@ -1,27 +1,26 @@ import { getInfo, downloadFromInfo, videoInfo, downloadOptions, videoFormat } from "ytdl-core"; import { Readable, PassThrough } from "stream"; import { resolve as resolvePath } from "path"; -import { createReadStream, createWriteStream, existsSync } from "fs"; +import { createReadStream, createWriteStream, existsSync, appendFileSync } from "fs"; // Inspired by ytdl-core-discord (https://github.com/amishshah/ytdl-core-discord) // 1048576 * 1 = 1MB export default function playSong(YoutubeLink: string, options: IdownloadOptions = { filter: "audio", quality: "highestaudio", highWaterMark: 1048576 * 32 }): Promise { return new Promise((resolve, reject) => { - getInfo(YoutubeLink, (err, info) => { - if (err) return reject(err); - const canDemux: boolean = info.formats.find(filter)! && Number(info.length_seconds) != 0; + getInfo(YoutubeLink).then((info) => { + const canDemux: boolean = info.formats.find(filter)! && Number(info.videoDetails.lengthSeconds) != 0; options = canDemux ? { ...options, filter } : { ...options }; - if (options.cache && info.formats.find(f => !f.live)) { - const path = resolvePath(process.cwd(), "cache", `${info.video_id}.webm`); - if (existsSync(resolvePath(path))) return resolve({ canDemux, info, stream: createReadStream(path), cache: true }); - const data = downloadFromInfo(info, options); - const stream = new PassThrough(); - const cache = createWriteStream(path); - data.on("data", (chunk) => { stream.write(chunk); cache.write(chunk); } ); - data.on("end", () => { stream.end(); cache.end(); }); - return resolve({ canDemux, info, stream, cache: false }); + if (options.cache && info.formats.find(f => !f.live) && !(Number(info.videoDetails.lengthSeconds) >= options.cacheMaxLength!)) { + const path = resolvePath(process.cwd(), "cache"); + const filePath = resolvePath(path, `${info.videoDetails.videoId}.webm`); + const finishMarkerPath = `${filePath}.jukeboxCacheFinish.marker`; + if (existsSync(resolvePath(filePath))) { + if (existsSync(resolvePath(finishMarkerPath))) return resolve({ canDemux, info, stream: createReadStream(filePath), cache: true }); + return cache(info, options, canDemux, filePath, finishMarkerPath, resolve); + } + return cache(info, options, canDemux, filePath, finishMarkerPath, resolve); } return resolve({ canDemux, info, stream: downloadFromInfo(info, options), cache: false }); - }); + }).catch(reject); }); } @@ -29,6 +28,16 @@ function filter(f: videoFormat): boolean { return f.codecs === "opus" && f.container === "webm" && Number(f.audioSampleRate) === 48000; } +function cache(info: videoInfo, options: IdownloadOptions, canDemux: boolean, path: string, finishMarkerPath: string, resolve: any,): any { + const data = downloadFromInfo(info, options); + const stream = new PassThrough(); + const cache = createWriteStream(path) + .on("close", () => appendFileSync(finishMarkerPath, "")); + data.on("data", (chunk) => { stream.write(chunk); cache.write(chunk); } ); + data.on("end", () => { stream.end(); cache.end(); }); + return resolve({ canDemux, info, stream, cache: false }); +} + interface ISongData { canDemux: boolean; info: videoInfo; @@ -36,4 +45,4 @@ interface ISongData { cache: boolean; } -interface IdownloadOptions extends downloadOptions { cache?: boolean } \ No newline at end of file +interface IdownloadOptions extends downloadOptions { cache?: boolean; cacheMaxLength?: number } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 78228c3a..f33315fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,23 +3,23 @@ "@babel/code-frame@^7.0.0": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.3.tgz#324bcfd8d35cd3d47dae18cde63d752086435e9a" - integrity sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg== + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.10.3" + "@babel/highlight" "^7.10.4" -"@babel/helper-validator-identifier@^7.10.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15" - integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/highlight@^7.10.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.3.tgz#c633bb34adf07c5c13156692f5922c81ec53f28d" - integrity sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw== +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.10.3" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" @@ -85,56 +85,71 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== -"@types/node@^14.0.13": - version "14.0.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9" - integrity sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA== +"@types/node@^14.0.14": + version "14.0.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.20.tgz#0da05cddbc761e1fa98af88a17244c8c1ff37231" + integrity sha512-MRn/NP3dee8yL5QhbSA6riuwkS+UOcsPUMOIOG3KMUQpuor/2TopdRBu8QaaB4fGU+gz/bzyDWt0FtUbeJ8H1A== "@typescript-eslint/eslint-plugin@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.4.0.tgz#8378062e6be8a1d049259bdbcf27ce5dfbeee62b" - integrity sha512-wfkpiqaEVhZIuQRmudDszc01jC/YR7gMSxa6ulhggAe/Hs0KVIuo9wzvFiDbG3JD5pRFQoqnf4m7REDsUvBnMQ== + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz#ba2b6cae478b8fca3f2e58ff1313e4198eea2d8a" + integrity sha512-ubHlHVt1lsPQB/CZdEov9XuOFhNG9YRC//kuiS1cMQI6Bs1SsqKrEmZnpgRwthGR09/kEDtr9MywlqXyyYd8GA== dependencies: - "@typescript-eslint/experimental-utils" "3.4.0" + "@typescript-eslint/experimental-utils" "3.6.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.4.0.tgz#8a44dfc6fb7f1d071937b390fe27608ebda122b8" - integrity sha512-rHPOjL43lOH1Opte4+dhC0a/+ks+8gOBwxXnyrZ/K4OTAChpSjP76fbI8Cglj7V5GouwVAGaK+xVwzqTyE/TPw== +"@typescript-eslint/experimental-utils@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.0.tgz#0138152d66e3e53a6340f606793fb257bf2d76a1" + integrity sha512-4Vdf2hvYMUnTdkCNZu+yYlFtL2v+N2R7JOynIOkFbPjf9o9wQvRwRkzUdWlFd2YiiUwJLbuuLnl5civNg5ykOQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "3.4.0" + "@typescript-eslint/types" "3.6.0" + "@typescript-eslint/typescript-estree" "3.6.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.4.0.tgz#fe52b68c5cb3bba3f5d875bd17adb70420d49d8d" - integrity sha512-ZUGI/de44L5x87uX5zM14UYcbn79HSXUR+kzcqU42gH0AgpdB/TjuJy3m4ezI7Q/jk3wTQd755mxSDLhQP79KA== + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.6.0.tgz#79b5232e1a2d06f1fc745942b690cd87aca7b60e" + integrity sha512-taghDxuLhbDAD1U5Fk8vF+MnR0yiFE9Z3v2/bYScFb0N1I9SK8eKHkdJl1DAD48OGFDMFTeOTX0z7g0W6SYUXw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.4.0" - "@typescript-eslint/typescript-estree" "3.4.0" + "@typescript-eslint/experimental-utils" "3.6.0" + "@typescript-eslint/types" "3.6.0" + "@typescript-eslint/typescript-estree" "3.6.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.4.0.tgz#6a787eb70b48969e4cd1ea67b057083f96dfee29" - integrity sha512-zKwLiybtt4uJb4mkG5q2t6+W7BuYx2IISiDNV+IY68VfoGwErDx/RfVI7SWL4gnZ2t1A1ytQQwZ+YOJbHHJ2rw== +"@typescript-eslint/types@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.6.0.tgz#4bd6eee55d2f9d35a4b36c4804be1880bf68f7bc" + integrity sha512-JwVj74ohUSt0ZPG+LZ7hb95fW8DFOqBuR6gE7qzq55KDI3BepqsCtHfBIoa0+Xi1AI7fq5nCu2VQL8z4eYftqg== + +"@typescript-eslint/typescript-estree@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.0.tgz#9b4cab43f1192b64ff51530815b8919f166ce177" + integrity sha512-G57NDSABHjvob7zVV09ehWyD1K6/YUKjz5+AufObFyjNO4DVmKejj47MHjVHHlZZKgmpJD2yyH9lfCXHrPITFg== dependencies: + "@typescript-eslint/types" "3.6.0" + "@typescript-eslint/visitor-keys" "3.6.0" debug "^4.1.1" - eslint-visitor-keys "^1.1.0" glob "^7.1.6" is-glob "^4.0.1" lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/visitor-keys@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.0.tgz#44185eb0cc47651034faa95c5e2e8b314ecebb26" + integrity sha512-p1izllL2Ubwunite0ITjubuMQRBGgjdVYwyG7lXPX8GbrA6qF0uwSRz9MnXZaHMxID4948gX0Ez8v9tUDi/KfQ== + dependencies: + eslint-visitor-keys "^1.1.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -158,19 +173,19 @@ acorn@^7.2.0: integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== ajv@^6.10.0, ajv@^6.10.2: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + version "6.12.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" + integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-colors@^3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-regex@^2.0.0: version "2.1.1" @@ -469,11 +484,11 @@ enabled@2.0.x: integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== enquirer@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381" - integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA== + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: - ansi-colors "^3.2.1" + ansi-colors "^4.1.1" escape-string-regexp@^1.0.5: version "1.0.5" @@ -500,10 +515,10 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.3.0.tgz#f9f1fc3dc1227985d0db88769f2bbac7b4b875d7" - integrity sha512-dJMVXwfU5PT1cj2Nv2VPPrKahKTGdX+5Dh0Q3YuKt+Y2UhdL2YbzsVaBMyG9HC0tBismlv/r1+eZqs6SMIV38Q== +eslint@^7.3.1: + version "7.4.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.4.0.tgz#4e35a2697e6c1972f9d6ef2b690ad319f80f206f" + integrity sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -866,9 +881,9 @@ libsodium@0.7.6: integrity sha512-hPb/04sEuLcTRdWDtd+xH3RXBihpmbPCsKW/Jtf4PsvdyKh+D6z2D2gvp/5BfoxseP+0FCOg66kE+0oGUE/loQ== lodash@^4.17.14, lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== logform@^2.2.0: version "2.2.0" @@ -906,11 +921,16 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -miniget@^1.6.1, miniget@^1.7.2: +miniget@^1.6.1: version "1.7.2" resolved "https://registry.yarnpkg.com/miniget/-/miniget-1.7.2.tgz#f95eff50b78d798dddee2460c4aa3c72db1aa23a" integrity sha512-USPNNK2bnHLOplX8BZVMehUkyQizS/DFpBdoH0TS+fM+hQoLNg9tWg4MeY9wE8gfY0pbzmx5UBEODujt3Lz8AA== +miniget@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/miniget/-/miniget-2.0.1.tgz#e2188573317ad8239bab33f056aae64804fc8e47" + integrity sha512-MX+QfVIPAutz6c+T7WKuFKtjcw0nOyRRh1ubhTDD+z/e/pKcSAsfAV63aQKUgb1MFRT1GyfJeW53N5fHkX0wIA== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -975,9 +995,9 @@ needle@^2.4.1: sax "^1.2.4" node-addon-api@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.1.tgz#4fd0931bf6d7e48b219ff3e6abc73cbb0252b7a3" - integrity sha512-2WVfwRfIr1AVn3dRq4yRc2Hn35ND+mPJH6inC6bjpYCZVrpXPB4j3T6i//OGVfqVsR1t/X/axRulDsheq4F0LQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== node-fetch@^2.6.0: version "2.6.0" @@ -1454,9 +1474,9 @@ type-fest@^0.8.1: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== typescript@^3.9.5: - version "3.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" - integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== uri-js@^4.2.2: version "4.2.2" @@ -1504,10 +1524,10 @@ winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" -winston@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.2.tgz#943773ea8e1c2353e088acd64a952c7809ac076e" - integrity sha512-vTOrUZlyQPS8VpCcQ1JT8BumDAUe4awCHZ9nmGgO7LqkV4atj0dKa5suA7Trf7QKtBszE2yUs9d8744Kz9j4jQ== +winston@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" + integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== dependencies: "@dabh/diagnostics" "^2.0.2" async "^3.1.0" @@ -1537,9 +1557,9 @@ write@1.0.3: mkdirp "^0.5.1" ws@^7.2.1: - version "7.3.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" - integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== yallist@^4.0.0: version "4.0.0" @@ -1551,14 +1571,14 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -ytdl-core@^2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/ytdl-core/-/ytdl-core-2.1.7.tgz#d62c74161691a114de211145e645f30f607a5f96" - integrity sha512-ithllxxlt4zmJVTnYtT8/31QLv5MGlK3fSk29lx2S4eKc1BGh+ELKQEAkRJqWIf2P8TYBYrKwijx11xND4JcXw== +ytdl-core@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/ytdl-core/-/ytdl-core-3.1.2.tgz#38402a65486a6baea9e3f9c65cb7c911091b50ba" + integrity sha512-H2ja9W9j89TTf+LMQxo1VrdpUNCcjjHnwY6M4NmvgUaQLwNZnIoBwbch4iaT8q75trUK83PzmbihkUBd5Poyfg== dependencies: html-entities "^1.3.1" m3u8stream "^0.7.1" - miniget "^1.7.2" + miniget "^2.0.1" sax "^1.1.3" zlib-sync@^0.1.7: