diff --git a/play-dl/Request/useragent.ts b/play-dl/Request/useragent.ts index 05ee668..6080354 100644 --- a/play-dl/Request/useragent.ts +++ b/play-dl/Request/useragent.ts @@ -1,10 +1,7 @@ const useragents: string[] = [ - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.43', 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko', - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36', - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0', 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36.0 (KHTML, like Gecko) Chrome/61.0.0.0 Safari/537.36.0', 'Mozilla/5.0 (Windows; U; Windows NT 6.1) AppleWebKit/531.35.5 (KHTML, like Gecko) Version/4.0.3 Safari/531.35.5', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246', diff --git a/play-dl/YouTube/search.ts b/play-dl/YouTube/search.ts index 208ee2a..d96cdbf 100644 --- a/play-dl/YouTube/search.ts +++ b/play-dl/YouTube/search.ts @@ -41,7 +41,7 @@ export async function yt_search(search: string, options: ParseSearchInterface = } const body = await request(url, { headers: { - 'accept-language': 'en-US,en;q=0.9' + 'accept-language': options.language || 'en-US;q=0.9' } }); if (body.indexOf('Our systems have detected unusual traffic from your computer network.') !== -1) diff --git a/play-dl/YouTube/stream.ts b/play-dl/YouTube/stream.ts index cfc9d9d..5674895 100644 --- a/play-dl/YouTube/stream.ts +++ b/play-dl/YouTube/stream.ts @@ -15,6 +15,7 @@ export interface StreamOptions { seekMode?: 'precise' | 'granular'; seek?: number; quality?: number; + language?: string; htmldata?: boolean; } @@ -46,7 +47,7 @@ export type YouTubeStream = Stream | LiveStream | SeekStream; * @returns Stream class with type and stream for playing. */ export async function stream(url: string, options: StreamOptions = {}): Promise { - const info = await video_stream_info(url, { htmldata: options.htmldata }); + const info = await video_stream_info(url, { htmldata: options.htmldata, language: options.language }); return await stream_from_info(info, options); } /** diff --git a/play-dl/YouTube/utils/extractor.ts b/play-dl/YouTube/utils/extractor.ts index 405a7c8..cc227f5 100644 --- a/play-dl/YouTube/utils/extractor.ts +++ b/play-dl/YouTube/utils/extractor.ts @@ -6,10 +6,12 @@ import { InfoData, StreamInfoData } from './constants'; interface InfoOptions { htmldata?: boolean; + language?: string; } interface PlaylistOptions { incomplete?: boolean; + language?: string; } const video_id_pattern = /^[a-zA-Z\d_-]{11,12}$/; @@ -110,7 +112,7 @@ export async function video_basic_info(url: string, options: InfoOptions = {}): const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`; body = await request(new_url, { headers: { - 'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' + 'accept-language': options.language || 'en-US;q=0.9' }, cookies: true }); @@ -120,12 +122,12 @@ export async function video_basic_info(url: string, options: InfoOptions = {}): const player_data = body .split('var ytInitialPlayerResponse = ')?.[1] ?.split(';')[0] - .split(/;\s*(var|const|let)/)[0]; + .split(/;\s*"(var|const|let)"/)[0]; if (!player_data) throw new Error('Initial Player Response Data is undefined.'); const initial_data = body .split('var ytInitialData = ')?.[1] ?.split(';')[0] - .split(/;\s*(var|const|let)/)[0]; + .split(/;\s*"(var|const|let)"/)[0]; if (!initial_data) throw new Error('Initial Response Data is undefined.'); const player_response = JSON.parse(player_data); const initial_response = JSON.parse(initial_data); @@ -340,12 +342,17 @@ export async function playlist_info(url: string, options: PlaylistOptions = {}): const body = await request(url, { headers: { - 'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' + 'accept-language': options.language || 'en-US;q=0.9' } }); 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!'); - const response = JSON.parse(body.split('var ytInitialData = ')[1].split(';')[0]); + const response = JSON.parse( + body + .split('var ytInitialData = ')[1] + .split(';')[0] + .split(/;\s*"(var|const|let)"/)[0] + ); if (response.alerts) { if (response.alerts[0].alertWithButtonRenderer?.type === 'INFO') { if (!options.incomplete) diff --git a/play-dl/YouTube/utils/parser.ts b/play-dl/YouTube/utils/parser.ts index 194451d..44503ff 100644 --- a/play-dl/YouTube/utils/parser.ts +++ b/play-dl/YouTube/utils/parser.ts @@ -6,6 +6,7 @@ import { YouTube } from '..'; export interface ParseSearchInterface { type?: 'video' | 'playlist' | 'channel'; limit?: number; + language?: string; } export interface thumbnail { @@ -28,7 +29,7 @@ export function ParseSearchResult(html: string, options?: ParseSearchInterface): const data = html .split('var ytInitialData = ')?.[1] ?.split(';')[0] - .split(/;\s*(var|const|let)/)[0]; + .split(/;\s*"(var|const|let)"/)[0]; const json_data = JSON.parse(data); const results = []; const details = diff --git a/play-dl/index.ts b/play-dl/index.ts index 6406d69..5f4bd38 100644 --- a/play-dl/index.ts +++ b/play-dl/index.ts @@ -65,6 +65,7 @@ interface SearchOptions { deezer?: 'track' | 'playlist' | 'album'; }; fuzzy?: boolean; + language?: string; } import { createInterface } from 'node:readline'; @@ -102,6 +103,9 @@ async function stream(url: string, options?: StreamOptions): Promise { if (!options.source) options.source = { youtube: 'video' }; query = encodeURIComponent(query); - if (options.source.youtube) return await yt_search(query, { limit: options.limit, type: options.source.youtube }); + if (options.source.youtube) + return await yt_search(query, { + limit: options.limit, + type: options.source.youtube, + language: options.language + }); 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); else if (options.source.deezer) @@ -237,8 +247,10 @@ async function stream_from_info(info: InfoData, options?: StreamOptions): Promis * @param info YouTube video info OR SoundCloud track Class * @param options * + * - `number` seek : No of seconds to seek in stream. + * - `string` seekMode : Choose type of seek you want. [ "precise" | "granular" ] + * - `string` language : Sets language of searched content [ YouTube search only. ], e.g. "en-US" * - `number` quality : Quality number. [ 0 = Lowest, 1 = Medium, 2 = Highest ] - * - `Proxy[]` proxy : sends data through a proxy * - `boolean` htmldata : given data is html data or not * @returns A {@link YouTubeStream} or {@link SoundCloudStream} Stream to play */