Merge branch 'developer' of https://github.com/play-dl/play-dl into developer
This commit is contained in:
commit
9a9aabc4d8
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-dl",
|
"name": "play-dl",
|
||||||
"version": "1.4.2",
|
"version": "1.4.3",
|
||||||
"description": "YouTube, SoundCloud, Spotify, Deezer searching and streaming for discord.js bots",
|
"description": "YouTube, SoundCloud, Spotify, Deezer searching and streaming for discord.js bots",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "dist/index.d.ts",
|
"typings": "dist/index.d.ts",
|
||||||
|
|||||||
@ -23,9 +23,9 @@ export type YouTube = YouTubeVideo | YouTubeChannel | YouTubePlayList;
|
|||||||
export async function yt_search(search: string, options: ParseSearchInterface = {}): Promise<YouTube[]> {
|
export async function yt_search(search: string, options: ParseSearchInterface = {}): Promise<YouTube[]> {
|
||||||
let url = 'https://www.youtube.com/results?search_query=' + search;
|
let url = 'https://www.youtube.com/results?search_query=' + search;
|
||||||
options.type ??= 'video';
|
options.type ??= 'video';
|
||||||
if (!url.match('&sp=')) {
|
if (url.indexOf('&sp=') === -1) {
|
||||||
url += '&sp=';
|
url += '&sp=';
|
||||||
switch (options?.type) {
|
switch (options.type) {
|
||||||
case 'channel':
|
case 'channel':
|
||||||
url += SearchType.Channel;
|
url += SearchType.Channel;
|
||||||
break;
|
break;
|
||||||
@ -35,16 +35,17 @@ export async function yt_search(search: string, options: ParseSearchInterface =
|
|||||||
case 'video':
|
case 'video':
|
||||||
url += SearchType.Video;
|
url += SearchType.Video;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown search type: ${options.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const body = await request(url, {
|
const body = await request(url, {
|
||||||
headers: {
|
headers: {
|
||||||
'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7',
|
'accept-language': 'en-US,en;q=0.9',
|
||||||
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36',
|
'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36',
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)
|
if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1)
|
||||||
throw new Error('Captcha page: YouTube has detected that you are a bot!');
|
throw new Error('Captcha page: YouTube has detected that you are a bot!');
|
||||||
const data = ParseSearchResult(body, options);
|
return ParseSearchResult(body, options);
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,8 @@ export interface thumbnail {
|
|||||||
export function ParseSearchResult(html: string, options?: ParseSearchInterface): YouTube[] {
|
export function ParseSearchResult(html: string, options?: ParseSearchInterface): YouTube[] {
|
||||||
if (!html) throw new Error("Can't parse Search result without data");
|
if (!html) throw new Error("Can't parse Search result without data");
|
||||||
if (!options) options = { type: 'video', limit: 0 };
|
if (!options) options = { type: 'video', limit: 0 };
|
||||||
if (!options.type) options.type = 'video';
|
else if (!options.type) options.type = 'video';
|
||||||
|
const hasLimit = typeof options.limit === 'number' && options.limit > 0;
|
||||||
|
|
||||||
const data = html
|
const data = html
|
||||||
.split('var ytInitialData = ')?.[1]
|
.split('var ytInitialData = ')?.[1]
|
||||||
@ -33,21 +34,27 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface):
|
|||||||
const details =
|
const details =
|
||||||
json_data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0]
|
json_data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0]
|
||||||
.itemSectionRenderer.contents;
|
.itemSectionRenderer.contents;
|
||||||
for (let i = 0; i < details.length; i++) {
|
for (const detail of details) {
|
||||||
if (typeof options.limit === 'number' && options.limit > 0 && results.length === options.limit) break;
|
if (hasLimit && results.length === options.limit) break;
|
||||||
if (!details[i].videoRenderer && !details[i].channelRenderer && !details[i].playlistRenderer) continue;
|
if (!detail.videoRenderer && !detail.channelRenderer && !detail.playlistRenderer) continue;
|
||||||
if (options.type === 'video') {
|
switch (options.type) {
|
||||||
const parsed = parseVideo(details[i]);
|
case 'video': {
|
||||||
if (!parsed) continue;
|
const parsed = parseVideo(detail);
|
||||||
results.push(parsed);
|
if (parsed) results.push(parsed);
|
||||||
} else if (options.type === 'channel') {
|
break;
|
||||||
const parsed = parseChannel(details[i]);
|
}
|
||||||
if (!parsed) continue;
|
case 'channel': {
|
||||||
results.push(parsed);
|
const parsed = parseChannel(detail);
|
||||||
} else if (options.type === 'playlist') {
|
if (parsed) results.push(parsed);
|
||||||
const parsed = parsePlaylist(details[i]);
|
break;
|
||||||
if (!parsed) continue;
|
}
|
||||||
results.push(parsed);
|
case 'playlist': {
|
||||||
|
const parsed = parsePlaylist(detail);
|
||||||
|
if (parsed) results.push(parsed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown search type: ${options.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
@ -58,7 +65,7 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface):
|
|||||||
* @returns seconds
|
* @returns seconds
|
||||||
*/
|
*/
|
||||||
function parseDuration(duration: string): number {
|
function parseDuration(duration: string): number {
|
||||||
duration ??= '0:00';
|
if (!duration) return 0;
|
||||||
const args = duration.split(':');
|
const args = duration.split(':');
|
||||||
let dur = 0;
|
let dur = 0;
|
||||||
|
|
||||||
@ -82,29 +89,24 @@ function parseDuration(duration: string): number {
|
|||||||
*/
|
*/
|
||||||
export function parseChannel(data?: any): YouTubeChannel {
|
export function parseChannel(data?: any): YouTubeChannel {
|
||||||
if (!data || !data.channelRenderer) throw new Error('Failed to Parse YouTube Channel');
|
if (!data || !data.channelRenderer) throw new Error('Failed to Parse YouTube Channel');
|
||||||
const badge = data.channelRenderer.ownerBadges && data.channelRenderer.ownerBadges[0];
|
const badge = data.channelRenderer.ownerBadges?.[0]?.metadataBadgeRenderer?.style?.toLowerCase();
|
||||||
const url = `https://www.youtube.com${
|
const url = `https://www.youtube.com${
|
||||||
data.channelRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||
|
data.channelRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||
|
||||||
data.channelRenderer.navigationEndpoint.commandMetadata.webCommandMetadata.url
|
data.channelRenderer.navigationEndpoint.commandMetadata.webCommandMetadata.url
|
||||||
}`;
|
}`;
|
||||||
|
const thumbnail = data.channelRenderer.thumbnail.thumbnails[data.channelRenderer.thumbnail.thumbnails.length - 1];
|
||||||
const res = new YouTubeChannel({
|
const res = new YouTubeChannel({
|
||||||
id: data.channelRenderer.channelId,
|
id: data.channelRenderer.channelId,
|
||||||
name: data.channelRenderer.title.simpleText,
|
name: data.channelRenderer.title.simpleText,
|
||||||
icon: {
|
icon: {
|
||||||
url: data.channelRenderer.thumbnail.thumbnails[
|
url: thumbnail.url.replace('//', 'https://'),
|
||||||
data.channelRenderer.thumbnail.thumbnails.length - 1
|
width: thumbnail.width,
|
||||||
].url.replace('//', 'https://'),
|
height: thumbnail.height
|
||||||
width: data.channelRenderer.thumbnail.thumbnails[data.channelRenderer.thumbnail.thumbnails.length - 1]
|
|
||||||
.width,
|
|
||||||
height: data.channelRenderer.thumbnail.thumbnails[data.channelRenderer.thumbnail.thumbnails.length - 1]
|
|
||||||
.height
|
|
||||||
},
|
},
|
||||||
url: url,
|
url: url,
|
||||||
verified: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('verified')),
|
verified: Boolean(badge?.includes('verified')),
|
||||||
artist: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('artist')),
|
artist: Boolean(badge?.includes('artist')),
|
||||||
subscribers: data.channelRenderer.subscriberCountText?.simpleText
|
subscribers: data.channelRenderer.subscriberCountText?.simpleText ?? '0 subscribers'
|
||||||
? data.channelRenderer.subscriberCountText.simpleText
|
|
||||||
: '0 subscribers'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -117,30 +119,29 @@ export function parseChannel(data?: any): YouTubeChannel {
|
|||||||
export function parseVideo(data?: any): YouTubeVideo {
|
export function parseVideo(data?: any): YouTubeVideo {
|
||||||
if (!data || !data.videoRenderer) throw new Error('Failed to Parse YouTube Video');
|
if (!data || !data.videoRenderer) throw new Error('Failed to Parse YouTube Video');
|
||||||
|
|
||||||
const badge = data.videoRenderer.ownerBadges && data.videoRenderer.ownerBadges[0];
|
const channel = data.videoRenderer.ownerText.runs[0];
|
||||||
|
const badge = data.videoRenderer.ownerBadges?.[0]?.metadataBadgeRenderer?.style?.toLowerCase();
|
||||||
const res = new YouTubeVideo({
|
const res = new YouTubeVideo({
|
||||||
id: data.videoRenderer.videoId,
|
id: data.videoRenderer.videoId,
|
||||||
url: `https://www.youtube.com/watch?v=${data.videoRenderer.videoId}`,
|
url: `https://www.youtube.com/watch?v=${data.videoRenderer.videoId}`,
|
||||||
title: data.videoRenderer.title.runs[0].text,
|
title: data.videoRenderer.title.runs[0].text,
|
||||||
description:
|
description: data.videoRenderer.detailedMetadataSnippets?.[0].snippetText.runs.length
|
||||||
data.videoRenderer.detailedMetadataSnippets &&
|
|
||||||
data.videoRenderer.detailedMetadataSnippets[0].snippetText.runs[0]
|
|
||||||
? data.videoRenderer.detailedMetadataSnippets[0].snippetText.runs.map((run: any) => run.text).join('')
|
? data.videoRenderer.detailedMetadataSnippets[0].snippetText.runs.map((run: any) => run.text).join('')
|
||||||
: '',
|
: '',
|
||||||
duration: data.videoRenderer.lengthText ? parseDuration(data.videoRenderer.lengthText.simpleText) : 0,
|
duration: data.videoRenderer.lengthText ? parseDuration(data.videoRenderer.lengthText.simpleText) : 0,
|
||||||
duration_raw: data.videoRenderer.lengthText ? data.videoRenderer.lengthText.simpleText : null,
|
duration_raw: data.videoRenderer.lengthText ? data.videoRenderer.lengthText.simpleText : null,
|
||||||
thumbnail: data.videoRenderer.thumbnail.thumbnails[data.videoRenderer.thumbnail.thumbnails.length - 1],
|
thumbnail: data.videoRenderer.thumbnail.thumbnails[data.videoRenderer.thumbnail.thumbnails.length - 1],
|
||||||
channel: {
|
channel: {
|
||||||
id: data.videoRenderer.ownerText.runs[0].navigationEndpoint.browseEndpoint.browseId || null,
|
id: channel.navigationEndpoint.browseEndpoint.browseId || null,
|
||||||
name: data.videoRenderer.ownerText.runs[0].text || null,
|
name: channel.text || null,
|
||||||
url: `https://www.youtube.com${
|
url: `https://www.youtube.com${
|
||||||
data.videoRenderer.ownerText.runs[0].navigationEndpoint.browseEndpoint.canonicalBaseUrl ||
|
channel.navigationEndpoint.browseEndpoint.canonicalBaseUrl ||
|
||||||
data.videoRenderer.ownerText.runs[0].navigationEndpoint.commandMetadata.webCommandMetadata.url
|
channel.navigationEndpoint.commandMetadata.webCommandMetadata.url
|
||||||
}`,
|
}`,
|
||||||
icons: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
|
icons: data.videoRenderer.channelThumbnailSupportedRenderers.channelThumbnailWithLinkRenderer.thumbnail
|
||||||
.thumbnails,
|
.thumbnails,
|
||||||
verified: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('verified')),
|
verified: Boolean(badge?.includes('verified')),
|
||||||
artist: Boolean(badge?.metadataBadgeRenderer?.style?.toLowerCase().includes('artist'))
|
artist: Boolean(badge?.includes('artist'))
|
||||||
},
|
},
|
||||||
uploadedAt: data.videoRenderer.publishedTimeText?.simpleText ?? null,
|
uploadedAt: data.videoRenderer.publishedTimeText?.simpleText ?? null,
|
||||||
views: data.videoRenderer.viewCountText?.simpleText?.replace(/[^0-9]/g, '') ?? 0,
|
views: data.videoRenderer.viewCountText?.simpleText?.replace(/[^0-9]/g, '') ?? 0,
|
||||||
@ -157,26 +158,24 @@ export function parseVideo(data?: any): YouTubeVideo {
|
|||||||
export function parsePlaylist(data?: any): YouTubePlayList {
|
export function parsePlaylist(data?: any): YouTubePlayList {
|
||||||
if (!data || !data.playlistRenderer) throw new Error('Failed to Parse YouTube Playlist');
|
if (!data || !data.playlistRenderer) throw new Error('Failed to Parse YouTube Playlist');
|
||||||
|
|
||||||
|
const thumbnail =
|
||||||
|
data.playlistRenderer.thumbnails[0].thumbnails[data.playlistRenderer.thumbnails[0].thumbnails.length - 1];
|
||||||
|
const channel = data.playlistRenderer.shortBylineText.runs?.[0];
|
||||||
|
|
||||||
const res = new YouTubePlayList(
|
const res = new YouTubePlayList(
|
||||||
{
|
{
|
||||||
id: data.playlistRenderer.playlistId,
|
id: data.playlistRenderer.playlistId,
|
||||||
title: data.playlistRenderer.title.simpleText,
|
title: data.playlistRenderer.title.simpleText,
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
id: data.playlistRenderer.playlistId,
|
id: data.playlistRenderer.playlistId,
|
||||||
url: data.playlistRenderer.thumbnails[0].thumbnails[
|
url: thumbnail.url,
|
||||||
data.playlistRenderer.thumbnails[0].thumbnails.length - 1
|
height: thumbnail.height,
|
||||||
].url,
|
width: thumbnail.width
|
||||||
height: data.playlistRenderer.thumbnails[0].thumbnails[
|
|
||||||
data.playlistRenderer.thumbnails[0].thumbnails.length - 1
|
|
||||||
].height,
|
|
||||||
width: data.playlistRenderer.thumbnails[0].thumbnails[
|
|
||||||
data.playlistRenderer.thumbnails[0].thumbnails.length - 1
|
|
||||||
].width
|
|
||||||
},
|
},
|
||||||
channel: {
|
channel: {
|
||||||
id: data.playlistRenderer.shortBylineText.runs?.[0].navigationEndpoint.browseEndpoint.browseId,
|
id: channel?.navigationEndpoint.browseEndpoint.browseId,
|
||||||
name: data.playlistRenderer.shortBylineText.runs?.[0].text,
|
name: channel?.text,
|
||||||
url: `https://www.youtube.com${data.playlistRenderer.shortBylineText.runs?.[0].navigationEndpoint.commandMetadata.webCommandMetadata.url}`
|
url: `https://www.youtube.com${channel?.navigationEndpoint.commandMetadata.webCommandMetadata.url}`
|
||||||
},
|
},
|
||||||
videos: parseInt(data.playlistRenderer.videoCount.replace(/[^0-9]/g, ''))
|
videos: parseInt(data.playlistRenderer.videoCount.replace(/[^0-9]/g, ''))
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user