This commit is contained in:
Snowflake107 2021-04-06 19:53:01 +05:45
parent c7e6962219
commit 2fb209ad28
3 changed files with 89 additions and 51 deletions

View file

@ -7,8 +7,8 @@ import AudioFilters from './utils/AudioFilters';
import Queue from './Structures/Queue'; import Queue from './Structures/Queue';
import Track from './Structures/Track'; import Track from './Structures/Track';
import { PlayerErrorEventCodes, PlayerEvents } from './utils/Constants'; import { PlayerErrorEventCodes, PlayerEvents } from './utils/Constants';
import PlayerError from "./utils/PlayerError"; import PlayerError from './utils/PlayerError';
import ytdl from "discord-ytdl-core"; import ytdl from 'discord-ytdl-core';
// @ts-ignore // @ts-ignore
import spotify from 'spotify-url-info'; import spotify from 'spotify-url-info';
@ -59,11 +59,7 @@ export default class Player extends EventEmitter {
return AudioFilters; return AudioFilters;
} }
private _searchTracks( private _searchTracks(message: Message, query: string, firstResult?: boolean): Promise<Track> {
message: Message,
query: string,
firstResult?: boolean
): Promise<Track> {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
let tracks: Track[] = []; let tracks: Track[] = [];
const queryType = Util.getQueryType(query); const queryType = Util.getQueryType(query);
@ -151,15 +147,15 @@ export default class Player extends EventEmitter {
} }
async play(message: Message, query: string | Track, firstResult?: boolean) { async play(message: Message, query: string | Track, firstResult?: boolean) {
if (!message) throw new PlayerError("Play function needs message"); if (!message) throw new PlayerError('Play function needs message');
if (!query) throw new PlayerError("Play function needs search query as a string or Player.Track object"); if (!query) throw new PlayerError('Play function needs search query as a string or Player.Track object');
if (this._cooldownsTimeout.has(`end_${message.guild.id}`)) { if (this._cooldownsTimeout.has(`end_${message.guild.id}`)) {
clearTimeout(this._cooldownsTimeout.get(`end_${message.guild.id}`)); clearTimeout(this._cooldownsTimeout.get(`end_${message.guild.id}`));
this._cooldownsTimeout.delete(`end_${message.guild.id}`); this._cooldownsTimeout.delete(`end_${message.guild.id}`);
} }
if (typeof query === "string") query = query.replace(/<(.+)>/g, '$1'); if (typeof query === 'string') query = query.replace(/<(.+)>/g, '$1');
let track; let track;
if (query instanceof Track) track = query; if (query instanceof Track) track = query;
@ -167,7 +163,13 @@ export default class Player extends EventEmitter {
if (ytdl.validateURL(query)) { if (ytdl.validateURL(query)) {
const info = await ytdl.getBasicInfo(query).catch(() => {}); const info = await ytdl.getBasicInfo(query).catch(() => {});
if (!info) return this.emit(PlayerEvents.NO_RESULTS, message, query); if (!info) return this.emit(PlayerEvents.NO_RESULTS, message, query);
if (info.videoDetails.isLiveContent && !this.options.enableLive) return this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.LIVE_VIDEO, message, new PlayerError("Live video is not enabled!")); if (info.videoDetails.isLiveContent && !this.options.enableLive)
return this.emit(
PlayerEvents.ERROR,
PlayerErrorEventCodes.LIVE_VIDEO,
message,
new PlayerError('Live video is not enabled!')
);
const lastThumbnail = info.videoDetails.thumbnails[info.videoDetails.thumbnails.length - 1]; const lastThumbnail = info.videoDetails.thumbnails[info.videoDetails.thumbnails.length - 1];
track = new Track(this, { track = new Track(this, {
@ -180,7 +182,7 @@ export default class Player extends EventEmitter {
views: parseInt(info.videoDetails.viewCount), views: parseInt(info.videoDetails.viewCount),
requestedBy: message.author, requestedBy: message.author,
fromPlaylist: false, fromPlaylist: false,
source: "youtube" source: 'youtube'
}); });
} else { } else {
track = await this._searchTracks(message, query, firstResult); track = await this._searchTracks(message, query, firstResult);
@ -199,16 +201,22 @@ export default class Player extends EventEmitter {
} }
isPlaying(message: Message) { isPlaying(message: Message) {
return this.queues.some(g => g.guildID === message.guild.id); return this.queues.some((g) => g.guildID === message.guild.id);
} }
getQueue(message: Message) { getQueue(message: Message) {
return this.queues.find(g => g.guildID === message.guild.id); return this.queues.find((g) => g.guildID === message.guild.id);
} }
private _addTrackToQueue(message: Message, track: Track) { private _addTrackToQueue(message: Message, track: Track) {
const queue = this.getQueue(message); const queue = this.getQueue(message);
if (!queue) this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_PLAYING, message, new PlayerError("Player is not available in this server")); if (!queue)
this.emit(
PlayerEvents.ERROR,
PlayerErrorEventCodes.NOT_PLAYING,
message,
new PlayerError('Player is not available in this server')
);
if (!track || !(track instanceof Track)) throw new PlayerError('No track specified to add to the queue'); if (!track || !(track instanceof Track)) throw new PlayerError('No track specified to add to the queue');
queue.tracks.push(track); queue.tracks.push(track);
return queue; return queue;
@ -216,25 +224,39 @@ export default class Player extends EventEmitter {
private _createQueue(message: Message, track: Track): Promise<Queue> { private _createQueue(message: Message, track: Track): Promise<Queue> {
return new Promise((resolve) => { return new Promise((resolve) => {
const channel = message.member.voice ? message.member.voice.channel : null const channel = message.member.voice ? message.member.voice.channel : null;
if (!channel) return this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.NOT_CONNECTED, message, new PlayerError("Voice connection is not available in this server!")); if (!channel)
return this.emit(
PlayerEvents.ERROR,
PlayerErrorEventCodes.NOT_CONNECTED,
message,
new PlayerError('Voice connection is not available in this server!')
);
const queue = new Queue(this, message, this.filters); const queue = new Queue(this, message, this.filters);
this.queues.set(message.guild.id, queue); this.queues.set(message.guild.id, queue);
channel.join().then((connection) => { channel
this.emit(PlayerEvents.CONNECTION_CREATE, message, connection); .join()
.then((connection) => {
this.emit(PlayerEvents.CONNECTION_CREATE, message, connection);
queue.voiceConnection = connection; queue.voiceConnection = connection;
if (this.options.autoSelfDeaf) connection.voice.setSelfDeaf(true); if (this.options.autoSelfDeaf) connection.voice.setSelfDeaf(true);
queue.tracks.push(track); queue.tracks.push(track);
this.emit(PlayerEvents.QUEUE_CREATE, message, queue); this.emit(PlayerEvents.QUEUE_CREATE, message, queue);
resolve(queue); resolve(queue);
// this._playTrack(queue, true) // this._playTrack(queue, true)
}).catch((err) => { })
this.queues.delete(message.guild.id); .catch((err) => {
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.UNABLE_TO_JOIN, message, new PlayerError(err.message ?? err)); this.queues.delete(message.guild.id);
}); this.emit(
PlayerEvents.ERROR,
PlayerErrorEventCodes.UNABLE_TO_JOIN,
message,
new PlayerError(err.message ?? err)
);
});
}); });
} }
@ -243,14 +265,14 @@ export default class Player extends EventEmitter {
if (queue.tracks.length === 1 && !queue.loopMode && !queue.repeatMode && !firstPlay) { if (queue.tracks.length === 1 && !queue.loopMode && !queue.repeatMode && !firstPlay) {
if (this.options.leaveOnEnd && !queue.stopped) { if (this.options.leaveOnEnd && !queue.stopped) {
this.queues.delete(queue.guildID) this.queues.delete(queue.guildID);
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
queue.voiceConnection.channel.leave(); queue.voiceConnection.channel.leave();
}, this.options.leaveOnEndCooldown || 0); }, this.options.leaveOnEndCooldown || 0);
this._cooldownsTimeout.set(`end_${queue.guildID}`, timeout); this._cooldownsTimeout.set(`end_${queue.guildID}`, timeout);
} }
this.queues.delete(queue.guildID) this.queues.delete(queue.guildID);
if (queue.stopped) { if (queue.stopped) {
return this.emit(PlayerEvents.MUSIC_STOP, queue.firstMessage); return this.emit(PlayerEvents.MUSIC_STOP, queue.firstMessage);
@ -278,7 +300,12 @@ export default class Player extends EventEmitter {
const ffmeg = Util.checkFFmpeg(); const ffmeg = Util.checkFFmpeg();
if (!ffmeg) return; if (!ffmeg) return;
const seekTime = typeof seek === "number" ? seek : updateFilter ? queue.voiceConnection.dispatcher.streamTime + queue.additionalStreamTime : undefined; const seekTime =
typeof seek === 'number'
? seek
: updateFilter
? queue.voiceConnection.dispatcher.streamTime + queue.additionalStreamTime
: undefined;
const encoderArgsFilters: string[] = []; const encoderArgsFilters: string[] = [];
Object.keys(queue.filters).forEach((filterName) => { Object.keys(queue.filters).forEach((filterName) => {
@ -291,27 +318,33 @@ export default class Player extends EventEmitter {
let encoderArgs: string[]; let encoderArgs: string[];
if (encoderArgsFilters.length < 1) { if (encoderArgsFilters.length < 1) {
encoderArgs = [] encoderArgs = [];
} else { } else {
encoderArgs = ['-af', encoderArgsFilters.join(',')] encoderArgs = ['-af', encoderArgsFilters.join(',')];
} }
let newStream: any; let newStream: any;
if (queue.playing.raw.source === "youtube") { if (queue.playing.raw.source === 'youtube') {
newStream = ytdl(queue.playing.url, { newStream = ytdl(queue.playing.url, {
filter: 'audioonly', filter: 'audioonly',
opusEncoded: true, opusEncoded: true,
encoderArgs, encoderArgs,
seek: seekTime / 1000, seek: seekTime / 1000,
// tslint:disable-next-line:no-bitwise
highWaterMark: 1 << 25, highWaterMark: 1 << 25,
...this.options.ytdlDownloadOptions ...this.options.ytdlDownloadOptions
}); });
} else { } else {
newStream = ytdl.arbitraryStream(queue.playing.raw.source === "soundcloud" ? await queue.playing.raw.engine.downloadProgressive() : queue.playing.raw.engine, { newStream = ytdl.arbitraryStream(
opusEncoded: true, queue.playing.raw.source === 'soundcloud'
encoderArgs, ? await queue.playing.raw.engine.downloadProgressive()
seek: seekTime / 1000 : queue.playing.raw.engine,
}); {
opusEncoded: true,
encoderArgs,
seek: seekTime / 1000
}
);
} }
setTimeout(() => { setTimeout(() => {
@ -337,7 +370,13 @@ export default class Player extends EventEmitter {
newStream.on('error', (error: Error) => { newStream.on('error', (error: Error) => {
if (error.message.toLowerCase().includes('video unavailable')) { if (error.message.toLowerCase().includes('video unavailable')) {
this.emit(PlayerEvents.ERROR, PlayerErrorEventCodes.VIDEO_UNAVAILABLE, queue.firstMessage, queue.playing, error); this.emit(
PlayerEvents.ERROR,
PlayerErrorEventCodes.VIDEO_UNAVAILABLE,
queue.firstMessage,
queue.playing,
error
);
this._playTrack(queue, false); this._playTrack(queue, false);
} else { } else {
this.emit(PlayerEvents.ERROR, error, queue.firstMessage, error); this.emit(PlayerEvents.ERROR, error, queue.firstMessage, error);
@ -346,5 +385,4 @@ export default class Player extends EventEmitter {
}, 1000); }, 1000);
}); });
} }
} }

View file

@ -14,14 +14,14 @@ export const PlayerEvents = {
SEARCH_INVALID_RESPONSE: 'searchInvalidResponse', SEARCH_INVALID_RESPONSE: 'searchInvalidResponse',
SEARCH_RESULTS: 'searchResults', SEARCH_RESULTS: 'searchResults',
TRACK_ADD: 'trackAdd', TRACK_ADD: 'trackAdd',
TRACK_START: 'trackStart', TRACK_START: 'trackStart'
}; };
export const PlayerErrorEventCodes = { export const PlayerErrorEventCodes = {
LIVE_VIDEO: "LiveVideo", LIVE_VIDEO: 'LiveVideo',
NOT_CONNECTED: "NotConnected", NOT_CONNECTED: 'NotConnected',
UNABLE_TO_JOIN: "UnableToJoin", UNABLE_TO_JOIN: 'UnableToJoin',
NOT_PLAYING: "NotPlaying", NOT_PLAYING: 'NotPlaying',
PARSE_ERROR: "ParseError", PARSE_ERROR: 'ParseError',
VIDEO_UNAVAILABLE: "VideoUnavailable" VIDEO_UNAVAILABLE: 'VideoUnavailable'
}; };

View file

@ -1,8 +1,8 @@
export default class PlayerError extends Error { export default class PlayerError extends Error {
constructor(msg: string, name?: string) { constructor(msg: string, name?: string) {
super(); super();
this.name = name ?? "PlayerError"; this.name = name ?? 'PlayerError';
this.message = msg; this.message = msg;
Error.captureStackTrace(this); Error.captureStackTrace(this);
} }
} }