aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/play.ts18
-rw-r--r--src/commands/queue.ts55
-rw-r--r--src/services/get-songs.ts4
-rw-r--r--src/services/natural-language-commands.ts7
-rw-r--r--src/services/player.ts23
-rw-r--r--src/services/queue.ts10
-rw-r--r--src/utils/get-progress-bar.ts15
-rw-r--r--src/utils/time.ts15
8 files changed, 123 insertions, 24 deletions
diff --git a/src/commands/play.ts b/src/commands/play.ts
index eceac94..eb972a8 100644
--- a/src/commands/play.ts
+++ b/src/commands/play.ts
@@ -48,11 +48,13 @@ export default class implements Command {
}
const queue = this.queueManager.get(msg.guild!.id);
+ const player = this.playerManager.get(msg.guild!.id);
const queueOldSize = queue.size();
+ const wasPlayingSong = queue.getCurrent() !== null;
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'));
return;
}
@@ -63,8 +65,8 @@ export default class implements Command {
return;
}
- await this.playerManager.get(msg.guild!.id).connect(targetVoiceChannel);
- await this.playerManager.get(msg.guild!.id).play();
+ await player.connect(targetVoiceChannel);
+ await player.play();
await res.stop('the stop-and-go light is now green');
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}`);
}
- if (this.playerManager.get(msg.guild!.id).voiceConnection === null) {
- await this.playerManager.get(msg.guild!.id).connect(targetVoiceChannel);
+ if (player.voiceConnection === null) {
+ await player.connect(targetVoiceChannel);
}
- if (queueOldSize === 0) {
- // Only auto-play if queue was empty before
- await this.playerManager.get(msg.guild!.id).play();
+ if (queueOldSize === 0 && !wasPlayingSong) {
+ // Only auto-play if queue was empty before and nothing was playing
+ await player.play();
}
}
}
diff --git a/src/commands/queue.ts b/src/commands/queue.ts
index 6c5cc63..1ec2016 100644
--- a/src/commands/queue.ts
+++ b/src/commands/queue.ts
@@ -1,8 +1,15 @@
-import {Message} from 'discord.js';
+import {Message, MessageEmbed} from 'discord.js';
import {TYPES} from '../types';
import {inject, injectable} from 'inversify';
import QueueManager from '../managers/queue';
+import PlayerManager from '../managers/player';
+import {STATUS} from '../services/player';
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()
export default class implements Command {
@@ -13,16 +20,54 @@ export default class implements Command {
];
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.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 player = this.playerManager.get(msg.guild!.id);
+
+ const currentlyPlaying = queue.getCurrent();
+
+ 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;
- await msg.channel.send('`' + JSON.stringify(queue.getCurrent()) + '`');
+ queue.get().slice(queuePageBegin, queuePageEnd).forEach((song, i) => {
+ embed.addField(`${(i + 1 + queuePageBegin).toString()}/${queueSize.toString()}`, song.title, false);
+ });
- await msg.channel.send('`' + JSON.stringify(queue.get().slice(0, 10)) + '`');
+ await msg.channel.send(embed);
+ } else {
+ await msg.channel.send('queue empty');
+ }
}
}
diff --git a/src/services/get-songs.ts b/src/services/get-songs.ts
index 347d9d6..08d5e90 100644
--- a/src/services/get-songs.ts
+++ b/src/services/get-songs.ts
@@ -100,7 +100,7 @@ export default class {
case '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};
@@ -109,7 +109,7 @@ export default class {
while (tracksResponse.next) {
// eslint-disable-next-line no-await-in-loop
({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)
}));
diff --git a/src/services/natural-language-commands.ts b/src/services/natural-language-commands.ts
index 5d5b7d5..af5ae0c 100644
--- a/src/services/natural-language-commands.ts
+++ b/src/services/natural-language-commands.ts
@@ -39,7 +39,7 @@ export default class {
await player.connect(channel);
}
- const isPlaying = queue.getCurrent();
+ const isPlaying = queue.getCurrent() !== null;
let oldPosition = 0;
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) => {
try {
setTimeout(async () => {
- if (isPlaying) {
- queue.back();
+ queue.removeCurrent();
+ if (isPlaying) {
await player.seek(oldPosition);
} else {
- queue.forward();
player.disconnect();
}
diff --git a/src/services/player.ts b/src/services/player.ts
index e4d54a6..19e01bb 100644
--- a/src/services/player.ts
+++ b/src/services/player.ts
@@ -21,6 +21,7 @@ export default class {
private dispatcher: StreamDispatcher | null = null;
private nowPlaying: QueuedSong | null = null;
private playPositionInterval: NodeJS.Timeout | undefined;
+ private lastSongURL = '';
private positionInSeconds = 0;
@@ -100,6 +101,7 @@ export default class {
if (this.dispatcher) {
this.dispatcher.resume();
this.status = STATUS.PLAYING;
+ this.startTrackingPosition();
return;
}
@@ -115,7 +117,13 @@ export default class {
this.status = STATUS.PLAYING;
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 {
@@ -235,7 +243,7 @@ export default class {
}
private startTrackingPosition(initalPosition?: number): void {
- if (initalPosition) {
+ if (initalPosition !== undefined) {
this.positionInSeconds = initalPosition;
}
@@ -270,9 +278,16 @@ export default class {
this.dispatcher.on('speaking', async isSpeaking => {
// Automatically advance queued song at end
if (!isSpeaking && this.status === STATUS.PLAYING) {
- if (this.queue.get().length > 0) {
+ if (this.queue.size() >= 0) {
this.queue.forward();
- await this.play();
+
+ this.positionInSeconds = 0;
+
+ if (this.queue.getCurrent()) {
+ await this.play();
+ } else {
+ this.status = STATUS.PAUSED;
+ }
}
}
});
diff --git a/src/services/queue.ts b/src/services/queue.ts
index 5db3b74..c5c8722 100644
--- a/src/services/queue.ts
+++ b/src/services/queue.ts
@@ -52,7 +52,7 @@ export default class {
this.queue.push(song);
} else {
// Not from playlist, add immediately
- let insertAt = 0;
+ let insertAt = this.position;
// Loop until playlist song
this.queue.some(song => {
@@ -83,6 +83,14 @@ export default class {
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 {
return this.get().length;
}
diff --git a/src/utils/get-progress-bar.ts b/src/utils/get-progress-bar.ts
new file mode 100644
index 0000000..7db3dd0
--- /dev/null
+++ b/src/utils/get-progress-bar.ts
@@ -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;
+};
diff --git a/src/utils/time.ts b/src/utils/time.ts
new file mode 100644
index 0000000..a54bfa7
--- /dev/null
+++ b/src/utils/time.ts
@@ -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;
+};