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
459 lines (448 loc) • 15 kB
JavaScript
const {
search,
validate,
setToken,
playlist_info,
video_info,
deezer,
spotify,
soundcloud,
} = require('play-dl');
const randomUserAgents = require('random-useragent').getRandom;
const trackModel = require('./__trackModeler');
const Album = require('./__album');
class playdlEngine {
static __cookies = undefined;
static __userAgents = undefined;
static async __rawExtractor(
rawQuery,
__trackBlueprint,
__scrapperOptions,
playdl,
queue,
) {
if (!(rawQuery && typeof rawQuery === 'string' && rawQuery !== '')) {
return undefined;
}
let __cacheGarbage;
let __indexCount = 0;
if (
__scrapperOptions?.fetchOptions?.rawCookies
&& playdlEngine.__cookies !== __scrapperOptions?.fetchOptions?.rawCookies
) {
playdlEngine.__cookies ??= __scrapperOptions?.fetchOptions?.rawCookies;
await setToken({
youtube: {
cookie: playdlEngine.__cookies,
},
});
}
if (
__scrapperOptions?.fetchOptions?.userAgents
&& typeof __scrapperOptions?.fetchOptions?.userAgents === 'string'
&& __scrapperOptions?.fetchOptions?.userAgents?.toLowerCase()?.trim()
=== 'random'
) {
playdlEngine.__userAgents = [
randomUserAgents(),
randomUserAgents(),
randomUserAgents(),
];
await setToken({
useragent: playdlEngine.__userAgents,
});
} else if (
__scrapperOptions?.fetchOptions?.userAgents
&& Array.isArray(__scrapperOptions?.fetchOptions?.userAgents)
&& __scrapperOptions?.fetchOptions?.userAgents?.length > 0
) {
playdlEngine.__userAgents = __scrapperOptions?.fetchOptions?.userAgents;
await setToken({
useragent: playdlEngine.__userAgents,
});
}
const validatedResult = await validate(rawQuery)?.catch(() => undefined);
let __searchResults = await playdlEngine.#__customSearch(
rawQuery,
__scrapperOptions,
validatedResult,
playdl,
__trackBlueprint?.orignal_extractor,
queue,
);
if (
!(
__searchResults
&& Array.isArray(__searchResults)
&& __searchResults?.length > 0
)
) {
return undefined;
}
if (
__scrapperOptions?.fetchOptions?.fetchLimit
&& !Number.isNaN(Number(__scrapperOptions?.fetchOptions?.fetchLimit))
&& parseInt(__scrapperOptions?.fetchOptions?.fetchLimit) > 0
&& parseInt(__scrapperOptions?.fetchOptions?.fetchLimit) < Infinity
&& !(
(validatedResult?.includes('playlist')
|| validatedResult?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
) {
__searchResults = __searchResults
?.slice(0, parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1))
?.filter(Boolean);
}
queue.add(__searchResults?.length, 'tracksCount');
let garbageResults = (
await __searchResults?.reduce(async (pervStatusData, newStatusData) => {
if (!queue || (queue && queue?.stopped)) return [undefined];
const privData = await pervStatusData;
__cacheGarbage = await playdlEngine.__trackModelling(
newStatusData,
{
...__trackBlueprint,
Id: ++__indexCount,
},
__scrapperOptions,
playdl,
queue,
);
queue.add(__cacheGarbage, 'track');
if (__cacheGarbage && __scrapperOptions?.eventReturn) {
playdl.emit(
'track',
__trackBlueprint?.orignal_extractor ?? 'youtube',
__cacheGarbage?.album ?? undefined,
__cacheGarbage,
queue,
__scrapperOptions?.eventReturn?.metadata,
);
queue.emit(
'track',
__trackBlueprint?.orignal_extractor ?? 'youtube',
__cacheGarbage?.album ?? undefined,
__cacheGarbage,
__scrapperOptions?.eventReturn?.metadata,
);
}
if (privData && Array.isArray(privData) && privData?.length > 0) return [...privData, __cacheGarbage];
return [__cacheGarbage];
}, Promise.resolve())
)?.filter(Boolean);
if (!queue?.stopped) {
queue.add(garbageResults?.length, 'tracksCount');
queue.complete();
playdl.emit(
'tracks',
garbageResults,
queue?.album,
queue,
__scrapperOptions?.eventReturn?.metadata,
);
queue.emit(
'tracks',
garbageResults,
queue?.album,
__scrapperOptions?.eventReturn?.metadata,
);
}
garbageResults = [];
return garbageResults;
}
static async #__customSearch(
rawQuery,
__scrapperOptions,
__validate,
playdl,
extractor = 'arbitary',
queue = undefined,
) {
if (extractor && extractor?.toLowerCase()?.trim() === 'arbitary') {
return {
albumId: false,
tracks: [
{
url: rawQuery,
},
],
};
}
let __rawResults;
let rawTracks;
let garbage;
let __videoDetails;
const __validateResults = [];
if (__validate && __validate?.includes('dz')) {
__validateResults[0] = 'deezer';
} else if (__validate && __validate?.includes('sp')) {
__validateResults[0] = 'spotify';
} else if (__validate && __validate?.includes('so')) {
__validateResults[0] = 'soundcloud';
} else if (__validate && __validate?.includes('yt')) {
__validateResults[0] = 'youtube';
}
__validateResults[1] = __validate ? __validate?.split('_')?.[1] : undefined;
switch (__validateResults[0]) {
case 'youtube':
if (
['video', 'track'].includes(
__validateResults[1]?.toLowerCase()?.trim(),
)
) {
__videoDetails = (await video_info(rawQuery))?.video_details;
return __videoDetails ? [__videoDetails] : undefined;
}
__rawResults = await playlist_info(rawQuery, {
incomplete: true,
});
rawTracks = await __rawResults?.all_videos();
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
garbage = new Album(
__rawResults,
queue,
rawTracks?.length,
__scrapperOptions?.eventReturn?.metadata,
);
garbage = null;
return rawTracks;
case 'deezer':
__rawResults = await deezer(rawQuery);
if (__rawResults && ['playlist', 'album'].includes(__rawResults.type)) {
rawTracks = await __rawResults?.all_tracks();
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
let garbage = new Album(
__rawResults,
queue,
rawTracks?.length,
__scrapperOptions?.eventReturn?.metadata,
);
garbage = null;
return rawTracks;
}
if (__rawResults && ['user'].includes(__rawResults)) return undefined;
return [__rawResults];
case 'soundcloud':
__rawResults = await soundcloud(rawQuery);
if (__rawResults && ['playlist', 'album'].includes(__rawResults.type)) {
return await __rawResults?.fetch();
}
if (__rawResults && ['user'].includes(__rawResults)) return undefined;
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
return [__rawResults];
case 'spotify':
__rawResults = await spotify(rawQuery);
if (__rawResults && ['playlist', 'album'].includes(__rawResults.type)) {
rawTracks = await __rawResults?.all_tracks();
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
let garbage = new Album(
__rawResults,
queue,
rawTracks?.length,
__scrapperOptions?.eventReturn?.metadata,
);
garbage = null;
return rawTracks;
}
if (__rawResults && ['user'].includes(__rawResults)) return undefined;
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
return [__rawResults];
default:
__rawResults = await search(rawQuery, {
limit:
[Infinity, 0].includes(
__scrapperOptions?.fetchOptions?.fetchLimit ?? 1,
) || (__scrapperOptions?.fetchOptions?.fetchLimit ?? 1) <= 0
? 10
: __scrapperOptions?.fetchOptions?.fetchLimit ?? 1,
});
rawTracks = rawTracks
&& !(
(__validate?.includes('playlist')
|| __validate?.includes('album'))
&& Boolean(__scrapperOptions?.fetchOptions?.skipAlbumLimit)
)
? rawTracks
?.slice(
0,
parseInt(__scrapperOptions?.fetchOptions?.fetchLimit ?? 1),
)
?.filter(Boolean)
: rawTracks;
if (
__rawResults
&& Array.isArray(__rawResults)
&& __rawResults?.length > 1
) {
return __rawResults;
}
if (!__rawResults[0]?.url) return undefined;
__videoDetails = await __rawResults?.reduce(async (total, result) => {
if (!queue || (queue && queue?.stopped)) return [undefined];
const privaData = await total;
const cachedData = await video_info(result?.url);
if (privaData && Array.isArray(privaData) && privaData?.length > 0) return [...privaData, cachedData?.video_details];
return [cachedData?.video_details];
}, Promise.resolve);
__videoDetails = __videoDetails?.filter(Boolean);
return __videoDetails
&& Array.isArray(__videoDetails)
&& __videoDetails?.length > 0
? __videoDetails
: __rawResults;
}
}
static async __trackModelling(
__rawTrack,
__trackBlueprint,
__scrapperOptions,
playdl,
queue,
) {
try {
if (!queue || (queue && queue?.stopped)) return undefined;
await playdl.__customRatelimit(__scrapperOptions?.ratelimit);
const Track = new trackModel(
{
trackId: parseInt(__trackBlueprint?.Id ?? 0) || 0,
url: __trackBlueprint?.url ?? __rawTrack?.url,
video_Id: __trackBlueprint?.video_Id ?? __rawTrack?.id,
title: __trackBlueprint?.title ?? __rawTrack?.title,
views: __trackBlueprint?.views ?? __rawTrack?.views,
author:
__trackBlueprint?.author
?? __rawTrack?.artist?.name
?? __rawTrack?.channel?.name,
author_link:
__trackBlueprint?.author_link
?? __rawTrack?.artist?.url
?? __rawTrack?.channel?.url,
description: __trackBlueprint?.description ?? __rawTrack?.description,
custom_extractor: 'play-dl',
duration:
(__trackBlueprint?.is_live || __rawTrack?.live
? 0
: __trackBlueprint?.duration)
?? (__rawTrack?.durationInSec ?? 0) * 1000,
human_duration:
__rawTrack?.durationRaw
?? trackModel.humanTimeConversion(
(__trackBlueprint?.is_live || __rawTrack?.live
? 0
: __trackBlueprint?.duration)
?? (__rawTrack?.durationInSec ?? 0) * 1000,
),
orignal_extractor: __trackBlueprint?.orignal_extractor ?? 'youtube',
thumbnail:
__trackBlueprint?.thumbnail
?? __rawTrack?.thumbnails?.sort((a, b) => a?.width - b?.width)?.pop()
?.url,
channelName:
__trackBlueprint?.channel_Name
?? __rawTrack?.channel?.name
?? __trackBlueprint?.author,
channelId: __trackBlueprint?.author ?? __rawTrack?.channel?.id,
channel_url:
__trackBlueprint?.author_link ?? __rawTrack?.channel?.url,
likes: __trackBlueprint?.likes ?? __rawTrack?.likes ?? 0,
is_live: __trackBlueprint?.is_live ?? __rawTrack?.live ?? false,
dislikes: __trackBlueprint?.dislikes ?? __rawTrack?.dislikes ?? 0,
metadata: __scrapperOptions?.eventReturn?.metadata,
},
__rawTrack,
playdl,
queue,
{
userAgents: playdlEngine.__userAgents,
},
);
if (
__scrapperOptions?.streamDownload
&& __rawTrack?.url
&& !(!queue || (queue && queue?.stopped))
) {
await Track.getStream(
__trackBlueprint?.stream,
__scrapperOptions?.ignoreInternalError,
true,
true,
);
}
if (
__scrapperOptions?.fetchLyrics
&& __rawTrack?.url
&& !(!queue || (queue && queue?.stopped))
) {
await Track.getLyrics();
}
return Track;
} catch (rawError) {
if (__scrapperOptions?.ignoreInternalError) {
return void playdl.__errorHandling(rawError);
}
throw rawError;
}
}
}
module.exports = playdlEngine;