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);