mirror of
https://github.com/BluemediaGER/muse.git
synced 2024-11-23 09:15:29 +01:00
Add pause/resume
This commit is contained in:
parent
e55acbb718
commit
2875c6ceb8
29
src/commands/pause.ts
Normal file
29
src/commands/pause.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import {Message} from 'discord.js';
|
||||||
|
import {TYPES} from '../types';
|
||||||
|
import {inject, injectable} from 'inversify';
|
||||||
|
import PlayerManager from '../managers/player';
|
||||||
|
import {STATUS} from '../services/player';
|
||||||
|
import Command from '.';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export default class implements Command {
|
||||||
|
public name = 'pause';
|
||||||
|
public description = 'pause currently playing song';
|
||||||
|
private readonly playerManager: PlayerManager;
|
||||||
|
|
||||||
|
constructor(@inject(TYPES.Managers.Player) playerManager: PlayerManager) {
|
||||||
|
this.playerManager = playerManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async execute(msg: Message, _: string []): Promise<void> {
|
||||||
|
const player = this.playerManager.get(msg.guild!.id);
|
||||||
|
|
||||||
|
if (player.status !== STATUS.PLAYING) {
|
||||||
|
await msg.channel.send('error: not currently playing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.pause();
|
||||||
|
await msg.channel.send('paused');
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,29 @@ export default class implements Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async execute(msg: Message, args: string []): Promise<void> {
|
public async execute(msg: Message, args: string []): Promise<void> {
|
||||||
|
const queue = this.queueManager.get(msg.guild!.id);
|
||||||
|
|
||||||
|
if (args.length === 0) {
|
||||||
|
if (this.playerManager.get(msg.guild!.id).status === STATUS.PLAYING) {
|
||||||
|
await msg.channel.send('error: already playing, give me a song name');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be resuming play
|
||||||
|
if (queue.get().length === 0) {
|
||||||
|
await msg.channel.send('error: nothing to play');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = getMostPopularVoiceChannel(msg.guild!);
|
||||||
|
|
||||||
|
await this.playerManager.get(msg.guild!.id).connect(channel);
|
||||||
|
await this.playerManager.get(msg.guild!.id).play();
|
||||||
|
|
||||||
|
await msg.channel.send('play resuming');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const newSongs: QueuedSong[] = [];
|
const newSongs: QueuedSong[] = [];
|
||||||
|
|
||||||
const res = new LoadingMessage(msg.channel as TextChannel, 'hold on a sec');
|
const res = new LoadingMessage(msg.channel as TextChannel, 'hold on a sec');
|
||||||
|
|
|
@ -24,6 +24,7 @@ import Command from './commands';
|
||||||
import Clear from './commands/clear';
|
import Clear from './commands/clear';
|
||||||
import Config from './commands/config';
|
import Config from './commands/config';
|
||||||
import ForwardSeek from './commands/fseek';
|
import ForwardSeek from './commands/fseek';
|
||||||
|
import Pause from './commands/pause';
|
||||||
import Play from './commands/play';
|
import Play from './commands/play';
|
||||||
import QueueCommad from './commands/queue';
|
import QueueCommad from './commands/queue';
|
||||||
import Seek from './commands/seek';
|
import Seek from './commands/seek';
|
||||||
|
@ -45,6 +46,7 @@ container.bind<QueueManager>(TYPES.Managers.Queue).to(QueueManager).inSingletonS
|
||||||
container.bind<Command>(TYPES.Command).to(Clear).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(Clear).inSingletonScope();
|
||||||
container.bind<Command>(TYPES.Command).to(Config).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(Config).inSingletonScope();
|
||||||
container.bind<Command>(TYPES.Command).to(ForwardSeek).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(ForwardSeek).inSingletonScope();
|
||||||
|
container.bind<Command>(TYPES.Command).to(Pause).inSingletonScope();
|
||||||
container.bind<Command>(TYPES.Command).to(Play).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(Play).inSingletonScope();
|
||||||
container.bind<Command>(TYPES.Command).to(QueueCommad).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(QueueCommad).inSingletonScope();
|
||||||
container.bind<Command>(TYPES.Command).to(Seek).inSingletonScope();
|
container.bind<Command>(TYPES.Command).to(Seek).inSingletonScope();
|
||||||
|
|
|
@ -20,8 +20,8 @@ export default class {
|
||||||
private readonly cacheDir: string;
|
private readonly cacheDir: string;
|
||||||
private voiceConnection: VoiceConnection | null = null;
|
private voiceConnection: VoiceConnection | null = null;
|
||||||
private dispatcher: StreamDispatcher | null = null;
|
private dispatcher: StreamDispatcher | null = null;
|
||||||
|
private playPositionInterval: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
private lastStreamTime = 0;
|
|
||||||
private positionInSeconds = 0;
|
private positionInSeconds = 0;
|
||||||
|
|
||||||
constructor(queue: Queue, cacheDir: string) {
|
constructor(queue: Queue, cacheDir: string) {
|
||||||
|
@ -37,6 +37,10 @@ export default class {
|
||||||
|
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
if (this.voiceConnection) {
|
if (this.voiceConnection) {
|
||||||
|
if (this.status === STATUS.PLAYING) {
|
||||||
|
this.pause();
|
||||||
|
}
|
||||||
|
|
||||||
this.voiceConnection.disconnect();
|
this.voiceConnection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,9 +58,12 @@ export default class {
|
||||||
|
|
||||||
await this.waitForCache(currentSong.url);
|
await this.waitForCache(currentSong.url);
|
||||||
|
|
||||||
this.attachListeners(this.voiceConnection.play(this.getCachedPath(currentSong.url), {seek: positionSeconds}));
|
this.dispatcher = this.voiceConnection.play(this.getCachedPath(currentSong.url), {seek: positionSeconds});
|
||||||
|
|
||||||
this.positionInSeconds = positionSeconds;
|
this.attachListeners();
|
||||||
|
this.startTrackingPosition(positionSeconds);
|
||||||
|
|
||||||
|
this.status = STATUS.PLAYING;
|
||||||
}
|
}
|
||||||
|
|
||||||
async forwardSeek(positionSeconds: number): Promise<void> {
|
async forwardSeek(positionSeconds: number): Promise<void> {
|
||||||
|
@ -73,9 +80,14 @@ export default class {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume from paused state
|
// Resume from paused state
|
||||||
if (this.status === STATUS.PAUSED && this.dispatcher) {
|
if (this.status === STATUS.PAUSED) {
|
||||||
this.dispatcher.resume();
|
if (this.dispatcher) {
|
||||||
this.status = STATUS.PLAYING;
|
this.dispatcher.resume();
|
||||||
|
this.status = STATUS.PLAYING;
|
||||||
|
} else {
|
||||||
|
await this.seek(this.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,27 +97,32 @@ export default class {
|
||||||
throw new Error('Queue empty.');
|
throw new Error('Queue empty.');
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatcher: StreamDispatcher;
|
|
||||||
|
|
||||||
if (await this.isCached(currentSong.url)) {
|
if (await this.isCached(currentSong.url)) {
|
||||||
dispatcher = this.voiceConnection.play(this.getCachedPath(currentSong.url));
|
this.dispatcher = this.voiceConnection.play(this.getCachedPath(currentSong.url));
|
||||||
} else {
|
} else {
|
||||||
const stream = await this.getStream(currentSong.url);
|
const stream = await this.getStream(currentSong.url);
|
||||||
dispatcher = this.voiceConnection.play(stream, {type: 'webm/opus'});
|
this.dispatcher = this.voiceConnection.play(stream, {type: 'webm/opus'});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attachListeners(dispatcher);
|
this.attachListeners();
|
||||||
|
|
||||||
this.status = STATUS.PLAYING;
|
this.status = STATUS.PLAYING;
|
||||||
this.dispatcher = dispatcher;
|
|
||||||
|
this.startTrackingPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
pause(): void {
|
pause(): void {
|
||||||
if (!this.dispatcher || this.status !== STATUS.PLAYING) {
|
if (this.status !== STATUS.PLAYING) {
|
||||||
throw new Error('Not currently playing.');
|
throw new Error('Not currently playing.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatcher.pause();
|
this.status = STATUS.PAUSED;
|
||||||
|
|
||||||
|
if (this.dispatcher) {
|
||||||
|
this.dispatcher.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopTrackingPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCurrentSong(): QueuedSong|null {
|
private getCurrentSong(): QueuedSong|null {
|
||||||
|
@ -235,12 +252,45 @@ export default class {
|
||||||
return capacitor.createReadStream();
|
return capacitor.createReadStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private attachListeners(stream: StreamDispatcher): void {
|
private startTrackingPosition(initalPosition?: number): void {
|
||||||
stream.on('speaking', async isSpeaking => {
|
if (initalPosition) {
|
||||||
// Update position
|
this.positionInSeconds = initalPosition;
|
||||||
this.positionInSeconds += (stream.streamTime - this.lastStreamTime) / 1000;
|
}
|
||||||
this.lastStreamTime = stream.streamTime;
|
|
||||||
|
|
||||||
|
if (this.playPositionInterval) {
|
||||||
|
clearInterval(this.playPositionInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playPositionInterval = setInterval(() => {
|
||||||
|
this.positionInSeconds++;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopTrackingPosition(): void {
|
||||||
|
if (this.playPositionInterval) {
|
||||||
|
clearInterval(this.playPositionInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private attachListeners(): void {
|
||||||
|
if (!this.voiceConnection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.voiceConnection.on('disconnect', () => {
|
||||||
|
// Automatically pause
|
||||||
|
if (this.status === STATUS.PLAYING) {
|
||||||
|
this.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatcher = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.dispatcher) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.get().length > 0) {
|
||||||
|
@ -249,12 +299,5 @@ export default class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stream.on('close', () => {
|
|
||||||
// Remove dispatcher from guild player
|
|
||||||
this.dispatcher = null;
|
|
||||||
|
|
||||||
// TODO: set voiceConnection null as well?
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue