From bef206aac690ff4445f3f418c1b4468eb31c94e0 Mon Sep 17 00:00:00 2001
From: GoodOne <89701985+GoodOne120@users.noreply.github.com>
Date: Thu, 4 Jul 2024 20:30:38 +0300
Subject: [PATCH] Music fix (#1046)
* Music fix
Bunch of action fixes
* Fix prettier
* No longer needed
* Fix lint
* Updated
---
actions/control_music_MOD.js | 32 ++--
actions/play_music_MOD.js | 210 +++++++++++++++++++++------
actions/play_youtube_playlist_MOD.js | 109 --------------
actions/remove_from_queue_MOD.js | 20 +--
actions/set_music_repeat_mode_MOD.js | 34 ++---
actions/store_queue_info_MOD.js | 111 ++++++++++----
actions/store_track_info_MOD.js | 16 +-
7 files changed, 297 insertions(+), 235 deletions(-)
delete mode 100644 actions/play_youtube_playlist_MOD.js
diff --git a/actions/control_music_MOD.js b/actions/control_music_MOD.js
index f4af85fc..5112e2ea 100644
--- a/actions/control_music_MOD.js
+++ b/actions/control_music_MOD.js
@@ -8,7 +8,6 @@ module.exports = {
authorUrl: 'https://github.com/dbm-network/mods',
downloadURL: 'https://github.com/dbm-network/mods/blob/master/actions/control_music_MOD.js',
},
- requiresAudioLibraries: true,
fields: ['action', 'volume', 'bitrate'],
subtitle(data) {
@@ -92,7 +91,7 @@ module.exports = {
action(cache) {
const { Bot } = this.getDBM();
const data = cache.actions[cache.index];
- const queue = Bot.bot.player.getQueue(cache.server);
+ const server = cache.server;
const action = parseInt(data.action, 10);
const volume = parseInt(this.evalMessage(data.volume, cache), 10);
const bitrate = this.evalMessage(data.bitrate, cache);
@@ -102,36 +101,45 @@ module.exports = {
return this.callNexAction(cache);
}
+ if (!Bot.bot.queue) return this.callNextAction(cache);
+
+ const queue = Bot.bot.queue.get(server.id);
if (!queue) return this.callNextAction(cache);
try {
switch (action) {
case 0:
- queue.destroy();
+ queue.connection.disconnect();
break;
case 1:
- queue.setPaused(true);
+ queue.player.pause();
break;
case 2:
- queue.setPaused(false);
+ queue.player.unpause();
break;
case 3:
- queue.skip();
+ queue.player.stop();
break;
case 4:
- queue.back();
+ queue.currentIndex -= 2;
+ queue.player.stop();
break;
case 5:
- queue.destroy(false);
+ queue.songs = [];
break;
- case 6:
- queue.shuffle();
+ case 6: {
+ const currentIndex = queue.currentIndex + 1;
+ for (let i = queue.songs.length - 1; i > currentIndex; i--) {
+ const j = Math.floor(Math.random() * (i - currentIndex + 1)) + currentIndex;
+ [queue.songs[i], queue.songs[j]] = [queue.songs[j], queue.songs[i]];
+ }
break;
+ }
case 7:
- queue.setVolume(volume);
+ queue.player.state.resource.volume.setVolume(volume / 100);
break;
case 8:
- queue.setBitrate(bitrate);
+ queue.player.state.resource.encoder.setBitrate(bitrate);
break;
}
} catch (err) {
diff --git a/actions/play_music_MOD.js b/actions/play_music_MOD.js
index 74919287..6b239ab7 100644
--- a/actions/play_music_MOD.js
+++ b/actions/play_music_MOD.js
@@ -8,7 +8,6 @@ module.exports = {
authorUrl: 'https://github.com/dbm-network/mods',
downloadURL: 'https://github.com/dbm-network/mods/blob/master/actions/play_music_MOD.js',
},
- requiresAudioLibraries: true,
fields: ['query', 'voiceChannel', 'varName', 'storage', 'varName2', 'type', 'volume', 'leaveOnEmpty', 'leaveOnEnd'],
subtitle(data) {
@@ -24,8 +23,8 @@ module.exports = {
return `
- YouTube Search
-
+ YouTube URL
+
@@ -61,10 +60,7 @@ module.exports = {
NOTE:
- Youtube URLs and IDs are hit and miss due to using ytdl-core.
- In theory you should be able to use the following:
- Soundcloud URL, YouTube Search, YouTube song/playlist URL, YouTube ID,
- Spotify Song/playlist/album, vimeo, facebook and reverbnation.
+ Supports both videos and playlists.
`;
@@ -75,66 +71,190 @@ module.exports = {
async action(cache) {
const data = cache.actions[cache.index];
const server = cache.server;
- const channel = cache.msg?.channel ?? cache.interaction?.channel;
const { Bot, Files } = this.getDBM();
const Mods = this.getMods();
- const playdl = Mods.require('play-dl');
- const player = Bot.bot.player;
+ const ytdl = Mods.require('@distube/ytdl-core');
+ const ytpl = Mods.require('@distube/ytpl');
+ const {
+ joinVoiceChannel,
+ createAudioPlayer,
+ createAudioResource,
+ AudioPlayerStatus,
+ VoiceConnectionStatus,
+ } = require('@discordjs/voice');
const voiceChannel = await this.getVoiceChannelFromData(data.voiceChannel, data.varName, cache);
- let volume = 80;
- if (data.volume) volume = parseInt(this.evalMessage(data.volume, cache), 10) || 80;
+ if (!Bot.bot.queue) Bot.bot.queue = new Map();
- // leaveOnEnd & leaveOnEmpty Cooldowns from DBM Settings
+ const volume = parseInt(this.evalMessage(data.volume, cache), 10) || 80;
+ const leaveOnEnd = data.leaveOnEnd;
+ const leaveOnEmpty = data.leaveOnEmpty;
+ const autoDeafen = (Files.data.settings.autoDeafen ?? 'true') === 'true';
const leaveVoiceTimeout = Files.data.settings.leaveVoiceTimeout ?? '10';
let seconds = parseInt(leaveVoiceTimeout, 10);
-
if (isNaN(seconds) || seconds < 0) seconds = 0;
if (leaveVoiceTimeout === '' || !isFinite(seconds)) seconds = 0;
- // Needs to be converted to Milliseconds, keeping the same variable.
if (seconds > 0) seconds *= 1000;
const query = this.evalMessage(data.query, cache);
- const queue = player.createQueue(server, {
- metadata: {
- channel,
- },
- autoSelfDeaf: (Files.data.settings.autoDeafen ?? 'true') === 'true',
- initialVolume: volume,
- leaveOnEmpty: data.leaveOnEmpty,
- leaveOnEmptyCooldown: seconds,
- leaveOnEnd: data.leaveOnEnd,
- async onBeforeCreateStream(track, source) {
- if (source === 'youtube') {
- return (await playdl.stream(track.url, { discordPlayerCompatibility: true })).stream;
- }
- },
- });
- const track = await player.search(query, {
- requestedBy: cache.getUser(),
- });
+ const serverQueue = Bot.bot.queue.get(server.id);
+
+ let songs = [];
+
+ if (ytpl.validateID(query)) {
+ let playlist;
+ try {
+ playlist = await ytpl(query);
+ } catch (error) {
+ console.log(error);
+ return this.callNextAction(cache);
+ }
+
+ songs = playlist.items.map((item) => ({
+ title: item.title,
+ thumbnail: item.thumbnail,
+ url: item.shortUrl,
+ author: item.author.name,
+ duration: item.duration.split(':').reduce((acc, time) => 60 * acc + Number(time)),
+ requestedBy: cache.getUser().id,
+ }));
+ } else {
+ let songInfo;
+ try {
+ songInfo = await ytdl.getInfo(query);
+ } catch (error) {
+ console.log(error);
+ return this.callNextAction(cache);
+ }
+
+ songs.push({
+ title: songInfo.videoDetails.title,
+ thumbnail: songInfo.videoDetails.thumbnails[songInfo.videoDetails.thumbnails.length - 1].url,
+ url: songInfo.videoDetails.video_url,
+ author: songInfo.videoDetails.author.name,
+ duration: songInfo.videoDetails.lengthSeconds,
+ requestedBy: cache.getUser().id,
+ });
+ }
+
+ if (!serverQueue) {
+ const queueData = {
+ connection: null,
+ player: createAudioPlayer(),
+ songs: [],
+ currentIndex: 0,
+ repeatMode: 0,
+ };
+
+ Bot.bot.queue.set(server.id, queueData);
+ queueData.songs.push(...songs);
- if (track.tracks.length > 0) {
+ let connection;
try {
- if (!queue.connection) await queue.connect(voiceChannel);
+ connection = joinVoiceChannel({
+ channelId: voiceChannel.id,
+ guildId: server.id,
+ adapterCreator: server.voiceAdapterCreator,
+ selfDeaf: autoDeafen,
+ });
} catch {
- queue.destroy();
console.log('Could not join voice channel');
+ queueData.player.stop();
+ queueData.player.removeAllListeners();
+ Bot.bot.queue.delete(server.id);
return this.callNextAction(cache);
}
- track.playlist ? queue.addTracks(track.tracks) : queue.addTrack(track.tracks[0]);
- if (data.type === '1') {
- await queue.play();
- }
- if (data.type === '0') {
- if (!queue.playing) await queue.play();
+ queueData.connection = connection;
+ connection.subscribe(queueData.player);
+
+ const stream = ytdl(queueData.songs[queueData.currentIndex].url, {
+ filter: 'audioonly',
+ fmt: 'mp3',
+ highWaterMark: 1 << 30,
+ liveBuffer: 20000,
+ dlChunkSize: 1024 * 1024,
+ quality: 'lowestaudio',
+ bitrate: 128,
+ });
+ const resource = createAudioResource(stream, { inlineVolume: true });
+ resource.volume.setVolume(volume / 100);
+ queueData.player.play(resource);
+
+ queueData.player.on(AudioPlayerStatus.Idle, async () => {
+ let nextSongUrl;
+ if (queueData.repeatMode === 1) {
+ nextSongUrl = queueData.songs[queueData.currentIndex].url;
+ } else if (queueData.repeatMode === 2 && queueData.songs.length > 0) {
+ queueData.currentIndex = (queueData.currentIndex + 1) % queueData.songs.length;
+ nextSongUrl = queueData.songs[queueData.currentIndex].url;
+ } else {
+ queueData.currentIndex += 1;
+ if (queueData.currentIndex < queueData.songs.length) {
+ nextSongUrl = queueData.songs[queueData.currentIndex].url;
+ } else {
+ if (leaveOnEnd) {
+ connection.disconnect();
+ }
+ return;
+ }
+ }
+
+ const nextStream = ytdl(nextSongUrl, {
+ filter: 'audioonly',
+ fmt: 'mp3',
+ highWaterMark: 1 << 30,
+ liveBuffer: 20000,
+ dlChunkSize: 1024 * 1024,
+ quality: 'lowestaudio',
+ bitrate: 128,
+ });
+ const nextResource = createAudioResource(nextStream, { inlineVolume: true });
+ nextResource.volume.setVolume(volume / 100);
+ queueData.player.play(nextResource);
+ });
+
+ if (leaveOnEmpty) {
+ Bot.bot.on('voiceStateUpdate', (oldState, newState) => {
+ const botChannel = connection.joinConfig.channelId;
+ if (!botChannel) return;
+
+ const botVoiceChannel = server.channels.cache.get(botChannel);
+ if (botVoiceChannel && botVoiceChannel.members.size === 1) {
+ setTimeout(() => {
+ if (botVoiceChannel.members.size === 1) {
+ connection.disconnect();
+ }
+ }, seconds);
+ }
+ });
}
- const storage = parseInt(data.storage, 10);
- const varName2 = this.evalMessage(data.varName2, cache);
- this.storeValue(track, storage, varName2, cache);
+
+ Bot.bot.on('voiceStateUpdate', (oldState, newState) => {
+ if (oldState.channelId && !newState.channelId && oldState.member.id === Bot.bot.user.id) {
+ connection.disconnect();
+ }
+ });
+
+ connection.on(VoiceConnectionStatus.Disconnected, () => {
+ connection.destroy();
+ queueData.player.stop();
+ queueData.player.removeAllListeners();
+ Bot.bot.queue.delete(server.id);
+ });
+ } else if (data.type === '1') {
+ const currentSong = serverQueue.songs[serverQueue.currentIndex];
+ serverQueue.songs.splice(serverQueue.currentIndex + 1, 0, songs[0]);
+ serverQueue.songs.splice(serverQueue.currentIndex + 2, 0, currentSong);
+ serverQueue.player.stop();
+ } else {
+ serverQueue.songs.push(...songs);
}
+
+ const storage = parseInt(data.storage, 10);
+ const varName2 = this.evalMessage(data.varName2, cache);
+ this.storeValue(songs[0], storage, varName2, cache);
this.callNextAction(cache);
},
diff --git a/actions/play_youtube_playlist_MOD.js b/actions/play_youtube_playlist_MOD.js
deleted file mode 100644
index ebc79411..00000000
--- a/actions/play_youtube_playlist_MOD.js
+++ /dev/null
@@ -1,109 +0,0 @@
-module.exports = {
- name: 'Play YouTube Playlist',
- section: 'Audio Control',
- meta: {
- version: '2.1.7',
- preciseCheck: false,
- author: 'DBM Mods',
- authorUrl: 'https://github.com/dbm-network/mods',
- downloadURL: 'https://github.com/dbm-network/mods/blob/master/actions/play_youtube_playlist_MOD.js',
- },
-
- requiresAudioLibraries: true,
-
- subtitle(data) {
- return `${data.url}`;
- },
-
- fields: ['url', 'seek', 'volume', 'passes', 'bitrate', 'maxvid'],
-
- html() {
- return `
-
- YouTube Playlist
-
-
-
- Video Seek Positions
-
- Video Volume
-
- Max Videos to Queue
-
-
-
- Video Passes
-
- Video Bitrate
-
-
`;
- },
-
- init() {},
-
- async action(cache) {
- const data = cache.actions[cache.index];
- const { Audio } = this.getDBM();
- const Mods = this.getMods();
- const url = this.evalMessage(data.url, cache);
- const maxvideos = this.evalMessage(data.maxvid, cache) || 250;
- const ytpl = Mods.require('ytpl');
- const { msg } = cache;
- const options = {
- watermark: 'highWaterMark: 1', // idk what this does, but the queue data has it, so i might as well add it in case someone needs it
- };
-
- // Check Input
- if (!url) {
- return console.log('Please insert a playlist url!');
- }
-
- // Check Options
- if (data.seek) {
- options.seek = parseInt(this.evalMessage(data.seek, cache), 10);
- }
- if (data.volume) {
- options.volume = parseInt(this.evalMessage(data.volume, cache), 10) / 100;
- } else if (cache.server) {
- options.volume = Audio.volumes[cache.server.id] || 0.5;
- } else {
- options.volume = 0.5;
- }
- if (data.passes) {
- options.passes = parseInt(this.evalMessage(data.passes, cache), 10);
- }
- if (data.bitrate) {
- options.bitrate = parseInt(this.evalMessage(data.bitrate, cache), 10);
- } else {
- options.bitrate = 'auto';
- }
- if (msg) {
- options.requester = msg.author;
- }
- ytpl(url, { limit: maxvideos }).then((playlist) => {
- playlist.items.forEach((video) => {
- if (video.id !== undefined) {
- const { title } = video;
- const duration = parseInt(video.durationSec, 10);
- const thumbnail = video.bestThumbnail.url;
- Audio.addToQueue(
- [
- 'yt',
- {
- ...options,
- title,
- duration,
- thumbnail,
- },
- video.shortUrl,
- ],
- cache,
- );
- }
- });
- });
- this.callNextAction(cache);
- },
-
- mod() {},
-};
diff --git a/actions/remove_from_queue_MOD.js b/actions/remove_from_queue_MOD.js
index 568c52bb..2699d2e8 100644
--- a/actions/remove_from_queue_MOD.js
+++ b/actions/remove_from_queue_MOD.js
@@ -26,11 +26,11 @@ module.exports = {
`;
},
@@ -39,16 +39,18 @@ module.exports = {
async action(cache) {
const data = cache.actions[cache.index];
- const { Audio } = this.getDBM();
+ const { Bot } = this.getDBM();
const targetServer = await this.getServerFromData(data.server, data.varName, cache);
- const position = parseInt(this.evalMessage(data.position, cache), 10);
+ const position = parseInt(this.evalMessage(data.position, cache), 10) - 1;
const amount = parseInt(this.evalMessage(data.amount, cache), 10);
- let queue;
- if (targetServer) queue = Audio.queue[targetServer.id];
- if (queue && queue.length >= 1 && queue.length > amount + position) {
- queue.splice(position, amount);
- Audio.queue[targetServer.id] = queue;
+ if (!Bot.bot.queue) return this.callNextAction(cache);
+
+ const queue = Bot.bot.queue.get(targetServer.id);
+ if (!queue) return this.callNextAction(cache);
+
+ if (queue.songs && queue.songs.length > position && amount > 0) {
+ queue.songs.splice(position, amount);
}
this.callNextAction(cache);
},
diff --git a/actions/set_music_repeat_mode_MOD.js b/actions/set_music_repeat_mode_MOD.js
index 973e6beb..e0d87407 100644
--- a/actions/set_music_repeat_mode_MOD.js
+++ b/actions/set_music_repeat_mode_MOD.js
@@ -8,24 +8,22 @@ module.exports = {
authorUrl: 'https://github.com/dbm-network/mods',
downloadURL: 'https://github.com/dbm-network/mods/blob/master/actions/set_music_repeat_mode_MOD.js',
},
- requiresAudioLibraries: true,
fields: ['action'],
subtitle(data) {
- const actions = ['Off', 'Track', 'Queue', 'Autoplay'];
+ const actions = ['Off', 'Track', 'Queue'];
return `${actions[parseInt(data.action, 10)]}`;
},
html() {
return `
- Music Action
-
- Off
- Track
- Queue
- Autoplay
-
+ Repeat Mode
+
+ Off
+ Track
+ Queue
+
`;
},
@@ -34,25 +32,13 @@ module.exports = {
action(cache) {
const { Bot } = this.getDBM();
const data = cache.actions[cache.index];
- const queue = Bot.bot.player.getQueue(cache.server);
+ const server = cache.server;
+ const queue = Bot.bot.queue.get(server.id);
const action = parseInt(data.action, 10);
if (!queue) return this.callNextAction(cache);
- switch (action) {
- case 0: // Off
- queue.setRepeatMode(0);
- break;
- case 1: // Track
- queue.setRepeatMode(1);
- break;
- case 2: // Queue
- queue.setRepeatMode(2);
- break;
- case 3: // Autoplay
- queue.setRepeatMode(3);
- break;
- }
+ queue.repeatMode = action;
this.callNextAction(cache);
},
diff --git a/actions/store_queue_info_MOD.js b/actions/store_queue_info_MOD.js
index a60aae1a..cf5b4932 100644
--- a/actions/store_queue_info_MOD.js
+++ b/actions/store_queue_info_MOD.js
@@ -51,30 +51,55 @@ module.exports = {
? '
'
: ''
}
-
-
-Queue Info
-
- Tracks
- Previous Tracks
- Is Playing?
- Repeat Mode
- Progress Bar
- Formatted Track List
- Now Playing
- Queue Channel
- Queue Object
-
-
-
-
-
-
-
-`;
+
+
+ Queue Info
+
+ Tracks
+ Previous Tracks
+ Is Playing?
+ Repeat Mode
+ Progress Bar
+ Formatted Track List
+ Now Playing
+ Queue Channel
+ Queue Object
+
+
+
+
+
+
+
+
+
+
+ NOTE:
+
Repeat mode can be:
+ 0: Disabled
+ 1: Repeat Track
+ 2: Repeat Queue
+
+
+ `;
},
- init() {},
+ init() {
+ const { document } = this;
+
+ document.toggleRepeatText = function () {
+ const infoSelect = document.getElementById('info');
+ const repeatModeText = document.getElementById('repeatModeText');
+
+ if (infoSelect.value === '3') {
+ repeatModeText.style.display = 'block';
+ } else {
+ repeatModeText.style.display = 'none';
+ }
+ };
+
+ document.toggleRepeatText();
+ },
async action(cache) {
const data = cache.actions[cache.index];
@@ -90,35 +115,59 @@ module.exports = {
const server = cache.msg?.guildId ?? cache.interaction?.guildId;
if (!server) return this.callNextAction(cache);
- queue = Bot.bot.player.getQueue(server);
+ if (!Bot.bot.queue) return this.callNextAction(cache);
+
+ queue = Bot.bot.queue.get(server);
if (!queue) return this.callNextAction(cache);
}
let result;
switch (info) {
case 0:
- result = queue.tracks;
+ result = queue.songs;
break;
case 1:
- result = queue.previousTracks;
+ result = queue.songs.slice(0, queue.currentIndex);
break;
case 2:
- result = queue.playing;
+ result = queue.player.state.status === 'playing';
break;
case 3:
result = queue.repeatMode;
break;
- case 4:
- result = queue.createProgressBar({ timecodes: true });
+ case 4: {
+ const song = queue.songs[queue.currentIndex];
+ const currentTime = queue.player.state.resource.playbackDuration / 1000;
+ const totalTime = song ? parseInt(song.duration, 10) : 0;
+ const progressBarLength = 14;
+ const progress = Math.round((currentTime / totalTime) * progressBarLength);
+ const progressBar = `${`▬`.repeat(progress)}🔘${`▬`.repeat(progressBarLength - progress)}`;
+
+ const currentHours = Math.floor(currentTime / 3600);
+ const currentMinutes = Math.floor((currentTime % 3600) / 60);
+ const currentSeconds = Math.floor(currentTime % 60);
+
+ const totalHours = Math.floor(totalTime / 3600);
+ const totalMinutes = Math.floor((totalTime % 3600) / 60);
+ const totalSeconds = Math.floor(totalTime % 60);
+
+ result = `${currentHours > 0 ? `${currentHours}:` : ''}${
+ currentHours > 0 && currentMinutes < 10 ? '0' : ''
+ }${currentMinutes}:${currentSeconds < 10 ? '0' : ''}${currentSeconds} ┃ ${progressBar} ┃ ${
+ totalHours > 0 ? `${totalHours}:` : ''
+ }${totalHours > 0 && totalMinutes < 10 ? '0' : ''}${totalMinutes}:${
+ totalSeconds < 10 ? '0' : ''
+ }${totalSeconds}`;
break;
+ }
case 5:
- result = queue.toString();
+ result = queue.songs.map((song, index) => `${index + 1}. ${song.title} - ${song.author}`).join('\n');
break;
case 6:
- result = queue.nowPlaying();
+ result = queue.songs[queue.currentIndex];
break;
case 7:
- result = queue.metadata.channel;
+ result = queue.connection.channel;
break;
case 8:
result = queue;
diff --git a/actions/store_track_info_MOD.js b/actions/store_track_info_MOD.js
index 95d10b0b..d64d3ca5 100644
--- a/actions/store_track_info_MOD.js
+++ b/actions/store_track_info_MOD.js
@@ -12,7 +12,14 @@ module.exports = {
fields: ['trackObject', 'varName', 'info', 'storage', 'varName1'],
subtitle({ info }) {
- const names = ['Track Title', 'Track Thumbnail', 'Track URL', 'Track Author', 'Track Duration', 'Requested By'];
+ const names = [
+ 'Track Title',
+ 'Track Thumbnail',
+ 'Track URL',
+ 'Track Author',
+ 'Track Duration (In seconds)',
+ 'Requested By (User ID)',
+ ];
return `${names[parseInt(info, 10)]}`;
},
@@ -37,8 +44,8 @@ module.exports = {
Track Thumbnail
Track URL
Track Author
-
Track Duration
-
Requested By
+
Track Duration (In seconds)
+
Requested By (User ID)