permanent-waves/discordeno-audio-plugin/src/player/queue-player.ts

134 lines
3.3 KiB
TypeScript

import { Queue } from "../../utils/mod.ts";
import { AudioSource, LoadSource } from "../audio-source/mod.ts";
import { ConnectionData } from "../connection-data.ts";
import { PlayerEventSource, AllEventTypes, PlayerListener } from "./events.ts";
import { RawPlayer } from "./raw-player.ts";
import { Player } from "./types.ts";
import { nowPlayingCallback } from "../../../commands/np.ts";
export class QueuePlayer
extends Queue<AudioSource>
implements Player<AudioSource>
{
playing = true;
looping = false;
playingSince?: number;
nowPlaying?: AudioSource;
#rawPlayer: RawPlayer;
#loadSource: LoadSource;
#events = new PlayerEventSource<AudioSource, AllEventTypes>();
constructor(conn: ConnectionData, loadSource: LoadSource) {
super();
this.#loadSource = loadSource;
this.#rawPlayer = new RawPlayer(conn);
this.playNext();
this.#rawPlayer.on("done", async () => {
const current = this.current();
if (current) {
this.#events.trigger("done", current);
}
await this.playNext();
if (!this.playing) {
this.pause();
}
});
}
async playNext() {
let song;
const current = this.current();
if (this.looping && current !== undefined) {
song = current;
this.#events.trigger("loop", song);
} else {
song = await super.next();
this.#events.trigger("next", song);
}
this.playingSince = Date.now();
this.nowPlaying = song;
this.#rawPlayer.setAudio(await song.data());
await nowPlayingCallback(this.#rawPlayer.conn);
}
clear() {
return super.clear();
}
play() {
this.playing = true;
this.#rawPlayer.play();
return Promise.resolve();
}
pause() {
this.playing = false;
this.#rawPlayer.pause();
}
stop() {
this.skip();
this.pause();
}
skip() {
this.looping = false;
this.#rawPlayer.clear();
}
loop(value: boolean) {
this.looping = value;
}
stopInterrupt() {
this.#rawPlayer.interrupt(undefined);
}
/**
* Listen to events:
*
* `next`: New sound started playing
*
* `done`: Last sound is done playing
*
* `loop`: New loop iteration was started
* @param event Event to listen to
* @param listener Triggered on event
* @returns Function that disconnects the listener
*/
on<J extends AllEventTypes>(
event: J,
listener: PlayerListener<AudioSource, J>
) {
return this.#events.on(event, listener);
}
/**
* Interrupts the current song, resumes when finished
* @param query Loads a universal song (local file or youtube search)
*/
async interruptQuery(guildId: bigint, query: string) {
const sources = await this.#loadSource(query as string, guildId);
this.#rawPlayer.interrupt(await sources[0].data());
}
async pushQuery(guildId: bigint, added_by?: string, ...queries: string[]) {
const sources = [];
for (const query of queries) {
sources.push(...(await this.#loadSource(query as string, guildId, String(added_by))));
this.push(...sources);
}
return sources;
}
async unshiftQuery(guildId: bigint, ...queries: string[]) {
const sources = [];
for (const query of queries) {
sources.push(...(await this.#loadSource(query as string, guildId)));
this.unshift(...sources);
}
return sources;
}
}