custom extractors support

This commit is contained in:
Snowflake107 2021-04-08 18:48:04 +05:45
parent 72af2eb46b
commit 7d19bae727
3 changed files with 114 additions and 2 deletions

View file

@ -8,6 +8,7 @@ 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';
import { ExtractorModel } from "./Structures/ExtractorModel";
// @ts-ignore // @ts-ignore
import spotify from 'spotify-url-info'; import spotify from 'spotify-url-info';
@ -24,6 +25,7 @@ export class Player extends EventEmitter {
public queues: Collection<Snowflake, Queue>; public queues: Collection<Snowflake, Queue>;
private _resultsCollectors: Collection<string, Collector<Snowflake, Message>>; private _resultsCollectors: Collection<string, Collector<Snowflake, Message>>;
private _cooldownsTimeout: Collection<string, NodeJS.Timeout>; private _cooldownsTimeout: Collection<string, NodeJS.Timeout>;
public Extractors = new Collection<string, ExtractorModel>();
constructor(client: Client, options?: PlayerOptions) { constructor(client: Client, options?: PlayerOptions) {
super(); super();
@ -62,6 +64,35 @@ export class Player extends EventEmitter {
return AudioFilters; return AudioFilters;
} }
/**
* Define custom extractor in this player
* @param extractorName The extractor name
* @param extractor The extractor itself
*/
use(extractorName: string, extractor: any) {
if (!extractorName) throw new PlayerError("Missing extractor name!", "PlayerExtractorError");
const methods = ["validate", "getInfo"];
for (const method of methods) {
if (typeof extractor[method] !== "function") throw new PlayerError("Invalid extractor supplied!", "PlayerExtractorError");
}
this.Extractors.set(extractorName, new ExtractorModel(extractorName, extractor));
return Player;
}
/**
* Remove existing extractor from this player
* @param extractorName The extractor name
*/
unuse(extractorName: string) {
if (!extractorName) throw new PlayerError("Missing extractor name!", "PlayerExtractorError");
return this.Extractors.delete(extractorName);
}
private _searchTracks(message: Message, query: string, firstResult?: boolean): Promise<Track> { private _searchTracks(message: Message, query: string, firstResult?: boolean): Promise<Track> {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
let tracks: Track[] = []; let tracks: Track[] = [];
@ -83,7 +114,7 @@ export class Player extends EventEmitter {
requestedBy: message.author, requestedBy: message.author,
fromPlaylist: false, fromPlaylist: false,
source: 'soundcloud', source: 'soundcloud',
engine: data engine: data.engine
}); });
tracks.push(track); tracks.push(track);
@ -342,7 +373,31 @@ export class Player extends EventEmitter {
source: 'youtube' source: 'youtube'
}); });
} else { } else {
track = await this._searchTracks(message, query, firstResult); for (const [_, extractor] of this.Extractors) {
if (extractor.validate(query)) {
const data = await extractor.handle(query);
if (data) {
console.log(data)
track = new Track(this, {
title: data.title,
description: data.description,
duration: Util.durationString(Util.parseMS(data.duration)),
thumbnail: data.thumbnail,
author: data.author,
views: data.views,
engine: data.engine,
source: 'arbitrary',
fromPlaylist: false,
requestedBy: message.author,
url: data.url
});
if (extractor.important) break;
}
}
}
if (!track) track = await this._searchTracks(message, query, firstResult);
} }
} }

View file

@ -0,0 +1,44 @@
import { ExtractorModelData } from "../types/types";
class ExtractorModel {
name: string;
private _raw: any;
constructor(extractorName: string, data: any) {
this.name = extractorName;
Object.defineProperty(this, "_raw", { value: data, configurable: false, writable: false, enumerable: false });
}
async handle(query: string) {
const data = await this._raw.getInfo(query);
if (!data) return null;
return {
title: data.title,
duration: data.duration,
thumbnail: data.thumbnail,
engine: data.engine,
views: data.views,
author: data.author,
description: data.description,
url: data.url
} as ExtractorModelData;
}
validate(query: string) {
return Boolean(this._raw.validate(query));
}
get version() {
return this._raw.version ?? "0.0.0";
}
get important() {
return Boolean(this._raw.important);
}
}
export default ExtractorModel;
export { ExtractorModel };

View file

@ -1,5 +1,6 @@
import { downloadOptions } from 'ytdl-core'; import { downloadOptions } from 'ytdl-core';
import { User } from 'discord.js'; import { User } from 'discord.js';
import { Readable, Duplex } from "stream";
export interface PlayerOptions { export interface PlayerOptions {
leaveOnEnd?: boolean; leaveOnEnd?: boolean;
@ -75,3 +76,15 @@ export type QueryType =
| 'reverbnation' | 'reverbnation'
| 'attachment' | 'attachment'
| 'youtube_search'; | 'youtube_search';
export interface ExtractorModelData {
title: string;
duration: number;
thumbnail: string;
engine: string | Readable | Duplex;
views: number;
author: string;
description: string;
url: string;
version?: string;
}