73 lines
1.8 KiB
JavaScript
73 lines
1.8 KiB
JavaScript
import Graph from 'graph-data-structure';
|
||
import fs from 'fs';
|
||
import path from 'path';
|
||
import util from 'util';
|
||
|
||
import * as api from './api.mjs';
|
||
import {graphToDOT} from './graph.mjs';
|
||
|
||
const YOUTUBE_WATCH = 'https://youtu.be/%s';
|
||
|
||
// Fetch the output path from command line
|
||
if (process.argv.length !== 3)
|
||
{
|
||
console.error(`Usage: node explore [output]`);
|
||
process.exit(1);
|
||
}
|
||
|
||
const dest = process.argv[2];
|
||
|
||
// 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
|
||
const videosGraph = Graph();
|
||
|
||
// Store metadata about each visited video
|
||
const videosMeta = Object.create(null);
|
||
|
||
/**
|
||
* Recursively explore a video and the video linked from it to fill
|
||
* the video graph.
|
||
*
|
||
* @param videoId Source video identifier.
|
||
*/
|
||
const exploreVideo = async videoId =>
|
||
{
|
||
// Make sure we don’t explore the same video twice
|
||
if (videoId in videosMeta)
|
||
{
|
||
return Promise.resolve();
|
||
}
|
||
|
||
const playerConfig = await api.getPlayerConfig(videoId);
|
||
videosMeta[videoId] = api.getVideoMeta(playerConfig);
|
||
|
||
const linkedVideos = [
|
||
...api.getEndScreenVideos(playerConfig),
|
||
...api.getCardVideos(playerConfig),
|
||
];
|
||
|
||
// Add links between this video and the linked ones
|
||
linkedVideos.forEach(id => videosGraph.addEdge(videoId, id));
|
||
|
||
// Recurse on linked videos
|
||
return Promise.all(linkedVideos.map(id => exploreVideo(id)));
|
||
};
|
||
|
||
// Metadata of the source video
|
||
const rootVideoId = 'EZGra6O8ClQ';
|
||
console.log('Starting to explore!');
|
||
|
||
exploreVideo(rootVideoId).then(() =>
|
||
{
|
||
fs.writeFileSync(
|
||
dest,
|
||
graphToDOT(
|
||
videosGraph,
|
||
id => videosMeta[id].title,
|
||
id => util.format(YOUTUBE_WATCH, id)
|
||
)
|
||
);
|
||
|
||
console.log('Finished. Result in ' + dest);
|
||
}).catch(console.error);
|