5 changed files with 100 additions and 17 deletions
@ -0,0 +1,35 @@ |
|||
import { spawn } from 'child_process'; |
|||
import xml2js from 'xml2js'; |
|||
|
|||
const parser = new xml2js.Parser(); |
|||
const builder = new xml2js.Builder({ headless: true }); |
|||
|
|||
const invokeDot = (input, format) => new Promise((resolve, reject) => |
|||
{ |
|||
const dot = spawn('dot', [`-T${format}`]); |
|||
let stdout = ''; |
|||
|
|||
dot.stdout.on('data', data => stdout += data); |
|||
|
|||
dot.on('close', code => { |
|||
if (code !== 0) |
|||
{ |
|||
reject(new Error(`dot returned error code ${code}`)); |
|||
} |
|||
else |
|||
{ |
|||
resolve(stdout); |
|||
} |
|||
}); |
|||
|
|||
dot.on('error', reject); |
|||
dot.stdin.write(input); |
|||
dot.stdin.end(); |
|||
}); |
|||
|
|||
export const toSVG = async input => |
|||
{ |
|||
const svg = await invokeDot(input, 'svg'); |
|||
const parsed = await parser.parseStringPromise(svg); |
|||
return builder.buildObject(parsed); |
|||
}; |
@ -1,22 +1,50 @@ |
|||
import express from 'express'; |
|||
import { exploreVideos, graphToDOT } from '../lib/explore.mjs'; |
|||
import { retryable } from '../lib/retry.mjs'; |
|||
import { exploreVideos, toGraphviz } from '../lib/explore.mjs'; |
|||
import { toSVG } from '../lib/graphviz.mjs'; |
|||
|
|||
const router = express.Router(); |
|||
export default router; |
|||
|
|||
const retryExploreVideos = retryable(exploreVideos); |
|||
const statusPending = Symbol('PENDING'); |
|||
const statusError = Symbol('ERROR'); |
|||
const cache = Object.create(null); |
|||
|
|||
router.get('/:videoId', async (req, res) => { |
|||
try |
|||
const {videoId} = req.params; |
|||
|
|||
if (videoId in cache) |
|||
{ |
|||
const graph = await retryExploreVideos(req.params.videoId); |
|||
const dot = graphToDOT(...graph); |
|||
res.send(dot); |
|||
if (cache[videoId] === statusPending) |
|||
{ |
|||
res.header('Refresh', '5'); |
|||
res.send('Exploration in progress… Please wait.'); |
|||
} |
|||
else if (cache[videoId] === statusError) |
|||
{ |
|||
res.status(500).send('Error'); |
|||
} |
|||
else |
|||
{ |
|||
res.send(cache[videoId]); |
|||
} |
|||
} |
|||
catch (err) |
|||
else |
|||
{ |
|||
console.error(err); |
|||
res.status(500).send('Error'); |
|||
cache[videoId] = statusPending; |
|||
res.header('Refresh', '1'); |
|||
res.send('Exploration in progress… Please wait.'); |
|||
|
|||
try |
|||
{ |
|||
const graph = await exploreVideos(req.params.videoId); |
|||
const graphviz = toGraphviz(...graph); |
|||
const svg = await toSVG(graphviz); |
|||
cache[videoId] = svg; |
|||
} |
|||
catch (err) |
|||
{ |
|||
console.error(err); |
|||
cache[videoId] = statusError; |
|||
} |
|||
} |
|||
}); |
|||
|
Loading…
Reference in new issue