import getYouTubeID from 'get-youtube-id'; import {EmbedBuilder} from 'discord.js'; import Player, {MediaSource, QueuedSong, STATUS} from '../services/player.js'; import getProgressBar from './get-progress-bar.js'; import {prettyTime} from './time.js'; import {truncate} from './string.js'; const PAGE_SIZE = 10; const getMaxSongTitleLength = (title: string) => { // eslint-disable-next-line no-control-regex const nonASCII = /[^\x00-\x7F]+/; return nonASCII.test(title) ? 28 : 48; }; const getSongTitle = ({title, url, offset, source}: QueuedSong, shouldTruncate = false) => { if (source === MediaSource.HLS) { return `[${title}](${url})`; } const cleanSongTitle = title.replace(/\[.*\]/, '').trim(); const songTitle = shouldTruncate ? truncate(cleanSongTitle, getMaxSongTitleLength(cleanSongTitle)) : cleanSongTitle; const youtubeId = url.length === 11 ? url : getYouTubeID(url) ?? ''; return `[${songTitle}](https://www.youtube.com/watch?v=${youtubeId}${offset === 0 ? '' : '&t=' + String(offset)})`; }; const getQueueInfo = (player: Player) => { const queueSize = player.queueSize(); if (queueSize === 0) { return '-'; } return queueSize === 1 ? '1 song' : `${queueSize} songs`; }; const getPlayerUI = (player: Player) => { const song = player.getCurrent(); if (!song) { return ''; } const position = player.getPosition(); const button = player.status === STATUS.PLAYING ? '⏚ī¸' : 'â–ļī¸'; const progressBar = getProgressBar(15, position / song.length); const elapsedTime = song.isLive ? 'live' : `${prettyTime(position)}/${prettyTime(song.length)}`; const loop = player.loopCurrentSong ? '🔂' : player.loopCurrentQueue ? '🔁' : ''; return `${button} ${progressBar} \`[${elapsedTime}]\` 🔉 ${loop}`; }; export const buildPlayingMessageEmbed = (player: Player): EmbedBuilder => { const currentlyPlaying = player.getCurrent(); if (!currentlyPlaying) { throw new Error('No playing song found'); } const {artist, thumbnailUrl, requestedBy} = currentlyPlaying; const message = new EmbedBuilder(); message .setColor(player.status === STATUS.PLAYING ? 'DarkGreen' : 'DarkRed') .setTitle(player.status === STATUS.PLAYING ? 'Now Playing' : 'Paused') .setDescription(` **${getSongTitle(currentlyPlaying)}** Requested by: <@${requestedBy}>\n ${getPlayerUI(player)} `) .setFooter({text: `Source: ${artist}`}); if (thumbnailUrl) { message.setThumbnail(thumbnailUrl); } return message; }; export const buildQueueEmbed = (player: Player, page: number): EmbedBuilder => { const currentlyPlaying = player.getCurrent(); if (!currentlyPlaying) { throw new Error('queue is empty'); } const queueSize = player.queueSize(); const maxQueuePage = Math.ceil((queueSize + 1) / PAGE_SIZE); if (page > maxQueuePage) { throw new Error('the queue isn\'t that big'); } const queuePageBegin = (page - 1) * PAGE_SIZE; const queuePageEnd = queuePageBegin + PAGE_SIZE; const queuedSongs = player .getQueue() .slice(queuePageBegin, queuePageEnd) .map((song, index) => { const songNumber = index + 1 + queuePageBegin; const duration = song.isLive ? 'live' : prettyTime(song.length); return `\`${songNumber}.\` ${getSongTitle(song, true)} \`[${duration}]\``; }) .join('\n'); const {artist, thumbnailUrl, playlist, requestedBy} = currentlyPlaying; const playlistTitle = playlist ? `(${playlist.title})` : ''; const totalLength = player.getQueue().reduce((accumulator, current) => accumulator + current.length, 0); const message = new EmbedBuilder(); let description = `**${getSongTitle(currentlyPlaying)}**\n`; description += `Requested by: <@${requestedBy}>\n\n`; description += `${getPlayerUI(player)}\n\n`; if (player.getQueue().length > 0) { description += '**Up next:**\n'; description += queuedSongs; } message .setTitle(player.status === STATUS.PLAYING ? `Now Playing ${player.loopCurrentSong ? '(loop on)' : ''}` : 'Queued songs') .setColor(player.status === STATUS.PLAYING ? 'DarkGreen' : 'NotQuiteBlack') .setDescription(description) .addFields([{name: 'In queue', value: getQueueInfo(player), inline: true}, { name: 'Total length', value: `${totalLength > 0 ? prettyTime(totalLength) : '-'}`, inline: true, }, {name: 'Page', value: `${page} out of ${maxQueuePage}`, inline: true}]) .setFooter({text: `Source: ${artist} ${playlistTitle}`}); if (thumbnailUrl) { message.setThumbnail(thumbnailUrl); } return message; };