add missing methods
This commit is contained in:
parent
8c50146300
commit
249a7e71ac
3 changed files with 299 additions and 12 deletions
288
src/Player.ts
288
src/Player.ts
|
@ -1,6 +1,6 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Client, Collection, Snowflake, Collector, Message, VoiceChannel } from 'discord.js';
|
import { Client, Collection, Snowflake, Collector, Message, VoiceChannel, VoiceState } from 'discord.js';
|
||||||
import { PlayerOptions, QueueFilters } from './types/types';
|
import { PlayerOptions, PlayerProgressbarOptions, QueueFilters } from './types/types';
|
||||||
import Util from './utils/Util';
|
import Util from './utils/Util';
|
||||||
import AudioFilters from './utils/AudioFilters';
|
import AudioFilters from './utils/AudioFilters';
|
||||||
import { Queue } from './Structures/Queue';
|
import { Queue } from './Structures/Queue';
|
||||||
|
@ -25,9 +25,9 @@ export class Player extends EventEmitter {
|
||||||
public client!: Client;
|
public client!: Client;
|
||||||
public options: PlayerOptions;
|
public options: PlayerOptions;
|
||||||
public filters: typeof AudioFilters;
|
public filters: typeof AudioFilters;
|
||||||
public queues: Collection<Snowflake, Queue>;
|
public queues = new Collection<Snowflake, Queue>();
|
||||||
private _resultsCollectors: Collection<string, Collector<Snowflake, Message>>;
|
private _resultsCollectors = new Collection<string, Collector<Snowflake, Message>>();
|
||||||
private _cooldownsTimeout: Collection<string, NodeJS.Timeout>;
|
private _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
||||||
public Extractors = new Collection<string, ExtractorModel>();
|
public Extractors = new Collection<string, ExtractorModel>();
|
||||||
|
|
||||||
constructor(client: Client, options?: PlayerOptions) {
|
constructor(client: Client, options?: PlayerOptions) {
|
||||||
|
@ -54,13 +54,7 @@ export class Player extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
this.filters = AudioFilters;
|
this.filters = AudioFilters;
|
||||||
|
|
||||||
/**
|
this.client.on('voiceStateUpdate', (o, n) => this._handleVoiceStateUpdate(o, n));
|
||||||
* Player queues
|
|
||||||
*/
|
|
||||||
this.queues = new Collection();
|
|
||||||
|
|
||||||
this._resultsCollectors = new Collection();
|
|
||||||
this._cooldownsTimeout = new Collection();
|
|
||||||
|
|
||||||
['Attachment', 'Facebook', 'Reverbnation', 'Vimeo'].forEach((ext) => this.use(ext, DP_EXTRACTORS[ext]));
|
['Attachment', 'Facebook', 'Reverbnation', 'Vimeo'].forEach((ext) => this.use(ext, DP_EXTRACTORS[ext]));
|
||||||
}
|
}
|
||||||
|
@ -508,6 +502,276 @@ export class Player extends EventEmitter {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pause(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.dispatcher) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.MUSIC_STARTING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.voiceConnection.dispatcher.pause();
|
||||||
|
queue.paused = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resume(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.dispatcher) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.MUSIC_STARTING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.voiceConnection.dispatcher.resume();
|
||||||
|
queue.paused = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.dispatcher) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.MUSIC_STARTING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.stopped = true;
|
||||||
|
queue.tracks = [];
|
||||||
|
if (queue.stream) queue.stream.destroy();
|
||||||
|
queue.voiceConnection.dispatcher.end();
|
||||||
|
if (this.options.leaveOnStop) queue.voiceConnection.channel.leave();
|
||||||
|
this.queues.delete(message.guild.id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolume(message: Message, percent: number) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.dispatcher) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.MUSIC_STARTING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.volume = percent;
|
||||||
|
queue.voiceConnection.dispatcher.setVolumeLogarithmic(queue.calculatedVolume / 200);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearQueue(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.tracks = queue.playing ? [queue.playing] : [];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
back(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.dispatcher) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.MUSIC_STARTING, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.tracks.splice(1, 0, queue.previousTracks.shift());
|
||||||
|
queue.voiceConnection.dispatcher.end();
|
||||||
|
queue.lastSkipped = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRepeatMode(message: Message, enabled: boolean) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.repeatMode = Boolean(enabled);
|
||||||
|
|
||||||
|
return queue.repeatMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoopMode(message: Message, enabled: boolean) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.loopMode = Boolean(enabled);
|
||||||
|
|
||||||
|
return queue.loopMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
nowPlaying(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queue.tracks[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle(message: Message) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTrack = queue.tracks.shift();
|
||||||
|
|
||||||
|
for (let i = queue.tracks.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[queue.tracks[i], queue.tracks[j]] = [queue.tracks[j], queue.tracks[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.tracks.unshift(currentTrack);
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(message: Message, track: Track | number) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) {
|
||||||
|
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let trackFound: Track = null;
|
||||||
|
if (typeof track === 'number') {
|
||||||
|
trackFound = queue.tracks[track];
|
||||||
|
if (trackFound) {
|
||||||
|
queue.tracks = queue.tracks.filter((t) => t !== trackFound);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trackFound = queue.tracks.find((s) => s === track);
|
||||||
|
if (trackFound) {
|
||||||
|
queue.tracks = queue.tracks.filter((s) => s !== trackFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trackFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeCode(message: Message, queueTime?: boolean) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) return;
|
||||||
|
|
||||||
|
const previousTracksTime =
|
||||||
|
queue.previousTracks.length > 0 ? queue.previousTracks.reduce((p, c) => p + c.durationMS, 0) : 0;
|
||||||
|
const currentStreamTime = queueTime ? previousTracksTime + queue.currentStreamTime : queue.currentStreamTime;
|
||||||
|
const totalTracksTime = queue.totalTime;
|
||||||
|
const totalTime = queueTime ? previousTracksTime + totalTracksTime : queue.playing.durationMS;
|
||||||
|
|
||||||
|
const currentTimecode = Util.buildTimeCode(Util.parseMS(currentStreamTime));
|
||||||
|
const endTimecode = Util.buildTimeCode(Util.parseMS(totalTime));
|
||||||
|
|
||||||
|
return {
|
||||||
|
current: currentTimecode,
|
||||||
|
end: endTimecode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createProgressBar(message: Message, options?: PlayerProgressbarOptions) {
|
||||||
|
const queue = this.getQueue(message);
|
||||||
|
if (!queue) return;
|
||||||
|
|
||||||
|
const previousTracksTime =
|
||||||
|
queue.previousTracks.length > 0 ? queue.previousTracks.reduce((p, c) => p + c.durationMS, 0) : 0;
|
||||||
|
const currentStreamTime = options?.queue
|
||||||
|
? previousTracksTime + queue.currentStreamTime
|
||||||
|
: queue.currentStreamTime;
|
||||||
|
const totalTracksTime = queue.totalTime;
|
||||||
|
const totalTime = options?.queue ? previousTracksTime + totalTracksTime : queue.playing.durationMS;
|
||||||
|
const length =
|
||||||
|
typeof options?.length === 'number'
|
||||||
|
? options?.length <= 0 || options?.length === Infinity
|
||||||
|
? 15
|
||||||
|
: options?.length
|
||||||
|
: options?.length;
|
||||||
|
|
||||||
|
const index = Math.round((currentStreamTime / totalTime) * length);
|
||||||
|
const indicator = '🔘';
|
||||||
|
const line = '▬';
|
||||||
|
|
||||||
|
if (index >= 1 && index <= length) {
|
||||||
|
const bar = line.repeat(length - 1).split('');
|
||||||
|
bar.splice(index, 0, indicator);
|
||||||
|
if (Boolean(options?.timecodes)) {
|
||||||
|
const currentTimecode = Util.buildTimeCode(Util.parseMS(currentStreamTime));
|
||||||
|
const endTimecode = Util.buildTimeCode(Util.parseMS(totalTime));
|
||||||
|
return `${currentTimecode} ┃ ${bar.join('')} ┃ ${endTimecode}`;
|
||||||
|
} else {
|
||||||
|
return `${bar.join('')}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Boolean(options?.timecodes)) {
|
||||||
|
const currentTimecode = Util.buildTimeCode(Util.parseMS(currentStreamTime));
|
||||||
|
const endTimecode = Util.buildTimeCode(Util.parseMS(totalTime));
|
||||||
|
return `${currentTimecode} ┃ ${indicator}${line.repeat(length)} ┃ ${endTimecode}`;
|
||||||
|
} else {
|
||||||
|
return `${indicator}${line.repeat(length)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
|
||||||
|
const queue = this.queues.find((g) => g.guildID === oldState.guild.id);
|
||||||
|
if (!queue) return;
|
||||||
|
|
||||||
|
if (newState.member.id === this.client.user.id && !newState.channelID) {
|
||||||
|
queue.stream.destroy();
|
||||||
|
this.queues.delete(newState.guild.id);
|
||||||
|
this.emit(PlayerEvents.BOT_DISCONNECT, queue.firstMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue.voiceConnection || !queue.voiceConnection.channel) return;
|
||||||
|
if (!this.options.leaveOnEmpty) return;
|
||||||
|
|
||||||
|
if (!oldState.channelID || newState.channelID) {
|
||||||
|
const emptyTimeout = this._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
|
||||||
|
const channelEmpty = Util.isVoiceEmpty(queue.voiceConnection.channel);
|
||||||
|
if (!channelEmpty && emptyTimeout) {
|
||||||
|
clearTimeout(emptyTimeout);
|
||||||
|
this._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!Util.isVoiceEmpty(queue.voiceConnection.channel)) return;
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
if (!Util.isVoiceEmpty(queue.voiceConnection.channel)) return;
|
||||||
|
if (!this.queues.has(queue.guildID)) return;
|
||||||
|
queue.voiceConnection.channel.leave();
|
||||||
|
this.queues.delete(queue.guildID);
|
||||||
|
this.emit(PlayerEvents.CHANNEL_EMPTY, queue.firstMessage, queue);
|
||||||
|
}, this.options.leaveOnEmptyCooldown || 0);
|
||||||
|
this._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _addTrackToQueue(message: Message, track: Track) {
|
private _addTrackToQueue(message: Message, track: Track) {
|
||||||
const queue = this.getQueue(message);
|
const queue = this.getQueue(message);
|
||||||
if (!queue)
|
if (!queue)
|
||||||
|
|
|
@ -88,3 +88,9 @@ export interface ExtractorModelData {
|
||||||
url: string;
|
url: string;
|
||||||
version?: string;
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlayerProgressbarOptions {
|
||||||
|
timecodes?: boolean;
|
||||||
|
queue?: boolean;
|
||||||
|
length?: number;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import YouTube from 'youtube-sr';
|
||||||
import { Track } from '../Structures/Track';
|
import { Track } from '../Structures/Track';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { validateURL as SoundcloudValidateURL } from 'soundcloud-scraper';
|
import { validateURL as SoundcloudValidateURL } from 'soundcloud-scraper';
|
||||||
|
import { VoiceChannel } from 'discord.js';
|
||||||
|
|
||||||
const spotifySongRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/;
|
const spotifySongRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/;
|
||||||
const spotifyPlaylistRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})/;
|
const spotifyPlaylistRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})/;
|
||||||
|
@ -145,6 +146,22 @@ export class Util {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isVoiceEmpty(channel: VoiceChannel) {
|
||||||
|
return channel.members.filter((member) => !member.user.bot).size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildTimeCode(data: any) {
|
||||||
|
const items = Object.keys(data);
|
||||||
|
const required = ['days', 'hours', 'minutes', 'seconds'];
|
||||||
|
|
||||||
|
const parsed = items.filter((x) => required.includes(x)).map((m) => (data[m] > 0 ? data[m] : ''));
|
||||||
|
const final = parsed
|
||||||
|
.filter((x) => !!x)
|
||||||
|
.map((x) => x.toString().padStart(2, '0'))
|
||||||
|
.join(':');
|
||||||
|
return final.length <= 3 ? `0:${final.padStart(2, '0') || 0}` : final;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Util;
|
export default Util;
|
||||||
|
|
Loading…
Reference in a new issue