commit
f8de856382
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "play-dl",
|
"name": "play-dl",
|
||||||
"version": "1.4.2",
|
"version": "1.4.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "play-dl",
|
"name": "play-dl",
|
||||||
"version": "1.4.2",
|
"version": "1.4.4",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.9.4",
|
"@types/node": "^16.9.4",
|
||||||
|
|||||||
@ -1,89 +0,0 @@
|
|||||||
import tls, { TLSSocket } from 'node:tls';
|
|
||||||
import { URL } from 'node:url';
|
|
||||||
|
|
||||||
interface ProxyOptions extends tls.ConnectionOptions {
|
|
||||||
method: 'GET';
|
|
||||||
headers?: Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Proxy {
|
|
||||||
parsed_url: URL;
|
|
||||||
statusCode: number;
|
|
||||||
rawHeaders: string;
|
|
||||||
headers: Object;
|
|
||||||
body: string;
|
|
||||||
socket: TLSSocket;
|
|
||||||
sentHeaders: string;
|
|
||||||
private options: ProxyOptions;
|
|
||||||
constructor(parsed_url: URL, options: ProxyOptions) {
|
|
||||||
this.parsed_url = parsed_url;
|
|
||||||
this.sentHeaders = '';
|
|
||||||
this.statusCode = 0;
|
|
||||||
this.rawHeaders = '';
|
|
||||||
this.body = '';
|
|
||||||
this.headers = {};
|
|
||||||
this.options = options;
|
|
||||||
this.socket = tls.connect(
|
|
||||||
{
|
|
||||||
host: this.parsed_url.hostname,
|
|
||||||
port: Number(this.parsed_url.port) || 443,
|
|
||||||
socket: options.socket,
|
|
||||||
rejectUnauthorized: false
|
|
||||||
},
|
|
||||||
() => this.onConnect()
|
|
||||||
);
|
|
||||||
if (options.headers) {
|
|
||||||
for (const [key, value] of Object.entries(options.headers)) {
|
|
||||||
this.sentHeaders += `${key}: ${value}\r\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onConnect() {
|
|
||||||
this.socket.write(
|
|
||||||
`${this.options.method} ${this.parsed_url.pathname}${this.parsed_url.search} HTTP/1.1\r\n` +
|
|
||||||
`Host: ${this.parsed_url.hostname}\r\n` +
|
|
||||||
this.sentHeaders +
|
|
||||||
`Connection: close\r\n` +
|
|
||||||
`\r\n`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseHeaders() {
|
|
||||||
const head_arr = this.rawHeaders.split('\r\n');
|
|
||||||
this.statusCode = Number(head_arr.shift()?.split(' ')[1]) ?? -1;
|
|
||||||
for (const head of head_arr) {
|
|
||||||
let [key, value] = head.split(': ');
|
|
||||||
if (!value) break;
|
|
||||||
key = key.trim().toLowerCase();
|
|
||||||
value = value.trim();
|
|
||||||
if (Object.keys(this.headers).includes(key)) {
|
|
||||||
let val = (this.headers as any)[key];
|
|
||||||
if (typeof val === 'string') val = [val];
|
|
||||||
Object.assign(this.headers, { [key]: [...val, value] });
|
|
||||||
} else Object.assign(this.headers, { [key]: value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(): Promise<Proxy> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.socket.setEncoding('utf-8');
|
|
||||||
this.socket.once('error', (err) => reject(err));
|
|
||||||
const parts: string[] = [];
|
|
||||||
this.socket.on('data', (chunk: string) => {
|
|
||||||
if (this.rawHeaders.length === 0) {
|
|
||||||
this.rawHeaders = chunk;
|
|
||||||
this.parseHeaders();
|
|
||||||
} else {
|
|
||||||
const arr = chunk.split('\r\n');
|
|
||||||
if (arr.length > 1 && arr[0].length < 5) arr.shift();
|
|
||||||
parts.push(...arr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.socket.on('end', () => {
|
|
||||||
this.body = parts.join('');
|
|
||||||
resolve(this);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +1,13 @@
|
|||||||
import http, { ClientRequest, IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import https, { RequestOptions } from 'node:https';
|
import https, { RequestOptions } from 'node:https';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
|
import zlib, { BrotliDecompress, Deflate, Gunzip } from 'node:zlib';
|
||||||
import { cookieHeaders, getCookies } from '../YouTube/utils/cookie';
|
import { cookieHeaders, getCookies } from '../YouTube/utils/cookie';
|
||||||
import { Proxy } from './classes';
|
import { getRandomUserAgent } from './useragent';
|
||||||
|
|
||||||
export type ProxyOptions = ProxyOpts | string;
|
|
||||||
|
|
||||||
interface RequestOpts extends RequestOptions {
|
interface RequestOpts extends RequestOptions {
|
||||||
body?: string;
|
body?: string;
|
||||||
method?: 'GET' | 'POST' | 'HEAD';
|
method?: 'GET' | 'POST' | 'HEAD';
|
||||||
proxies?: ProxyOptions[];
|
|
||||||
cookies?: boolean;
|
cookies?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,56 +46,46 @@ export function request_stream(req_url: string, options: RequestOpts = { method:
|
|||||||
*/
|
*/
|
||||||
export function request(req_url: string, options: RequestOpts = { method: 'GET' }): Promise<string> {
|
export function request(req_url: string, options: RequestOpts = { method: 'GET' }): Promise<string> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
if (!options?.proxies || options.proxies.length === 0) {
|
let cookies_added = false;
|
||||||
let cookies_added = false;
|
if (options.cookies) {
|
||||||
if (options.cookies) {
|
let cook = getCookies();
|
||||||
let cook = getCookies();
|
if (typeof cook === 'string' && options.headers) {
|
||||||
if (typeof cook === 'string' && options.headers) {
|
Object.assign(options.headers, { cookie: cook });
|
||||||
Object.assign(options.headers, { cookie: cook });
|
cookies_added = true;
|
||||||
cookies_added = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let res = await https_getter(req_url, options).catch((err: Error) => err);
|
|
||||||
if (res instanceof Error) {
|
|
||||||
reject(res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (res.headers && res.headers['set-cookie'] && cookies_added) {
|
|
||||||
cookieHeaders(res.headers['set-cookie']);
|
|
||||||
}
|
|
||||||
if (Number(res.statusCode) >= 300 && Number(res.statusCode) < 400) {
|
|
||||||
res = await https_getter(res.headers.location as string, options);
|
|
||||||
} else if (Number(res.statusCode) > 400) {
|
|
||||||
reject(new Error(`Got ${res.statusCode} from the request`));
|
|
||||||
}
|
|
||||||
const data: string[] = [];
|
|
||||||
res.setEncoding('utf-8');
|
|
||||||
res.on('data', (c) => data.push(c));
|
|
||||||
res.on('end', () => resolve(data.join('')));
|
|
||||||
} else {
|
|
||||||
let cookies_added = false;
|
|
||||||
if (options.cookies) {
|
|
||||||
let cook = getCookies();
|
|
||||||
if (typeof cook === 'string' && options.headers) {
|
|
||||||
Object.assign(options.headers, { cookie: cook });
|
|
||||||
cookies_added = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let res = await proxy_getter(req_url, options.proxies, options.headers).catch((e: Error) => e);
|
|
||||||
if (res instanceof Error) {
|
|
||||||
reject(res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (res.headers && (res.headers as any)['set-cookie'] && cookies_added) {
|
|
||||||
cookieHeaders((res.headers as any)['set-cookie']);
|
|
||||||
}
|
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
|
||||||
res = await proxy_getter((res.headers as any)['location'], options.proxies, options.headers);
|
|
||||||
} else if (res.statusCode > 400) {
|
|
||||||
reject(new Error(`GOT ${res.statusCode} from proxy request`));
|
|
||||||
}
|
|
||||||
resolve(res.body);
|
|
||||||
}
|
}
|
||||||
|
if (options.headers) {
|
||||||
|
options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
'accept-encoding': 'gzip, deflate, br',
|
||||||
|
'user-agent': getRandomUserAgent()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let res = await https_getter(req_url, options).catch((err: Error) => err);
|
||||||
|
if (res instanceof Error) {
|
||||||
|
reject(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (res.headers && res.headers['set-cookie'] && cookies_added) {
|
||||||
|
cookieHeaders(res.headers['set-cookie']);
|
||||||
|
}
|
||||||
|
if (Number(res.statusCode) >= 300 && Number(res.statusCode) < 400) {
|
||||||
|
res = await https_getter(res.headers.location as string, options).catch((err) => err);
|
||||||
|
if (res instanceof Error) throw res;
|
||||||
|
} else if (Number(res.statusCode) > 400) {
|
||||||
|
reject(new Error(`Got ${res.statusCode} from the request`));
|
||||||
|
}
|
||||||
|
const data: string[] = [];
|
||||||
|
let decoder: BrotliDecompress | Gunzip | Deflate;
|
||||||
|
const encoding = res.headers['content-encoding'];
|
||||||
|
if (encoding === 'gzip') decoder = zlib.createGunzip();
|
||||||
|
else if (encoding === 'br') decoder = zlib.createBrotliDecompress();
|
||||||
|
else decoder = zlib.createDeflate();
|
||||||
|
|
||||||
|
res.pipe(decoder);
|
||||||
|
decoder.setEncoding('utf-8');
|
||||||
|
decoder.on('data', (c) => data.push(c));
|
||||||
|
decoder.on('end', () => resolve(data.join('')));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,75 +114,6 @@ export function request_resolve_redirect(url: string): Promise<string> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Chooses one random number between max and min number.
|
|
||||||
* @param min Minimum number
|
|
||||||
* @param max Maximum number
|
|
||||||
* @returns Random Number
|
|
||||||
*/
|
|
||||||
function randomIntFromInterval(min: number, max: number): number {
|
|
||||||
let x = Math.floor(Math.random() * (max - min + 1) + min);
|
|
||||||
if (x === 0) return 0;
|
|
||||||
else return x - 1;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Main module that play-dl uses for proxy.
|
|
||||||
* @param req_url URL to make https request to
|
|
||||||
* @param req_proxy Proxies array
|
|
||||||
* @returns Object with statusCode, head and body
|
|
||||||
*/
|
|
||||||
function proxy_getter(req_url: string, req_proxy: ProxyOptions[], headers?: Object): Promise<Proxy> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const proxy: string | ProxyOpts = req_proxy[randomIntFromInterval(0, req_proxy.length)];
|
|
||||||
const parsed_url = new URL(req_url);
|
|
||||||
let opts: ProxyOpts;
|
|
||||||
if (typeof proxy === 'string') {
|
|
||||||
const parsed = new URL(proxy);
|
|
||||||
opts = {
|
|
||||||
host: parsed.hostname,
|
|
||||||
port: Number(parsed.port),
|
|
||||||
authentication: {
|
|
||||||
username: parsed.username,
|
|
||||||
password: parsed.password
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else
|
|
||||||
opts = {
|
|
||||||
host: proxy.host,
|
|
||||||
port: Number(proxy.port)
|
|
||||||
};
|
|
||||||
let req: ClientRequest;
|
|
||||||
if (!opts.authentication) {
|
|
||||||
req = http.request({
|
|
||||||
host: opts.host,
|
|
||||||
port: opts.port,
|
|
||||||
method: 'CONNECT',
|
|
||||||
path: `${parsed_url.host}:443`
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
req = http.request({
|
|
||||||
host: opts.host,
|
|
||||||
port: opts.port,
|
|
||||||
method: 'CONNECT',
|
|
||||||
path: `${parsed_url.host}:443`,
|
|
||||||
headers: {
|
|
||||||
'Proxy-Authorization': `Basic ${Buffer.from(
|
|
||||||
`${opts.authentication?.username}:${opts.authentication?.password}`
|
|
||||||
).toString('base64')}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
req.on('connect', async function (res, socket) {
|
|
||||||
const conn_proxy = new Proxy(parsed_url, { method: 'GET', socket: socket, headers: headers });
|
|
||||||
await conn_proxy.fetch();
|
|
||||||
socket.end();
|
|
||||||
resolve(conn_proxy);
|
|
||||||
});
|
|
||||||
req.on('error', (e: Error) => reject(e));
|
|
||||||
req.end();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main module that play-dl uses for making a https request
|
* Main module that play-dl uses for making a https request
|
||||||
* @param req_url URL to make https request to
|
* @param req_url URL to make https request to
|
||||||
|
|||||||
27
play-dl/Request/useragent.ts
Normal file
27
play-dl/Request/useragent.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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',
|
||||||
|
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1'
|
||||||
|
];
|
||||||
|
|
||||||
|
export function setUserAgent(array: string[]): void {
|
||||||
|
useragents.push(...array);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomInt(min: number, max: number): number {
|
||||||
|
min = Math.ceil(min);
|
||||||
|
max = Math.floor(max);
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomUserAgent() {
|
||||||
|
const random = getRandomInt(0, useragents.length - 1);
|
||||||
|
return useragents[random];
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Readable } from 'node:stream';
|
import { Readable } from 'node:stream';
|
||||||
import { IncomingMessage } from 'node:http';
|
import { IncomingMessage } from 'node:http';
|
||||||
import { parseAudioFormats, StreamOptions, StreamType } from '../stream';
|
import { parseAudioFormats, StreamOptions, StreamType } from '../stream';
|
||||||
import { ProxyOptions as Proxy, request, request_stream } from '../../Request';
|
import { request, request_stream } from '../../Request';
|
||||||
import { video_info } from '..';
|
import { video_info } from '..';
|
||||||
|
|
||||||
export interface FormatInterface {
|
export interface FormatInterface {
|
||||||
@ -230,10 +230,6 @@ export class Stream {
|
|||||||
* Quality given by user. [ Used only for retrying purposes only. ]
|
* Quality given by user. [ Used only for retrying purposes only. ]
|
||||||
*/
|
*/
|
||||||
private quality: number;
|
private quality: number;
|
||||||
/**
|
|
||||||
* Proxy config given by user. [ Used only for retrying purposes only. ]
|
|
||||||
*/
|
|
||||||
private proxy: Proxy[] | undefined;
|
|
||||||
/**
|
/**
|
||||||
* Incoming message that we recieve.
|
* Incoming message that we recieve.
|
||||||
*
|
*
|
||||||
@ -261,7 +257,6 @@ export class Stream {
|
|||||||
this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} });
|
this.stream = new Readable({ highWaterMark: 5 * 1000 * 1000, read() {} });
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.quality = options.quality as number;
|
this.quality = options.quality as number;
|
||||||
this.proxy = options.proxy || undefined;
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.bytes_count = 0;
|
this.bytes_count = 0;
|
||||||
this.video_url = video_url;
|
this.video_url = video_url;
|
||||||
@ -282,7 +277,7 @@ export class Stream {
|
|||||||
* Retry if we get 404 or 403 Errors.
|
* Retry if we get 404 or 403 Errors.
|
||||||
*/
|
*/
|
||||||
private async retry() {
|
private async retry() {
|
||||||
const info = await video_info(this.video_url, { proxy: this.proxy });
|
const info = await video_info(this.video_url);
|
||||||
const audioFormat = parseAudioFormats(info.format);
|
const audioFormat = parseAudioFormats(info.format);
|
||||||
this.url = audioFormat[this.quality].url;
|
this.url = audioFormat[this.quality].url;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,9 +40,8 @@ export async function yt_search(search: string, options: ParseSearchInterface =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const body = await request(url, {
|
const body = await request(url, {
|
||||||
headers: {
|
headers: {
|
||||||
'accept-language': 'en-US,en;q=0.9',
|
'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',
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
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)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { video_info } from '.';
|
import { video_info } from '.';
|
||||||
import { LiveStream, Stream } from './classes/LiveStream';
|
import { LiveStream, Stream } from './classes/LiveStream';
|
||||||
import { ProxyOptions as Proxy } from './../Request';
|
|
||||||
import { InfoData } from './utils/constants';
|
import { InfoData } from './utils/constants';
|
||||||
|
|
||||||
export enum StreamType {
|
export enum StreamType {
|
||||||
@ -13,7 +12,6 @@ export enum StreamType {
|
|||||||
|
|
||||||
export interface StreamOptions {
|
export interface StreamOptions {
|
||||||
quality?: number;
|
quality?: number;
|
||||||
proxy?: Proxy[];
|
|
||||||
htmldata?: boolean;
|
htmldata?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,17 +39,17 @@ export type YouTubeStream = Stream | LiveStream;
|
|||||||
/**
|
/**
|
||||||
* Stream command for YouTube
|
* Stream command for YouTube
|
||||||
* @param url YouTube URL
|
* @param url YouTube URL
|
||||||
* @param options lets you add quality, cookie, proxy support for stream
|
* @param options lets you add quality for stream
|
||||||
* @returns Stream class with type and stream for playing.
|
* @returns Stream class with type and stream for playing.
|
||||||
*/
|
*/
|
||||||
export async function stream(url: string, options: StreamOptions = {}): Promise<YouTubeStream> {
|
export async function stream(url: string, options: StreamOptions = {}): Promise<YouTubeStream> {
|
||||||
const info = await video_info(url, { proxy: options.proxy, htmldata: options.htmldata });
|
const info = await video_info(url, { htmldata: options.htmldata });
|
||||||
return await stream_from_info(info, options);
|
return await stream_from_info(info, options);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Stream command for YouTube using info from video_info or decipher_info function.
|
* Stream command for YouTube using info from video_info or decipher_info function.
|
||||||
* @param info video_info data
|
* @param info video_info data
|
||||||
* @param options lets you add quality, cookie, proxy support for stream
|
* @param options lets you add quality for stream
|
||||||
* @returns Stream class with type and stream for playing.
|
* @returns Stream class with type and stream for playing.
|
||||||
*/
|
*/
|
||||||
export async function stream_from_info(info: InfoData, options: StreamOptions = {}): Promise<YouTubeStream> {
|
export async function stream_from_info(info: InfoData, options: StreamOptions = {}): Promise<YouTubeStream> {
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
import { ProxyOptions as Proxy, request } from './../../Request/index';
|
import { request } from './../../Request/index';
|
||||||
import { format_decipher } from './cipher';
|
import { format_decipher } from './cipher';
|
||||||
import { YouTubeVideo } from '../classes/Video';
|
import { YouTubeVideo } from '../classes/Video';
|
||||||
import { YouTubePlayList } from '../classes/Playlist';
|
import { YouTubePlayList } from '../classes/Playlist';
|
||||||
import { InfoData, StreamInfoData } from './constants';
|
import { InfoData, StreamInfoData } from './constants';
|
||||||
|
|
||||||
interface InfoOptions {
|
interface InfoOptions {
|
||||||
proxy?: Proxy[];
|
|
||||||
htmldata?: boolean;
|
htmldata?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlaylistOptions {
|
interface PlaylistOptions {
|
||||||
incomplete?: boolean;
|
incomplete?: boolean;
|
||||||
proxy?: Proxy[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const video_id_pattern = /^[a-zA-Z\d_-]{11,12}$/;
|
const video_id_pattern = /^[a-zA-Z\d_-]{11,12}$/;
|
||||||
@ -93,24 +91,11 @@ export function extractID(url: string): string {
|
|||||||
* const video = await play.video_basic_info('youtube video url')
|
* const video = await play.video_basic_info('youtube video url')
|
||||||
*
|
*
|
||||||
* const res = ... // Any https package get function.
|
* const res = ... // Any https package get function.
|
||||||
* const video = await play.video_basic_info(res.body, { htmldata : true })
|
|
||||||
*
|
*
|
||||||
* const video = await play.video_basic_info('youtube video url', { proxy : [{
|
* const video = await play.video_basic_info(res.body, { htmldata : true })
|
||||||
host : "IP or hostname",
|
|
||||||
port : 8080,
|
|
||||||
authentication: {
|
|
||||||
username: 'username';
|
|
||||||
password: 'very secret';
|
|
||||||
}
|
|
||||||
}] }) // Authentication is optional.
|
|
||||||
|
|
||||||
// OR
|
|
||||||
|
|
||||||
const video = await play.video_basic_info('youtube video url', { proxy : ['url'] })
|
|
||||||
* ```
|
* ```
|
||||||
* @param url YouTube url or ID or html body data
|
* @param url YouTube url or ID or html body data
|
||||||
* @param options Video Info Options
|
* @param options Video Info Options
|
||||||
* - `Proxy[]` proxy : sends data through a proxy
|
|
||||||
* - `boolean` htmldata : given data is html data or not
|
* - `boolean` htmldata : given data is html data or not
|
||||||
* @returns Video Basic Info {@link InfoData}.
|
* @returns Video Basic Info {@link InfoData}.
|
||||||
*/
|
*/
|
||||||
@ -123,11 +108,9 @@ export async function video_basic_info(url: string, options: InfoOptions = {}):
|
|||||||
const video_id: string = extractID(url);
|
const video_id: string = extractID(url);
|
||||||
const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;
|
const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;
|
||||||
body = await request(new_url, {
|
body = await request(new_url, {
|
||||||
proxies: options.proxy ?? [],
|
headers: {
|
||||||
headers: {
|
'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7'
|
||||||
'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7',
|
},
|
||||||
'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',
|
|
||||||
},
|
|
||||||
cookies: true
|
cookies: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -309,24 +292,11 @@ function parseSeconds(seconds: number): string {
|
|||||||
* const video = await play.video_info('youtube video url')
|
* const video = await play.video_info('youtube video url')
|
||||||
*
|
*
|
||||||
* const res = ... // Any https package get function.
|
* const res = ... // Any https package get function.
|
||||||
* const video = await play.video_info(res.body, { htmldata : true })
|
|
||||||
*
|
*
|
||||||
* const video = await play.video_info('youtube video url', { proxy : [{
|
* const video = await play.video_info(res.body, { htmldata : true })
|
||||||
host : "IP or hostname",
|
|
||||||
port : 8080,
|
|
||||||
authentication: {
|
|
||||||
username: 'username';
|
|
||||||
password: 'very secret';
|
|
||||||
}
|
|
||||||
}] }) // Authentication is optional.
|
|
||||||
|
|
||||||
// OR
|
|
||||||
|
|
||||||
const video = await play.video_info('youtube video url', { proxy : ['url'] })
|
|
||||||
* ```
|
* ```
|
||||||
* @param url YouTube url or ID or html body data
|
* @param url YouTube url or ID or html body data
|
||||||
* @param options Video Info Options
|
* @param options Video Info Options
|
||||||
* - `Proxy[]` proxy : sends data through a proxy
|
|
||||||
* - `boolean` htmldata : given data is html data or not
|
* - `boolean` htmldata : given data is html data or not
|
||||||
* @returns Deciphered Video Info {@link InfoData}.
|
* @returns Deciphered Video Info {@link InfoData}.
|
||||||
*/
|
*/
|
||||||
@ -357,24 +327,10 @@ export async function decipher_info<T extends InfoData | StreamInfoData>(data: T
|
|||||||
* const playlist = await play.playlist_info('youtube playlist url')
|
* const playlist = await play.playlist_info('youtube playlist url')
|
||||||
*
|
*
|
||||||
* const playlist = await play.playlist_info('youtube playlist url', { incomplete : true })
|
* const playlist = await play.playlist_info('youtube playlist url', { incomplete : true })
|
||||||
*
|
|
||||||
* const playlist = await play.playlist_info('youtube playlist url', { proxy : [{
|
|
||||||
host : "IP or hostname",
|
|
||||||
port : 8080,
|
|
||||||
authentication: {
|
|
||||||
username: 'username';
|
|
||||||
password: 'very secret';
|
|
||||||
}
|
|
||||||
}] }) // Authentication is optional.
|
|
||||||
|
|
||||||
// OR
|
|
||||||
|
|
||||||
const playlist = await play.playlist_info('youtube playlist url', { proxy : ['url'] })
|
|
||||||
* ```
|
* ```
|
||||||
* @param url Playlist URL
|
* @param url Playlist URL
|
||||||
* @param options Playlist Info Options
|
* @param options Playlist Info Options
|
||||||
* - `boolean` incomplete : If set to true, parses playlist with hidden videos.
|
* - `boolean` incomplete : If set to true, parses playlist with hidden videos.
|
||||||
* - `Proxy[]` proxy : sends data through a proxy
|
|
||||||
*
|
*
|
||||||
* @returns YouTube Playlist
|
* @returns YouTube Playlist
|
||||||
*/
|
*/
|
||||||
@ -388,10 +344,8 @@ export async function playlist_info(url: string, options: PlaylistOptions = {}):
|
|||||||
const new_url = `https://www.youtube.com/playlist?list=${Playlist_id}`;
|
const new_url = `https://www.youtube.com/playlist?list=${Playlist_id}`;
|
||||||
|
|
||||||
const body = await request(new_url, {
|
const body = await request(new_url, {
|
||||||
proxies: options.proxy ?? undefined,
|
headers: {
|
||||||
headers: {
|
'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7'
|
||||||
'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7',
|
|
||||||
'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)
|
||||||
|
|||||||
@ -103,7 +103,6 @@ import { DeezerAlbum, DeezerPlaylist, DeezerTrack } from './Deezer/classes';
|
|||||||
* @param options
|
* @param options
|
||||||
*
|
*
|
||||||
* - `number` quality : Quality number. [ 0 = Lowest, 1 = Medium, 2 = Highest ]
|
* - `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
|
* - `boolean` htmldata : given data is html data or not
|
||||||
* @returns A {@link YouTubeStream} or {@link SoundCloudStream} Stream to play
|
* @returns A {@link YouTubeStream} or {@link SoundCloudStream} Stream to play
|
||||||
*/
|
*/
|
||||||
@ -123,35 +122,6 @@ export async function stream(url: string, options: StreamOptions = {}): Promise<
|
|||||||
else return await yt_stream(url, options);
|
else return await yt_stream(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches through a particular source and gives respective info.
|
|
||||||
*
|
|
||||||
* Example
|
|
||||||
* ```ts
|
|
||||||
* const searched = await play.search('Rick Roll', { source : { youtube : "video" } }) // YouTube Video Search
|
|
||||||
*
|
|
||||||
* const searched = await play.search('Rick Roll', { limit : 1 }) // YouTube Video Search but returns only 1 video.
|
|
||||||
*
|
|
||||||
* const searched = await play.search('Rick Roll', { source : { spotify : "track" } }) // Spotify Track Search
|
|
||||||
*
|
|
||||||
* const searched = await play.search('Rick Roll', { source : { soundcloud : "tracks" } }) // SoundCloud Track Search
|
|
||||||
*
|
|
||||||
* const searched = await play.search('Rick Roll', { source : { deezer : "track" } }) // Deezer Track Search
|
|
||||||
* ```
|
|
||||||
* @param query string to search.
|
|
||||||
* @param options
|
|
||||||
*
|
|
||||||
* - `number` limit : No of searches you want to have.
|
|
||||||
* - `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
|
|
||||||
* - youtube : 'video' | 'playlist' | 'channel';
|
|
||||||
- spotify : 'album' | 'playlist' | 'track';
|
|
||||||
- soundcloud : 'tracks' | 'playlists' | 'albums';
|
|
||||||
- deezer : 'track' | 'playlist' | 'album';
|
|
||||||
```
|
|
||||||
* @returns Array of {@link YouTube} or {@link Spotify} or {@link SoundCloud} or {@link Deezer} type
|
|
||||||
*/
|
|
||||||
export async function search(
|
export async function search(
|
||||||
query: string,
|
query: string,
|
||||||
options: { source: { deezer: 'album' } } & SearchOptions
|
options: { source: { deezer: 'album' } } & SearchOptions
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { setUserAgent } from './Request/useragent';
|
||||||
import { setSoundCloudToken } from './SoundCloud';
|
import { setSoundCloudToken } from './SoundCloud';
|
||||||
import { setSpotifyToken } from './Spotify';
|
import { setSpotifyToken } from './Spotify';
|
||||||
import { setCookieToken } from './YouTube/utils/cookie';
|
import { setCookieToken } from './YouTube/utils/cookie';
|
||||||
@ -15,6 +16,7 @@ interface tokenOptions {
|
|||||||
youtube?: {
|
youtube?: {
|
||||||
cookie: string;
|
cookie: string;
|
||||||
};
|
};
|
||||||
|
useragent?: string[];
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Sets
|
* Sets
|
||||||
@ -25,6 +27,8 @@ interface tokenOptions {
|
|||||||
*
|
*
|
||||||
* iii> Spotify :- client ID, client secret, refresh token, market.
|
* iii> Spotify :- client ID, client secret, refresh token, market.
|
||||||
*
|
*
|
||||||
|
* iv> Useragents :- array of string.
|
||||||
|
*
|
||||||
* locally in memory.
|
* locally in memory.
|
||||||
* @param options {@link tokenOptions}
|
* @param options {@link tokenOptions}
|
||||||
*/
|
*/
|
||||||
@ -32,4 +36,5 @@ export function setToken(options: tokenOptions) {
|
|||||||
if (options.spotify) setSpotifyToken(options.spotify);
|
if (options.spotify) setSpotifyToken(options.spotify);
|
||||||
if (options.soundcloud) setSoundCloudToken(options.soundcloud);
|
if (options.soundcloud) setSoundCloudToken(options.soundcloud);
|
||||||
if (options.youtube) setCookieToken(options.youtube);
|
if (options.youtube) setCookieToken(options.youtube);
|
||||||
|
if (options.useragent) setUserAgent(options.useragent);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user