YouTube examples

This commit is contained in:
killer069 2021-08-13 14:58:57 +05:30
parent 04770cc929
commit 06cc44d485
7 changed files with 93 additions and 27 deletions

View File

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

View File

@ -3,7 +3,6 @@ import { url_get } from "../utils/request";
import { Thumbnail } from "./Thumbnail"; import { Thumbnail } from "./Thumbnail";
import { Channel } from "./Channel"; import { Channel } from "./Channel";
import { Video } from "./Video"; import { Video } from "./Video";
import fs from 'fs'
const BASE_API = "https://www.youtube.com/youtubei/v1/browse?key="; const BASE_API = "https://www.youtube.com/youtubei/v1/browse?key=";
export class PlayList{ export class PlayList{
@ -16,7 +15,7 @@ export class PlayList{
link?: string; link?: string;
channel?: Channel; channel?: Channel;
thumbnail?: Thumbnail; thumbnail?: Thumbnail;
videos?: []; private videos?: [];
private fetched_videos : Map<string, Video[]> private fetched_videos : Map<string, Video[]>
private _continuation: { api?: string; token?: string; clientVersion?: string } = {}; private _continuation: { api?: string; token?: string; clientVersion?: string } = {};
private __count : number private __count : number

View File

@ -1,5 +1,4 @@
import { url_get } from "./utils/request"; import { url_get } from "./utils/request";
import fs from 'fs'
import { ParseSearchInterface, ParseSearchResult } from "./utils/parser"; import { ParseSearchInterface, ParseSearchResult } from "./utils/parser";
import { Video } from "./classes/Video"; import { Video } from "./classes/Video";
import { Channel } from "./classes/Channel"; import { Channel } from "./classes/Channel";

View File

@ -153,10 +153,10 @@ function download_url(format: formatOptions, sig : string){
format.url = parsed_url.toString(); 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 body = await url_get(html5player)
let tokens = js_tokens(body) let tokens = js_tokens(body)
format.forEach((format) => { formats.forEach((format) => {
let cipher = format.signatureCipher || format.cipher; let cipher = format.signatureCipher || format.cipher;
if(cipher){ if(cipher){
Object.assign(format, querystring.parse(cipher)) Object.assign(format, querystring.parse(cipher))
@ -167,7 +167,9 @@ export async function format_decipher(format: formatOptions[], html5player : str
if(tokens && format.s){ if(tokens && format.s){
sig = deciper_signature(tokens, format.s) sig = deciper_signature(tokens, format.s)
download_url(format, sig) download_url(format, sig)
delete format.s
delete format.sp
} }
}); });
return format return formats
} }

View File

@ -3,30 +3,25 @@ import { format_decipher, js_tokens } from './cipher'
import { Video } from '../classes/Video' import { Video } from '../classes/Video'
import { RequestInit } from 'node-fetch' import { RequestInit } from 'node-fetch'
import { PlayList } from '../classes/Playlist' import { PlayList } from '../classes/Playlist'
import fs from 'fs'
const DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8"; const DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";
const youtube_url = /https:\/\/www.youtube.com\//g const youtube_url = /https:\/\/www.youtube.com\//g
const video_pattern = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/; 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){ 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') 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(";</script>")[0]) let player_response = JSON.parse(body.split("var ytInitialPlayerResponse = ")[1].split(";</script>")[0])
if(player_response.playabilityStatus.status === 'ERROR') throw new Error(`While getting info from url \n ${player_response.playabilityStatus.reason}`) 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(";</script>")[0])
let html5player = 'https://www.youtube.com' + body.split('"jsUrl":"')[1].split('"')[0] let html5player = 'https://www.youtube.com' + body.split('"jsUrl":"')[1].split('"')[0]
let format = [] let format = []
format.push(player_response.streamingData.formats[0]) format.push(player_response.streamingData.formats[0])
format.push(...player_response.streamingData.adaptiveFormats) format.push(...player_response.streamingData.adaptiveFormats)
let vid = player_response.videoDetails let vid = player_response.videoDetails
let microformat = player_response.microformat.playerMicroformatRenderer let microformat = player_response.microformat.playerMicroformatRenderer
let video_details = new Video ({ let video_details = {
id : vid.videoId, id : vid.videoId,
url : 'https://www.youtube.com/watch?v=' + vid.videoId, url : 'https://www.youtube.com/watch?v=' + vid.videoId,
title : vid.title, title : vid.title,
@ -48,10 +43,8 @@ export async function video_basic_info(url : string){
averageRating : vid.averageRating, averageRating : vid.averageRating,
live : vid.isLiveContent, live : vid.isLiveContent,
private : vid.isPrivate private : vid.isPrivate
}) }
return { return {
player_response,
response,
html5player, html5player,
format, format,
video_details video_details
@ -69,9 +62,7 @@ export async function video_info(url : string) {
} }
} }
export async function playlist_info(url : string , options? : PlaylistOptions) { export async function playlist_info(url : string) {
if (!options) options = { limit: 100, requestOptions: {} };
if(!options.limit) options.limit = 100
if (!url || typeof url !== "string") throw new Error(`Expected playlist url, received ${typeof url}!`); 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') 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("}};</script>")[0]).items; let playlistDetails = JSON.parse(body.split('{"playlistSidebarRenderer":')[1].split("}};</script>")[0]).items;
let API_KEY = body.split('INNERTUBE_API_KEY":"')[1]?.split('"')[0] ?? body.split('innertubeApiKey":"')[1]?.split('"')[0] ?? DEFAULT_API_KEY; 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; let data = playlistDetails[0].playlistSidebarPrimaryInfoRenderer;
if (!data.title.runs || !data.title.runs.length) return undefined; if (!data.title.runs || !data.title.runs.length) return undefined;

View File

@ -1,13 +1,10 @@
import { Video } from "../classes/Video"; import { Video } from "../classes/Video";
import { PlayList } from "../classes/Playlist"; import { PlayList } from "../classes/Playlist";
import { Channel } from "../classes/Channel"; import { Channel } from "../classes/Channel";
import { RequestInit } from "node-fetch";
import fs from 'fs'
export interface ParseSearchInterface { export interface ParseSearchInterface {
type?: "video" | "playlist" | "channel" | "all"; type?: "video" | "playlist" | "channel" | "all";
limit?: number; limit?: number;
requestOptions?: RequestInit;
} }
export function ParseSearchResult(html :string, options? : ParseSearchInterface): (Video | PlayList | Channel)[] { export function ParseSearchResult(html :string, options? : ParseSearchInterface): (Video | PlayList | Channel)[] {

View File

@ -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 main = async() => {
let time_start = Date.now() 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() let time_end = Date.now()
console.log(`Time Taken : ${(time_end - time_start)/1000} seconds`) console.log(`Time Taken : ${(time_end - time_start)/1000} seconds`)
} }