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") { if (query.includes("music.youtube")) { const info = await playdl.video_info(query); if(!info) return resolve({ playlist: null, info: null }); const track = { title: info.video_details.title, duration: info.video_details.durationInSec * 1000, thumbnail: info.video_details.thumbnails[0].url, async engine() { return (await playdl.stream(`https://music.youtube.com/watch?v=${info.video_details.id}`, { discordPlayerCompatibility : true })).stream; }, views: info.video_details.views, author: info.video_details.channel.name, description: "", url: `https://music.youtube.com/watch?v=${info.video_details.id}`, related_videos: info.related_videos, raw: info, source: "youtube-music-custom" }; return resolve({ playlist: null, info: [track] }); } const info = await playdl.video_info(query); if(!info) return resolve({ playlist: null, info: null }); const track = { title: info.video_details.title, duration: info.video_details.durationInSec * 1000, thumbnail: info.video_details.thumbnails[0].url, async engine() { return (await playdl.stream(info.video_details.url, { discordPlayerCompatibility : true })).stream; }, views: info.video_details.views, author: info.video_details.channel.name, description: "", url: info.video_details.url, related_videos: info.related_videos, raw: info, source: "youtube-custom" }; return resolve({ playlist: null, info: [track] }); } else if (playdl.yt_validate(query) === "playlist") { if (query.includes("music.youtube")) { const info = await playdl.playlist_info(query, { incomplete: true }); const trackList = await info.videos; const tracks = trackList.map(track => { return { title: track.title, duration: track.durationInSec * 1000, thumbnail: track.thumbnails[0].url, async engine() { return (await playdl.stream(`https://music.youtube.com/watch?v=${track.id}`, { discordPlayerCompatibility : true })).stream; }, views: track.views, author: track.channel.name, description: "", url: track.url, raw: info, source: "youtube-music-playlist-custom" }; }); const playlist = { title: info.title, description: "", thumbnail: info.thumbnail ? info.thumbnail.url : null, type: "playlist", source: "youtube-music-playlist-custom", author: info.channel.name, id: info.id, url: info.url, rawPlaylist: info }; return resolve({ playlist: playlist, info: tracks }); } 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, raw: info, 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 playdl.search(query, { limit: 1 }); if (search) { const track = { title: search[0].title, duration: search[0].durationInSec * 1000, thumbnail: search[0].thumbnails[0].url, async engine() { return (await playdl.stream(search[0].url, { discordPlayerCompatibility : true })).stream; }, views: search[0].views, author: search[0].channel.name, description: "", url: search[0].url, related_videos: search[0].related_videos, raw: search[0], source: "youtube-custom" }; return resolve({ playlist: null, info: [track] }); } 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 }); } }); } };