Cookies update

This commit is contained in:
killer069 2021-10-08 14:58:06 +05:30
parent 0058e71efd
commit 4608f3d59b
8 changed files with 257 additions and 23 deletions

110
play-dl/Request/classes.ts Normal file
View 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
View 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)
})
}

View File

@ -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;
}

View File

@ -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
);
}

View 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));
}

View File

@ -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] + '}}');

View File

@ -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`));
}

View File

@ -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());
});
}
};