Cookies update
This commit is contained in:
parent
0058e71efd
commit
4608f3d59b
110
play-dl/Request/classes.ts
Normal file
110
play-dl/Request/classes.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import tls , { TLSSocket } from "tls";
|
||||
import { URL } from "url";
|
||||
import { Timer } from "../YouTube/classes/LiveStream";
|
||||
|
||||
interface ResponseOptions extends tls.ConnectionOptions{
|
||||
body?: string;
|
||||
method: 'GET' | 'POST';
|
||||
cookies? : boolean
|
||||
headers? : Object;
|
||||
timeout? : number
|
||||
}
|
||||
|
||||
export class Response {
|
||||
parsed_url : URL
|
||||
statusCode : number;
|
||||
rawHeaders : string;
|
||||
headers : Object;
|
||||
body : string;
|
||||
socket : TLSSocket;
|
||||
sentHeaders : string;
|
||||
sentBody : string;
|
||||
private options : ResponseOptions;
|
||||
private timer : Timer | null
|
||||
constructor(req_url : string, options : ResponseOptions){
|
||||
this.parsed_url = new URL(req_url)
|
||||
this.sentHeaders = ''
|
||||
this.statusCode = 0
|
||||
this.sentBody = ""
|
||||
this.rawHeaders = ''
|
||||
this.body = ''
|
||||
this.headers = {}
|
||||
this.timer = null
|
||||
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`
|
||||
}
|
||||
}
|
||||
if(options.body) this.sentBody = options.body
|
||||
}
|
||||
|
||||
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` +
|
||||
this.sentBody
|
||||
)
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
stream(): Promise<TLSSocket>{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.timer = new Timer(() => this.socket.end(), this.options.timeout || 1)
|
||||
this.socket.once('error', (err) => reject(err))
|
||||
this.socket.once('data', (chunk) => {
|
||||
this.rawHeaders = chunk.toString('utf-8')
|
||||
this.parseHeaders()
|
||||
resolve(this.socket)
|
||||
})
|
||||
this.socket.on('data', () => this.timer?.reuse())
|
||||
this.socket.once('end', () => this.timer?.destroy())
|
||||
})
|
||||
}
|
||||
|
||||
fetch(): Promise<Response>{
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.setEncoding('utf-8');
|
||||
this.socket.once('error', (err) => reject(err))
|
||||
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()
|
||||
this.body += arr.join('')
|
||||
}
|
||||
})
|
||||
this.socket.on('end', () => {
|
||||
resolve(this)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
53
play-dl/Request/index.ts
Normal file
53
play-dl/Request/index.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Response } from "./classes";
|
||||
|
||||
|
||||
export type Proxy = ProxyOpts | string;
|
||||
|
||||
interface ProxyOpts {
|
||||
host: string;
|
||||
port: number;
|
||||
authentication?: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface RequestOptions {
|
||||
body?: string;
|
||||
method: 'GET' | 'POST';
|
||||
proxies?: Proxy[];
|
||||
cookies? : boolean
|
||||
headers? : Object;
|
||||
timeout? : number
|
||||
}
|
||||
|
||||
interface StreamGetterOptions{
|
||||
method: 'GET' | 'POST';
|
||||
cookies? : boolean
|
||||
headers : Object;
|
||||
}
|
||||
|
||||
export function request_stream(req_url : string, options : RequestOptions = {method : "GET"}): Promise<Response>{
|
||||
return new Promise(async(resolve, reject) => {
|
||||
let res = new Response(req_url, options)
|
||||
await res.stream()
|
||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
||||
res = await request_stream((res.headers as any).location, options);
|
||||
await res.stream()
|
||||
}
|
||||
resolve(res)
|
||||
})
|
||||
}
|
||||
|
||||
export function request(req_url : string, options : RequestOptions = {method : "GET"}): Promise<Response>{
|
||||
return new Promise(async(resolve, reject) => {
|
||||
let res = new Response(req_url, options)
|
||||
await res.fetch()
|
||||
if (Number(res.statusCode) >= 300 && Number(res.statusCode) < 400) {
|
||||
res = await request((res.headers as any).location, options);
|
||||
} else if (Number(res.statusCode) > 400) {
|
||||
reject(new Error(`Got ${res.statusCode} from the request`));
|
||||
}
|
||||
resolve(res)
|
||||
})
|
||||
}
|
||||
@ -130,7 +130,6 @@ export class Stream {
|
||||
private per_sec_bytes: number;
|
||||
private content_length: number;
|
||||
private video_url: string;
|
||||
private cookie: string;
|
||||
private timer: Timer;
|
||||
private quality: number;
|
||||
private proxy: Proxy[] | undefined;
|
||||
@ -141,7 +140,6 @@ export class Stream {
|
||||
duration: number,
|
||||
contentLength: number,
|
||||
video_url: string,
|
||||
cookie: string,
|
||||
options: StreamOptions
|
||||
) {
|
||||
this.stream = new PassThrough({ highWaterMark: 10 * 1000 * 1000 });
|
||||
@ -151,7 +149,6 @@ export class Stream {
|
||||
this.type = type;
|
||||
this.bytes_count = 0;
|
||||
this.video_url = video_url;
|
||||
this.cookie = cookie;
|
||||
this.per_sec_bytes = Math.ceil(contentLength / duration);
|
||||
this.content_length = contentLength;
|
||||
this.request = null;
|
||||
@ -166,7 +163,7 @@ export class Stream {
|
||||
}
|
||||
|
||||
private async retry() {
|
||||
const info = await video_info(this.video_url, { cookie: this.cookie, proxy: this.proxy });
|
||||
const info = await video_info(this.video_url, { proxy: this.proxy });
|
||||
const audioFormat = parseAudioFormats(info.format);
|
||||
this.url = audioFormat[this.quality].url;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ export enum StreamType {
|
||||
|
||||
export interface StreamOptions {
|
||||
quality?: number;
|
||||
cookie?: string;
|
||||
proxy?: Proxy[];
|
||||
}
|
||||
|
||||
@ -54,7 +53,7 @@ export type YouTubeStream = Stream | LiveStreaming;
|
||||
* @returns Stream class with type and stream for playing.
|
||||
*/
|
||||
export async function stream(url: string, options: StreamOptions = {}): Promise<YouTubeStream> {
|
||||
const info = await video_info(url, { cookie: options.cookie, proxy: options.proxy });
|
||||
const info = await video_info(url, { proxy: options.proxy });
|
||||
const final: any[] = [];
|
||||
if (
|
||||
info.LiveStreamData.isLive === true &&
|
||||
@ -83,7 +82,6 @@ export async function stream(url: string, options: StreamOptions = {}): Promise<
|
||||
info.video_details.durationInSec,
|
||||
Number(final[0].contentLength),
|
||||
info.video_details.url,
|
||||
options.cookie as string,
|
||||
options
|
||||
);
|
||||
}
|
||||
@ -122,7 +120,6 @@ export async function stream_from_info(info: InfoData, options: StreamOptions =
|
||||
info.video_details.durationInSec,
|
||||
Number(final[0].contentLength),
|
||||
info.video_details.url,
|
||||
options.cookie as string,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
31
play-dl/YouTube/utils/cookie.ts
Normal file
31
play-dl/YouTube/utils/cookie.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import fs from 'fs';
|
||||
|
||||
let youtubeData: youtubeDataOptions;
|
||||
if (fs.existsSync('.data/youtube.data')) {
|
||||
youtubeData = JSON.parse(fs.readFileSync('.data/youtube.data').toString());
|
||||
}
|
||||
|
||||
interface youtubeDataOptions {
|
||||
cookie?: Object;
|
||||
}
|
||||
|
||||
export function getCookies(): undefined | string {
|
||||
let result = ''
|
||||
if(!youtubeData?.cookie) return undefined
|
||||
for (const [ key, value ] of Object.entries(youtubeData.cookie)){
|
||||
result+= `${key}=${value};`
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function setCookie(key: string, value: string): boolean {
|
||||
if (!youtubeData?.cookie) return false;
|
||||
key = key.trim()
|
||||
value = value.trim()
|
||||
Object.assign(youtubeData.cookie, { [key] : value })
|
||||
return true
|
||||
}
|
||||
|
||||
export function uploadCookie() {
|
||||
if(youtubeData) fs.writeFileSync('.data/youtube.data', JSON.stringify(youtubeData, undefined, 4));
|
||||
}
|
||||
@ -4,7 +4,6 @@ import { YouTubeVideo } from '../classes/Video';
|
||||
import { YouTubePlayList } from '../classes/Playlist';
|
||||
|
||||
interface InfoOptions {
|
||||
cookie?: string;
|
||||
proxy?: Proxy[];
|
||||
}
|
||||
|
||||
@ -78,12 +77,8 @@ export async function video_basic_info(url: string, options: InfoOptions = {}) {
|
||||
const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`;
|
||||
const body = await request(new_url, {
|
||||
proxies: options.proxy ?? undefined,
|
||||
headers: options.cookie
|
||||
? {
|
||||
'cookie': options.cookie,
|
||||
'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' }
|
||||
headers: { 'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' },
|
||||
cookies : true
|
||||
});
|
||||
const player_response = JSON.parse(body.split('var ytInitialPlayerResponse = ')[1].split('}};')[0] + '}}');
|
||||
const initial_response = JSON.parse(body.split('var ytInitialData = ')[1].split('}};')[0] + '}}');
|
||||
|
||||
@ -2,6 +2,7 @@ import https, { RequestOptions } from 'https';
|
||||
import tls from 'tls';
|
||||
import http, { ClientRequest, IncomingMessage } from 'http';
|
||||
import { URL } from 'url';
|
||||
import { getCookies, setCookie, uploadCookie } from './cookie';
|
||||
/**
|
||||
* Types for Proxy
|
||||
*/
|
||||
@ -18,7 +19,7 @@ interface ProxyOpts {
|
||||
|
||||
interface ProxyOutput {
|
||||
statusCode: number;
|
||||
head: string;
|
||||
head: string[];
|
||||
body: string;
|
||||
}
|
||||
|
||||
@ -26,6 +27,7 @@ interface RequestOpts extends RequestOptions {
|
||||
body?: string;
|
||||
method?: 'GET' | 'POST';
|
||||
proxies?: Proxy[];
|
||||
cookies? : boolean
|
||||
}
|
||||
/**
|
||||
* Main module that play-dl uses for making a https request
|
||||
@ -135,7 +137,7 @@ async function proxy_getter(req_url: string, req_proxy: Proxy[]): Promise<ProxyO
|
||||
const head = y.shift() as string;
|
||||
resolve({
|
||||
statusCode: Number(head.split('\n')[0].split(' ')[1]),
|
||||
head: head,
|
||||
head: head.split('\r\n'),
|
||||
body: y.join('\n')
|
||||
});
|
||||
});
|
||||
@ -150,15 +152,29 @@ async function proxy_getter(req_url: string, req_proxy: Proxy[]): Promise<ProxyO
|
||||
* @param options Request options for https request
|
||||
* @returns body of that request
|
||||
*/
|
||||
export async function request(url: string, options?: RequestOpts): Promise<string> {
|
||||
export async function request(url: string, options: RequestOpts = {}): Promise<string> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!options?.proxies || options.proxies.length === 0) {
|
||||
let data = '';
|
||||
let cook = getCookies()
|
||||
if (typeof cook === 'string' && options.headers) {
|
||||
Object.assign(options.headers, { cookie : cook });
|
||||
}
|
||||
let res = await https_getter(url, options).catch((err: Error) => err);
|
||||
if (res instanceof Error) {
|
||||
reject(res);
|
||||
return;
|
||||
}
|
||||
if(res.headers && res.headers['set-cookie'] && cook){
|
||||
res.headers['set-cookie'].forEach((x) => {
|
||||
x.split(';').forEach((x) => {
|
||||
const [key, value] = x.split('=');
|
||||
if (!value) return;
|
||||
setCookie(key, value);
|
||||
});
|
||||
})
|
||||
uploadCookie()
|
||||
}
|
||||
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) {
|
||||
@ -168,13 +184,30 @@ export async function request(url: string, options?: RequestOpts): Promise<strin
|
||||
res.on('data', (c) => (data += c));
|
||||
res.on('end', () => resolve(data));
|
||||
} else {
|
||||
let cook = getCookies()
|
||||
if (typeof cook === 'string' && options.headers) {
|
||||
Object.assign(options.headers, { cookie : cook });
|
||||
}
|
||||
let res = await proxy_getter(url, options.proxies).catch((e: Error) => e);
|
||||
if (res instanceof Error) {
|
||||
reject(res);
|
||||
return;
|
||||
}
|
||||
if(res.head && cook){
|
||||
let cookies = res.head.filter((x) => x.toLocaleLowerCase().startsWith('set-cookie: '));
|
||||
cookies.forEach((x) => {
|
||||
x.toLocaleLowerCase().split('set-cookie: ')[1].split(';').forEach((y) => {
|
||||
let [key, value] = y.split('=');
|
||||
if (!value)
|
||||
return;
|
||||
setCookie(key, value);
|
||||
});
|
||||
});
|
||||
uploadCookie()
|
||||
}
|
||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
||||
res = await proxy_getter(res.head.split('Location: ')[1].split('\n')[0], options.proxies);
|
||||
let url = res.head.filter((x) => x.startsWith('Location: '));
|
||||
res = await proxy_getter(url[0].split('\n')[0], options.proxies);
|
||||
} else if (res.statusCode > 400) {
|
||||
reject(new Error(`GOT ${res.statusCode} from proxy request`));
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ export function authorization(): void {
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
ask.question('Choose your service - sc (for SoundCloud) / sp (for Spotify) : ', (msg) => {
|
||||
ask.question('Choose your service - sc (for SoundCloud) / sp (for Spotify) / yo (for YouTube): ', (msg) => {
|
||||
if (msg.toLowerCase().startsWith('sp')) {
|
||||
let client_id: string, client_secret: string, redirect_url: string, market: string;
|
||||
ask.question('Start by entering your Client ID : ', (id) => {
|
||||
@ -157,9 +157,8 @@ export function authorization(): void {
|
||||
});
|
||||
});
|
||||
} else if (msg.toLowerCase().startsWith('sc')) {
|
||||
let client_id: string;
|
||||
ask.question('Client ID : ', async (id) => {
|
||||
client_id = id;
|
||||
let client_id = id;
|
||||
if (!client_id) {
|
||||
console.log("You didn't provide a client ID. Try again...");
|
||||
ask.close();
|
||||
@ -173,6 +172,25 @@ export function authorization(): void {
|
||||
} else console.log("That doesn't look like a valid client ID. Retry with a correct client ID.");
|
||||
ask.close();
|
||||
});
|
||||
} else if (msg.toLowerCase().startsWith('yo')) {
|
||||
ask.question('Cookies : ', (cook: string) => {
|
||||
if (!cook || cook.length === 0) {
|
||||
console.log("You didn't provide a cookie. Try again...");
|
||||
ask.close();
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync('.data')) fs.mkdirSync('.data');
|
||||
console.log('Cookies has been added successfully.');
|
||||
let cookie: Object = {};
|
||||
cook.split(';').forEach((x) => {
|
||||
let [ key, value ] = x.split('=')
|
||||
key = key.trim()
|
||||
value = value.trim()
|
||||
Object.assign(cookie, { [key] : value })
|
||||
})
|
||||
fs.writeFileSync('.data/youtube.data', JSON.stringify({ cookie }, undefined, 4));
|
||||
ask.close();
|
||||
});
|
||||
} else {
|
||||
console.log("That option doesn't exist. Try again...");
|
||||
ask.close();
|
||||
@ -189,4 +207,4 @@ export function attachListeners(player: EventEmitter, resource: YouTubeStream |
|
||||
player.removeListener(AudioPlayerStatus.AutoPaused, () => resource.pause());
|
||||
player.removeListener(AudioPlayerStatus.Playing, () => resource.resume());
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user