1cb93a386Sopenharmony_ci/** 2cb93a386Sopenharmony_ci * Command line application to run Lottie-Web perf on a Lottie file in the 3cb93a386Sopenharmony_ci * browser and then exporting that result. 4cb93a386Sopenharmony_ci * 5cb93a386Sopenharmony_ci */ 6cb93a386Sopenharmony_ciconst puppeteer = require('puppeteer'); 7cb93a386Sopenharmony_ciconst express = require('express'); 8cb93a386Sopenharmony_ciconst fs = require('fs'); 9cb93a386Sopenharmony_ciconst commandLineArgs = require('command-line-args'); 10cb93a386Sopenharmony_ciconst commandLineUsage= require('command-line-usage'); 11cb93a386Sopenharmony_ciconst fetch = require('node-fetch'); 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ciconst opts = [ 14cb93a386Sopenharmony_ci { 15cb93a386Sopenharmony_ci name: 'input', 16cb93a386Sopenharmony_ci typeLabel: '{underline file}', 17cb93a386Sopenharmony_ci description: 'The Lottie JSON file to process.' 18cb93a386Sopenharmony_ci }, 19cb93a386Sopenharmony_ci { 20cb93a386Sopenharmony_ci name: 'output', 21cb93a386Sopenharmony_ci typeLabel: '{underline file}', 22cb93a386Sopenharmony_ci description: 'The perf file to write. Defaults to perf.json', 23cb93a386Sopenharmony_ci }, 24cb93a386Sopenharmony_ci { 25cb93a386Sopenharmony_ci name: 'use_gpu', 26cb93a386Sopenharmony_ci description: 'Whether we should run in non-headless mode with GPU.', 27cb93a386Sopenharmony_ci type: Boolean, 28cb93a386Sopenharmony_ci }, 29cb93a386Sopenharmony_ci { 30cb93a386Sopenharmony_ci name: 'port', 31cb93a386Sopenharmony_ci description: 'The port number to use, defaults to 8081.', 32cb93a386Sopenharmony_ci type: Number, 33cb93a386Sopenharmony_ci }, 34cb93a386Sopenharmony_ci { 35cb93a386Sopenharmony_ci name: 'lottie_player', 36cb93a386Sopenharmony_ci description: 'The path to lottie.min.js, defaults to a local npm install location.', 37cb93a386Sopenharmony_ci type: String, 38cb93a386Sopenharmony_ci }, 39cb93a386Sopenharmony_ci { 40cb93a386Sopenharmony_ci name: 'backend', 41cb93a386Sopenharmony_ci description: 'Which lottie-web backend to use. Options: canvas or svg.', 42cb93a386Sopenharmony_ci type: String, 43cb93a386Sopenharmony_ci }, 44cb93a386Sopenharmony_ci { 45cb93a386Sopenharmony_ci name: 'help', 46cb93a386Sopenharmony_ci alias: 'h', 47cb93a386Sopenharmony_ci type: Boolean, 48cb93a386Sopenharmony_ci description: 'Print this usage guide.' 49cb93a386Sopenharmony_ci }, 50cb93a386Sopenharmony_ci]; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ciconst usage = [ 53cb93a386Sopenharmony_ci { 54cb93a386Sopenharmony_ci header: 'Lottie-Web Perf', 55cb93a386Sopenharmony_ci content: 'Command line application to run Lottie-Web perf.', 56cb93a386Sopenharmony_ci }, 57cb93a386Sopenharmony_ci { 58cb93a386Sopenharmony_ci header: 'Options', 59cb93a386Sopenharmony_ci optionList: opts, 60cb93a386Sopenharmony_ci }, 61cb93a386Sopenharmony_ci]; 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci// Parse and validate flags. 64cb93a386Sopenharmony_ciconst options = commandLineArgs(opts); 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ciif (options.backend != 'canvas' && options.backend != 'svg') { 67cb93a386Sopenharmony_ci console.error('You must supply a lottie-web backend (canvas, svg).'); 68cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 69cb93a386Sopenharmony_ci process.exit(1); 70cb93a386Sopenharmony_ci} 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ciif (!options.output) { 73cb93a386Sopenharmony_ci options.output = 'perf.json'; 74cb93a386Sopenharmony_ci} 75cb93a386Sopenharmony_ciif (!options.port) { 76cb93a386Sopenharmony_ci options.port = 8081; 77cb93a386Sopenharmony_ci} 78cb93a386Sopenharmony_ciif (!options.lottie_player) { 79cb93a386Sopenharmony_ci options.lottie_player = 'node_modules/lottie-web/build/player/lottie.min.js'; 80cb93a386Sopenharmony_ci} 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ciif (options.help) { 83cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 84cb93a386Sopenharmony_ci process.exit(0); 85cb93a386Sopenharmony_ci} 86cb93a386Sopenharmony_ci 87cb93a386Sopenharmony_ciif (!options.input) { 88cb93a386Sopenharmony_ci console.error('You must supply a Lottie JSON filename.'); 89cb93a386Sopenharmony_ci console.log(commandLineUsage(usage)); 90cb93a386Sopenharmony_ci process.exit(1); 91cb93a386Sopenharmony_ci} 92cb93a386Sopenharmony_ci 93cb93a386Sopenharmony_ci// Start up a web server to serve the three files we need. 94cb93a386Sopenharmony_cilet lottieJS = fs.readFileSync(options.lottie_player, 'utf8'); 95cb93a386Sopenharmony_cilet lottieJSON = fs.readFileSync(options.input, 'utf8'); 96cb93a386Sopenharmony_cilet driverHTML; 97cb93a386Sopenharmony_ciif (options.backend == 'svg') { 98cb93a386Sopenharmony_ci console.log('Using lottie-web-perf.html'); 99cb93a386Sopenharmony_ci driverHTML = fs.readFileSync('lottie-web-perf.html', 'utf8'); 100cb93a386Sopenharmony_ci} else { 101cb93a386Sopenharmony_ci console.log('Using lottie-web-canvas-perf.html'); 102cb93a386Sopenharmony_ci driverHTML = fs.readFileSync('lottie-web-canvas-perf.html', 'utf8'); 103cb93a386Sopenharmony_ci} 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci// Find number of frames from the lottie JSON. 106cb93a386Sopenharmony_cilet lottieJSONContent = JSON.parse(lottieJSON); 107cb93a386Sopenharmony_ciconst totalFrames = lottieJSONContent.op - lottieJSONContent.ip; 108cb93a386Sopenharmony_ciconsole.log('Total frames: ' + totalFrames); 109cb93a386Sopenharmony_ci 110cb93a386Sopenharmony_ciconst app = express(); 111cb93a386Sopenharmony_ciapp.get('/', (req, res) => res.send(driverHTML)); 112cb93a386Sopenharmony_ciapp.get('/res/lottie.js', (req, res) => res.send(lottieJS)); 113cb93a386Sopenharmony_ciapp.get('/res/lottie.json', (req, res) => res.send(lottieJSON)); 114cb93a386Sopenharmony_ciapp.listen(options.port, () => console.log('- Local web server started.')) 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci// Utility function. 117cb93a386Sopenharmony_ciasync function wait(ms) { 118cb93a386Sopenharmony_ci await new Promise(resolve => setTimeout(() => resolve(), ms)); 119cb93a386Sopenharmony_ci return ms; 120cb93a386Sopenharmony_ci} 121cb93a386Sopenharmony_ci 122cb93a386Sopenharmony_ciconst targetURL = "http://localhost:" + options.port + "/#" + totalFrames; 123cb93a386Sopenharmony_ciconst viewPort = {width: 1000, height: 1000}; 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci// Drive chrome to load the web page from the server we have running. 126cb93a386Sopenharmony_ciasync function driveBrowser() { 127cb93a386Sopenharmony_ci console.log('- Launching chrome for ' + options.input); 128cb93a386Sopenharmony_ci let browser; 129cb93a386Sopenharmony_ci let page; 130cb93a386Sopenharmony_ci const headless = !options.use_gpu; 131cb93a386Sopenharmony_ci let browser_args = [ 132cb93a386Sopenharmony_ci '--no-sandbox', 133cb93a386Sopenharmony_ci '--disable-setuid-sandbox', 134cb93a386Sopenharmony_ci '--window-size=' + viewPort.width + ',' + viewPort.height, 135cb93a386Sopenharmony_ci ]; 136cb93a386Sopenharmony_ci if (options.use_gpu) { 137cb93a386Sopenharmony_ci browser_args.push('--ignore-gpu-blacklist'); 138cb93a386Sopenharmony_ci browser_args.push('--ignore-gpu-blocklist'); 139cb93a386Sopenharmony_ci browser_args.push('--enable-gpu-rasterization'); 140cb93a386Sopenharmony_ci } 141cb93a386Sopenharmony_ci console.log("Running with headless: " + headless + " args: " + browser_args); 142cb93a386Sopenharmony_ci try { 143cb93a386Sopenharmony_ci browser = await puppeteer.launch({headless: headless, args: browser_args}); 144cb93a386Sopenharmony_ci page = await browser.newPage(); 145cb93a386Sopenharmony_ci await page.setViewport(viewPort); 146cb93a386Sopenharmony_ci } catch (e) { 147cb93a386Sopenharmony_ci console.log('Could not open the browser.', e); 148cb93a386Sopenharmony_ci process.exit(1); 149cb93a386Sopenharmony_ci } 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci console.log("Loading " + targetURL); 152cb93a386Sopenharmony_ci try { 153cb93a386Sopenharmony_ci // Start trace. 154cb93a386Sopenharmony_ci await page.tracing.start({ 155cb93a386Sopenharmony_ci path: options.output, 156cb93a386Sopenharmony_ci screenshots: false, 157cb93a386Sopenharmony_ci categories: ["blink", "cc", "gpu"] 158cb93a386Sopenharmony_ci }); 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci await page.goto(targetURL, { 161cb93a386Sopenharmony_ci timeout: 60000, 162cb93a386Sopenharmony_ci waitUntil: 'networkidle0' 163cb93a386Sopenharmony_ci }); 164cb93a386Sopenharmony_ci 165cb93a386Sopenharmony_ci console.log('- Waiting 60s for run to be done.'); 166cb93a386Sopenharmony_ci await page.waitForFunction('window._lottieWebDone === true', { 167cb93a386Sopenharmony_ci timeout: 60000, 168cb93a386Sopenharmony_ci }); 169cb93a386Sopenharmony_ci 170cb93a386Sopenharmony_ci // Stop trace. 171cb93a386Sopenharmony_ci await page.tracing.stop(); 172cb93a386Sopenharmony_ci } catch(e) { 173cb93a386Sopenharmony_ci console.log('Timed out while loading or drawing. Either the JSON file was ' + 174cb93a386Sopenharmony_ci 'too big or hit a bug in the player.', e); 175cb93a386Sopenharmony_ci await browser.close(); 176cb93a386Sopenharmony_ci process.exit(1); 177cb93a386Sopenharmony_ci } 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ci await browser.close(); 180cb93a386Sopenharmony_ci // Need to call exit() because the web server is still running. 181cb93a386Sopenharmony_ci process.exit(0); 182cb93a386Sopenharmony_ci} 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_cidriveBrowser(); 185