Guest User

Untitled

a guest
May 15th, 2025
44
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const {
  2.   joinVoiceChannel,
  3.   createAudioPlayer,
  4.   createAudioResource,
  5.   AudioPlayerStatus,
  6.   StreamType,
  7. } = require('@discordjs/voice');
  8. const { EmbedBuilder } = require('discord.js');
  9. const { getVideoTitle, searchTrack } = require('./yt-dlp');
  10. const { getTrackData, saveCache } = require('./cacheManager');
  11.  
  12. // Maps for per-guild states
  13. const playerMap = new Map();
  14. const queueMap = new Map();
  15. const playingMap = new Map();
  16. const volumeMap = new Map();
  17. const autoplayMap = new Map();
  18. const channelMap = new Map();
  19. const loopMap = new Map();
  20.  
  21. // Voice channel connection
  22. function joinChannel(channel, guild) {
  23.   return joinVoiceChannel({
  24.     channelId: channel.id,
  25.     guildId: guild.id,
  26.     adapterCreator: guild.voiceAdapterCreator,
  27.   });
  28. }
  29.  
  30. // Player creation
  31. function createPlayer() {
  32.   return createAudioPlayer();
  33. }
  34.  
  35. // Text channel mapping
  36. function setTextChannel(guildId, channel) {
  37.   channelMap.set(guildId, channel);
  38. }
  39.  
  40. function getTextChannel(guildId) {
  41.   return channelMap.get(guildId);
  42. }
  43.  
  44. // Create styled embed
  45. function createPinkEmbed(title, description = '', fields = [], footerText = 'Music Bot đŸŽĩ') {
  46.   return new EmbedBuilder()
  47.     .setColor('#FF69B4')
  48.     .setTitle(title)
  49.     .setDescription(description || 'No description provided')
  50.     .addFields(fields)
  51.     .setFooter({ text: footerText });
  52. }
  53.  
  54. // Add track to queue (fixed version)
  55. async function addToQueue(guildId, songQuery, message) {
  56.   const queue = queueMap.get(guildId) || [];
  57.   queueMap.set(guildId, queue);
  58.  
  59.   try {
  60.     const cached = await getTrackData(songQuery);
  61.     if (cached) {
  62.       queue.push(cached);
  63.       return {
  64.         embeds: [createPinkEmbed(
  65.           'đŸŽļ Added to queue (from cache)',
  66.           `**${cached.title}**`,
  67.           [
  68.             { name: 'Source', value: 'YouTube', inline: true },
  69.             { name: 'Requested by', value: message.author.username, inline: true },
  70.             { name: 'Link', value: `[Watch on YouTube](${cached.link})` },
  71.           ]
  72.         )],
  73.       };
  74.     }
  75.  
  76.     const track = await searchTrack(songQuery);
  77.     if (!track) {
  78.       return {
  79.         embeds: [createPinkEmbed('âš ī¸ Track not found', 'Try a different search query.')],
  80.       };
  81.     }
  82.  
  83.     const { title, audioUrl, videoId, thumbnail } = track;
  84.     const link = `https://youtube.com/watch?v=${videoId}`;
  85.  
  86.     const song = {
  87.       songQuery,
  88.       originalQuery: songQuery,
  89.       user: message.author.username,
  90.       title,
  91.       audioUrl,
  92.       link,
  93.       thumbnail,
  94.     };
  95.  
  96.     queue.push(song);
  97.     await saveCache({ [songQuery]: song });
  98.  
  99.     return {
  100.       embeds: [createPinkEmbed(
  101.         'đŸŽļ Added to queue',
  102.         `**${title}**`,
  103.         [
  104.           { name: 'Source', value: 'YouTube', inline: true },
  105.           { name: 'Requested by', value: message.author.username, inline: true },
  106.           { name: 'Link', value: `[Watch on YouTube](${link})` },
  107.         ]
  108.       )],
  109.     };
  110.   } catch (err) {
  111.     console.error('❌ Error in addToQueue:', err);
  112.     return {
  113.       embeds: [createPinkEmbed('âš ī¸ Failed to add track', 'Something went wrong. Try again.')],
  114.     };
  115.   }
  116. }
  117.  
  118. // Play the next track in queue
  119. async function handleQueue(player, connection, guildId) {
  120.   const queue = queueMap.get(guildId);
  121.   if (!queue?.length || playingMap.get(guildId)) return;
  122.  
  123.   const song = queue.shift();
  124.   const { title, user, audioUrl, link, thumbnail } = song;
  125.   playingMap.set(guildId, true);
  126.  
  127.   try {
  128.     const resource = createAudioResource(audioUrl, {
  129.       inputType: StreamType.Arbitrary,
  130.       metadata: { title },
  131.       inlineVolume: true,
  132.     });
  133.  
  134.     const volume = volumeMap.get(guildId) || 1.0;
  135.     resource.volume.setVolume(volume);
  136.  
  137.     player.play(resource);
  138.     connection.subscribe(player);
  139.  
  140.     const textChannel = getTextChannel(guildId);
  141.     if (textChannel) {
  142.       const videoId = getYouTubeID(link);
  143.       const embed = createPinkEmbed(
  144.         'đŸŽļ Now playing',
  145.         `**${title}**`,
  146.         [
  147.           { name: 'Source', value: 'YouTube', inline: true },
  148.           { name: 'Requested by', value: user, inline: true },
  149.           { name: 'Link', value: `[Watch on YouTube](${link})` },
  150.         ]
  151.       ).setThumbnail(thumbnail || `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`);
  152.  
  153.       textChannel.send({ embeds: [embed] });
  154.     }
  155.  
  156.     player.removeAllListeners();
  157.     player.once(AudioPlayerStatus.Idle, async () => {
  158.       if (loopMap.get(guildId)) queue.unshift(song);
  159.       playingMap.set(guildId, false);
  160.       await handleQueue(player, connection, guildId);
  161.     });
  162.  
  163.     player.once('error', error => {
  164.       console.error('❌ Audio Player Error:', error);
  165.       playingMap.set(guildId, false);
  166.       handleQueue(player, connection, guildId);
  167.     });
  168.   } catch (err) {
  169.     console.error('❌ Error handling queue:', err);
  170.     playingMap.set(guildId, false);
  171.     handleQueue(player, connection, guildId);
  172.   }
  173. }
  174.  
  175. // Helpers
  176. function getYouTubeID(url) {
  177.   const match = url.match(/(youtu\.be\/|v=)([^#&?]{11})/);
  178.   return match ? match[2] : null;
  179. }
  180.  
  181. function getPlayer(guildId) {
  182.   return playerMap.get(guildId);
  183. }
  184.  
  185. function setPlayer(guildId, player) {
  186.   if (player) playerMap.set(guildId, player);
  187.   else playerMap.delete(guildId);
  188. }
  189.  
  190. function skipSong(guildId) {
  191.   const player = getPlayer(guildId);
  192.   if (!player) return { embeds: [createPinkEmbed('âš ī¸ Nothing is playing')] };
  193.  
  194.   player.stop();
  195.   return { embeds: [createPinkEmbed('â­ī¸ Skipped to next song')] };
  196. }
  197.  
  198. function stopPlayback(guildId, connection) {
  199.   const player = getPlayer(guildId);
  200.   if (!player) return { embeds: [createPinkEmbed('âš ī¸ Nothing is playing')] };
  201.  
  202.   queueMap.set(guildId, []);
  203.   player.stop();
  204.   connection.destroy();
  205.   return { embeds: [createPinkEmbed('âšī¸ Stopped playback and cleared queue')] };
  206. }
  207.  
  208. function toggleAutoplay(guildId) {
  209.   const current = autoplayMap.get(guildId) || false;
  210.   autoplayMap.set(guildId, !current);
  211.   return {
  212.     embeds: [
  213.       createPinkEmbed(current ? '🔕 Autoplay disabled' : '🔁 Autoplay enabled'),
  214.     ],
  215.   };
  216. }
  217.  
  218. function toggleLoop(guildId) {
  219.   const current = loopMap.get(guildId) || false;
  220.   loopMap.set(guildId, !current);
  221.   return {
  222.     embeds: [
  223.       createPinkEmbed(current ? 'âžĄī¸ Loop disabled' : '🔁 Loop enabled'),
  224.     ],
  225.   };
  226. }
  227.  
  228. function setVolume(guildId, volume) {
  229.   const player = getPlayer(guildId);
  230.   if (!player) return { embeds: [createPinkEmbed('❌ No active player')] };
  231.  
  232.   const clamped = Math.min(Math.max(volume, 0.0), 2.0);
  233.   volumeMap.set(guildId, clamped);
  234.  
  235.   const resource = player.state.resource;
  236.   if (resource) resource.volume.setVolume(clamped);
  237.  
  238.   return { embeds: [createPinkEmbed('🔊 Volume set', `${Math.round(clamped * 100)}%`)] };
  239. }
  240.  
  241. // Exports
  242. module.exports = {
  243.   joinChannel,
  244.   createPlayer,
  245.   playSong: handleQueue,
  246.   getPlayer,
  247.   setPlayer,
  248.   addToQueue,
  249.   handleQueue,
  250.   skipSong,
  251.   stopPlayback,
  252.   toggleAutoplay,
  253.   toggleLoop,
  254.   setVolume,
  255.   setTextChannel,
  256.  
  257.   // Expose maps for advanced control/debug
  258.   queueMap,
  259.   playingMap,
  260.   playerMap,
  261.   loopMap,
  262. };
  263.  
Advertisement
Add Comment
Please, Sign In to add comment