Spaces:
Running
Running
| import {Router} from 'express'; | |
| import Bottleneck from 'bottleneck'; | |
| import { | |
| clear, | |
| doRender, | |
| explodeUrl, | |
| generateOutputBundle, | |
| getNpmScript, | |
| listOutputFiles, | |
| } from './renderer.js'; | |
| import path, {dirname} from 'path'; | |
| import {fileURLToPath} from 'url'; | |
| import pkg from 'common-utils'; | |
| const {Utils, PerformanceRecorder, FileUploader, Vault} = pkg; | |
| import bodyParser from 'body-parser'; | |
| import { existsSync } from 'fs'; | |
| const RenderRouter = Router(); | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = dirname(__filename); | |
| const limiter = new Bottleneck({ | |
| maxConcurrent: 1, | |
| }); | |
| RenderRouter.use(bodyParser.json()); | |
| RenderRouter.use(bodyParser.urlencoded()); | |
| RenderRouter.post('/api/render-sync', async (req, res) => { | |
| let fileUrl = req.body.fileUrl; | |
| let targetUrl = req.body.targetUrl; | |
| let skipClear = req.body.skip_clear; | |
| let skipRender = req.body.skip_render; | |
| let zip = req.body.zip ?? true; | |
| if (!fileUrl) { | |
| return res.status(400).send({ | |
| message: | |
| 'Missing `fileUrl` in body. Required params `fileUrl`, `targetUrl`. Optionally pass `zip` to skip zipping and directly upload the files to targetUrl/{fileName}, `skip_clear` if you dont want to clear the folders after render, `skip_render` is you want to skip rendering, useful with `skip_clear` ', | |
| }); | |
| } | |
| if (!targetUrl) { | |
| return res.status(400).send({ | |
| message: | |
| 'Missing `targetUrl` in body. The result will be uploaded to `targetUrl`', | |
| }); | |
| } | |
| // make sure only i request is being processed at a time | |
| // set headers appoprately to hint that timeout must be large | |
| let jobId = req.body.jobId || Utils.generateUID(fileUrl); | |
| res.setHeader('X-Job-Id', jobId); | |
| res.setTimeout(0); | |
| res.setHeader('Connection', 'keep-alive'); | |
| let logs = []; | |
| try { | |
| const run = async () => { | |
| let pref = new PerformanceRecorder(); | |
| const dir = path.join(__dirname, 'public') | |
| const zipFile = path.join(dir, `exported-${jobId}.zip`) | |
| if (!existsSync(zipFile)) { | |
| if (!skipClear) { | |
| clear(); | |
| } | |
| await explodeUrl(fileUrl, jobId, dir, zipFile); | |
| } | |
| else { | |
| console.log(`Job ${jobId} assets already exploded to public. Not downloading again.`) | |
| } | |
| let originalManuscript = Utils.readFileToObject( | |
| path.join(__dirname, `public`, `original_manuscript.json`) | |
| ); | |
| if (!skipRender) { | |
| await doRender( | |
| jobId, | |
| originalManuscript.meta.renderComposition, | |
| (jobId, log) => { | |
| logs.push(log); | |
| }, | |
| getNpmScript(req.query.media_type), | |
| req.body.ssrOptions | |
| ); | |
| } | |
| const uploader = new FileUploader('oracle', { | |
| url: targetUrl + jobId + '/', | |
| }); | |
| uploader.creds.url = uploader.creds.url; | |
| if (zip) { | |
| let outFile = await generateOutputBundle(jobId); | |
| let uploadResult = await uploader.upload(outFile); | |
| if (!skipClear) { | |
| clear(); | |
| } | |
| return { | |
| response_time: pref.elapsed(), | |
| urls: [uploadResult.url], | |
| url: uploadResult.url, | |
| original_manuscript: originalManuscript, | |
| }; | |
| } else { | |
| let outFiles = await listOutputFiles(jobId); | |
| let urls = await Promise.all( | |
| outFiles.map((file) => { | |
| return uploader.upload(file); | |
| }) | |
| ).then((results) => { | |
| return results.map((result) => result.url); | |
| }); | |
| let url = | |
| urls.find((u) => u.includes('.mp4')) || | |
| urls.find((u) => u.includes('.jpg')) || | |
| urls.find((u) => u.includes('.jpeg')) || | |
| urls.find((u) => u.includes('.png')); | |
| return { | |
| response_time: pref.elapsed(), | |
| urls: urls, | |
| url, | |
| original_manuscript: originalManuscript, | |
| }; | |
| } | |
| } | |
| const result = await limiter.schedule(run); | |
| res.status(200).json({jobId, success: true, ...result}); | |
| } catch (err) { | |
| console.error(`Render job ${jobId} failed:`, err.message); | |
| res.status(500).json({ | |
| jobId, | |
| success: false, | |
| message: err.message + ' Details : ' + logs.join('\n'), | |
| }); | |
| } | |
| }); | |
| export default RenderRouter; | |