2017-04-02 00:54:46 +00:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const Graph = require('graph-data-structure');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const util = require('util');
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
const api = require('./api');
|
2017-04-02 00:54:46 +00:00
|
|
|
|
const {graphToDOT} = require('./graph');
|
|
|
|
|
|
|
|
|
|
const YOUTUBE_WATCH = 'https://youtu.be/%s';
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Fetch the output path from command line
|
2019-07-07 18:49:11 +00:00
|
|
|
|
if (process.argv.length !== 3)
|
|
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
|
console.error(`Usage: node explore [output]`);
|
2019-07-07 18:49:11 +00:00
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dest = process.argv[2];
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Graph of visited videos. Each node is a video which is linked to all the
|
|
|
|
|
// videos to which there is a link, either through a card or an endscreen item
|
2017-04-02 00:54:46 +00:00
|
|
|
|
const videosGraph = Graph();
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Store metadata about each visited video
|
2017-04-02 00:54:46 +00:00
|
|
|
|
const videosMeta = Object.create(null);
|
|
|
|
|
|
|
|
|
|
/**
|
2020-07-15 17:14:31 +00:00
|
|
|
|
* Recursively explore a video and the video linked from it to fill
|
|
|
|
|
* the video graph.
|
2017-04-02 00:54:46 +00:00
|
|
|
|
*
|
2020-07-15 17:14:31 +00:00
|
|
|
|
* @param videoId Source video identifier.
|
2017-04-02 00:54:46 +00:00
|
|
|
|
*/
|
2020-07-15 17:14:31 +00:00
|
|
|
|
const exploreVideo = async videoId =>
|
2017-04-02 00:54:46 +00:00
|
|
|
|
{
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Make sure we don’t explore the same video twice
|
|
|
|
|
if (videoId in videosMeta)
|
2017-04-02 00:54:46 +00:00
|
|
|
|
{
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
const playerConfig = await api.getPlayerConfig(videoId);
|
|
|
|
|
videosMeta[videoId] = api.getVideoMeta(playerConfig);
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
const linkedVideos = [
|
|
|
|
|
...api.getEndScreenVideos(playerConfig),
|
|
|
|
|
...api.getCardVideos(playerConfig),
|
|
|
|
|
];
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Add links between this video and the linked ones
|
|
|
|
|
linkedVideos.forEach(id => videosGraph.addEdge(videoId, id));
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Recurse on linked videos
|
|
|
|
|
return Promise.all(linkedVideos.map(id => exploreVideo(id)));
|
2017-04-02 00:54:46 +00:00
|
|
|
|
};
|
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
// Metadata of the source video
|
2017-04-02 00:54:46 +00:00
|
|
|
|
const rootVideoId = 'EZGra6O8ClQ';
|
2020-07-15 17:14:31 +00:00
|
|
|
|
console.log('Starting to explore!');
|
2017-04-02 00:54:46 +00:00
|
|
|
|
|
|
|
|
|
exploreVideo(rootVideoId).then(() =>
|
|
|
|
|
{
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
|
dest,
|
|
|
|
|
graphToDOT(
|
|
|
|
|
videosGraph,
|
|
|
|
|
id => videosMeta[id].title,
|
|
|
|
|
id => util.format(YOUTUBE_WATCH, id)
|
|
|
|
|
)
|
|
|
|
|
);
|
2019-07-07 18:49:11 +00:00
|
|
|
|
|
2020-07-15 17:14:31 +00:00
|
|
|
|
console.log('Finished. Result in ' + dest);
|
2017-04-02 00:54:46 +00:00
|
|
|
|
}).catch(console.error);
|