2020-07-15 17:31:09 +00:00
|
|
|
import util from 'util';
|
|
|
|
import request from 'request';
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
|
|
const YOUTUBE_BASE = 'https://www.youtube.com/%s';
|
2020-07-15 17:14:31 +00:00
|
|
|
const WATCH_BASE = util.format(YOUTUBE_BASE, 'watch?v=%s');
|
2020-07-15 17:31:09 +00:00
|
|
|
const PLAYER_REGEX = /ytplayer\.config = (\{.*?\});/;
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
|
|
/**
|
2020-07-15 17:14:31 +00:00
|
|
|
* Fetch the `ytplayer.config` object for a YouTube video.
|
2017-04-02 00:54:46 +00:00
|
|
|
*
|
2020-07-15 17:14:31 +00:00
|
|
|
* @async
|
|
|
|
* @param videoId Identifier of the video to fetch.
|
|
|
|
* @return The player configuration object.
|
2017-04-02 00:54:46 +00:00
|
|
|
*/
|
2020-07-15 17:31:09 +00:00
|
|
|
export const getPlayerConfig = videoId =>
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
const url = util.format(WATCH_BASE, videoId);
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
{
|
2020-11-27 23:17:04 +00:00
|
|
|
request(url, (err, _, body) =>
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
// Look for the definition of ytplayer.config and unserialize it
|
|
|
|
// and the player_response subobject
|
|
|
|
try
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
2020-07-15 17:31:09 +00:00
|
|
|
const playerConfig = JSON.parse(body.match(PLAYER_REGEX)[1]);
|
2020-07-15 17:14:31 +00:00
|
|
|
playerConfig.args.player_response
|
|
|
|
= JSON.parse(playerConfig.args.player_response);
|
|
|
|
resolve(playerConfig);
|
2017-04-02 00:54:46 +00:00
|
|
|
}
|
2020-07-15 17:14:31 +00:00
|
|
|
catch (err)
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
reject(err);
|
2017-04-02 00:54:46 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2020-07-15 17:14:31 +00:00
|
|
|
* Get metadata about a YouTube video.
|
2017-04-02 00:54:46 +00:00
|
|
|
*
|
2020-07-15 17:14:31 +00:00
|
|
|
* @param playerConfig The `ytplayer.config` object corresponding to the source
|
|
|
|
* YouTube video, as obtained from `getPlayerConfig`.
|
|
|
|
* @return Object containing the video metadata.
|
2017-04-02 00:54:46 +00:00
|
|
|
*/
|
2020-07-15 17:31:09 +00:00
|
|
|
export const getVideoMeta = playerConfig => ({
|
2020-07-15 17:14:31 +00:00
|
|
|
videoId: playerConfig.args.player_response.videoDetails.videoId,
|
|
|
|
title: playerConfig.args.player_response.videoDetails.title,
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find videos linked from the endscreen of a YouTube video.
|
|
|
|
*
|
|
|
|
* @param playerConfig The `ytplayer.config` object corresponding to the source
|
|
|
|
* YouTube video, as obtained from `getPlayerConfig`.
|
|
|
|
* @return List of identifiers of linked videos.
|
|
|
|
*/
|
2020-07-15 17:31:09 +00:00
|
|
|
export const getEndScreenVideos = playerConfig =>
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
const response = playerConfig.args.player_response;
|
2017-04-02 00:54:46 +00:00
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
if (!('endscreen' in response))
|
2017-04-02 00:54:46 +00:00
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
return [];
|
|
|
|
}
|
2017-04-02 00:54:46 +00:00
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
return response.endscreen.endscreenRenderer.elements
|
|
|
|
.map(elt => elt.endscreenElementRenderer)
|
|
|
|
.filter(rdr => 'watchEndpoint' in rdr.endpoint)
|
|
|
|
.map(rdr => rdr.endpoint.watchEndpoint.videoId);
|
|
|
|
};
|
2017-04-02 00:54:46 +00:00
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
/**
|
|
|
|
* Find videos linked from as cards from a YouTube video.
|
|
|
|
*
|
|
|
|
* @param playerConfig The `ytplayer.config` object corresponding to the source
|
|
|
|
* YouTube video, as obtained from `getPlayerConfig`.
|
|
|
|
* @return List of identifiers of linked videos.
|
|
|
|
*/
|
2020-07-15 17:31:09 +00:00
|
|
|
export const getCardVideos = playerConfig =>
|
2020-07-15 17:14:31 +00:00
|
|
|
{
|
|
|
|
const response = playerConfig.args.player_response;
|
|
|
|
|
|
|
|
if (!('cards' in response))
|
|
|
|
{
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.cards.cardCollectionRenderer.cards
|
|
|
|
.map(card => card.cardRenderer.content)
|
|
|
|
.filter(content => 'videoInfoCardContentRenderer' in content)
|
|
|
|
.map(content => content.videoInfoCardContentRenderer)
|
|
|
|
.map(rdr => rdr.action.watchEndpoint.videoId);
|
2017-04-02 00:54:46 +00:00
|
|
|
};
|