diff --git a/src/Player.ts b/src/Player.ts index 917bcbf..f1d3f49 100644 --- a/src/Player.ts +++ b/src/Player.ts @@ -1,11 +1,13 @@ import { Client, Collection, Guild, Snowflake } from "discord.js"; import { TypedEmitter as EventEmitter } from "tiny-typed-emitter"; import { Queue } from "./Structures/Queue"; +import { VoiceUtils } from "./VoiceInterface/VoiceUtils"; import { PlayerOptions } from "./types/types"; class DiscordPlayer extends EventEmitter { public readonly client: Client; public readonly queues = new Collection(); + public readonly voiceUtils = new VoiceUtils(); constructor(client: Client) { super(); diff --git a/src/Structures/Playlist.ts b/src/Structures/Playlist.ts index 69eafab..44f1643 100644 --- a/src/Structures/Playlist.ts +++ b/src/Structures/Playlist.ts @@ -9,7 +9,6 @@ class Playlist { this.player = player; this.tracks = tracks ?? []; } - } export { Playlist }; diff --git a/src/Structures/Queue.ts b/src/Structures/Queue.ts index abe7073..b86b96b 100644 --- a/src/Structures/Queue.ts +++ b/src/Structures/Queue.ts @@ -1,6 +1,5 @@ import { Guild, StageChannel, VoiceChannel } from "discord.js"; import { Player } from "../Player"; -import { VoiceUtils } from "../VoiceInterface/VoiceUtils"; import { VoiceSubscription } from "../VoiceInterface/VoiceSubscription"; import Track from "./Track"; import { PlayerOptions } from "../types/types"; @@ -36,10 +35,14 @@ class Queue { ); } + get current() { + return this.voiceConnection.audioResource?.metadata ?? this.tracks[0]; + } + async joinVoiceChannel(channel: StageChannel | VoiceChannel) { if (!["stage", "voice"].includes(channel.type)) throw new TypeError(`Channel type must be voice or stage, got ${channel.type}!`); - const connection = await VoiceUtils.connect(channel); + const connection = await this.player.voiceUtils.connect(channel); this.voiceConnection = connection; return this; @@ -50,6 +53,10 @@ class Queue { this.voiceConnection.disconnect(); this.player.queues.delete(this.guild.id); } + + play() { + throw new Error("Not implemented"); + } } export { Queue }; diff --git a/src/VoiceInterface/VoiceSubscription.ts b/src/VoiceInterface/VoiceSubscription.ts index db782d3..fb12731 100644 --- a/src/VoiceInterface/VoiceSubscription.ts +++ b/src/VoiceInterface/VoiceSubscription.ts @@ -12,6 +12,8 @@ import { } from "@discordjs/voice"; import { Duplex, Readable } from "stream"; import { TypedEmitter as EventEmitter } from "tiny-typed-emitter"; +import Track from "../Structures/Track"; +import PlayerError from "../utils/PlayerError"; export interface VoiceEvents { error: (error: AudioPlayerError) => any; @@ -24,6 +26,7 @@ class VoiceSubscription extends EventEmitter { public readonly voiceConnection: VoiceConnection; public readonly audioPlayer: AudioPlayer; public connectPromise?: Promise; + public audioResource?: AudioResource; constructor(connection: VoiceConnection) { super(); @@ -79,11 +82,13 @@ class VoiceSubscription extends EventEmitter { * @returns {AudioResource} */ createStream(src: Readable | Duplex | string, ops?: { type?: StreamType; data?: any; inlineVolume?: boolean }) { - return createAudioResource(src, { + this.audioResource = createAudioResource(src, { inputType: ops?.type ?? StreamType.Arbitrary, metadata: ops?.data, inlineVolume: Boolean(ops?.inlineVolume) }); + + return this.audioResource; } /** @@ -119,11 +124,18 @@ class VoiceSubscription extends EventEmitter { * Play stream * @param {AudioResource} resource The audio resource to play */ - playStream(resource: AudioResource) { + playStream(resource: AudioResource = this.audioResource) { + if (!resource) throw new PlayerError("Audio resource is not available!"); + if (!this.audioResource && resource) this.audioResource = resource; this.audioPlayer.play(resource); return this; } + + get streamTime() { + if (!this.audioResource) return 0; + return this.audioResource.playbackDuration; + } } export { VoiceSubscription }; diff --git a/src/VoiceInterface/VoiceUtils.ts b/src/VoiceInterface/VoiceUtils.ts index 3d3189e..093af34 100644 --- a/src/VoiceInterface/VoiceUtils.ts +++ b/src/VoiceInterface/VoiceUtils.ts @@ -1,11 +1,9 @@ -import { VoiceChannel, StageChannel } from "discord.js"; +import { VoiceChannel, StageChannel, Collection, Snowflake } from "discord.js"; import { entersState, joinVoiceChannel, VoiceConnection, VoiceConnectionStatus } from "@discordjs/voice"; import { VoiceSubscription } from "./VoiceSubscription"; class VoiceUtils { - constructor() { - throw new Error("Cannot instantiate static class!"); - } + public cache = new Collection(); /** * Joins a voice channel @@ -13,7 +11,7 @@ class VoiceUtils { * @param {({deaf?: boolean;maxTime?: number;})} [options] Join options * @returns {Promise} */ - public static async connect( + public async connect( channel: VoiceChannel | StageChannel, options?: { deaf?: boolean; @@ -29,7 +27,9 @@ class VoiceUtils { try { conn = await entersState(conn, VoiceConnectionStatus.Ready, options?.maxTime ?? 20000); - return new VoiceSubscription(conn); + const sub = new VoiceSubscription(conn); + this.cache.set(channel.guild.id, sub); + return sub; } catch (err) { conn.destroy(); throw err; @@ -40,10 +40,14 @@ class VoiceUtils { * Disconnects voice connection * @param {VoiceConnection} connection The voice connection */ - public static disconnect(connection: VoiceConnection | VoiceSubscription) { + public disconnect(connection: VoiceConnection | VoiceSubscription) { if (connection instanceof VoiceSubscription) return connection.voiceConnection.destroy(); return connection.destroy(); } + + public getConnection(guild: Snowflake) { + return this.cache.get(guild); + } } export { VoiceUtils };