2021-10-11 18:38:08 +05:30
import { ProxyOptions as Proxy , request } from './../../Request/index' ;
2021-09-20 09:40:15 +05:30
import { format_decipher } from './cipher' ;
2021-09-27 22:20:50 +05:30
import { YouTubeVideo } from '../classes/Video' ;
import { YouTubePlayList } from '../classes/Playlist' ;
2021-11-18 15:38:25 +05:30
import { InfoData } from './constants' ;
2021-09-17 14:36:32 +05:30
2021-09-28 21:05:43 +05:30
interface InfoOptions {
proxy? : Proxy [ ] ;
2021-11-01 15:32:51 +05:30
htmldata? : boolean ;
2021-09-28 20:45:45 +05:30
}
interface PlaylistOptions {
2021-09-28 21:05:43 +05:30
incomplete? : boolean ;
proxy? : Proxy [ ] ;
2021-09-28 20:45:45 +05:30
}
2021-10-02 13:26:50 +05:30
const video_id_pattern = /^[a-zA-Z\d_-]{11,12}$/ ;
2021-10-31 17:02:32 +01:00
const playlist_id_pattern = /^(PL|UU|LL|RD|OL)[a-zA-Z\d_-]{16,41}$/ ;
2021-09-17 14:36:32 +05:30
const DEFAULT_API_KEY = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8' ;
const video_pattern =
2021-11-22 13:13:00 +05:30
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|shorts\/|embed\/|v\/)?)([\w\-]+)(\S+)?$/ ;
2021-10-02 13:26:50 +05:30
const playlist_pattern =
2021-10-31 14:50:26 +01:00
/^((?:https?:)?\/\/)?(?:(?:www|m)\.)?(youtube\.com)\/(?:(playlist|watch))(.*)?((\?|\&)list=)(PL|UU|LL|RD|OL)[a-zA-Z\d_-]{16,41}(.*)?$/ ;
2021-09-29 20:23:16 +05:30
/ * *
2021-11-22 13:13:00 +05:30
* Validate YouTube URL or ID .
2021-11-23 09:56:08 +05:30
*
2021-11-22 13:13:00 +05:30
* * * CAUTION : * * If your search word is 11 - 12 long , you might get it validated as video ID .
2021-11-23 09:56:08 +05:30
*
2021-11-22 13:13:00 +05:30
* To avoid above , add one more condition to yt_validate
* ` ` ` ts
* if ( url . startsWith ( 'https' ) && yt_validate ( url ) === 'video' ) {
* // YouTube Video Url.
* }
* ` ` `
* @param url YouTube URL OR ID
2021-11-23 09:56:08 +05:30
* @returns
2021-11-22 13:13:00 +05:30
* ` ` `
* 'playlist' | 'video' | 'search' | false
* ` ` `
2021-09-29 20:23:16 +05:30
* /
2021-10-09 16:18:05 +05:30
export function yt_validate ( url : string ) : 'playlist' | 'video' | 'search' | false {
2021-09-17 14:36:32 +05:30
if ( url . indexOf ( 'list=' ) === - 1 ) {
2021-10-02 13:26:50 +05:30
if ( url . startsWith ( 'https' ) ) {
2021-10-09 16:18:05 +05:30
if ( url . match ( video_pattern ) ) {
2021-10-11 10:35:29 +05:30
let id : string ;
if ( url . includes ( 'youtu.be/' ) ) id = url . split ( 'youtu.be/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-12 14:09:14 +05:30
else if ( url . includes ( 'youtube.com/embed/' ) )
id = url . split ( 'youtube.com/embed/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-26 14:57:19 +05:30
else if ( url . includes ( 'youtube.com/shorts/' ) )
id = url . split ( 'youtube.com/shorts/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-11 10:35:29 +05:30
else id = url . split ( 'watch?v=' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-09 18:59:16 +05:30
if ( id . match ( video_id_pattern ) ) return 'video' ;
else return false ;
} else return false ;
2021-10-02 13:26:50 +05:30
} else {
if ( url . match ( video_id_pattern ) ) return 'video' ;
else if ( url . match ( playlist_id_pattern ) ) return 'playlist' ;
2021-10-09 16:18:05 +05:30
else return 'search' ;
2021-10-02 13:26:50 +05:30
}
2021-09-17 14:36:32 +05:30
} else {
if ( ! url . match ( playlist_pattern ) ) return false ;
2021-10-11 11:51:47 +05:30
else return 'playlist' ;
2021-08-31 10:16:14 +05:30
}
}
2021-09-29 20:23:16 +05:30
/ * *
2021-11-22 13:13:00 +05:30
* Extract ID of YouTube url .
2021-09-29 20:23:16 +05:30
* @param url ID or url of YouTube
* @returns ID of video or playlist .
* /
2021-09-17 14:36:32 +05:30
export function extractID ( url : string ) : string {
2021-10-09 16:18:05 +05:30
const check = yt_validate ( url ) ;
if ( ! check || check === 'search' ) throw new Error ( 'This is not a YouTube url or videoId or PlaylistID' ) ;
2021-09-17 14:36:32 +05:30
if ( url . startsWith ( 'https' ) ) {
if ( url . indexOf ( 'list=' ) === - 1 ) {
let video_id : string ;
2021-10-11 10:35:29 +05:30
if ( url . includes ( 'youtu.be/' ) ) video_id = url . split ( 'youtu.be/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-12 14:09:14 +05:30
else if ( url . includes ( 'youtube.com/embed/' ) )
video_id = url . split ( 'youtube.com/embed/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-26 14:21:29 +05:30
else if ( url . includes ( 'youtube.com/shorts/' ) )
video_id = url . split ( 'youtube.com/shorts/' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-10-11 10:35:29 +05:30
else video_id = url . split ( 'watch?v=' ) [ 1 ] . split ( /(\?|\/|&)/ ) [ 0 ] ;
2021-09-17 14:36:32 +05:30
return video_id ;
} else {
return url . split ( 'list=' ) [ 1 ] . split ( '&' ) [ 0 ] ;
2021-09-06 10:33:37 +05:30
}
2021-09-17 14:36:32 +05:30
} else return url ;
2021-09-06 10:33:37 +05:30
}
2021-09-29 20:23:16 +05:30
/ * *
* Basic function to get data from a YouTube url or ID .
2021-11-22 13:13:00 +05:30
*
* Example
* ` ` ` ts
* const video = await play . video_basic_info ( 'youtube video url' )
*
* 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 : [ {
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 options Video Info Options
* - ` Proxy[] ` proxy : sends data through a proxy
* - ` boolean ` htmldata : given data is html data or not
* @returns Video Basic Info { @link InfoData } .
2021-09-29 20:23:16 +05:30
* /
2021-11-23 09:56:08 +05:30
export async function video_basic_info ( url : string , options : InfoOptions = { } ) : Promise < InfoData > {
2021-11-01 15:32:51 +05:30
let body : string ;
if ( options . htmldata ) {
2021-11-01 15:22:02 +05:30
body = url ;
2021-11-01 15:32:51 +05:30
} else {
2021-11-01 15:22:02 +05:30
if ( yt_validate ( url ) !== 'video' ) throw new Error ( 'This is not a YouTube Watch URL' ) ;
const video_id : string = extractID ( url ) ;
const new_url = ` https://www.youtube.com/watch?v= ${ video_id } &has_verified=1 ` ;
body = await request ( new_url , {
proxies : options.proxy ? ? [ ] ,
headers : { 'accept-language' : 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' } ,
cookies : true
} ) ;
}
2021-11-28 18:49:08 +05:30
if ( body . indexOf ( 'Our systems have detected unusual traffic from your computer network.' ) !== - 1 ) throw new Error ( 'Captcha page: YouTube has detected that you are a bot!' ) ;
2021-10-26 14:31:30 +05:30
const player_data = body
. split ( 'var ytInitialPlayerResponse = ' ) ? . [ 1 ]
? . split ( ';</script>' ) [ 0 ]
2021-11-10 14:21:12 -08:00
. split ( /;\s*(var|const|let)/ ) [ 0 ] ;
2021-10-26 14:31:30 +05:30
if ( ! player_data ) throw new Error ( 'Initial Player Response Data is undefined.' ) ;
2021-10-26 14:40:50 +05:30
const initial_data = body
. split ( 'var ytInitialData = ' ) ? . [ 1 ]
? . split ( ';</script>' ) [ 0 ]
2021-11-10 14:21:12 -08:00
. split ( /;\s*(var|const|let)/ ) [ 0 ] ;
2021-10-26 14:40:50 +05:30
if ( ! initial_data ) throw new Error ( 'Initial Response Data is undefined.' ) ;
2021-10-26 14:31:30 +05:30
const player_response = JSON . parse ( player_data ) ;
2021-10-26 14:40:50 +05:30
const initial_response = JSON . parse ( initial_data ) ;
2021-09-17 14:36:32 +05:30
if ( player_response . playabilityStatus . status !== 'OK' )
throw new Error (
` While getting info from url \ n ${
player_response . playabilityStatus . errorScreen . playerErrorMessageRenderer ? . reason . simpleText ? ?
player_response . playabilityStatus . errorScreen . playerKavRenderer ? . reason . simpleText
} `
) ;
2021-11-23 09:56:08 +05:30
const ownerInfo =
initial_response . contents . twoColumnWatchNextResults . results ? . results ? . contents [ 1 ] ? . videoSecondaryInfoRenderer
? . owner ? . videoOwnerRenderer ;
const badge = ownerInfo ? . badges && ownerInfo ? . badges [ 0 ] ;
2021-09-17 14:36:32 +05:30
const html5player = ` https://www.youtube.com ${ body . split ( '"jsUrl":"' ) [ 1 ] . split ( '"' ) [ 0 ] } ` ;
2021-09-27 22:20:50 +05:30
const related : string [ ] = [ ] ;
2021-09-24 12:49:39 +05:30
initial_response . contents . twoColumnWatchNextResults . secondaryResults . secondaryResults . results . forEach (
( res : any ) = > {
if ( res . compactVideoRenderer )
related . push ( ` https://www.youtube.com/watch?v= ${ res . compactVideoRenderer . videoId } ` ) ;
}
) ;
2021-09-17 14:36:32 +05:30
const format = [ ] ;
const vid = player_response . videoDetails ;
const microformat = player_response . microformat . playerMicroformatRenderer ;
2021-11-27 14:32:43 +01:00
const ratingButtons =
initial_response . contents . twoColumnWatchNextResults . results . results . contents . find (
( content : any ) = > content . videoPrimaryInfoRenderer
) ? . videoPrimaryInfoRenderer . videoActions . menuRenderer . topLevelButtons ? ? [ ] ;
2021-09-27 22:20:50 +05:30
const video_details = new YouTubeVideo ( {
2021-09-17 14:36:32 +05:30
id : vid.videoId ,
url : ` https://www.youtube.com/watch?v= ${ vid . videoId } ` ,
title : vid.title ,
description : vid.shortDescription ,
2021-10-05 18:47:09 +05:30
duration : Number ( vid . lengthSeconds ) ,
duration_raw : parseSeconds ( Number ( vid . lengthSeconds ) ) ,
2021-10-05 11:09:41 +05:30
uploadedAt : microformat.publishDate ,
2021-09-17 14:36:32 +05:30
thumbnail : vid.thumbnail.thumbnails [ vid . thumbnail . thumbnails . length - 1 ] ,
channel : {
name : vid.author ,
id : vid.channelId ,
url : ` https://www.youtube.com/channel/ ${ vid . channelId } ` ,
2021-10-31 12:19:06 +01:00
verified : Boolean ( badge ? . metadataBadgeRenderer ? . style ? . toLowerCase ( ) . includes ( 'verified' ) ) ,
2021-11-22 13:13:00 +05:30
artist : Boolean ( badge ? . metadataBadgeRenderer ? . style ? . toLowerCase ( ) . includes ( 'artist' ) ) ,
2021-11-23 09:56:08 +05:30
icons : ownerInfo?.thumbnail?.thumbnails || undefined
2021-09-17 14:36:32 +05:30
} ,
views : vid.viewCount ,
tags : vid.keywords ,
averageRating : vid.averageRating ,
2021-11-27 14:32:43 +01:00
likes : parseInt (
ratingButtons
. find ( ( button : any ) = > button . toggleButtonRenderer . defaultIcon . iconType === 'LIKE' )
? . toggleButtonRenderer . defaultText . accessibility ? . accessibilityData . label . replace ( /\D+/g , '' ) ? ? 0
) ,
dislikes : parseInt (
ratingButtons
. find ( ( button : any ) = > button . toggleButtonRenderer . defaultIcon . iconType === 'DISLIKE' )
? . toggleButtonRenderer . defaultText . accessibility ? . accessibilityData . label . replace ( /\D+/g , '' ) ? ? 0
) ,
2021-09-17 14:36:32 +05:30
live : vid.isLiveContent ,
private : vid . isPrivate
2021-09-27 22:20:50 +05:30
} ) ;
2021-09-20 17:20:15 +05:30
format . push ( . . . ( player_response . streamingData . formats ? ? [ ] ) ) ;
format . push ( . . . ( player_response . streamingData . adaptiveFormats ? ? [ ] ) ) ;
2021-09-17 14:36:32 +05:30
const LiveStreamData = {
isLive : video_details.live ,
dashManifestUrl : player_response.streamingData?.dashManifestUrl ? ? null ,
hlsManifestUrl : player_response.streamingData?.hlsManifestUrl ? ? null
} ;
return {
LiveStreamData ,
html5player ,
format ,
2021-09-22 15:33:56 +05:30
video_details ,
2021-09-24 12:49:39 +05:30
related_videos : related
2021-09-17 14:36:32 +05:30
} ;
2021-08-07 15:53:18 +05:30
}
2021-09-29 20:23:16 +05:30
/ * *
* Function to convert seconds to [ hour : minutes : seconds ] format
* @param seconds seconds to convert
* @returns [ hour : minutes : seconds ] format
* /
2021-09-17 14:36:32 +05:30
function parseSeconds ( seconds : number ) : string {
const d = Number ( seconds ) ;
const h = Math . floor ( d / 3600 ) ;
const m = Math . floor ( ( d % 3600 ) / 60 ) ;
const s = Math . floor ( ( d % 3600 ) % 60 ) ;
2021-08-23 13:05:40 +05:30
2021-09-17 14:36:32 +05:30
const hDisplay = h > 0 ? ( h < 10 ? ` 0 ${ h } ` : h ) + ':' : '' ;
const mDisplay = m > 0 ? ( m < 10 ? ` 0 ${ m } ` : m ) + ':' : '00:' ;
const sDisplay = s > 0 ? ( s < 10 ? ` 0 ${ s } ` : s ) : '00' ;
return hDisplay + mDisplay + sDisplay ;
2021-08-23 13:05:40 +05:30
}
2021-09-29 20:23:16 +05:30
/ * *
2021-11-22 13:13:00 +05:30
* Gets data from YouTube url or ID or html body data and deciphers it .
* ` ` `
* video_basic_info + decipher_info = video_info
* ` ` `
*
* Example
* ` ` ` ts
* const video = await play . video_info ( 'youtube video url' )
*
* 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 : [ {
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 options Video Info Options
* - ` Proxy[] ` proxy : sends data through a proxy
* - ` boolean ` htmldata : given data is html data or not
* @returns Deciphered Video Info { @link InfoData } .
2021-09-29 20:23:16 +05:30
* /
2021-11-18 15:38:25 +05:30
export async function video_info ( url : string , options : InfoOptions = { } ) : Promise < InfoData > {
2021-09-28 20:45:45 +05:30
const data = await video_basic_info ( url , options ) ;
2021-11-26 23:36:31 +01:00
return await decipher_info ( data ) ;
2021-08-07 15:53:18 +05:30
}
2021-10-16 16:32:49 +02:00
/ * *
* Function uses data from video_basic_info and deciphers it if it contains signatures .
2021-11-22 13:13:00 +05:30
* @param data Data - { @link InfoData }
* @returns Deciphered Video Info { @link InfoData }
2021-10-16 16:32:49 +02:00
* /
2021-10-17 21:41:16 +02:00
export async function decipher_info ( data : InfoData ) {
2021-11-22 13:13:00 +05:30
if ( data . LiveStreamData . isLive === true && data . LiveStreamData . dashManifestUrl !== null ) {
2021-10-16 16:32:49 +02:00
return data ;
} else if ( data . format [ 0 ] . signatureCipher || data . format [ 0 ] . cipher ) {
data . format = await format_decipher ( data . format , data . html5player ) ;
return data ;
} else {
return data ;
}
}
2021-09-29 20:23:16 +05:30
/ * *
2021-11-22 13:13:00 +05:30
* Gets YouTube playlist info from a playlist url .
*
* Example
* ` ` ` ts
* 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' , { 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' ] } )
* ` ` `
2021-09-29 20:23:16 +05:30
* @param url Playlist URL
2021-11-22 13:13:00 +05:30
* @param options Playlist Info Options
* - ` boolean ` incomplete : If set to true , parses playlist with hidden videos .
* - ` Proxy[] ` proxy : sends data through a proxy
*
2021-09-29 20:23:16 +05:30
* @returns YouTube Playlist
* /
export async function playlist_info ( url : string , options : PlaylistOptions = { } ) : Promise < YouTubePlayList > {
2021-09-17 14:36:32 +05:30
if ( ! url || typeof url !== 'string' ) throw new Error ( ` Expected playlist url, received ${ typeof url } ! ` ) ;
let Playlist_id : string ;
if ( url . startsWith ( 'https' ) ) {
if ( yt_validate ( url ) !== 'playlist' ) throw new Error ( 'This is not a Playlist URL' ) ;
Playlist_id = extractID ( url ) ;
} else Playlist_id = url ;
const new_url = ` https://www.youtube.com/playlist?list= ${ Playlist_id } ` ;
const body = await request ( new_url , {
2021-10-01 12:08:46 +05:30
proxies : options.proxy ? ? undefined ,
2021-09-17 14:36:32 +05:30
headers : { 'accept-language' : 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7' }
} ) ;
const response = JSON . parse ( body . split ( 'var ytInitialData = ' ) [ 1 ] . split ( ';</script>' ) [ 0 ] ) ;
if ( response . alerts ) {
if ( response . alerts [ 0 ] . alertWithButtonRenderer ? . type === 'INFO' ) {
2021-09-28 20:45:45 +05:30
if ( ! options . incomplete )
2021-09-17 14:36:32 +05:30
throw new Error (
` While parsing playlist url \ n ${ response . alerts [ 0 ] . alertWithButtonRenderer . text . simpleText } `
) ;
} else if ( response . alerts [ 0 ] . alertRenderer ? . type === 'ERROR' )
throw new Error ( ` While parsing playlist url \ n ${ response . alerts [ 0 ] . alertRenderer . text . runs [ 0 ] . text } ` ) ;
else throw new Error ( 'While parsing playlist url\nUnknown Playlist Error' ) ;
2021-08-23 23:34:10 +05:30
}
2021-08-13 13:16:34 +05:30
2021-09-17 14:36:32 +05:30
const rawJSON = ` ${ body . split ( '{"playlistVideoListRenderer":{"contents":' ) [ 1 ] . split ( '}],"playlistId"' ) [ 0 ] } }] ` ;
const parsed = JSON . parse ( rawJSON ) ;
const playlistDetails = JSON . parse ( body . split ( '{"playlistSidebarRenderer":' ) [ 1 ] . split ( '}};</script>' ) [ 0 ] ) . items ;
2021-08-13 13:16:34 +05:30
2021-09-17 14:36:32 +05:30
const API_KEY =
body . split ( 'INNERTUBE_API_KEY":"' ) [ 1 ] ? . split ( '"' ) [ 0 ] ? ?
body . split ( 'innertubeApiKey":"' ) [ 1 ] ? . split ( '"' ) [ 0 ] ? ?
DEFAULT_API_KEY ;
const videos = getPlaylistVideos ( parsed , 100 ) ;
2021-08-13 13:16:34 +05:30
2021-09-17 14:36:32 +05:30
const data = playlistDetails [ 0 ] . playlistSidebarPrimaryInfoRenderer ;
2021-09-29 20:23:16 +05:30
if ( ! data . title . runs || ! data . title . runs . length ) throw new Error ( 'Failed to Parse Playlist info.' ) ;
2021-08-13 13:16:34 +05:30
2021-09-17 14:36:32 +05:30
const author = playlistDetails [ 1 ] ? . playlistSidebarSecondaryInfoRenderer . videoOwner ;
const views = data . stats . length === 3 ? data . stats [ 1 ] . simpleText . replace ( /[^0-9]/g , '' ) : 0 ;
const lastUpdate =
data . stats
. find ( ( x : any ) = > 'runs' in x && x [ 'runs' ] . find ( ( y : any ) = > y . text . toLowerCase ( ) . includes ( 'last update' ) ) )
? . runs . pop ( ) ? . text ? ? null ;
const videosCount = data . stats [ 0 ] . runs [ 0 ] . text . replace ( /[^0-9]/g , '' ) || 0 ;
2021-08-13 13:16:34 +05:30
2021-09-27 22:20:50 +05:30
const res = new YouTubePlayList ( {
2021-08-13 13:16:34 +05:30
continuation : {
api : API_KEY ,
token : getContinuationToken ( parsed ) ,
2021-09-17 14:36:32 +05:30
clientVersion :
body . split ( '"INNERTUBE_CONTEXT_CLIENT_VERSION":"' ) [ 1 ] ? . split ( '"' ) [ 0 ] ? ?
body . split ( '"innertube_context_client_version":"' ) [ 1 ] ? . split ( '"' ) [ 0 ] ? ?
'<some version>'
2021-08-13 13:16:34 +05:30
} ,
id : data.title.runs [ 0 ] . navigationEndpoint . watchEndpoint . playlistId ,
title : data.title.runs [ 0 ] . text ,
videoCount : parseInt ( videosCount ) || 0 ,
lastUpdate : lastUpdate ,
views : parseInt ( views ) || 0 ,
videos : videos ,
url : ` https://www.youtube.com/playlist?list= ${ data . title . runs [ 0 ] . navigationEndpoint . watchEndpoint . playlistId } ` ,
link : ` https://www.youtube.com ${ data . title . runs [ 0 ] . navigationEndpoint . commandMetadata . webCommandMetadata . url } ` ,
author : author
? {
name : author.videoOwnerRenderer.title.runs [ 0 ] . text ,
id : author.videoOwnerRenderer.title.runs [ 0 ] . navigationEndpoint . browseEndpoint . browseId ,
2021-09-17 14:36:32 +05:30
url : ` https://www.youtube.com ${
author . videoOwnerRenderer . navigationEndpoint . commandMetadata . webCommandMetadata . url ||
author . videoOwnerRenderer . navigationEndpoint . browseEndpoint . canonicalBaseUrl
} ` ,
icon : author.videoOwnerRenderer.thumbnail.thumbnails.length
? author . videoOwnerRenderer . thumbnail . thumbnails [
author . videoOwnerRenderer . thumbnail . thumbnails . length - 1
] . url
: null
2021-08-13 13:16:34 +05:30
}
: { } ,
2021-09-17 14:36:32 +05:30
thumbnail : data.thumbnailRenderer.playlistVideoThumbnailRenderer?.thumbnail.thumbnails.length
? data . thumbnailRenderer . playlistVideoThumbnailRenderer . thumbnail . thumbnails [
data . thumbnailRenderer . playlistVideoThumbnailRenderer . thumbnail . thumbnails . length - 1
2021-09-27 22:20:50 +05:30
]
2021-09-17 14:36:32 +05:30
: null
2021-08-13 13:16:34 +05:30
} ) ;
return res ;
}
2021-09-29 20:23:16 +05:30
/ * *
* Function to parse Playlist from YouTube search
* @param data html data of that request
* @param limit No . of videos to parse
* @returns Array of YouTubeVideo .
* /
2021-09-27 22:20:50 +05:30
export function getPlaylistVideos ( data : any , limit = Infinity ) : YouTubeVideo [ ] {
2021-08-13 13:16:34 +05:30
const videos = [ ] ;
for ( let i = 0 ; i < data . length ; i ++ ) {
if ( limit === videos . length ) break ;
const info = data [ i ] . playlistVideoRenderer ;
if ( ! info || ! info . shortBylineText ) continue ;
videos . push (
2021-09-27 22:20:50 +05:30
new YouTubeVideo ( {
2021-08-13 13:16:34 +05:30
id : info.videoId ,
index : parseInt ( info . index ? . simpleText ) || 0 ,
2021-11-10 22:04:46 +01:00
duration : parseInt ( info . lengthSeconds ) || 0 ,
2021-09-17 14:36:32 +05:30
duration_raw : info.lengthText?.simpleText ? ? '0:00' ,
2021-08-13 13:16:34 +05:30
thumbnail : {
id : info.videoId ,
url : info.thumbnail.thumbnails [ info . thumbnail . thumbnails . length - 1 ] . url ,
height : info.thumbnail.thumbnails [ info . thumbnail . thumbnails . length - 1 ] . height ,
width : info.thumbnail.thumbnails [ info . thumbnail . thumbnails . length - 1 ] . width
} ,
title : info.title.runs [ 0 ] . text ,
channel : {
id : info.shortBylineText.runs [ 0 ] . navigationEndpoint . browseEndpoint . browseId || undefined ,
name : info.shortBylineText.runs [ 0 ] . text || undefined ,
2021-09-17 14:36:32 +05:30
url : ` https://www.youtube.com ${
info . shortBylineText . runs [ 0 ] . navigationEndpoint . browseEndpoint . canonicalBaseUrl ||
info . shortBylineText . runs [ 0 ] . navigationEndpoint . commandMetadata . webCommandMetadata . url
} ` ,
2021-08-13 13:16:34 +05:30
icon : undefined
}
} )
) ;
}
2021-09-17 14:36:32 +05:30
return videos ;
2021-08-13 13:16:34 +05:30
}
2021-09-29 20:23:16 +05:30
/ * *
* Function to get Continuation Token
* @param data html data of playlist url
* @returns token
* /
2021-09-17 14:36:32 +05:30
export function getContinuationToken ( data : any ) : string {
2021-11-04 19:05:02 +01:00
return data . find ( ( x : any ) = > Object . keys ( x ) [ 0 ] === 'continuationItemRenderer' ) ? . continuationItemRenderer
. continuationEndpoint ? . continuationCommand ? . token ;
2021-09-07 22:05:01 +09:00
}