TelescopeJS/Telescope

View on GitHub
src/server/headless.js

Summary

Maintainability
A
1 hr
Test Coverage
/**
 * @file headless
 * @author Cuttle Cong
 * @date 2018/2/27
 * @description
 */
const puppeteer = require('puppeteer')
const md5 = require('md5')
const u = require('url')
const p = require('pify')
const fs = require('fs')
const nps = require('path')
const mkdirp = require('mkdirp')

const RUNTIME_PATH = nps.join(__dirname, '../../runtime')
mkdirp.sync(RUNTIME_PATH)

async function getOrSet(key, getter) {
  const files = await p(fs.readdir)(RUNTIME_PATH)
  if (files.includes(key)) {
    const stat = await p(fs.stat)(nps.join(RUNTIME_PATH, key))
    if (Date.now() - stat.mtime.getTime() >= 1000 * 60 * 60 * 24) {
      return await getter()
    }
    return nps.join(RUNTIME_PATH, key)
  }
  return await getter()
}

async function savePdf(url, key, options = {}) {
  const browser = await puppeteer.launch({ timeout: 100000, args: ['--no-sandbox', '--disable-setuid-sandbox'] })
  const page = await browser.newPage()
  await page.goto(url, { waitUntil: 'networkidle2', timeout: 100000 })
  // await page.waitFor('img')
  const buffer = await page.pdf({
    format: 'A4',
    printBackground: true,
    margin: {
      top: '10mm',
      bottom: '9.5mm',
      left: '10mm',
      right: '9.5mm'
    },
    ...options,
    path: nps.join(RUNTIME_PATH, key)
  })
  await browser.close()
  return nps.join(RUNTIME_PATH, key)
}

async function saveImg(url, key, options = {}) {
  const browser = await puppeteer.launch({ timeout: 100000, args: ['--no-sandbox', '--disable-setuid-sandbox'] })
  const page = await browser.newPage()
  await page.goto(url, { waitUntil: 'networkidle2', timeout: 100000 })
  // await page.waitFor('img')
  let {
    rect
  } = await page.evaluate(() => {
    const rect = document.querySelector('.telescope > pre:first-child > code')
                || document.querySelector('.telescope')
    const tele = document.querySelector('.telescope')
    function calRect(dom) {
      return {
        x: dom.offsetLeft,
        y: dom.offsetTop,
        width: dom.offsetWidth,
        height: dom.offsetHeight,
      }
    }
    return {
      rect: calRect(rect),
      telescope: tele ? calRect(tele) : null
    }
      // body: document.body.getBoundingClientRect().toJSON(),
  })

  options = {
    fullPage: false,
    ...options,
    path: nps.join(RUNTIME_PATH, key),
    clip: rect
  }
  // console.log(telescope, rect)
  if (options.fullPage) {
    delete options.clip
    await page.setViewport({ width: parseInt(rect.width), height: parseInt(rect.height) })
  }
  else {
    await page.setViewport({ width: parseInt(rect.width), height: parseInt(rect.height) })
  }
  const buffer = await page.screenshot(options)
  await browser.close()
  return nps.join(RUNTIME_PATH, key)
}

export async function pdf(url, options = {}) {
  const { force, ...opt } = options
  const k = md5(u.parse(url).query + JSON.stringify(opt)) + '.pdf'
  if (force) {
    await savePdf(url, k, opt)
  }
  return await getOrSet(k, async () => await savePdf(url, k, opt))
}

export async function img(url, options = {}) {
  const { force, ...opt } = options
  if (opt.quality) {
    opt.quality = +opt.quality
  }
  const k = md5(u.parse(url).query + JSON.stringify(opt)) + '.png'
  if (force) {
    return await saveImg(url, k, opt)
  }
  return await getOrSet(k, async () => await saveImg(url, k, opt))
}