playdl-music-extractor
Version:
PlayDL Music Extractor is a Extractor/Scrapper and Helps Players to fetch data from play-dl or Custom Extractors , as Per reduces extra work and credentials
270 lines (254 loc) • 9.32 kB
JavaScript
const path = require('path');
const EventEmitter = require('events');
const fileSystem = require('fs');
const spotify = require('./bin/__spotify');
const youtube = require('./bin/__youtube');
const soundcloud = require('./bin/__soundCloud');
const facebook = require('./bin/__facebook');
const reverbnation = require('./bin/__reverbnation');
const deezer = require('./bin/__deezer');
const arbitary = require('./bin/__arbitary');
const Track = require('./bin/__trackModeler');
const Album = require('./bin/__album');
const queues = require('./bin/__queue');
/**
* @class playdl Main Handler to Fetch and Parse Songs from Youtube and SoundCloud and many Others from play-dl as its base source
*/
class playdl extends EventEmitter {
/**
* @static
* @private
* @property {Object} #__privateCaches Private Caches for functions to Store basic Options and Queue Data for Ratelimit
*/
static scrapperOptions = {
fetchLyrics: true,
eventReturn: { metadata: undefined },
ratelimit: 0,
ignoreInternalError: true,
playersCompatibility: false,
fetchOptions: {
tokens: {},
fetchLimit: Infinity,
streamQuality: undefined,
rawCookies: undefined,
userAgents: undefined,
skipAlbumLimit: true,
},
streamDownload: false,
waitForPromise: true,
sweeper: true,
};
/**
* @constructor
* @param {scrapperOptions} __scrapperOptions Scrapping Options for functions and base Source Engine
*/
constructor(__scrapperOptions = playdl?.scrapperOptions) {
super();
/**
* @type {Map<string,queues>} Queues of queue
*/
this.queues = new Map();
if (__scrapperOptions?.sweeper) setInterval(() => this.sweep(), 60 * 1000);
this.__scrapperOptions = {
...playdl?.scrapperOptions,
...__scrapperOptions,
fetchOptions: {
...playdl.scrapperOptions?.fetchOptions,
...__scrapperOptions?.fetchOptions,
},
};
}
/**
* exec() Raw and in-built function for fetching Data for other methods with no exceptions
* @param {string} rawQuery A String Value for Song Name or Url to be Parsed and Fetch Data about it
* @param {scrapperOptions} __scrapperOptions Scrapping Options for functions and base Source Engine
* @returns {Promise<queues>} album and Tracks from play-dl
*/
async exec(rawQuery, __scrapperOptions = playdl?.scrapperOptions) {
try {
__scrapperOptions = {
...this.__scrapperOptions,
...__scrapperOptions,
fetchOptions: {
...this.__scrapperOptions?.fetchOptions,
...__scrapperOptions?.fetchOptions,
},
};
if (
!(rawQuery && typeof rawQuery === 'string' && rawQuery?.trim() !== '')
) {
throw new Error(
'playdl-music-extractor Error : Invalid Query is Provided to Parse and Stream for Client',
);
}
await this.__customRatelimit(__scrapperOptions?.ratelimit);
const queue = new queues(rawQuery, __scrapperOptions, this);
if (spotify.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await spotify.__extractor(rawQuery, __scrapperOptions, this, queue);
} else spotify.__extractor(rawQuery, __scrapperOptions, this, queue);
} else if (soundcloud.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await soundcloud.__extractor(
rawQuery,
__scrapperOptions,
this,
queue,
);
} else {
soundcloud.__extractor(rawQuery, __scrapperOptions, this, queue);
}
} else if (facebook.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await facebook.__extractor(rawQuery, __scrapperOptions, this, queue);
} else {
facebook.__extractor(rawQuery, __scrapperOptions, this, queue);
}
} else if (reverbnation.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await reverbnation.__extractor(
rawQuery,
__scrapperOptions,
this,
queue,
);
} else {
reverbnation.__extractor(rawQuery, __scrapperOptions, this, queue);
}
} else if (deezer.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await deezer.__extractor(rawQuery, __scrapperOptions, this, queue);
} else deezer.__extractor(rawQuery, __scrapperOptions, this, queue);
} else if (youtube.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await youtube.__extractor(rawQuery, __scrapperOptions, this, queue);
} else youtube.__extractor(rawQuery, __scrapperOptions, this, queue);
} else if (arbitary.__test(rawQuery)) {
if (__scrapperOptions?.waitForPromise) {
await arbitary.__extractor(rawQuery, __scrapperOptions, this, queue);
} else {
arbitary.__extractor(rawQuery, __scrapperOptions, this, queue);
}
} else {
throw new Error(
'playdl-music-extractor Error : Un-Supportable Query is Provided to Parse and Stream for Client',
);
}
this.queues.set(queue?.id, queue);
this.emit('queue', queue);
return queue;
} catch (rawError) {
if (__scrapperOptions?.ignoreInternalError) {
return void this.__errorHandling(rawError);
}
throw rawError;
}
}
/**
* streamExtractor() Raw and in-built function for fetching Data for other methods with no exceptions
* @param {string} rawQuery A String Value for Song Name or Url to be Parsed and Fetch Data about it
* @param {scrapperOptions} __scrapperOptions Scrapping Options for functions and base Source Engine
* @param {string | "tracks" | "streams"} returnType Return Type for method , And Optional choice and By Default its "tracks"
* @returns {Promise<queues>} album and Tracks from play-dl
*/
async streamExtractor(
rawQuery,
__scrapperOptions = playdl?.scrapperOptions,
returnType = 'tracks',
) {
const __rawResponse = await this.exec(rawQuery, {
...__scrapperOptions,
fetchLyrics: false,
streamDownload: true,
});
if (returnType && returnType?.toLowerCase()?.trim()?.includes('stream')) return __rawResponse?.filter('stream');
return __rawResponse?.filter();
}
/**
* softExtractor() Raw and in-built function for fetching Data for other methods with no exceptions
* @param {string} rawQuery A String Value for Song Name or Url to be Parsed and Fetch Data about it
* @param {scrapperOptions} __scrapperOptions Scrapping Options for functions and base Source Engine
* @returns {Promise<queues>} album and Tracks from play-dl
*/
async softExtractor(rawQuery, __scrapperOptions = playdl?.scrapperOptions) {
const __rawResponse = await this.exec(rawQuery, {
...__scrapperOptions,
fetchLyrics: false,
streamDownload: false,
});
return __rawResponse?.filter();
}
__errorHandling(error = new Error()) {
if (!error?.message) return undefined;
if (!fileSystem.existsSync(path.join(__dirname, '/cache'))) {
fileSystem.mkdirSync(path.join(__dirname, '/cache'));
}
const __cacheLocation = path.join(__dirname, '/cache', '/__errorLogs.txt');
if (!__cacheLocation) return undefined;
if (!fileSystem.existsSync(__cacheLocation)) {
fileSystem.writeFileSync(
__cacheLocation,
`${new Date()} | `
+ `\n ErrorMessage: ${error?.message ?? `${error}`}\n ErrorStack: ${
error?.stack ?? 'Unknown-Stack'
}`,
);
} else if (
(fileSystem.readFileSync(__cacheLocation)?.length ?? 0) < 500000
) {
fileSystem.appendFileSync(
__cacheLocation,
`\n\n${new Date()} | `
+ `\n ErrorMessage: ${error?.message ?? `${error}`}\n ErrorStack: ${
error?.stack ?? 'Unknown-Stack'
}`,
'utf8',
);
} else {
fileSystem.writeFileSync(
__cacheLocation,
`${new Date()} | `
+ `\n ErrorMessage: ${error?.message ?? `${error}`}\n ErrorStack: ${
error?.stack ?? 'Unknown-Stack'
}`,
);
}
return true;
}
async __customRatelimit(waitTime = 2 * 1000, forceSkip = false) {
if (forceSkip) return true;
const __rawtimeMs = new Date().getTime();
playdl.ratelimitQueue ??= __rawtimeMs;
if (playdl?.ratelimitQueue - __rawtimeMs > 1000) {
playdl.ratelimitQueue += waitTime;
await this.#sleep(playdl?.ratelimitQueue - __rawtimeMs);
return true;
}
return true;
}
sweep() {
Array.from(this.queues?.values()).map((queue) => {
if (
queue?.destroyed
|| (queue?.creationTime
&& queue?.creationTime - Date.now() > 2 * 60 * 60 * 1000)
) this.queues?.delete(queue?.id?.trim());
return undefined;
});
return true;
}
#sleep(waitTime = 2 * 1000) {
if (!(waitTime && typeof waitTime === 'number' && waitTime > 500)) {
return true;
}
return new Promise((resolve) => setTimeout(resolve, waitTime));
}
static playdlQuick = new playdl();
}
module.exports = {
playdl,
playdlQuick: playdl.playdlQuick,
Track,
Album,
queue: queues,
};