import util from 'util'; import request from 'request'; const YOUTUBE_BASE = 'https://www.youtube.com/%s'; const WATCH_BASE = util.format(YOUTUBE_BASE, 'watch?v=%s'); const PLAYER_REGEX = /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. */ export 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(PLAYER_REGEX)[1]); playerConfig.args.player_response = JSON.parse(playerConfig.args.player_response); resolve(playerConfig); } catch (err) { reject(err); } }); }); }; /** * 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. */ export const getVideoMeta = playerConfig => ({ 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. */ export 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); }; /** * 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. */ export 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); };