youtube-maze/explore/api.js

118 lines
3.3 KiB
JavaScript

'use strict';
const util = require('util');
const request = require('request');
const cheerio = require('cheerio');
const YOUTUBE_BASE = 'https://www.youtube.com/%s';
const WATCH_BASE = util.format(YOUTUBE_BASE, 'watch?v=%s');
const VIDEO_ID_REGEX = /watch\?v=([^&]*)/i;
// const END_SCREEN_BASE = util.format(YOUTUBE_BASE, 'get_endscreen?v=%s');
// const CARD_BASE = util.format(YOUTUBE_BASE, 'annotations_invideo?video_id=%s');
const playerRegex = /ytplayer\.config = (\{.*?\});/;
/**
* Fetch the `ytplayer.config` object for a YouTube video.
*
* @async
* @param videoId Identifier of the video to fetch.
* @return The player configuration object.
*/
const getPlayerConfig = videoId =>
{
const url = util.format(WATCH_BASE, videoId);
return new Promise((resolve, reject) =>
{
request(url, (err, res, body) =>
{
if (err)
{
reject(err);
return;
}
// Look for the definition of ytplayer.config and unserialize it
// and the player_response subobject
try
{
const playerConfig = JSON.parse(body.match(playerRegex)[1]);
playerConfig.args.player_response
= JSON.parse(playerConfig.args.player_response);
resolve(playerConfig);
}
catch (err)
{
reject(err);
}
});
});
};
exports.getPlayerConfig = getPlayerConfig;
/**
* Get metadata about a YouTube video.
*
* @param playerConfig The `ytplayer.config` object corresponding to the source
* YouTube video, as obtained from `getPlayerConfig`.
* @return Object containing the video metadata.
*/
const getVideoMeta = playerConfig => ({
videoId: playerConfig.args.player_response.videoDetails.videoId,
title: playerConfig.args.player_response.videoDetails.title,
});
exports.getVideoMeta = getVideoMeta;
/**
* 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.
*/
const getEndScreenVideos = playerConfig =>
{
const response = playerConfig.args.player_response;
if (!('endscreen' in response))
{
return [];
}
return response.endscreen.endscreenRenderer.elements
.map(elt => elt.endscreenElementRenderer)
.filter(rdr => 'watchEndpoint' in rdr.endpoint)
.map(rdr => rdr.endpoint.watchEndpoint.videoId);
};
exports.getEndScreenVideos = getEndScreenVideos;
/**
* 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.
*/
const getCardVideos = playerConfig =>
{
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);
};
exports.getCardVideos = getCardVideos;