UNPKG

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
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, };