diff --git a/node-youtube-dl/YouTube/README.md b/node-youtube-dl/YouTube/README.md new file mode 100644 index 0000000..65081cf --- /dev/null +++ b/node-youtube-dl/YouTube/README.md @@ -0,0 +1,76 @@ +# YouTube Downloader/Search +### Downloades youtube videos, playlist and also searches song + +This is a light-weight youtube downloader and searcher. + +- searches by video, playlist, channel +- obtains audio playback url. + +## Video commands usage :- +### 1. video_basic_info(url : `string`) +*This is what downloader gets first.* +```js +let video = await video_basic_info(url) +``` +### 2. video_info(url : `string`) +*This contains everything with deciphered formats along with video_details.* +```js +let video = await video_info(url) +``` +### 3. formats +*This shows all formats availiable of a video* +```js + let video = await video_info(url) + console.log(video.format) +``` + +## Playlist commands usage :- +### 1. playlist_info(url : `string`) +*This containes every thing about a playlist* +```js +let playlist = await playlist_info(url) //This only fetches first 100 songs from a playlist +``` + +#### 2. playlist.fetch() +*This fetches whole playlist.* +```js +let playlist = await playlist_info(url) //This only fetches first 100 songs from a playlist +await playlist.fetch() // This one fetches all songs from a playlist. +``` +#### 3. playlist.page(page_number : `number`) +*This gives you no. of videos from a page* +> Pages : every 100 songs have been divided into pages. +> So for example: There is 782 songs in a playlist, so there will be 8 pages. + +```js +let playlist = await playlist_info(url) //This only fetches first 100 songs from a playlist +await playlist.fetch() // This one fetches all songs from a playlist. +console.log(playlist.page(1)) // This displays first 100 songs of a playlist +``` +#### 4. playlist.total_videos +*This tells you total no of videos that have been fetched so far.* +```js +let playlist = await playlist_info(url) //This only fetches first 100 songs from a playlist +await playlist.fetch() // This one fetches all songs from a playlist. +console.log(playlist.total_videos) // This displays total no. of videos fetched so far. +``` +#### 5. playlist.videoCount +*This tells total no. of songs in a playlist.* +```js +let playlist = await playlist_info(url) //This only fetches first 100 songs from a playlist +await playlist.fetch() // This one fetches all songs from a playlist. +console.log(playlist.videoCount) // This displays total no. of videos in a playlist +``` + +## Search Command Usage :- +### 1. search(url : `string`, options? : `SearchOptions`) +*This enables all searching mechanism (video, channel, playlist)* +```js +let result = await search('Rick Roll') +console.log(result[0].url) +``` +### SearchOptions +``` +type?: "video" | "playlist" | "channel" | "all"; +limit?: number; +``` diff --git a/node-youtube-dl/YouTube/classes/Playlist.ts b/node-youtube-dl/YouTube/classes/Playlist.ts index 46a48e1..2eb5ec2 100644 --- a/node-youtube-dl/YouTube/classes/Playlist.ts +++ b/node-youtube-dl/YouTube/classes/Playlist.ts @@ -3,7 +3,6 @@ import { url_get } from "../utils/request"; import { Thumbnail } from "./Thumbnail"; import { Channel } from "./Channel"; import { Video } from "./Video"; -import fs from 'fs' const BASE_API = "https://www.youtube.com/youtubei/v1/browse?key="; export class PlayList{ @@ -16,7 +15,7 @@ export class PlayList{ link?: string; channel?: Channel; thumbnail?: Thumbnail; - videos?: []; + private videos?: []; private fetched_videos : Map private _continuation: { api?: string; token?: string; clientVersion?: string } = {}; private __count : number diff --git a/node-youtube-dl/YouTube/search.ts b/node-youtube-dl/YouTube/search.ts index 0d597d7..4df4f6d 100644 --- a/node-youtube-dl/YouTube/search.ts +++ b/node-youtube-dl/YouTube/search.ts @@ -1,5 +1,4 @@ import { url_get } from "./utils/request"; -import fs from 'fs' import { ParseSearchInterface, ParseSearchResult } from "./utils/parser"; import { Video } from "./classes/Video"; import { Channel } from "./classes/Channel"; diff --git a/node-youtube-dl/YouTube/utils/cipher.ts b/node-youtube-dl/YouTube/utils/cipher.ts index e6ebada..0c2cb98 100644 --- a/node-youtube-dl/YouTube/utils/cipher.ts +++ b/node-youtube-dl/YouTube/utils/cipher.ts @@ -153,10 +153,10 @@ function download_url(format: formatOptions, sig : string){ format.url = parsed_url.toString(); } -export async function format_decipher(format: formatOptions[], html5player : string){ +export async function format_decipher(formats: formatOptions[], html5player : string){ let body = await url_get(html5player) let tokens = js_tokens(body) - format.forEach((format) => { + formats.forEach((format) => { let cipher = format.signatureCipher || format.cipher; if(cipher){ Object.assign(format, querystring.parse(cipher)) @@ -167,7 +167,9 @@ export async function format_decipher(format: formatOptions[], html5player : str if(tokens && format.s){ sig = deciper_signature(tokens, format.s) download_url(format, sig) + delete format.s + delete format.sp } }); - return format + return formats } \ No newline at end of file diff --git a/node-youtube-dl/YouTube/utils/extractor.ts b/node-youtube-dl/YouTube/utils/extractor.ts index 8ea2078..25b5bb8 100644 --- a/node-youtube-dl/YouTube/utils/extractor.ts +++ b/node-youtube-dl/YouTube/utils/extractor.ts @@ -3,30 +3,25 @@ import { format_decipher, js_tokens } from './cipher' import { Video } from '../classes/Video' import { RequestInit } from 'node-fetch' import { PlayList } from '../classes/Playlist' -import fs from 'fs' const DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; const youtube_url = /https:\/\/www.youtube.com\//g const video_pattern = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; -export interface PlaylistOptions { - limit?: number; - requestOptions?: RequestInit; -} - export async function video_basic_info(url : string){ if(!url.match(youtube_url) || !url.match(video_pattern)) throw new Error('This is not a YouTube URL') - let body = await url_get(url) + let video_id = url.split('watch?v=')[1].split('&')[0] + let new_url = 'https://www.youtube.com/watch?v=' + video_id + let body = await url_get(new_url) let player_response = JSON.parse(body.split("var ytInitialPlayerResponse = ")[1].split(";")[0]) if(player_response.playabilityStatus.status === 'ERROR') throw new Error(`While getting info from url \n ${player_response.playabilityStatus.reason}`) - let response = JSON.parse(body.split("var ytInitialData = ")[1].split(";")[0]) let html5player = 'https://www.youtube.com' + body.split('"jsUrl":"')[1].split('"')[0] let format = [] format.push(player_response.streamingData.formats[0]) format.push(...player_response.streamingData.adaptiveFormats) let vid = player_response.videoDetails let microformat = player_response.microformat.playerMicroformatRenderer - let video_details = new Video ({ + let video_details = { id : vid.videoId, url : 'https://www.youtube.com/watch?v=' + vid.videoId, title : vid.title, @@ -48,10 +43,8 @@ export async function video_basic_info(url : string){ averageRating : vid.averageRating, live : vid.isLiveContent, private : vid.isPrivate - }) + } return { - player_response, - response, html5player, format, video_details @@ -69,9 +62,7 @@ export async function video_info(url : string) { } } -export async function playlist_info(url : string , options? : PlaylistOptions) { - if (!options) options = { limit: 100, requestOptions: {} }; - if(!options.limit) options.limit = 100 +export async function playlist_info(url : string) { if (!url || typeof url !== "string") throw new Error(`Expected playlist url, received ${typeof url}!`); if(url.search('(\\?|\\&)list\\=') === -1) throw new Error('This is not a PlayList URL') @@ -87,7 +78,7 @@ export async function playlist_info(url : string , options? : PlaylistOptions) { let playlistDetails = JSON.parse(body.split('{"playlistSidebarRenderer":')[1].split("}};")[0]).items; let API_KEY = body.split('INNERTUBE_API_KEY":"')[1]?.split('"')[0] ?? body.split('innertubeApiKey":"')[1]?.split('"')[0] ?? DEFAULT_API_KEY; - let videos = getPlaylistVideos(parsed, options.limit); + let videos = getPlaylistVideos(parsed, 100); let data = playlistDetails[0].playlistSidebarPrimaryInfoRenderer; if (!data.title.runs || !data.title.runs.length) return undefined; diff --git a/node-youtube-dl/YouTube/utils/parser.ts b/node-youtube-dl/YouTube/utils/parser.ts index ab8c702..b0bdfc1 100644 --- a/node-youtube-dl/YouTube/utils/parser.ts +++ b/node-youtube-dl/YouTube/utils/parser.ts @@ -1,13 +1,10 @@ import { Video } from "../classes/Video"; import { PlayList } from "../classes/Playlist"; import { Channel } from "../classes/Channel"; -import { RequestInit } from "node-fetch"; -import fs from 'fs' export interface ParseSearchInterface { type?: "video" | "playlist" | "channel" | "all"; limit?: number; - requestOptions?: RequestInit; } export function ParseSearchResult(html :string, options? : ParseSearchInterface): (Video | PlayList | Channel)[] { diff --git a/node-youtube-dl/index.ts b/node-youtube-dl/index.ts index 4b7e624..c2176cf 100644 --- a/node-youtube-dl/index.ts +++ b/node-youtube-dl/index.ts @@ -1,8 +1,10 @@ -import { playlist_info } from "./YouTube"; +//This File is in testing stage, everything will change in this +import { playlist_info, video_basic_info, video_info, search } from "./YouTube"; let main = async() => { let time_start = Date.now() - let playlist = await playlist_info('https://www.youtube.com/watch?v=bM7SZ5SBzyY&list=PLzkuLC6Yvumv_Rd5apfPRWEcjf9b1JRnq') + let result = await search('Rick Roll') + console.log(result[0].url) let time_end = Date.now() console.log(`Time Taken : ${(time_end - time_start)/1000} seconds`) }