From cf171f5ccbd683eb8856817093fdc454ceb9405c Mon Sep 17 00:00:00 2001 From: JonnyBro Date: Thu, 4 Aug 2022 22:06:26 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=D1=8B=20=D0=98=D1=81?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D0=BC=20=D0=BA=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=BC=D0=BD=D1=8B=D0=B9=20=D1=8D=D0=BA=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BA=D1=82=D0=BE=D1=80=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=84=D0=B8=D0=BA=D1=81=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1?= =?UTF-8?q?=D0=BE=D0=BA=20discord-player?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/JaBa.js | 25 +++-- commands/Music/play.js | 22 ++-- commands/Music/queue.js | 4 +- commands/Music/skipto.js | 8 +- helpers/extractor.js | 212 +++++++++++++++++++++++++++++++++++++++ package-lock.json | 51 ++++++++-- package.json | 6 +- 7 files changed, 290 insertions(+), 38 deletions(-) create mode 100644 helpers/extractor.js diff --git a/base/JaBa.js b/base/JaBa.js index ad5f9afc..73527652 100644 --- a/base/JaBa.js +++ b/base/JaBa.js @@ -2,15 +2,17 @@ const { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder } = r { GiveawaysManager } = require("discord-giveaways"), { Player } = require("discord-player"), { REST } = require("@discordjs/rest"), - { Routes } = require("discord-api-types/v10"); + { Routes } = require("discord-api-types/v10"), + { DiscordTogether } = require("../helpers/discordTogether"); const BaseEvent = require("./BaseEvent.js"), BaseCommand = require("./BaseCommand.js"), - { DiscordTogether } = require("../helpers/discordTogether"), AmeClient = require("amethyste-api"), path = require("path"), fs = require("fs").promises, mongoose = require("mongoose"), + extractor = require("../helpers/extractor"), + playdl = require("play-dl"), moment = require("moment"); moment.relativeTimeThreshold("ss", 5); @@ -49,12 +51,23 @@ class JaBa extends Client { this.discordTogether = new DiscordTogether(this); - this.player = new Player(this, { - autoRegisterExtractor: true, - leaveOnEnd: true, - leaveOnStop: true + playdl.getFreeClientID().then((clientID) => { + playdl.setToken({ + soundcloud: { + client_id: clientID + } + }); }); + this.player = new Player(this); + // ytdlOptions: { + // filter: "audioonly", + // highWaterMark: 1 << 30, + // dlChunkSize: 0, + // liveBuffer: 4900 + // } + this.player.use("jaba", extractor); + this.player .on("trackStart", async (queue, track) => { const m = await queue.metadata.channel.send({ content: this.translate("music/play:NOW_PLAYING", { songName: track.title }, queue.metadata.channel.guild.data.language) }); diff --git a/commands/Music/play.js b/commands/Music/play.js index d4c77542..65e1d219 100644 --- a/commands/Music/play.js +++ b/commands/Music/play.js @@ -1,5 +1,4 @@ -const { SlashCommandBuilder, PermissionsBitField } = require("discord.js"), - { QueryType } = require("discord-player"); +const { SlashCommandBuilder, PermissionsBitField } = require("discord.js"); const BaseCommand = require("../../base/BaseCommand"); class Play extends BaseCommand { @@ -44,7 +43,7 @@ class Play extends BaseCommand { const searchResult = await client.player.search(query, { requestedBy: interaction.user, - searchEngine: query.includes("soundcloud") ? QueryType.SOUNDCLOUD_SEARCH : QueryType.AUTO + searchEngine: "jaba" }).catch(() => {}); if (!searchResult || !searchResult.tracks.length) return interaction.editReply({ content: interaction.translate("music/play:NO_RESULT", { @@ -56,19 +55,14 @@ class Play extends BaseCommand { metadata: { channel: interaction.channel }, - ytdlOptions: { - filter: "audioonly", - highWaterMark: 1 << 30, - dlChunkSize: 0, - liveBuffer: 4900 - } + leaveOnEnd: true, + leaveOnStop: true, + bufferingTimeout: 1000, + disableVolume: false, + spotifyBridge: false }); - if (!queue.tracks[0]) { - searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[0]); - } else { - searchResult.playlist ? searchResult.tracks.forEach(track => queue.insert(track)) : queue.insert(searchResult.tracks[0]); - } + searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[0]); try { if (!queue.connection) await queue.connect(interaction.member.voice.channel); diff --git a/commands/Music/queue.js b/commands/Music/queue.js index 663cc410..0151fb7b 100644 --- a/commands/Music/queue.js +++ b/commands/Music/queue.js @@ -35,8 +35,8 @@ class Queue extends BaseCommand { if (!queue) return interaction.error("music/play:NOT_PLAYING"); const currentTrack = queue.current; - const tracks = queue.tracks.slice(0, 10).map(track => { - return `${queue.tracks.indexOf(track)}. [${track.title}](${track.url})\n> ${interaction.translate("music/queue:ADDED")} ${track.requestedBy}`; + const tracks = queue.tracks.slice(0, 10).map((track, i) => { + return `${i + 1}. [${track.title}](${track.url})\n> ${interaction.translate("music/queue:ADDED")} ${track.requestedBy}`; }); const embed = new EmbedBuilder() diff --git a/commands/Music/skipto.js b/commands/Music/skipto.js index e6caeb05..4184fa6f 100644 --- a/commands/Music/skipto.js +++ b/commands/Music/skipto.js @@ -42,13 +42,13 @@ class Skipto extends BaseCommand { if (!queue) return interaction.error("music/play:NOT_PLAYING"); if (position < 0) return interaction.error("music/skipto:NO_PREV_SONG"); - if (queue.tracks[position]) { - queue.skipTo(queue.tracks[position]); + if (queue.tracks[position - 1]) { + queue.skipTo(queue.tracks[position - 1]); interaction.success("music/skipto:SUCCESS", { - position + position: position - 1 }); - } else return interaction.error("music/skipto:ERROR", { position }); + } else return interaction.error("music/skipto:ERROR", { position: position - 1 }); } } diff --git a/helpers/extractor.js b/helpers/extractor.js new file mode 100644 index 00000000..7b0637e5 --- /dev/null +++ b/helpers/extractor.js @@ -0,0 +1,212 @@ +const playdl = require("play-dl"); +const fetch = require("node-fetch"); +const { getData, getPreview, getTracks } = require("spotify-url-info")(fetch); +const Youtube = require("youtube-sr").default; + +/* + Thanks to: https://github.com/nizewn/Dodong + + hello stranger, + + as a result of my headaches while dealing with the discord-player extractor API, + what you will see here will mostly be poorly-written code. + this could use modularisation or some cleanup (i might do it once i have free time) + + thanks :) + + -nize +*/ + +module.exports = { + important: true, + validate: () => true, // true, since we're also using this for searches when query isnt a valid link + getInfo: async (query) => { + // eslint-disable-next-line no-async-promise-executor, no-unused-vars + return new Promise(async (resolve, reject) => { + try { + // ---- start soundcloud ---- + if (["track", "playlist"].includes(await playdl.so_validate(query))) { + const info = await playdl.soundcloud(query); + if (info.type === "track") { + const track = { + title: info.name, + duration: info.durationInMs, + thumbnail: info.thumbnail, + async engine() { + return (await playdl.stream(info.url, { discordPlayerCompatibility: true })).stream; + }, + views: 0, + author: info.publisher ? info.publisher.name ?? info.publisher.artist ?? info.publisher.writer_composer ?? null : null, + description: "", + url: info.permalink, + source: "soundcloud-custom" + }; + return resolve({ playlist: null, info: [track] }); + } else if (info.type === "playlist") { + const trackList = await info.all_tracks(); + const tracks = await trackList.map(track => { + return { + title: track.name, + duration: track.durationInMs, + thumbnail: track.thumbnail, + async engine() { + return (await playdl.stream(track.url, { discordPlayerCompatibility: true })).stream; + }, + views: 0, + author: track.publisher ? track.publisher.name ?? track.publisher.artist ?? track.publisher.writer_composer ?? null : null, + description: "", + url: track.permalink, + source: "soundcloud-custom" + }; + }); + const playlist = { + title: info.name, + description: "", + thumbnail: null, + type: "playlist", + source: "soundcloud-custom", + author: info.user, + id: info.id, + url: info.url, + rawPlaylist: info + }; + return resolve({ playlist: playlist, info: tracks }); + } + } + // ---- end soundcloud ---- + + // ---- start spotify ---- + if (query.includes("open.spotify.com") || query.includes("play.spotify.com")) { + const info = await getPreview(query); + if (info.type === "track") { + const spotifyTrack = await getData(query); + const track = { + title: info.title, + duration: spotifyTrack.duration_ms, + thumbnail: info.image, + async engine() { + return (await playdl.stream(await Youtube.search(`${info.artist} ${info.title} lyric`, {limit: 1, type: "video", safeSearch: true}).then(x => x[0] ? `https://youtu.be/${x[0].id}` : "https://youtu.be/Wch3gJG2GJ4"), { discordPlayerCompatibility : true })).stream; + }, + views: 0, + author: info.artist, + description: "", + url: info.link, + source: "spotify-custom" + }; + return resolve({ playlist: null, info: [track] }); + } else if (["album", "artist", "playlist"].includes(info.type)) { + const trackList = await getTracks(query); + const tracks = trackList.map(track => { + return { + title: track.name, + duration: track.duration_ms, + thumbnail: track.album && track.album.images.length ? track.album.images[0].url : null, + async engine() { + return (await playdl.stream(await Youtube.search(`${track.artists[0].name} ${track.name} lyric`, {limit: 1, type: "video", safeSearch: true}).then(x => x[0] ? `https://youtu.be/${x[0].id}` : "https://youtu.be/Wch3gJG2GJ4"), { discordPlayerCompatibility : true })).stream; + }, + views: 0, + author: track.artists ? track.artists[0].name : null, + description: "", + url: track.external_urls.spotify, + source: "spotify-custom" + }; + }); + const playlist = { + title: info.title, + description: "", + thumbnail: info.image, + type: info.type === "album" ? "album" : "playlist", + source: "spotify-custom", + author: info.artist, + id: null, + url: info.link, + rawPlaylist: info + }; + return resolve({ playlist: playlist, info: tracks }); + } + } + // ---- end spotify ---- + + if (query.startsWith("https")) { + query = query.split("&")[0]; + } + if (query.startsWith("https") && playdl.yt_validate(query) === "video") { + const info = await Youtube.search(query, {limit: 1, type: "video", safeSearch: true}); + if(!info || !info.length) + return resolve({ playlist: null, info: null }); + + const track = { + title: info[0].title, + duration: info[0].duration, + thumbnail: info[0].thumbnail ? info[0].thumbnail.url : null, + async engine() { + return (await playdl.stream(`https://youtu.be/${info[0].id}`, { discordPlayerCompatibility : true })).stream; + }, + views: info[0].views, + author: info[0].channel.name, + description: "", + url: `https://youtu.be/${info[0].id}`, + source: "youtube-custom" + }; + return resolve({ playlist: null, info: [track] }); + } else if (playdl.yt_validate(query) === "playlist") { + const info = await playdl.playlist_info(query, { incomplete: true }); + const trackList = await info.all_videos(); + const tracks = trackList.map(track => { + return { + title: track.title, + duration: track.durationInSec * 1000, + thumbnail: track.thumbnails ? track.thumbnails[0] ? track.thumbnails[0].url : null : null, + async engine() { + return (await playdl.stream(await Youtube.search(track.url, {limit: 1, type: "video", safeSearch: true}).then(x => x[0] ? `https://youtu.be/${x[0].id}` : "https://youtu.be/Wch3gJG2GJ4"), { discordPlayerCompatibility : true })).stream; + }, + views: track.views, + author: track.channel.name, + description: "", + url: track.url, + source: "youtube-custom" + }; + }); + const playlist = { + title: info.title, + description: "", + thumbnail: info.thumbnail ? info.thumbnail.url : null, + type: "playlist", + source: "youtube-custom", + author: info.channel.name, + id: info.id, + url: info.url, + rawPlaylist: info + }; + return resolve({ playlist: playlist, info: tracks }); + } + + const search = await Youtube.search(query, { limit: 5, type: "video", safeSearch: true }); + + if(search && search.length) { + const tracks = search.map(track => { + return { + title: track.title, + duration: track.duration, + thumbnail: track.thumbnail ? track.thumbnail.url : null, + async engine() { + return (await playdl.stream(`https://youtu.be/${track.id}`, { discordPlayerCompatibility: true })).stream; + }, + views: track.views, + author: track.channel.name, + description: "", + url: `https://youtu.be/${track.id}`, + source: "youtube-custom" + }; + }); + return resolve({ playlist: null, info: tracks }); + } + + return resolve({ playlist: null, info: null }); + } catch(error) { + console.log(`Extractor: An error occurred while attempting to resolve ${query} :\n${error}`); + return resolve({ playlist: null, info: null }); + } + }); + } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6995dd94..43529ff5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "cron": "^1.7.2", "discord-api-types": "^0.37.0", "discord-giveaways": "^6.0.0", - "discord-player": "^5.3.0-dev.3", + "discord-player": "^5.3.0", "discord.js": "^14.1.2", "ejs": "^3.1.3", "express": "^4.17.1", @@ -36,7 +36,9 @@ "moment": "^2.26.0", "mongoose": "^5.13.14", "ms": "^2.1.3", - "prism-media": "^1.3.4" + "play-dl": "^1.9.5", + "prism-media": "^1.3.4", + "spotify-url-info": "^3.1.2" }, "devDependencies": { "eslint": "^7.5.0" @@ -1369,9 +1371,9 @@ } }, "node_modules/discord-player": { - "version": "5.3.0-dev.3", - "resolved": "https://registry.npmjs.org/discord-player/-/discord-player-5.3.0-dev.3.tgz", - "integrity": "sha512-rrbEBS4mzCyIGk5S9E5O0XWnC4nub2cmBAFr9pZLH7RWBX4+z2nhAt5VwemgenmW/RJy1rseuZZ75WqJDnrHnw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/discord-player/-/discord-player-5.3.0.tgz", + "integrity": "sha512-OskERYrsVqNvyq8tKkp3aV3NMY9dP5BhNeyn5NTTDohVd9eEE8P6rZGvxqORJl1OxXCTBLbiojNgLiFNsAIaow==", "dependencies": { "@discordjs/voice": "^0.11.0", "libsodium-wrappers": "^0.7.10", @@ -1379,7 +1381,7 @@ "spotify-url-info": "^3.1.2", "tiny-typed-emitter": "^2.1.0", "tslib": "^2.4.0", - "youtube-sr": "^4.2.0", + "youtube-sr": "^4.3.0", "ytdl-core": "^4.11.0" }, "funding": { @@ -3610,6 +3612,22 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/play-audio": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/play-audio/-/play-audio-0.5.2.tgz", + "integrity": "sha512-ZAqHUKkQLix2Iga7pPbsf1LpUoBjcpwU93F1l3qBIfxYddQLhxS6GKmS0d3jV8kSVaUbr6NnOEcEMFvuX93SWQ==" + }, + "node_modules/play-dl": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/play-dl/-/play-dl-1.9.5.tgz", + "integrity": "sha512-tfjBgpU6AD63snK6sXiSuAOi+3iLsqVvsFcvCritOetF/zIo2OcB4BURX+WaQLUmEX3sUJhzP/vqG8SSl7WEpA==", + "dependencies": { + "play-audio": "^0.5.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5736,9 +5754,9 @@ } }, "discord-player": { - "version": "5.3.0-dev.3", - "resolved": "https://registry.npmjs.org/discord-player/-/discord-player-5.3.0-dev.3.tgz", - "integrity": "sha512-rrbEBS4mzCyIGk5S9E5O0XWnC4nub2cmBAFr9pZLH7RWBX4+z2nhAt5VwemgenmW/RJy1rseuZZ75WqJDnrHnw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/discord-player/-/discord-player-5.3.0.tgz", + "integrity": "sha512-OskERYrsVqNvyq8tKkp3aV3NMY9dP5BhNeyn5NTTDohVd9eEE8P6rZGvxqORJl1OxXCTBLbiojNgLiFNsAIaow==", "requires": { "@discordjs/voice": "^0.11.0", "libsodium-wrappers": "^0.7.10", @@ -5746,7 +5764,7 @@ "spotify-url-info": "^3.1.2", "tiny-typed-emitter": "^2.1.0", "tslib": "^2.4.0", - "youtube-sr": "^4.2.0", + "youtube-sr": "^4.3.0", "ytdl-core": "^4.11.0" } }, @@ -7448,6 +7466,19 @@ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0-alpha.5.tgz", "integrity": "sha512-pJohF/tDwV3ntnT5+EkUo4E700q/j/OCDuPxtM+5/kFGjyOai/sK4/We4Cy1MB2OiTQliWU5DxPvYIKQAdPqAA==" }, + "play-audio": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/play-audio/-/play-audio-0.5.2.tgz", + "integrity": "sha512-ZAqHUKkQLix2Iga7pPbsf1LpUoBjcpwU93F1l3qBIfxYddQLhxS6GKmS0d3jV8kSVaUbr6NnOEcEMFvuX93SWQ==" + }, + "play-dl": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/play-dl/-/play-dl-1.9.5.tgz", + "integrity": "sha512-tfjBgpU6AD63snK6sXiSuAOi+3iLsqVvsFcvCritOetF/zIo2OcB4BURX+WaQLUmEX3sUJhzP/vqG8SSl7WEpA==", + "requires": { + "play-audio": "^0.5.2" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index df9dedd0..fa1eedaf 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "cron": "^1.7.2", "discord-api-types": "^0.37.0", "discord-giveaways": "^6.0.0", - "discord-player": "^5.3.0-dev.3", + "discord-player": "^5.3.0", "discord.js": "^14.1.2", "ejs": "^3.1.3", "express": "^4.17.1", @@ -39,7 +39,9 @@ "moment": "^2.26.0", "mongoose": "^5.13.14", "ms": "^2.1.3", - "prism-media": "^1.3.4" + "play-dl": "^1.9.5", + "prism-media": "^1.3.4", + "spotify-url-info": "^3.1.2" }, "devDependencies": { "eslint": "^7.5.0"