Add option to unblur thumbnails in YouTube search results
This commit is contained in:
parent
5d4485a54e
commit
9670e6ccfb
@ -2,11 +2,23 @@ import { YouTubeVideo } from '../classes/Video';
|
||||
import { YouTubePlayList } from '../classes/Playlist';
|
||||
import { YouTubeChannel } from '../classes/Channel';
|
||||
import { YouTube } from '..';
|
||||
import { YouTubeThumbnail } from '../classes/Thumbnail';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
const BLURRED_THUMBNAILS = [
|
||||
'-oaymwEpCOADEI4CSFryq4qpAxsIARUAAAAAGAElAADIQj0AgKJDeAHtAZmZGUI=',
|
||||
'-oaymwEiCOADEI4CSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BmZkZQg==',
|
||||
'-oaymwEiCOgCEMoBSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmbmQQ==',
|
||||
'-oaymwEiCNAFEJQDSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmZmQg==',
|
||||
'-oaymwEdCNAFEJQDSFryq4qpAw8IARUAAIhCGAHtAWZmZkI=',
|
||||
'-oaymwEdCNACELwBSFryq4qpAw8IARUAAIhCGAHtAT0K10E='
|
||||
];
|
||||
|
||||
export interface ParseSearchInterface {
|
||||
type?: 'video' | 'playlist' | 'channel';
|
||||
limit?: number;
|
||||
language?: string;
|
||||
unblurNSFWThumbnails?: boolean;
|
||||
}
|
||||
|
||||
export interface thumbnail {
|
||||
@ -25,6 +37,7 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface):
|
||||
if (!options) options = { type: 'video', limit: 0 };
|
||||
else if (!options.type) options.type = 'video';
|
||||
const hasLimit = typeof options.limit === 'number' && options.limit > 0;
|
||||
options.unblurNSFWThumbnails ??= false;
|
||||
|
||||
const data = html
|
||||
.split('var ytInitialData = ')?.[1]
|
||||
@ -35,13 +48,17 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface):
|
||||
const details =
|
||||
json_data.contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[0]
|
||||
.itemSectionRenderer.contents;
|
||||
writeFileSync('results.json', JSON.stringify(details));
|
||||
for (const detail of details) {
|
||||
if (hasLimit && results.length === options.limit) break;
|
||||
if (!detail.videoRenderer && !detail.channelRenderer && !detail.playlistRenderer) continue;
|
||||
switch (options.type) {
|
||||
case 'video': {
|
||||
const parsed = parseVideo(detail);
|
||||
if (parsed) results.push(parsed);
|
||||
if (parsed) {
|
||||
if (options.unblurNSFWThumbnails) parsed.thumbnails.forEach(unblurThumbnail);
|
||||
results.push(parsed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'channel': {
|
||||
@ -51,7 +68,10 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface):
|
||||
}
|
||||
case 'playlist': {
|
||||
const parsed = parsePlaylist(detail);
|
||||
if (parsed) results.push(parsed);
|
||||
if (parsed) {
|
||||
if (options.unblurNSFWThumbnails && parsed.thumbnail) unblurThumbnail(parsed.thumbnail);
|
||||
results.push(parsed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -189,3 +209,36 @@ export function parsePlaylist(data?: any): YouTubePlayList {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function unblurThumbnail(thumbnail: YouTubeThumbnail) {
|
||||
if (BLURRED_THUMBNAILS.find((sqp) => thumbnail.url.includes(sqp))) {
|
||||
thumbnail.url = thumbnail.url.split('?')[0];
|
||||
|
||||
// we need to update the size parameters as the sqp parameter also included a cropped size
|
||||
switch (thumbnail.url.split('/').at(-1)!.split('.')[0]) {
|
||||
case 'hq2':
|
||||
case 'hqdefault':
|
||||
thumbnail.width = 480;
|
||||
thumbnail.height = 360;
|
||||
break;
|
||||
case 'hq720':
|
||||
thumbnail.width = 1280;
|
||||
thumbnail.height = 720;
|
||||
break;
|
||||
case 'sddefault':
|
||||
thumbnail.width = 640;
|
||||
thumbnail.height = 480;
|
||||
break;
|
||||
case 'mqdefault':
|
||||
thumbnail.width = 320;
|
||||
thumbnail.height = 180;
|
||||
break;
|
||||
case 'default':
|
||||
thumbnail.width = 120;
|
||||
thumbnail.height = 90;
|
||||
break;
|
||||
default:
|
||||
thumbnail.width = thumbnail.height = NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +67,11 @@ interface SearchOptions {
|
||||
};
|
||||
fuzzy?: boolean;
|
||||
language?: string;
|
||||
/**
|
||||
* !!! Before enabling this for public servers, please consider using Discord features like NSFW channels as not everyone in your server wants to see NSFW images. !!!
|
||||
* Unblurred images will likely have different dimensions than specified in the {@link YouTubeThumbnail} objects.
|
||||
*/
|
||||
unblurNSFWThumbnails?: boolean;
|
||||
}
|
||||
|
||||
import { createInterface } from 'node:readline';
|
||||
@ -184,6 +189,9 @@ async function search(query: string, options?: SearchOptions): Promise<YouTubeVi
|
||||
*
|
||||
* - `number` limit : No of searches you want to have.
|
||||
* - `string` language : Sets language of searched content [ YouTube search only. ], e.g. "en-US"
|
||||
* - `boolean` unblurNSFWThumbnails : Unblurs NSFW thumbnails. Defaults to `false` [ YouTube search only. ]
|
||||
* !!! Before enabling this for public servers, please consider using Discord features like NSFW channels as not everyone in your server wants to see NSFW images. !!!
|
||||
* Unblurred images will likely have different dimensions than specified in the {@link YouTubeThumbnail} objects.
|
||||
* - `boolean` fuzzy : Whether the search should be fuzzy or only return exact matches. Defaults to `true`. [ for `Deezer` Only ]
|
||||
* - `Object` source : Contains type of source and type of result you want to have
|
||||
* ```ts
|
||||
@ -204,7 +212,8 @@ async function search(
|
||||
return await yt_search(query, {
|
||||
limit: options.limit,
|
||||
type: options.source.youtube,
|
||||
language: options.language
|
||||
language: options.language,
|
||||
unblurNSFWThumbnails: options.unblurNSFWThumbnails
|
||||
});
|
||||
else if (options.source.spotify) return await sp_search(query, options.source.spotify, options.limit);
|
||||
else if (options.source.soundcloud) return await so_search(query, options.source.soundcloud, options.limit);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user