Music player embed for Spotify

This commit is contained in:
2022-07-01 22:04:54 +07:00
parent 4c1293b319
commit 67c0c752a4
5 changed files with 149 additions and 66 deletions
+41 -24
View File
@@ -32,11 +32,13 @@ import DiscordMusicPlayer, {
VoiceDisconnectedEvent, VoiceDisconnectedEvent,
ValidTracks, ValidTracks,
DiscordMusicPlayerInstance, DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer'; } from '../../providers/DiscordMusicPlayer';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import { SpotifyTrack, YouTubeVideo } from 'play-dl';
const EMBEDS = { const EMBEDS = {
VOICECHANNEL_JOINED: (data: HybridInteractionMessage, locale: I18n) => { VOICECHANNEL_JOINED: (data: HybridInteractionMessage, locale: I18n) => {
@@ -81,35 +83,37 @@ const EMBEDS = {
user: data.getUser() user: data.getUser()
}); });
}, },
NOW_PLAYING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_PLAYING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing'), title: locale.__('musicplayer.now_playing'),
icon: '🎵 ', icon: '🎵 ',
description: track.title, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
NOW_REPEATING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_REPEATING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing_repeating'), title: locale.__('musicplayer.now_playing_repeating'),
icon: '🎵 ', icon: '🎵 ',
description: `${track.title}`, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
} }
@@ -227,24 +231,37 @@ export async function joinVoiceChannelProcedure(
if (event.instance.queue.track[0] !== previousTrack) isLoopMessageSent = false; if (event.instance.queue.track[0] !== previousTrack) isLoopMessageSent = false;
else if (event.instance.queue.track[0] === previousTrack && isLoopMessageSent) return; else if (event.instance.queue.track[0] === previousTrack && isLoopMessageSent) return;
const row = new ActionRowBuilder<ButtonBuilder>().addComponents( const row = new ActionRowBuilder<ButtonBuilder>();
new ButtonBuilder() if(event.instance.queue.track[0] instanceof SpotifyTrack)
.setEmoji('▶️') row.addComponents(
.setLabel('Open on YouTube') new ButtonBuilder()
.setURL(encodeURI(`https://www.youtube.com/watch?v=${event.instance.queue.track[0].id}`)) .setEmoji('🟢')
.setStyle(ButtonStyle.Link) .setLabel('Open on Spotify')
); .setURL(encodeURI(`https://open.spotify.com/track/${event.instance.queue.track[0].id}`))
.setStyle(ButtonStyle.Link),
)
if(event.instance.queue.track[0] instanceof YouTubeVideo || event.instance.queue.track[0] instanceof SpotifyTrack) {
const actualPlaybackURL = event.instance.getActualPlaybackURL();
if(event.instance.queue.track[0] instanceof SpotifyTrack && !actualPlaybackURL) return;
row.addComponents(
new ButtonBuilder()
.setEmoji('🔴')
.setLabel('Open on YouTube')
.setURL(event.instance.queue.track[0] instanceof YouTubeVideo ? encodeURI(`https://www.youtube.com/watch?v=${event.instance.queue.track[0].id}`) : encodeURI(event.instance.getActualPlaybackURL()!))
.setStyle(ButtonStyle.Link),
)
}
if (event.instance.textChannel) { if (event.instance.textChannel) {
if (event.instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current) { if (event.instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current) {
isLoopMessageSent = true; isLoopMessageSent = true;
await sendMessage(event.instance.textChannel, undefined, { await sendMessage(event.instance.textChannel, undefined, {
embeds: [EMBEDS.NOW_REPEATING(data, locale, event.instance.queue.track[0])], embeds: [await EMBEDS.NOW_REPEATING(data, locale, event.instance.queue.track[0])],
components: [row] components: [row]
}); });
} else { } else {
await sendMessage(event.instance.textChannel, undefined, { await sendMessage(event.instance.textChannel, undefined, {
embeds: [EMBEDS.NOW_PLAYING(data, locale, event.instance.queue.track[0])], embeds: [await EMBEDS.NOW_PLAYING(data, locale, event.instance.queue.track[0])],
components: [row] components: [row]
}); });
} }
+29 -14
View File
@@ -1,26 +1,29 @@
import { Message, CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { Message, CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import { I18n } from "i18n"; import { I18n } from "i18n";
import { SpotifyTrack, YouTubeVideo } from "play-dl";
import DiscordProvider from "../../providers/Discord"; import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
import { makeErrorEmbed, makeInfoEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage"; import { makeErrorEmbed, makeInfoEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
const EMBEDS = { const EMBEDS = {
NOW_PLAYING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_PLAYING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing'), title: locale.__('musicplayer.now_playing'),
icon: '🎵 ', icon: '🎵 ',
description: track.title, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => (prev.height * prev.width > current.height * current.width) ? prev : current) if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
if(highestResolutionThumbnail)
embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
@@ -55,15 +58,27 @@ export default class NowPlaying extends DiscordModule {
const instance = DiscordMusicPlayer.getGuildInstance(guild.id); const instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance || !instance.queue.track[0]) return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] }); if(!instance || !instance.queue.track[0]) return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] });
const row = new ActionRowBuilder<ButtonBuilder>() const row = new ActionRowBuilder<ButtonBuilder>();
.addComponents(
if(instance.queue.track[0] instanceof SpotifyTrack)
row.addComponents(
new ButtonBuilder() new ButtonBuilder()
.setEmoji('▶️') .setEmoji('🟢')
.setLabel('Open on YouTube') .setLabel('Open on Spotify')
.setURL(encodeURI(`https://www.youtube.com/watch?v=${instance.queue.track[0].id}`)) .setURL(encodeURI(`https://open.spotify.com/track/${instance.queue.track[0].id}`))
.setStyle(ButtonStyle.Link), .setStyle(ButtonStyle.Link),
) )
if(instance.queue.track[0] instanceof YouTubeVideo || instance.queue.track[0] instanceof SpotifyTrack) {
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NOW_PLAYING(data, locale, instance.queue.track[0])], components: [row] }); const actualPlaybackURL = instance.getActualPlaybackURL();
if(instance.queue.track[0] instanceof SpotifyTrack && !actualPlaybackURL) return;
row.addComponents(
new ButtonBuilder()
.setEmoji('🔴')
.setLabel('Open on YouTube')
.setURL(instance.queue.track[0] instanceof YouTubeVideo ? encodeURI(`https://www.youtube.com/watch?v=${instance.queue.track[0].id}`) : encodeURI(instance.getActualPlaybackURL()!))
.setStyle(ButtonStyle.Link),
)
}
return await sendHybridInteractionMessageResponse(data, { embeds: [await EMBEDS.NOW_PLAYING(data, locale, instance.queue.track[0])], components: [row] });
} }
} }
+68 -19
View File
@@ -14,7 +14,7 @@ import { I18n } from 'i18n';
import { joinVoiceChannelProcedure } from './Join'; import { joinVoiceChannelProcedure } from './Join';
import DiscordProvider from '../../providers/Discord'; import DiscordProvider from '../../providers/Discord';
import DiscordMusicPlayer, { ValidTracks } from '../../providers/DiscordMusicPlayer'; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
@@ -24,6 +24,7 @@ import {
sendHybridInteractionMessageResponse, sendHybridInteractionMessageResponse,
makeInfoEmbed makeInfoEmbed
} from '../../utils/DiscordMessage'; } from '../../utils/DiscordMessage';
import { SpotifyTrack, YouTubeVideo } from 'play-dl';
const EMBEDS = { const EMBEDS = {
PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => { PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => {
@@ -33,24 +34,25 @@ const EMBEDS = {
fields: [ fields: [
{ {
name: locale.__('common.available_args'), name: locale.__('common.available_args'),
value: locale.__('musicplayer_play.valid_args'), value: locale.__('musicplayer_play.valid_args')
} }
], ],
user: data.getUser() user: data.getUser()
}); });
}, },
ADDED_QUEUE: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { ADDED_QUEUE: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
let embed = makeSuccessEmbed({ let embed = makeSuccessEmbed({
title: locale.__('musicplayer_play.queue_added_song'), title: locale.__('musicplayer_play.queue_added_song'),
description: track.title, description: title,
user: data.getUser() user: data.getUser()
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
@@ -158,7 +160,7 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel); await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance) return; if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -223,7 +225,8 @@ export default class Play extends DiscordModule {
const voiceChannel = DiscordProvider.client.guilds.cache.get(guild.id)?.channels.cache.get(payload.d.v); const voiceChannel = DiscordProvider.client.guilds.cache.get(guild.id)?.channels.cache.get(payload.d.v);
//let member = DiscordProvider.client.guilds.cache.get(guild.id)?.members.cache.get(user.id); //let member = DiscordProvider.client.guilds.cache.get(guild.id)?.members.cache.get(user.id);
if (!voiceChannel || !(voiceChannel instanceof VoiceChannel || voiceChannel instanceof StageChannel)) return; if (!voiceChannel || !(voiceChannel instanceof VoiceChannel || voiceChannel instanceof StageChannel))
return;
if (!member.voice.channel) if (!member.voice.channel)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -236,12 +239,16 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel); await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance) return; if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
hybridData, hybridData,
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)] }, {
embeds: [
EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)
]
},
true true
); );
@@ -267,7 +274,7 @@ export default class Play extends DiscordModule {
instance.addTrackToQueue(result); instance.addTrackToQueue(result);
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
hybridData, hybridData,
{ embeds: [EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] }, { embeds: [await EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] },
true true
); );
} }
@@ -306,7 +313,7 @@ export default class Play extends DiscordModule {
} }
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance) return; if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -368,13 +375,55 @@ export default class Play extends DiscordModule {
); );
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)], embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)],
components: [row] components: [row]
}); });
} else } else
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)] embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)]
}); });
} else if (DiscordMusicPlayer.isSpotifyLink(query)) {
let linkData = DiscordMusicPlayer.parseSpotifyLink(query);
if (linkData.type === 'playlist' || linkData.type === 'album') {
let playlist = await DiscordMusicPlayer.getSpotifySongsInPlayList(query);
const songs = await playlist.all_tracks();
for (const song of songs) {
instance.addTrackToQueue(song);
}
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.ADDED_SONGS_QUEUE(data, locale, songs)] },
true
);
} else if (false) {
sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.LOOKUP_ERROR(data, locale, new Error('Album not supported for now'))] },
true
);
return null;
}
let result = await DiscordMusicPlayer.searchSpotifyBySpotifyLink(linkData).catch((err) => {
sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.LOOKUP_ERROR(data, locale, err)] }, true);
return null;
});
if (!result) return;
if (data.isMessage() && data.getMessage().embeds.length > 0) {
data.getMessage().suppressEmbeds(true);
}
instance.addTrackToQueue(result);
return await sendHybridInteractionMessageResponse(data, {
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)]
});
} else { } else {
let result = await DiscordMusicPlayer.searchYouTubeByQuery(query); let result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
@@ -407,13 +456,13 @@ export default class Play extends DiscordModule {
); );
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])], embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])],
components: [row] components: [row]
}); });
} }
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])] embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])]
}); });
} }
} }
+9 -7
View File
@@ -3,7 +3,8 @@ import { I18n } from 'i18n';
import DiscordMusicPlayer, { import DiscordMusicPlayer, {
DiscordMusicPlayerInstance, DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer'; } from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
@@ -21,14 +22,15 @@ const EMBEDS = {
user: data.getUser() user: data.getUser()
}); });
else { else {
const nowPlayingText = `${ const nowPlayingText = `${
instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current
? ` ${locale.__('musicplayer_queue.looping_current')}` ? ` ${locale.__('musicplayer_queue.looping_current')}`
: '' : ''
}: [${queue.track[0].title}](${queue.track[0].url})`; }: [${TrackUtils.getTitle(queue.track[0])}](${queue.track[0].url})`;
if (queue.track.length == 1) { if (queue.track.length == 1) {
let queueString = `1. [${queue.track[0].title}](${queue.track[0].url})`; let queueString = `1. [${TrackUtils.getTitle(queue.track[0])}](${queue.track[0].url})`;
return makeInfoEmbed({ return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'), title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n
@@ -36,10 +38,10 @@ const EMBEDS = {
user: data.getUser() user: data.getUser()
}); });
} else if (queue.track.length >= 10) { } else if (queue.track.length >= 10) {
const upcomingText = `[${queue.track[1].title}](${queue.track[1].url})`; const upcomingText = `[${TrackUtils.getTitle(queue.track[1])}](${queue.track[1].url})`;
const first10 = queue.track.slice(0, 10); const first10 = queue.track.slice(0, 10);
let queueString = first10 let queueString = first10
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`) .map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n'); .join('\n');
return makeInfoEmbed({ return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'), title: locale.__('musicplayer_queue.title'),
@@ -50,9 +52,9 @@ const EMBEDS = {
user: data.getUser() user: data.getUser()
}); });
} else { } else {
const upcomingText = `[${queue.track[1].title}](${queue.track[1].url})`; const upcomingText = `[${TrackUtils.getTitle(queue.track[1])}](${queue.track[1].url})`;
const queueString = queue.track const queueString = queue.track
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`) .map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n'); .join('\n');
return makeInfoEmbed({ return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'), title: locale.__('musicplayer_queue.title'),
+2 -2
View File
@@ -3,7 +3,7 @@ import { I18n } from "i18n";
import { joinVoiceChannelProcedure } from "./Join"; import { joinVoiceChannelProcedure } from "./Join";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
@@ -28,7 +28,7 @@ const EMBEDS = {
title: locale.__('musicplayer_search.title_result'), title: locale.__('musicplayer_search.title_result'),
description: `${locale.__('musicplayer_search.x_results_found', { description: `${locale.__('musicplayer_search.x_results_found', {
COUNT: result.length.toString() COUNT: result.length.toString()
})}\n\n${result.map(track => `- [${track.title}](${track.url})`).join('\n')}`, })}\n\n${result.map(track => `- [${TrackUtils.getTitle(track)}](${track.url})`).join('\n')}`,
user: data.getUser() user: data.getUser()
}); });
}, },