mirror of
https://github.com/BluemediaDev/muse.git
synced 2025-04-19 21:03:56 +02:00
Add queue output, various bug fixes
This commit is contained in:
parent
0357373123
commit
7f39642c49
8 changed files with 123 additions and 24 deletions
|
@ -48,11 +48,13 @@ export default class implements Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queue = this.queueManager.get(msg.guild!.id);
|
const queue = this.queueManager.get(msg.guild!.id);
|
||||||
|
const player = this.playerManager.get(msg.guild!.id);
|
||||||
|
|
||||||
const queueOldSize = queue.size();
|
const queueOldSize = queue.size();
|
||||||
|
const wasPlayingSong = queue.getCurrent() !== null;
|
||||||
|
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
if (this.playerManager.get(msg.guild!.id).status === STATUS.PLAYING) {
|
if (player.status === STATUS.PLAYING) {
|
||||||
await res.stop(errorMsg('already playing, give me a song name'));
|
await res.stop(errorMsg('already playing, give me a song name'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,8 +65,8 @@ export default class implements Command {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.playerManager.get(msg.guild!.id).connect(targetVoiceChannel);
|
await player.connect(targetVoiceChannel);
|
||||||
await this.playerManager.get(msg.guild!.id).play();
|
await player.play();
|
||||||
|
|
||||||
await res.stop('the stop-and-go light is now green');
|
await res.stop('the stop-and-go light is now green');
|
||||||
return;
|
return;
|
||||||
|
@ -141,13 +143,13 @@ export default class implements Command {
|
||||||
await res.stop(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${extraMsg}`);
|
await res.stop(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${extraMsg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.playerManager.get(msg.guild!.id).voiceConnection === null) {
|
if (player.voiceConnection === null) {
|
||||||
await this.playerManager.get(msg.guild!.id).connect(targetVoiceChannel);
|
await player.connect(targetVoiceChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queueOldSize === 0) {
|
if (queueOldSize === 0 && !wasPlayingSong) {
|
||||||
// Only auto-play if queue was empty before
|
// Only auto-play if queue was empty before and nothing was playing
|
||||||
await this.playerManager.get(msg.guild!.id).play();
|
await player.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
import {Message} from 'discord.js';
|
import {Message, MessageEmbed} from 'discord.js';
|
||||||
import {TYPES} from '../types';
|
import {TYPES} from '../types';
|
||||||
import {inject, injectable} from 'inversify';
|
import {inject, injectable} from 'inversify';
|
||||||
import QueueManager from '../managers/queue';
|
import QueueManager from '../managers/queue';
|
||||||
|
import PlayerManager from '../managers/player';
|
||||||
|
import {STATUS} from '../services/player';
|
||||||
import Command from '.';
|
import Command from '.';
|
||||||
|
import getProgressBar from '../utils/get-progress-bar';
|
||||||
|
import errorMsg from '../utils/error-msg';
|
||||||
|
import {prettyTime} from '../utils/time';
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class implements Command {
|
export default class implements Command {
|
||||||
|
@ -13,16 +20,54 @@ export default class implements Command {
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly queueManager: QueueManager;
|
private readonly queueManager: QueueManager;
|
||||||
|
private readonly playerManager: PlayerManager;
|
||||||
|
|
||||||
constructor(@inject(TYPES.Managers.Queue) queueManager: QueueManager) {
|
constructor(@inject(TYPES.Managers.Queue) queueManager: QueueManager, @inject(TYPES.Managers.Player) playerManager: PlayerManager) {
|
||||||
this.queueManager = queueManager;
|
this.queueManager = queueManager;
|
||||||
|
this.playerManager = playerManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async execute(msg: Message, _: string []): Promise<void> {
|
public async execute(msg: Message, args: string []): Promise<void> {
|
||||||
const queue = this.queueManager.get(msg.guild!.id);
|
const queue = this.queueManager.get(msg.guild!.id);
|
||||||
|
const player = this.playerManager.get(msg.guild!.id);
|
||||||
|
|
||||||
await msg.channel.send('`' + JSON.stringify(queue.getCurrent()) + '`');
|
const currentlyPlaying = queue.getCurrent();
|
||||||
|
|
||||||
await msg.channel.send('`' + JSON.stringify(queue.get().slice(0, 10)) + '`');
|
if (currentlyPlaying) {
|
||||||
|
const queueSize = queue.size();
|
||||||
|
const queuePage = args[0] ? parseInt(args[0], 10) : 1;
|
||||||
|
|
||||||
|
if (queuePage * PAGE_SIZE > queueSize && queuePage > Math.ceil((queueSize + 1) / PAGE_SIZE)) {
|
||||||
|
await msg.channel.send(errorMsg('the queue isn\'t that big'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new MessageEmbed();
|
||||||
|
|
||||||
|
embed.setTitle(currentlyPlaying.title);
|
||||||
|
embed.setURL(`https://www.youtube.com/watch?v=${currentlyPlaying.url}`);
|
||||||
|
embed.setFooter(`Source: ${currentlyPlaying.artist}`);
|
||||||
|
|
||||||
|
let description = player.status === STATUS.PLAYING ? '⏹️' : '▶️';
|
||||||
|
description += ' ';
|
||||||
|
description += getProgressBar(20, player.getPosition() / currentlyPlaying.length);
|
||||||
|
description += ' ';
|
||||||
|
description += `\`[${prettyTime(player.getPosition())}/${prettyTime(currentlyPlaying.length)}]\``;
|
||||||
|
description += ' 🔉';
|
||||||
|
description += queue.isEmpty() ? '' : '\n\n**Next up:**';
|
||||||
|
|
||||||
|
embed.setDescription(description);
|
||||||
|
|
||||||
|
const queuePageBegin = (queuePage - 1) * PAGE_SIZE;
|
||||||
|
const queuePageEnd = queuePageBegin + PAGE_SIZE;
|
||||||
|
|
||||||
|
queue.get().slice(queuePageBegin, queuePageEnd).forEach((song, i) => {
|
||||||
|
embed.addField(`${(i + 1 + queuePageBegin).toString()}/${queueSize.toString()}`, song.title, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await msg.channel.send(embed);
|
||||||
|
} else {
|
||||||
|
await msg.channel.send('queue empty');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ export default class {
|
||||||
case 'playlist': {
|
case 'playlist': {
|
||||||
const uri = parsed as spotifyURI.Playlist;
|
const uri = parsed as spotifyURI.Playlist;
|
||||||
|
|
||||||
let [{body: playlistResponse}, {body: tracksResponse}] = await Promise.all([this.spotify.getPlaylist(uri.id), this.spotify.getPlaylistTracks(uri.id, {limit: 1})]);
|
let [{body: playlistResponse}, {body: tracksResponse}] = await Promise.all([this.spotify.getPlaylist(uri.id), this.spotify.getPlaylistTracks(uri.id, {limit: 50})]);
|
||||||
|
|
||||||
playlist = {title: playlistResponse.name, source: playlistResponse.href};
|
playlist = {title: playlistResponse.name, source: playlistResponse.href};
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ export default class {
|
||||||
while (tracksResponse.next) {
|
while (tracksResponse.next) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
({body: tracksResponse} = await this.spotify.getPlaylistTracks(uri.id, {
|
({body: tracksResponse} = await this.spotify.getPlaylistTracks(uri.id, {
|
||||||
limit: parseInt(new URL(tracksResponse.next).searchParams.get('limit') ?? '1', 10),
|
limit: parseInt(new URL(tracksResponse.next).searchParams.get('limit') ?? '50', 10),
|
||||||
offset: parseInt(new URL(tracksResponse.next).searchParams.get('offset') ?? '0', 10)
|
offset: parseInt(new URL(tracksResponse.next).searchParams.get('offset') ?? '0', 10)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default class {
|
||||||
await player.connect(channel);
|
await player.connect(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPlaying = queue.getCurrent();
|
const isPlaying = queue.getCurrent() !== null;
|
||||||
let oldPosition = 0;
|
let oldPosition = 0;
|
||||||
|
|
||||||
queue.add({title: 'GO PACKERS!', artist: 'Unknown', url: 'https://www.youtube.com/watch?v=qkdtID7mY3E', length: 204, playlist: null, isLive: false});
|
queue.add({title: 'GO PACKERS!', artist: 'Unknown', url: 'https://www.youtube.com/watch?v=qkdtID7mY3E', length: 204, playlist: null, isLive: false});
|
||||||
|
@ -54,12 +54,11 @@ export default class {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
if (isPlaying) {
|
queue.removeCurrent();
|
||||||
queue.back();
|
|
||||||
|
|
||||||
|
if (isPlaying) {
|
||||||
await player.seek(oldPosition);
|
await player.seek(oldPosition);
|
||||||
} else {
|
} else {
|
||||||
queue.forward();
|
|
||||||
player.disconnect();
|
player.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default class {
|
||||||
private dispatcher: StreamDispatcher | null = null;
|
private dispatcher: StreamDispatcher | null = null;
|
||||||
private nowPlaying: QueuedSong | null = null;
|
private nowPlaying: QueuedSong | null = null;
|
||||||
private playPositionInterval: NodeJS.Timeout | undefined;
|
private playPositionInterval: NodeJS.Timeout | undefined;
|
||||||
|
private lastSongURL = '';
|
||||||
|
|
||||||
private positionInSeconds = 0;
|
private positionInSeconds = 0;
|
||||||
|
|
||||||
|
@ -100,6 +101,7 @@ export default class {
|
||||||
if (this.dispatcher) {
|
if (this.dispatcher) {
|
||||||
this.dispatcher.resume();
|
this.dispatcher.resume();
|
||||||
this.status = STATUS.PLAYING;
|
this.status = STATUS.PLAYING;
|
||||||
|
this.startTrackingPosition();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +117,13 @@ export default class {
|
||||||
this.status = STATUS.PLAYING;
|
this.status = STATUS.PLAYING;
|
||||||
this.nowPlaying = currentSong;
|
this.nowPlaying = currentSong;
|
||||||
|
|
||||||
this.startTrackingPosition();
|
if (currentSong.url === this.lastSongURL) {
|
||||||
|
this.startTrackingPosition();
|
||||||
|
} else {
|
||||||
|
// Reset position counter
|
||||||
|
this.startTrackingPosition(0);
|
||||||
|
this.lastSongURL = currentSong.url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pause(): void {
|
pause(): void {
|
||||||
|
@ -235,7 +243,7 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
private startTrackingPosition(initalPosition?: number): void {
|
private startTrackingPosition(initalPosition?: number): void {
|
||||||
if (initalPosition) {
|
if (initalPosition !== undefined) {
|
||||||
this.positionInSeconds = initalPosition;
|
this.positionInSeconds = initalPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,9 +278,16 @@ export default class {
|
||||||
this.dispatcher.on('speaking', async isSpeaking => {
|
this.dispatcher.on('speaking', async isSpeaking => {
|
||||||
// Automatically advance queued song at end
|
// Automatically advance queued song at end
|
||||||
if (!isSpeaking && this.status === STATUS.PLAYING) {
|
if (!isSpeaking && this.status === STATUS.PLAYING) {
|
||||||
if (this.queue.get().length > 0) {
|
if (this.queue.size() >= 0) {
|
||||||
this.queue.forward();
|
this.queue.forward();
|
||||||
await this.play();
|
|
||||||
|
this.positionInSeconds = 0;
|
||||||
|
|
||||||
|
if (this.queue.getCurrent()) {
|
||||||
|
await this.play();
|
||||||
|
} else {
|
||||||
|
this.status = STATUS.PAUSED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default class {
|
||||||
this.queue.push(song);
|
this.queue.push(song);
|
||||||
} else {
|
} else {
|
||||||
// Not from playlist, add immediately
|
// Not from playlist, add immediately
|
||||||
let insertAt = 0;
|
let insertAt = this.position;
|
||||||
|
|
||||||
// Loop until playlist song
|
// Loop until playlist song
|
||||||
this.queue.some(song => {
|
this.queue.some(song => {
|
||||||
|
@ -83,6 +83,14 @@ export default class {
|
||||||
this.queue = newQueue;
|
this.queue = newQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeCurrent(): void {
|
||||||
|
this.queue = [...this.queue.slice(0, this.position), ...this.queue.slice(this.position + 1)];
|
||||||
|
|
||||||
|
if (this.position !== 0) {
|
||||||
|
this.position--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size(): number {
|
size(): number {
|
||||||
return this.get().length;
|
return this.get().length;
|
||||||
}
|
}
|
||||||
|
|
15
src/utils/get-progress-bar.ts
Normal file
15
src/utils/get-progress-bar.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export default (width: number, progress: number): string => {
|
||||||
|
const dotPosition = Math.floor(width * progress);
|
||||||
|
|
||||||
|
let res = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < width; i++) {
|
||||||
|
if (i === dotPosition) {
|
||||||
|
res += '🔘';
|
||||||
|
} else {
|
||||||
|
res += '▬';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
15
src/utils/time.ts
Normal file
15
src/utils/time.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
export const prettyTime = (seconds: number): string => {
|
||||||
|
const nSeconds = seconds % 60;
|
||||||
|
const nMinutes = Math.floor(seconds / 60);
|
||||||
|
const nHours = Math.floor(nMinutes / 60);
|
||||||
|
|
||||||
|
let res = '';
|
||||||
|
|
||||||
|
if (nHours !== 0) {
|
||||||
|
res += `${Math.round(nHours).toString().padStart(2, '0')}:`;
|
||||||
|
}
|
||||||
|
|
||||||
|
res += `${Math.round(nMinutes).toString().padStart(2, '0')}:${Math.round(nSeconds).toString().padStart(2, '0')}`;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue