From 2f0c2c387ce5578748eb0ba8d06c2677b47b45fe Mon Sep 17 00:00:00 2001 From: Kiera Affarantia Date: Sat, 4 Jan 2025 02:43:50 +0700 Subject: [PATCH] save tracks and queue to add new added tracks and queue to the database so it can continue playing, when the node disconnected or crashed. still not tested yet --- src/commands/music/play.js | 30 +++++++++++++++++++++++++++ src/commands/music/search.js | 29 ++++++++++++++++++++++++++ src/database/schemas/Queue.js | 21 +++++++++++++++++++ src/events/player/playerDisconnect.js | 25 ++++++++++++++++++++++ src/events/player/queueEnd.js | 4 ++++ src/events/player/trackEnd.js | 13 ++++++++++++ src/events/player/trackStart.js | 29 ++++++++++++++++++++++++++ 7 files changed, 151 insertions(+) create mode 100644 src/database/schemas/Queue.js diff --git a/src/commands/music/play.js b/src/commands/music/play.js index 08b89b3..4017bb7 100644 --- a/src/commands/music/play.js +++ b/src/commands/music/play.js @@ -1,5 +1,6 @@ const { EmbedBuilder, ApplicationCommandOptionType } = require("discord.js"); const { EMBED_COLORS, MUSIC } = require("@root/config"); +const Queue = require("@src/database/schemas/Queue"); /** * @type {import("@structures/Command")} @@ -118,6 +119,8 @@ async function play({ member, guild, channel }, searchQuery, source) { case "playlist": { player.queue.add(res.tracks); + await saveQueueToDatabase(player); + const playlistEmbed = new EmbedBuilder() .setAuthor({ name: "Added Playlist to queue" }) .setThumbnail(res.playlist.thumbnail) @@ -148,6 +151,8 @@ async function play({ member, guild, channel }, searchQuery, source) { const track = res.tracks[0]; player.queue.add(track); + await saveQueueToDatabase(player); + const trackEmbed = new EmbedBuilder() .setAuthor({ name: "Added Track to queue" }) .setColor(EMBED_COLORS.BOT_EMBED) @@ -183,4 +188,29 @@ async function play({ member, guild, channel }, searchQuery, source) { guild.client.logger.error("Search Exception", JSON.stringify(error)); return "🚫 An error occurred while searching for the song"; } +} + +// Function to save the current queue and track to the database +async function saveQueueToDatabase(player) { + const queueData = { + guildId: player.guildId, + tracks: player.queue.tracks.map(track => ({ + title: track.info.title, + uri: track.info.uri, + duration: track.info.duration, + requester: track.requester.id, + })), + currentTrack: player.queue.current ? { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + duration: player.queue.current.info.duration, + requester: player.queue.current.requester.id, + } : null, + }; + + await Queue.findOneAndUpdate( + { guildId: player.guildId }, + queueData, + { upsert: true, new: true } + ); } \ No newline at end of file diff --git a/src/commands/music/search.js b/src/commands/music/search.js index 02a7d06..66c493f 100644 --- a/src/commands/music/search.js +++ b/src/commands/music/search.js @@ -6,6 +6,7 @@ const { ComponentType, } = require("discord.js"); const { EMBED_COLORS, MUSIC } = require("@root/config"); +const Queue = require("@src/database/schemas/Queue"); /** * @type {import("@structures/Command")} @@ -161,6 +162,9 @@ async function search({ member, guild, channel }, query, source) { const selectedTrack = results[response.values[0]]; player.queue.add(selectedTrack); + // Save the current state to the database + await saveQueueToDatabase(player); + const trackEmbed = new EmbedBuilder() .setAuthor({ name: "Added Track to queue" }) .setDescription(`[${selectedTrack.info.title}](${selectedTrack.info.uri})`) @@ -183,3 +187,28 @@ async function search({ member, guild, channel }, query, source) { return "🚫 Failed to register your response"; } } + +// Function to save the current queue and track to the database +async function saveQueueToDatabase(player) { + const queueData = { + guildId: player.guildId, + tracks: player.queue.tracks.map(track => ({ + title: track.info.title, + uri: track.info.uri, + duration: track.info.duration, + requester: track.requester.id, + })), + currentTrack: player.queue.current ? { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + duration: player.queue.current.info.duration, + requester: player.queue.current.requester.id, + } : null, + }; + + await Queue.findOneAndUpdate( + { guildId: player.guildId }, + queueData, + { upsert: true, new: true } + ); +} diff --git a/src/database/schemas/Queue.js b/src/database/schemas/Queue.js new file mode 100644 index 0000000..85fa380 --- /dev/null +++ b/src/database/schemas/Queue.js @@ -0,0 +1,21 @@ +const mongoose = require("mongoose"); + +const queueSchema = new mongoose.Schema({ + guildId: { type: String, required: true, unique: true }, + tracks: [ + { + title: String, + uri: String, + duration: Number, + requester: String, + }, + ], + currentTrack: { + title: String, + uri: String, + duration: Number, + requester: String, + }, +}); + +module.exports = mongoose.model("Queue", queueSchema); diff --git a/src/events/player/playerDisconnect.js b/src/events/player/playerDisconnect.js index c25f8ad..71cfcb8 100644 --- a/src/events/player/playerDisconnect.js +++ b/src/events/player/playerDisconnect.js @@ -1,7 +1,32 @@ +const Queue = require("@src/database/schemas/Queue"); + module.exports = async (client, player) => { const guild = client.guilds.cache.get(player.guildId); if (!guild) return; + // Save the current track and queue to the database + const queueData = { + guildId: player.guildId, + tracks: player.queue.tracks.map(track => ({ + title: track.info.title, + uri: track.info.uri, + duration: track.info.duration, + requester: track.requester.id, + })), + currentTrack: player.queue.current ? { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + duration: player.queue.current.info.duration, + requester: player.queue.current.requester.id, + } : null, + }; + + await Queue.findOneAndUpdate( + { guildId: player.guildId }, + queueData, + { upsert: true, new: true } + ); + if (player.voiceChannelId) { await client.utils.setVoiceStatus(client, player.voiceChannelId, ""); } diff --git a/src/events/player/queueEnd.js b/src/events/player/queueEnd.js index a8e63b5..2f4ad4c 100644 --- a/src/events/player/queueEnd.js +++ b/src/events/player/queueEnd.js @@ -1,5 +1,6 @@ const { MUSIC, EMBED_COLORS } = require("@root/config"); const { EmbedBuilder } = require("discord.js"); +const Queue = require("@src/database/schemas/Queue"); module.exports = async (client, player) => { const guild = client.guilds.cache.get(player.guildId); @@ -27,4 +28,7 @@ module.exports = async (client, player) => { 10 ); } + + // Clean up the queue from the database if all tracks are played + await Queue.deleteOne({ guildId: player.guildId }); }; \ No newline at end of file diff --git a/src/events/player/trackEnd.js b/src/events/player/trackEnd.js index b13f72a..6ace2e3 100644 --- a/src/events/player/trackEnd.js +++ b/src/events/player/trackEnd.js @@ -1,5 +1,6 @@ const { autoplayFunction } = require("@handlers/player"); const { MUSIC } = require("@root/config.js"); +const Queue = require("@src/database/schemas/Queue"); module.exports = async (client, player, track) => { const guild = client.guilds.cache.get(player.guildId); @@ -14,6 +15,18 @@ module.exports = async (client, player, track) => { await msg.delete().catch(() => {}); } + // Check if the current track was saved in the database + const queueData = await Queue.findOne({ guildId: player.guildId }); + + // Remove the current track from the database after it has been played + if (queueData) { + const updatedTracks = queueData.tracks.filter(t => t.uri !== track.info.uri); + await Queue.updateOne( + { guildId: player.guildId }, + { tracks: updatedTracks, currentTrack: null } + ); + } + if (player.get("autoplay") === true) { await autoplayFunction(client, track, player); } diff --git a/src/events/player/trackStart.js b/src/events/player/trackStart.js index e82f09b..be54533 100644 --- a/src/events/player/trackStart.js +++ b/src/events/player/trackStart.js @@ -1,10 +1,39 @@ const { EmbedBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder } = require("discord.js"); const { EMBED_COLORS } = require("@root/config"); +const Queue = require("@src/database/schemas/Queue"); module.exports = async (client, player, track) => { const guild = client.guilds.cache.get(player.guildId); if (!guild) return; + // Check if the queue exists in the database + const queueData = await Queue.findOne({ guildId: player.guildId }); + if (queueData) { + // Restore the queue + player.queue.add(queueData.tracks.map(track => ({ + info: { + title: track.title, + uri: track.uri, + duration: track.duration, + }, + requester: { id: track.requester }, + }))); + + if (queueData.currentTrack) { + player.queue.current = { + info: { + title: queueData.currentTrack.title, + uri: queueData.currentTrack.uri, + duration: queueData.currentTrack.duration, + }, + requester: { id: queueData.currentTrack.requester }, + }; + } + + // Clear the database entry after restoring + await Queue.deleteOne({ guildId: player.guildId }); + } + if (!player.textChannelId || !track) return; const channel = guild.channels.cache.get(player.textChannelId);