diff --git a/commands/music/play.js b/commands/music/play.js index 099cb36..911e87a 100644 --- a/commands/music/play.js +++ b/commands/music/play.js @@ -1,4 +1,5 @@ require("dotenv").config(); +const musicFuncs = require('../../utils/sharedFunctions.js') const { SlashCommandBuilder } = require("@discordjs/builders"); const { ActionRowBuilder, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } = require("discord.js"); const { Player, QueryType } = require('discord-player'); @@ -20,43 +21,23 @@ module.exports = { if (!interaction.member.voice.channelId) return await interaction.reply({ content: "❌ | You are not in a voice channel!", ephemeral: true }); if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) return await interaction.reply({ content: "❌ | You are not in my voice channel!", ephemeral: true }); - const player = Player.singleton(); const query = interaction.options.getString("music"); - var checkqueue = player.nodes.get(interaction.guild.id); - - if (!checkqueue) { - player.nodes.create(interaction.guild.id, { - leaveOnEmpty: client.config.leaveOnEmpty, - leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, - leaveOnEnd: client.config.leaveOnEnd, - leaveOnEndCooldown: client.config.leaveOnEndCooldown, - leaveOnStop: client.config.leaveOnStop, - leaveOnStopCooldown: client.config.leaveOnStopCooldown, - selfDeaf: client.config.selfDeafen, - skipOnNoStream: true, - metadata: { - channel: interaction.channel, - requestedBy: interaction.user, - client: interaction.guild.members.me, - } - }) - } - - var queue = player.nodes.get(interaction.guild.id); + const player = Player.singleton(); + await musicFuncs.getQueue(interaction); try { const search = await player.search(query, { requestedBy: interaction.user, searchEngine: QueryType.AUTO }) - + + //console.log(search) if (!search || search.tracks.length == 0 || !search.tracks) { return interaction.reply({ content: `❌ | Ooops... something went wrong, couldn't find the song with the requested query.`, ephemeral: true }) } //Otherwise it has found so defer reply await interaction.deferReply(); - //console.log(search) //If there is more than one search result if (search.tracks.length >= 2 && !search.playlist) { @@ -81,11 +62,10 @@ module.exports = { actionmenu.components[0].addOptions( new StringSelectMenuOptionBuilder() .setLabel(result.title) - .setValue(`${!result.playlist ? 'song' : 'playlist' }_${result.url}`) + .setValue(`${!result.playlist ? 'song' : 'playlist' }_${result.url}_false`) .setDescription(`Duration - ${result.duration}`) .setEmoji(emojis[count-1]) ) - count++ } @@ -104,87 +84,7 @@ module.exports = { //There is only one search result, play it direct else { - try { - if (!queue.connection) await queue.connect(interaction.member.voice.channel); - } - - catch (err) { - queue.delete(); - return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) - } - - try { - search.playlist ? queue.addTrack(search.tracks) : queue.addTrack(search.tracks[0]) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } - - if (!queue.isPlaying()) { - try { - await queue.node.play(queue.tracks[0]); - queue.node.setVolume(client.config.defaultVolume); - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) - } - - if (search.playlist) { - const playlistembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs and started to play the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [playlistembed] }) - } - - else { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [playsongembed] }) - } - } - - else { - if (search.playlist) { - const queueplaylistembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [queueplaylistembed] }) - } - - else { - const queuesongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [queuesongembed] }) - } - } + await musicFuncs.addTracks(interaction, 'false', search, 'send') } } @@ -199,28 +99,10 @@ client.on('interactionCreate', async (interaction) => { if (!interaction.isStringSelectMenu()) return; if (interaction.customId == "playsearch") { const player = Player.singleton(); - var checkqueue = player.nodes.get(interaction.guild.id); - - if (!checkqueue) { - player.nodes.create(interaction.guild.id, { - leaveOnEmpty: client.config.leaveOnEmpty, - leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, - leaveOnEnd: client.config.leaveOnEnd, - leaveOnEndCooldown: client.config.leaveOnEndCooldown, - leaveOnStop: client.config.leaveOnStop, - leaveOnStopCooldown: client.config.leaveOnStopCooldown, - selfDeaf: client.config.selfDeafen, - skipOnNoStream: true, - metadata: { - channel: interaction.channel, - requestedBy: interaction.user, - client: interaction.guild.members.me, - } - }) - } - - var queue = player.nodes.get(interaction.guild.id); + await musicFuncs.getQueue(interaction); var allcomponents = interaction.values; + var getPlayNext = allcomponents[0].split('_')[2] != null && allcomponents[0].split('_')[2] == "true" ? true : false + //console.log(allcomponents) try { const search = await player.search(allcomponents[0].split('_')[1], { @@ -235,91 +117,7 @@ client.on('interactionCreate', async (interaction) => { //Defer update from menu interaction await interaction.deferUpdate(); - try { - if (!queue.connection) await queue.connect(interaction.member.voice.channel); - } - - catch (err) { - queue.delete(); - return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) - } - - try { - search.playlist ? queue.addTrack(search.tracks) : queue.addTrack(search.tracks[0]) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } - - if (!queue.isPlaying()) { - try { - await queue.node.play(queue.tracks[0]); - queue.node.setVolume(client.config.defaultVolume); - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) - } - - if (search.playlist) { - const playlistembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs and started to play the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playlistembed], components: [] }) - } - - else { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playsongembed], components: [] }) - } - } - - else { - if (search.playlist) { - const queueplaylistembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [queueplaylistembed], components: [] }) - } - - else { - const queuesongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [queuesongembed], components: [] }) - } - } + await musicFuncs.addTracks(interaction, getPlayNext, search, 'edit') } catch (err) { diff --git a/commands/music/playnext.js b/commands/music/playnext.js index b70bfcc..0c765f8 100644 --- a/commands/music/playnext.js +++ b/commands/music/playnext.js @@ -1,4 +1,5 @@ require("dotenv").config(); +const musicFuncs = require('../../utils/sharedFunctions.js') const { SlashCommandBuilder } = require("@discordjs/builders"); const { EmbedBuilder } = require("discord.js"); const { Player, QueryType } = require('discord-player'); @@ -20,36 +21,17 @@ module.exports = { if (!interaction.member.voice.channelId) return await interaction.reply({ content: "❌ | You are not in a voice channel!", ephemeral: true }); if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) return await interaction.reply({ content: "❌ | You are not in my voice channel!", ephemeral: true }); - const player = Player.singleton(); const query = interaction.options.getString("music"); - var checkqueue = player.nodes.get(interaction.guild.id); - - if (!checkqueue) { - player.nodes.create(interaction.guild.id, { - leaveOnEmpty: client.config.leaveOnEmpty, - leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, - leaveOnEnd: client.config.leaveOnEnd, - leaveOnEndCooldown: client.config.leaveOnEndCooldown, - leaveOnStop: client.config.leaveOnStop, - leaveOnStopCooldown: client.config.leaveOnStopCooldown, - selfDeaf: client.config.selfDeafen, - skipOnNoStream: true, - metadata: { - channel: interaction.channel, - requestedBy: interaction.user, - client: interaction.guild.members.me, - } - }) - } - - var queue = player.nodes.get(interaction.guild.id); + const player = Player.singleton(); + await musicFuncs.getQueue(interaction); try { const search = await player.search(query, { requestedBy: interaction.user, searchEngine: QueryType.AUTO }) - + + //console.log(search) if (!search || search.tracks.length == 0 || !search.tracks) { return interaction.reply({ content: `❌ | Ooops... something went wrong, couldn't find the song with the requested query.`, ephemeral: true }) } @@ -61,47 +43,58 @@ module.exports = { //Otherwise it has found so defer reply await interaction.deferReply(); - try { - if (!queue.connection) await queue.connect(interaction.member.voice.channel); - } - - catch (err) { - queue.delete(); - return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) - } - - try { - queue.insertTrack(search.tracks[0]) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track to the queue.`, ephemeral: true }) - } - - if (!queue.isPlaying()) { - try { - await queue.node.play(queue.tracks[0]); - queue.node.setVolume(client.config.defaultVolume); + //If there is more than one search result + if (search.tracks.length >= 2 && !search.playlist) { + var foundItems = [] + let count = 1 + let emojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣','5️⃣', '6️⃣', '7️⃣', '8️⃣','9️⃣', '🔟'] + + var actionmenu = new ActionRowBuilder() + .addComponents( + new StringSelectMenuBuilder() + .setCustomId("playsearch") + .setMinValues(1) + .setMaxValues(1) + .setPlaceholder('Add an item to queue 👈') + //.addOptions(options) + ) + + for (var result of search.tracks) { + if (count > 10) break + if (result.playlist) return + foundItems.push({ name: `[${count}] Song Result (${result.duration})`, value: `${result.description}` }) + + actionmenu.components[0].addOptions( + new StringSelectMenuOptionBuilder() + .setLabel(result.title) + .setValue(`song_${result.url}_true`) + .setDescription(`Duration - ${result.duration}`) + .setEmoji(emojis[count-1]) + ) + count++ } - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) - } + const searchembed = new EmbedBuilder() + .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) + .setThumbnail(interaction.guild.iconURL({dynamic: true})) + .setTitle(`Music Search Results 🎵`) + .setDescription('Found multiple songs matching the provided search query, select one form the menu below.') + .addFields(foundItems) + .setColor(client.config.embedColour) + .setTimestamp() + .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) + + interaction.followUp({ embeds: [searchembed], components: [actionmenu] }) } - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail(search.tracks[0].thumbnail) - .setColor(client.config.embedColour) - .setTitle(`Added to the top of the queue ⏱️`) - .setDescription(`Added song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''} to the top of the queue (playing next)!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [playsongembed] }) + //There is only one search result, play it direct + else { + await musicFuncs.addTracks(interaction, true, search, 'send') + } } catch (err) { + console.log(err) return interaction.followUp({ content: `❌ | Ooops... something went wrong whilst attempting to play the requested song. Please try again.`, ephemeral: true }) } } diff --git a/commands/music/plex.js b/commands/music/plex.js index 11bf943..bc9f62f 100644 --- a/commands/music/plex.js +++ b/commands/music/plex.js @@ -1,7 +1,7 @@ require("dotenv").config(); +const musicFuncs = require('../../utils/sharedFunctions.js') const { SlashCommandBuilder } = require("@discordjs/builders"); -const { ActionRowBuilder, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, AttachmentBuilder } = require("discord.js"); -const { Player, QueryType, Track } = require('discord-player'); +const { ActionRowBuilder, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } = require("discord.js"); module.exports = { data: new SlashCommandBuilder() @@ -24,9 +24,18 @@ module.exports = { .setDescription("Search query for a single song or playlist.") .setRequired(true) ) + ) + .addSubcommand((subcommand) => subcommand + .setName("playnext") + .setDescription("Add a song from your plex to the top of the queue.") + .addStringOption((option) => option + .setName("music") + .setDescription("Search query for a single song or playlist.") + .setRequired(true) + ) ), async execute(interaction) { - if (interaction.options.getSubcommand() === "play") { + if (interaction.options.getSubcommand() === "play" || interaction.options.getSubcommand() === "playnext") { if (client.config.enableDjMode) { if (!interaction.member.roles.cache.has(client.config.djRole)) return interaction.reply({ content: `❌ | DJ Mode is active! You must have the DJ role <@&${client.config.djRole}> to use any music commands!`, ephemeral: true }); } @@ -38,55 +47,23 @@ module.exports = { if (!interaction.member.voice.channelId) return await interaction.reply({ content: "❌ | You are not in a voice channel!", ephemeral: true }); if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) return await interaction.reply({ content: "❌ | You are not in my voice channel!", ephemeral: true }); - const player = Player.singleton(); const query = interaction.options.getString("music"); - var checkqueue = player.nodes.get(interaction.guild.id); + await musicFuncs.getQueue(interaction); - if (!checkqueue) { - player.nodes.create(interaction.guild.id, { - leaveOnEmpty: client.config.leaveOnEmpty, - leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, - leaveOnEnd: client.config.leaveOnEnd, - leaveOnEndCooldown: client.config.leaveOnEndCooldown, - leaveOnStop: client.config.leaveOnStop, - leaveOnStopCooldown: client.config.leaveOnStopCooldown, - selfDeaf: client.config.selfDeafen, - skipOnNoStream: true, - metadata: { - channel: interaction.channel, - requestedBy: interaction.user, - client: interaction.guild.members.me, - } - }) - } - - var queue = player.nodes.get(interaction.guild.id); - - const fetch = require('node-fetch'); try { - const search = await fetch(`${client.config.plexServer}/hubs/search?X-Plex-Token=${client.config.plexAuthtoken}&query=${query}&limit=10&type=10,15`, { - method: 'GET', - headers: { accept: 'application/json'} - }) - - var searchRes = await search.json() - var allSongs = searchRes.MediaContainer.Hub.find(type => type.type == 'track').Metadata - var allPlaylists = searchRes.MediaContainer.Hub.find(type => type.type == 'playlist').Metadata - //console.log(allSongs.Metadata) - - if (!allSongs && !allPlaylists) { + var results = await musicFuncs.plexSearchQuery(query); + if (!results.songs && !results.playlists) { return interaction.reply({ content: `❌ | Ooops... something went wrong, couldn't find the song or playlist with the requested query.`, ephemeral: true }) } - //Otherwise it has found so defer reply + //Otherwise something is found so defer reply await interaction.deferReply(); - //If there is more than one search result - if (((allSongs ? allSongs.length : 0) + (allPlaylists ? allPlaylists.length : 0)) >= 2) { - var foundItems = [] + //More than one search result, show menu + if (results.size >= 2) { + var embedFields = [] let count = 1 let emojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣','5️⃣', '6️⃣', '7️⃣', '8️⃣','9️⃣', '🔟'] - //console.log(searchRes) var actionmenu = new ActionRowBuilder() .addComponents( @@ -95,44 +72,38 @@ module.exports = { .setMinValues(1) .setMaxValues(1) .setPlaceholder('Add an item to queue 👈') - //.addOptions(options) ) - - var allSongs = searchRes.MediaContainer.Hub.find(type => type.type == 'track').Metadata - var allPlaylists = searchRes.MediaContainer.Hub.find(type => type.type == 'playlist').Metadata - if (allSongs) { - for (var result of allSongs) { - //console.log(result) - let date = new Date(result.duration) - foundItems.push({ name: `[${count}] ${result.type.charAt(0).toUpperCase() + result.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${result.parentTitle} - ${result.grandparentTitle}` }) + if (results.songs) { + for (let item of results.songs) { + //console.log(item) + let date = new Date(item.duration) + embedFields.push({ name: `[${count}] ${item.type.charAt(0).toUpperCase() + item.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${item.parentTitle} - ${item.grandparentTitle}` }) actionmenu.components[0].addOptions( new StringSelectMenuOptionBuilder() - .setLabel(`${result.parentTitle} - ${result.grandparentTitle}`) - .setValue(`${result.type}_${result.key}`) + .setLabel(`${item.parentTitle} - ${item.grandparentTitle}`) + .setValue(`${item.type}_${item.key}_${interaction.options.getSubcommand() == "playnext" ? "true" : "false"}`) .setDescription(`Duration - ${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`) .setEmoji(emojis[count-1]) ) - count++ } } - if (allPlaylists) { - for (var result of allPlaylists) { - //console.log(result) - let date = new Date(result.duration) - foundItems.push({ name: `[${count}] ${result.type.charAt(0).toUpperCase() + result.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${result.title}` }) + if (results.playlists && interaction.options.getSubcommand() != "playnext") { + for (var item of results.playlists) { + //console.log(item) + let date = new Date(item.duration) + embedFields.push({ name: `[${count}] ${item.type.charAt(0).toUpperCase() + item.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${item.title}` }) actionmenu.components[0].addOptions( new StringSelectMenuOptionBuilder() - .setLabel(`${result.title}`) - .setValue(`${result.type}_${result.key}`) + .setLabel(`${item.title}`) + .setValue(`${item.type}_${item.key}_${interaction.options.getSubcommand() == "playnext" ? "true" : "false"}`) .setDescription(`Duration - ${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`) .setEmoji(emojis[count-1]) ) - count++ } } @@ -142,159 +113,25 @@ module.exports = { .setThumbnail(interaction.guild.iconURL({dynamic: true})) .setTitle(`Plex Search Results 🎵`) .setDescription('Found multiple songs matching the provided search query, select one form the menu below.') - .addFields(foundItems) + .addFields(embedFields) .setColor(client.config.embedColour) .setTimestamp() .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - + interaction.followUp({ embeds: [searchembed], components: [actionmenu] }) } //There is only one search result, play it direct else { - var songFound = await (allSongs ? allSongs[0] : null) || (allPlaylists ? allPlaylists[0] : null) - - if (songFound.type == 'playlist') { - var playlistID = songFound.ratingKey - const search = await fetch(`${client.config.plexServer}/playlists/${playlistID}/items?X-Plex-Token=${client.config.plexAuthtoken}`, { - method: 'GET', - headers: { accept: 'application/json'} - }) - - var searchRes = await search.json() - - for await (var result of searchRes.MediaContainer.Metadata) { - let date = new Date(result.duration) - //console.log(result) - var newTrack = new Track(player, { - title: result.title, - author: result.grandparentTitle, - url: `${client.config.plexServer}${result.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - thumbnail: `${client.config.plexServer}${result.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, - views: '69', - playlist: null, - description: null, - requestedBy: interaction.user, - source: 'arbitrary', - engine: `${client.config.plexServer}${result.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - queryType: QueryType.ARBITRARY - }) - - try { - queue.addTrack(newTrack) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } - } - - var coverImage = new AttachmentBuilder(`${client.config.plexServer}${searchRes.MediaContainer.Metadata[0].thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, { name: 'coverimage.jpg', description: `Song Cover Image for ${searchRes.MediaContainer.Metadata[0].title}` }) + var itemFound = await (results.songs ? results.songs[0] : null) || (results.playlists ? results.playlists[0] : null) + //console.log(itemFound) + + if (itemFound.type == 'playlist') { + await musicFuncs.plexAddPlaylist(interaction, itemFound, 'send') } else { - let date = new Date(songFound.duration) - var newTrack = new Track(player, { - title: songFound.title, - author: songFound.grandparentTitle, - url: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - thumbnail: `${client.config.plexServer}${songFound.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, - views: '69', - playlist: null, - description: null, - requestedBy: interaction.user, - source: 'arbitrary', - engine: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - queryType: QueryType.ARBITRARY - }) - - try { - queue.addTrack(newTrack) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } - - var coverImage = new AttachmentBuilder(`${client.config.plexServer}${songFound.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, { name: 'coverimage.jpg', description: `Song Cover Image for ${songFound.title}` }) - } - - try { - if (!queue.connection) await queue.connect(interaction.member.voice.channel); - } - - catch (err) { - queue.delete(); - return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) - } - //console.log(songFound.Media[0].Part[0]) - //console.log(`${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`) - - if (!queue.isPlaying()) { - try { - await queue.node.play(queue.tracks[0]); - queue.node.setVolume(client.config.defaultVolume); - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) - } - - if (songFound.type == 'playlist') { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Imported the **${searchRes.MediaContainer.title} playlist** with **${searchRes.MediaContainer.size}** songs and started to play the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [playsongembed], files: [coverImage] }) - } - - else { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Began playing the song **${newTrack.title}**!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [playsongembed], files: [coverImage] }) - } - } - - else { - if (songFound.type == 'playlist') { - const queuesongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Imported the **${searchRes.MediaContainer.title} playlist** with **${searchRes.MediaContainer.size}** songs!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [queuesongembed], files: [coverImage] }) - } - - else { - const queuesongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Added song **${newTrack.title}** to the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - interaction.followUp({ embeds: [queuesongembed], files: [coverImage] }) - } + await musicFuncs.plexAddTrack(interaction, interaction.options.getSubcommand() == "playnext" ? true : false, itemFound, 'send') } } } @@ -306,7 +143,7 @@ module.exports = { } else if (interaction.options.getSubcommand() === "search") { - if (process.env.ENABLE_DJMODE == true) { + if (client.config.enableDjMode) { if (!interaction.member.roles.cache.has(client.config.djRole)) return interaction.reply({ content: `❌ | DJ Mode is active! You must have the DJ role <@&${client.config.djRole}> to use any music commands!`, ephemeral: true }); } @@ -314,24 +151,24 @@ module.exports = { return interaction.reply({ content: `❌ | Plex is currently disabled! Ask the server admin to enable and configure this in the environment file.`, ephemeral: true }); } - if (!interaction.member.voice.channelId) return await interaction.followUp({ content: "❌ | You are not in a voice channel!", ephemeral: true }); - if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) return await interaction.followUp({ content: "❌ | You are not in my voice channel!", ephemeral: true }); + if (!interaction.member.voice.channelId) return await interaction.reply({ content: "❌ | You are not in a voice channel!", ephemeral: true }); + if (interaction.guild.members.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.members.me.voice.channelId) return await interaction.reply({ content: "❌ | You are not in my voice channel!", ephemeral: true }); - const player = Player.singleton(); const query = interaction.options.getString("music"); + await musicFuncs.getQueue(interaction); - const fetch = require('node-fetch'); try { - const search = await fetch(`${client.config.plexServer}/hubs/search?X-Plex-Token=${client.config.plexAuthtoken}&query=${query}&limit=10&type=10,15`, { - method: 'GET', - headers: { accept: 'application/json'} - }) + var results = await musicFuncs.plexSearchQuery(query); + if (!results.songs && !results.playlists) { + return interaction.reply({ content: `❌ | Ooops... something went wrong, couldn't find the song or playlist with the requested query.`, ephemeral: true }) + } + + //Otherwise something is found so defer reply + await interaction.deferReply(); - var searchRes = await search.json() - var foundItems = [] + var embedFields = [] let count = 1 let emojis = ['1️⃣', '2️⃣', '3️⃣', '4️⃣','5️⃣', '6️⃣', '7️⃣', '8️⃣','9️⃣', '🔟'] - //console.log(searchRes) var actionmenu = new ActionRowBuilder() .addComponents( @@ -340,51 +177,38 @@ module.exports = { .setMinValues(1) .setMaxValues(1) .setPlaceholder('Add an item to queue 👈') - //.addOptions(options) ) - var allSongs = searchRes.MediaContainer.Hub.find(type => type.type == 'track').Metadata - var allPlaylists = searchRes.MediaContainer.Hub.find(type => type.type == 'playlist').Metadata - - if (!allSongs && !allPlaylists) { - return interaction.reply({ content: `❌ | Ooops... something went wrong, couldn't find the song or playlist with the requested query.`, ephemeral: true }) - } - - //Otherwise it has found so defer reply - await interaction.deferReply(); - - if (allSongs) { - for (var result of allSongs) { - //console.log(result) - let date = new Date(result.duration) - foundItems.push({ name: `[${count}] ${result.type.charAt(0).toUpperCase() + result.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${result.parentTitle} - ${result.grandparentTitle}` }) + if (results.songs) { + for (let item of results.songs) { + //console.log(item) + let date = new Date(item.duration) + embedFields.push({ name: `[${count}] ${item.type.charAt(0).toUpperCase() + item.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${item.parentTitle} - ${item.grandparentTitle}` }) actionmenu.components[0].addOptions( new StringSelectMenuOptionBuilder() - .setLabel(`${result.parentTitle} - ${result.grandparentTitle}`) - .setValue(`${result.type}_${result.key}`) + .setLabel(`${item.parentTitle} - ${item.grandparentTitle}`) + .setValue(`${item.type}_${item.key}`) .setDescription(`Duration - ${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`) .setEmoji(emojis[count-1]) ) - count++ } } - if (allPlaylists) { - for (var result of allPlaylists) { - //console.log(result) - let date = new Date(result.duration) - foundItems.push({ name: `[${count}] ${result.type.charAt(0).toUpperCase() + result.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${result.title}` }) + if (results.playlists) { + for (var item of results.playlists) { + //console.log(item) + let date = new Date(item.duration) + embedFields.push({ name: `[${count}] ${item.type.charAt(0).toUpperCase() + item.type.slice(1)} Result (${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()})`, value: `${item.title}` }) actionmenu.components[0].addOptions( new StringSelectMenuOptionBuilder() - .setLabel(`${result.title}`) - .setValue(`${result.type}_${result.key}`) + .setLabel(`${item.title}`) + .setValue(`${item.type}_${item.key}`) .setDescription(`Duration - ${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`) .setEmoji(emojis[count-1]) ) - count++ } } @@ -393,11 +217,11 @@ module.exports = { .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) .setThumbnail(interaction.guild.iconURL({dynamic: true})) .setTitle(`Plex Search Results 🎵`) - .addFields(foundItems) + .addFields(embedFields) .setColor(client.config.embedColour) .setTimestamp() .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - + interaction.followUp({ embeds: [searchembed], components: [actionmenu] }) } @@ -412,194 +236,34 @@ module.exports = { client.on('interactionCreate', async (interaction) => { if (!interaction.isStringSelectMenu()) return; if (interaction.customId == "plexsearch") { - const player = Player.singleton(); - var checkqueue = player.nodes.get(interaction.guild.id); - - if (!checkqueue) { - player.nodes.create(interaction.guild.id, { - leaveOnEmpty: client.config.leaveOnEmpty, - leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, - leaveOnEnd: client.config.leaveOnEnd, - leaveOnEndCooldown: client.config.leaveOnEndCooldown, - leaveOnStop: client.config.leaveOnStop, - leaveOnStopCooldown: client.config.leaveOnStopCooldown, - selfDeaf: client.config.selfDeafen, - skipOnNoStream: true, - metadata: { - channel: interaction.channel, - requestedBy: interaction.user, - client: interaction.guild.members.me, - } - }) - } - - var queue = player.nodes.get(interaction.guild.id); - const fetch = require('node-fetch'); + await musicFuncs.getQueue(interaction); var allcomponents = interaction.values; //console.log(allcomponents) for await (option of allcomponents) { var getItemType = option.split('_')[0] var getItemKey = option.split('_')[1] + var getPlayNext = option.split('_')[2] != null && option.split('_')[2] == "true" ? true : false - //Playlist - if (getItemType == 'playlist') { - const search = await fetch(`${client.config.plexServer}${getItemKey}?X-Plex-Token=${client.config.plexAuthtoken}`, { - method: 'GET', - headers: { accept: 'application/json'} - }) - - var searchRes = await search.json() - //console.log(searchRes) - - for await (var result of searchRes.MediaContainer.Metadata) { - let date = new Date(result.duration) - //console.log(result) - var newTrack = new Track(player, { - title: result.title, - author: result.grandparentTitle, - url: `${client.config.plexServer}${result.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - thumbnail: `${client.config.plexServer}${result.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, - views: '69', - playlist: null, - description: null, - requestedBy: interaction.user, - source: 'arbitrary', - engine: `${client.config.plexServer}${result.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - queryType: QueryType.ARBITRARY - }) - - try { - queue.addTrack(newTrack) - - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } - } - - var coverImage = new AttachmentBuilder(`${client.config.plexServer}${searchRes.MediaContainer.Metadata[0].thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, { name: 'coverimage.jpg', description: `Song Cover Image for ${searchRes.MediaContainer.Metadata[0].title}` }) - } - - //Single song - else { - const search = await fetch(`${client.config.plexServer}${getItemKey}?X-Plex-Token=${client.config.plexAuthtoken}`, { - method: 'GET', - headers: { accept: 'application/json'} - }) - - var searchRes = await search.json() - //console.log(searchRes) - - var songFound = await searchRes.MediaContainer.Metadata[0] - let date = new Date(songFound.duration) - const newTrack = new Track(player, { - title: songFound.title, - author: songFound.grandparentTitle, - url: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - thumbnail: `${client.config.plexServer}${songFound.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, - views: '69', - playlist: null, - description: null, - requestedBy: interaction.user, - source: 'arbitrary', - engine: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, - queryType: QueryType.ARBITRARY - }) - - try { - queue.addTrack(newTrack) - } - - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) - } + var request = await fetch(`${client.config.plexServer}${getItemKey}?X-Plex-Token=${client.config.plexAuthtoken}`, { + method: 'GET', + headers: { accept: 'application/json'} + }) - var coverImage = new AttachmentBuilder(`${client.config.plexServer}${songFound.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, { name: 'coverimage.jpg', description: `Song Cover Image for ${songFound.title}` }) - } + var result = await request.json() //Defer update from menu interaction await interaction.deferUpdate(); - - try { - if (!queue.connection) await queue.connect(interaction.member.voice.channel); - } - - catch (err) { - queue.delete(); - return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) - } - } - - if (!queue.isPlaying()) { - try { - await queue.node.play(queue.tracks[0]); - queue.node.setVolume(client.config.defaultVolume); - } - catch (err) { - return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) - } - - if (getItemType == 'playlist') { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Imported the **${searchRes.MediaContainer.title} playlist** with **${searchRes.MediaContainer.size}** songs and started to play the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playsongembed], files: [coverImage], components: [] }) - } - - else { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Started playback ▶️`) - .setDescription(`Began playing the song **${songFound.title}**!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playsongembed], files: [coverImage], components: [] }) - } - } - - else { + //Playlist if (getItemType == 'playlist') { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Imported the **${searchRes.MediaContainer.title} playlist** with **${searchRes.MediaContainer.size}** songs!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playsongembed], files: [coverImage], components: [] }) + result.MediaContainer.type = getItemType; + await musicFuncs.plexAddPlaylist(interaction, result.MediaContainer, 'edit') } + //Single song else { - const playsongembed = new EmbedBuilder() - .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) - .setThumbnail('attachment://coverimage.jpg') - .setColor(client.config.embedColour) - .setTitle(`Added to queue ⏱️`) - .setDescription(`Added song **${songFound.title}** to the queue!`) - .setTimestamp() - .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) - - var sourceMessage = interaction.message - sourceMessage.edit({ embeds: [playsongembed], files: [coverImage], components: [] }) + await musicFuncs.plexAddTrack(interaction, getPlayNext, result.MediaContainer.Metadata[0], 'edit') } } } diff --git a/events/ready.js b/events/ready.js index ea9887d..60f773a 100644 --- a/events/ready.js +++ b/events/ready.js @@ -114,7 +114,7 @@ module.exports = { resolve(); }) .then(() => { - console.log(`[ELITE_CONFIG] Configuration loaded... Current config:\nv${JSON.stringify(client.config, null, 3)}`) + console.log(`[ELITE_CONFIG] Configuration loaded... Current config:\n${JSON.stringify(client.config, null, 3)}`) console.log(`Note: If some configuration option is incorrect, please double check that it is correctly set within your .ENV file!\nOtherwise, where a configuraiton option is invalid, the default from defaultConsts.js will be used.`) console.log("\n[ELITE_STATUS] Loading successful. Core of the bot is ready!"); }) diff --git a/utils/sharedFunctions.js b/utils/sharedFunctions.js new file mode 100644 index 0000000..dd7f92c --- /dev/null +++ b/utils/sharedFunctions.js @@ -0,0 +1,299 @@ +require("dotenv").config(); +const { EmbedBuilder, AttachmentBuilder } = require("discord.js"); +const { Player, QueryType, Track } = require('discord-player'); +const player = Player.singleton(); + +//Core music functions +async function getQueue(interaction) { + const player = Player.singleton(); + var checkqueue = player.nodes.get(interaction.guild.id); + + if (!checkqueue) { + player.nodes.create(interaction.guild.id, { + leaveOnEmpty: client.config.leaveOnEmpty, + leaveOnEmptyCooldown: client.config.leaveOnEmptyCooldown, + leaveOnEnd: client.config.leaveOnEnd, + leaveOnEndCooldown: client.config.leaveOnEndCooldown, + leaveOnStop: client.config.leaveOnStop, + leaveOnStopCooldown: client.config.leaveOnStopCooldown, + selfDeaf: client.config.selfDeafen, + skipOnNoStream: true, + metadata: { + channel: interaction.channel, + requestedBy: interaction.user, + client: interaction.guild.members.me, + } + }) + } + + return player.nodes.get(interaction.guild.id); +} + +async function addTracks(interaction, nextSong, search, responseType) { + try { + let queue = await getQueue(interaction); + + if (nextSong) { + queue.insertTrack(search.tracks[0]); + } + + else { + queue.addTrack(search.tracks[0]); + } + + await queuePlay(interaction, responseType, search, nextSong); + } + + catch (err) { + console.log(err) + return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) + } +} + +async function queuePlay(interaction, responseType, search, nextSong) { + var queue = await getQueue(interaction); + + try { + if (!queue.connection) await queue.connect(interaction.member.voice.channel); + } + + catch (err) { + queue.delete(); + return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) + } + + const embed = new EmbedBuilder() + .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) + .setThumbnail(search.tracks[0].thumbnail) + .setColor(client.config.embedColour) + .setTimestamp() + .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) + + if (!queue.isPlaying()) { + try { + await queue.node.play(queue.tracks[0]); + queue.node.setVolume(client.config.defaultVolume); + } + + catch (err) { + return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) + } + + if (search.playlist) { + embed.setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs and started to play the queue!`) + } + + else { + embed.setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) + } + + embed.setTitle(`Started playback ▶️`) + } + + else { + if (search.playlist) { + embed.setDescription(`Imported the **${search.tracks[0].playlist.title} ([Link](${search.tracks[0].playlist.url})) playlist** with **${search.tracks.length}** songs!`) + } + + else { + if (nextSong) { + embed.setDescription(`Added song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''} to the top of the queue (playing next)!`) + embed.setTitle(`Added to the top of the queue ⏱️`) + } + + else { + embed.setDescription(`Began playing the song **${search.tracks[0].title}** ${search.tracks[0].queryType != 'arbitrary' ? `([Link](${search.tracks[0].url}))` : ''}!`) + embed.setTitle(`Added to queue ⏱️`) + } + } + } + + if (responseType == 'edit') { + interaction.message.edit({ embeds: [embed], components: [] }) + } + + else { + interaction.followUp({ embeds: [embed] }) + } +} + + +//Plex optional feature functions +async function plexSearchQuery(query) { + try { + var request = await fetch(`${client.config.plexServer}/search?X-Plex-Token=${client.config.plexAuthtoken}&query=${encodeURIComponent(query)}&limit=10&type=10,15`, { + method: 'GET', + headers: { accept: 'application/json'} + }) + + var result = await request.json() + let allSongs = result.MediaContainer.Metadata.filter(x => x.type == 'track') + let allPlaylists = result.MediaContainer.Metadata.filter(x => x.type == 'playlist') + + //Object: songs array, playlists array, total size of both arrays, default thumbnail + return { + songs: allSongs, + playlists: allPlaylists, + size: (allSongs ? allSongs.length : 0) + (allPlaylists ? allPlaylists.length : 0) + } + } + + catch (err) { + console.log(err) + return false + } +} + +async function plexAddTrack(interaction, nextSong, itemMetadata, responseType) { + var request = await fetch(`${client.config.plexServer}${itemMetadata.key}?X-Plex-Token=${client.config.plexAuthtoken}`, { + method: 'GET', + headers: { accept: 'application/json'} + }) + + var result = await request.json() + var songFound = await result.MediaContainer.Metadata[0] + + let date = new Date(songFound.duration) + var newTrack = new Track(player, { + title: songFound.title, + author: songFound.grandparentTitle, + url: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + thumbnail: `${client.config.plexServer}${songFound.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, + views: '69', + playlist: null, + description: null, + requestedBy: interaction.user, + source: 'arbitrary', + engine: `${client.config.plexServer}${songFound.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + queryType: QueryType.ARBITRARY + }) + + try { + let queue = await getQueue(interaction); + + if (nextSong) { + queue.insertTrack(newTrack); + } + + else { + queue.addTrack(newTrack); + } + + await plexQueuePlay(interaction, responseType, itemMetadata, songFound.thumb, nextSong); + } + + catch (err) { + return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) + } +} + +async function plexAddPlaylist(interaction, itemMetadata, responseType) { + var request = await fetch(`${client.config.plexServer}/playlists/${itemMetadata.ratingKey}/items?X-Plex-Token=${client.config.plexAuthtoken}`, { + method: 'GET', + headers: { accept: 'application/json'} + }) + + var result = await request.json() + for await (var item of result.MediaContainer.Metadata) { + let date = new Date(item.duration) + //console.log(item) + var newTrack = new Track(player, { + title: item.title, + author: item.grandparentTitle, + url: `${client.config.plexServer}${item.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + thumbnail: `${client.config.plexServer}${item.thumb}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + duration: `${date.getMinutes()}:${date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()}`, + views: '69', + playlist: null, + description: null, + requestedBy: interaction.user, + source: 'arbitrary', + engine: `${client.config.plexServer}${item.Media[0].Part[0].key}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, + queryType: QueryType.ARBITRARY + }) + + try { + let queue = await getQueue(interaction); + queue.addTrack(newTrack); + } + + catch (err) { + return interaction.followUp({ content: `❌ | Ooops... something went wrong, failed to add the track(s) to the queue.`, ephemeral: true }) + } + } + + await plexQueuePlay(interaction, responseType, itemMetadata, result.MediaContainer.Metadata[0].thumb) +} + +async function plexQueuePlay(interaction, responseType, itemMetadata, defaultThumbnail, nextSong) { + var queue = await getQueue(interaction); + + try { + if (!queue.connection) await queue.connect(interaction.member.voice.channel); + } + + catch (err) { + queue.delete(); + return interaction.followUp({ content: `❌ | Ooops... something went wrong, couldn't join your channel.`, ephemeral: true }) + } + + const coverImage = new AttachmentBuilder(`${client.config.plexServer}${defaultThumbnail}?download=1&X-Plex-Token=${client.config.plexAuthtoken}`, { name: 'coverimage.jpg', description: `${itemMetadata.type == 'playlist' ? "Playlist" : "Song"} Cover Image` }) + + const embed = new EmbedBuilder() + .setAuthor({ name: interaction.client.user.tag, iconURL: interaction.client.user.displayAvatarURL() }) + .setThumbnail('attachment://coverimage.jpg') + .setColor(client.config.embedColour) + .setTimestamp() + .setFooter({ text: `Requested by: ${interaction.user.discriminator != 0 ? interaction.user.tag : interaction.user.username}` }) + + if (!queue.isPlaying()) { + try { + await queue.node.play(queue.tracks[0]); + queue.node.setVolume(client.config.defaultVolume); + } + + catch (err) { + return interaction.followUp({ content: `❌ | Ooops... something went wrong, there was a playback related error. Please try again.`, ephemeral: true }) + } + + if (itemMetadata.type == 'playlist') { + embed.setDescription(`Imported the **${itemMetadata.title} playlist** with **${itemMetadata.leafCount}** songs and started to play the queue!`) + } + + else { + embed.setDescription(`Began playing the song **${itemMetadata.title}**!`) + } + + embed.setTitle(`Started playback ▶️`) + } + + else { + if (itemMetadata.type == 'playlist') { + embed.setDescription(`Imported the **${itemMetadata.title} playlist** with **${itemMetadata.leafCount}** songs!`) + embed.setTitle(`Added to queue ⏱️`) + } + + else { + if (nextSong) { + embed.setDescription(`Added song **${itemMetadata.title}** to the top of the queue (playing next)!`) + embed.setTitle(`Added to the top of the queue ⏱️`) + } + + else { + embed.setDescription(`Added song **${itemMetadata.title}** to the queue!`) + embed.setTitle(`Added to queue ⏱️`) + } + } + } + + if (responseType == 'edit') { + interaction.message.edit({ embeds: [embed], files: [coverImage], components: [] }) + } + + else { + interaction.followUp({ embeds: [embed], files: [coverImage] }) + } +} + +module.exports = { getQueue, addTracks, queuePlay, plexSearchQuery, plexAddTrack, plexAddPlaylist, plexQueuePlay }; \ No newline at end of file