diff --git a/.eslintrc b/.eslintrc index b117cc0..4e6eab3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,11 @@ { "extends": "airbnb-base", "rules": { - "no-underscore-dangle": 0 + "no-underscore-dangle": 0, + "global-require": 0 + }, + "env": { + "browser": true, + "node": true } } diff --git a/README.md b/README.md index ba608a6..9886c08 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,13 @@ $ npm install @ffmpeg/ffmpeg ```javascript const fs = require('fs'); -const ffmpeg = require('@ffmpeg/ffmpeg'); +const { createWorker } = require('@ffmpeg/ffmpeg'); + +const worker = createWorker(); (async () => { - await ffmpeg.load(); - const data = ffmpeg.transcode('./test.avi', 'mp4'); + await worker.load(); + const { data } = await worker.transcode('./test.avi', 'mp4'); fs.wrieFileSync('./test.mp4', data); })(); ``` diff --git a/examples/browser/transcode.html b/examples/browser/transcode.html new file mode 100644 index 0000000..95209ac --- /dev/null +++ b/examples/browser/transcode.html @@ -0,0 +1,36 @@ + + + + + + + + + + diff --git a/examples/node/transcode.js b/examples/node/transcode.js new file mode 100755 index 0000000..c465155 --- /dev/null +++ b/examples/node/transcode.js @@ -0,0 +1,12 @@ +const { createWorker } = require('../../src'); + +const { argv } = process; +const [,, inputPath, outputPath] = argv; + +const worker = createWorker(); + +(async () => { + await worker.load(); + const { data } = await worker.transcode(inputPath, outputPath.split('.').pop()); + console.log(data.length); +})(); diff --git a/examples/transcode.js b/examples/transcode.js deleted file mode 100755 index 724a182..0000000 --- a/examples/transcode.js +++ /dev/null @@ -1,10 +0,0 @@ -const fs = require('fs'); -const ffmpeg = require('../src'); -const { argv } = process; -const [,, inputPath, outputPath] = argv; - -(async () => { - await ffmpeg.load(); - const data = ffmpeg.transcode(inputPath, outputPath.split('.').pop()); - fs.writeFileSync(outputPath, data); -})(); diff --git a/package-lock.json b/package-lock.json index 89a4cc6..117c728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3056,8 +3056,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -3098,8 +3097,7 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", @@ -3109,8 +3107,7 @@ "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -3227,8 +3224,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -3240,7 +3236,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3366,8 +3361,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -3500,7 +3494,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3520,7 +3513,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4199,6 +4191,11 @@ "has-symbols": "^1.0.0" } }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -4848,6 +4845,11 @@ } } }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -5530,6 +5532,11 @@ "regenerate": "^1.4.0" } }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + }, "regenerator-transform": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", @@ -5680,8 +5687,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "restore-cursor": { "version": "2.0.0", diff --git a/package.json b/package.json index b1ef872..88a0027 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,11 @@ }, "homepage": "https://github.com/jeromewu/ffmpeg.js#readme", "dependencies": { - "@ffmpeg/core": "^0.1.0" + "@ffmpeg/core": "^0.1.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.0", + "regenerator-runtime": "^0.13.3", + "resolve-url": "^0.2.1" }, "devDependencies": { "@babel/core": "^7.6.4", diff --git a/scripts/webpack.config.dev.js b/scripts/webpack.config.dev.js index f40b444..25299a7 100644 --- a/scripts/webpack.config.dev.js +++ b/scripts/webpack.config.dev.js @@ -32,8 +32,8 @@ module.exports = [ library: 'FFmpeg', libraryTarget: 'umd', }), - //genConfig({ - // entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'), - // filename: 'worker.dev.js', - //}), + genConfig({ + entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'), + filename: 'worker.dev.js', + }), ]; diff --git a/scripts/webpack.config.prod.js b/scripts/webpack.config.prod.js index 2948874..74ef83d 100644 --- a/scripts/webpack.config.prod.js +++ b/scripts/webpack.config.prod.js @@ -23,8 +23,8 @@ module.exports = [ library: 'FFmpeg', libraryTarget: 'umd', }), - //genConfig({ - // entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'), - // filename: 'worker.min.js', - //}), + genConfig({ + entry: path.resolve(__dirname, '..', 'src', 'worker-script', 'browser', 'index.js'), + filename: 'worker.min.js', + }), ]; diff --git a/src/constants/defaultOptions.js b/src/constants/defaultOptions.js new file mode 100644 index 0000000..0a82163 --- /dev/null +++ b/src/constants/defaultOptions.js @@ -0,0 +1,3 @@ +module.exports = { + logger: () => {}, +}; diff --git a/src/createJob.js b/src/createJob.js new file mode 100644 index 0000000..f90364d --- /dev/null +++ b/src/createJob.js @@ -0,0 +1,21 @@ +const getId = require('./utils/getId'); + +let jobCounter = 0; + +module.exports = ({ + id: _id, + action, + payload = {}, +}) => { + let id = _id; + if (typeof id === 'undefined') { + id = getId('Job', jobCounter); + jobCounter += 1; + } + + return { + id, + action, + payload, + }; +}; diff --git a/src/createWorker.js b/src/createWorker.js new file mode 100644 index 0000000..01b1ea2 --- /dev/null +++ b/src/createWorker.js @@ -0,0 +1,109 @@ +const createJob = require('./createJob'); +const { log } = require('./utils/log'); +const getId = require('./utils/getId'); +const { + defaultOptions, + spawnWorker, + terminateWorker, + onMessage, + loadMedia, + send, +} = require('./worker/node'); + +let workerCounter = 0; + +module.exports = (_options = {}) => { + const id = getId('Worker', workerCounter); + const { + logger, + ...options + } = { + ...defaultOptions, + ..._options, + }; + const resolves = {}; + const rejects = {}; + let worker = spawnWorker(options); + + workerCounter += 1; + + const setResolve = (action, res) => { + resolves[action] = res; + }; + + const setReject = (action, rej) => { + rejects[action] = rej; + }; + + const startJob = ({ id: jobId, action, payload }) => ( + new Promise((resolve, reject) => { + log(`[${id}]: Start ${jobId}, action=${action}`); + setResolve(action, resolve); + setReject(action, reject); + send(worker, { + workerId: id, + jobId, + action, + payload, + }); + }) + ); + + const load = (jobId) => ( + startJob(createJob({ + id: jobId, action: 'load', payload: { options }, + })) + ); + + const transcode = async (media, outputExt, opts, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'transcode', + payload: { + media: await loadMedia(media), + outputExt, + options: opts, + }, + })) + ); + + const terminate = async (jobId) => { + if (worker !== null) { + await startJob(createJob({ + id: jobId, + action: 'terminate', + })); + terminateWorker(worker); + worker = null; + } + return Promise.resolve(); + }; + + onMessage(worker, ({ + workerId, jobId, status, action, data, + }) => { + if (status === 'resolve') { + log(`[${workerId}]: Complete ${jobId}`); + let d = data; + if (action === 'transcode') { + d = Array.from({ ...data, length: Object.keys(data).length }); + } + resolves[action]({ jobId, data: d }); + } else if (status === 'reject') { + rejects[action](data); + throw Error(data); + } else if (status === 'progress') { + logger(data); + } + }); + + return { + id, + worker, + setResolve, + setReject, + load, + transcode, + terminate, + }; +}; diff --git a/src/index.js b/src/index.js index d1c275c..de010f7 100755 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,6 @@ -const load = require('./load'); -const transcode = require('./transcode'); +require('regenerator-runtime/runtime'); +const createWorker = require('./createWorker'); module.exports = { - load, - transcode, + createWorker, }; diff --git a/src/load.js b/src/load.js deleted file mode 100755 index e1fe32c..0000000 --- a/src/load.js +++ /dev/null @@ -1,12 +0,0 @@ -const FFmpegCore = require('@ffmpeg/core'); -const { setModule } = require('./util/module'); - -module.exports = () => ( - new Promise((resolve) => { - FFmpegCore() - .then((Module) => { - setModule(Module); - resolve(); - }); - }) -); diff --git a/src/transcode.js b/src/transcode.js deleted file mode 100755 index 5197b8e..0000000 --- a/src/transcode.js +++ /dev/null @@ -1,17 +0,0 @@ -const fs = require('fs'); -const { getModule } = require('./util/module'); -const getFFmpeg = require('./util/getFFmpeg'); -const strList2ptr = require('./util/strList2ptr'); -const defaultArgs = require('./constants/defaultArgs'); - -module.exports = (inputPath, outputExt, options = '') => { - const Module = getModule(); - const data = new Uint8Array(fs.readFileSync(inputPath)); - const iPath = `file.${inputPath.split('.').pop()}`; - const oPath = `file.${outputExt}`; - const ffmpeg = getFFmpeg(); - const args = [...defaultArgs, ...`${options} -i ${iPath} ${oPath}`.trim().split(' ')]; - Module.FS.writeFile(iPath, data); - ffmpeg(args.length, strList2ptr(args)); - return Buffer.from(Module.FS.readFile(oPath)); -}; diff --git a/src/util/getFFmpeg.js b/src/util/getFFmpeg.js deleted file mode 100755 index 18c6692..0000000 --- a/src/util/getFFmpeg.js +++ /dev/null @@ -1,6 +0,0 @@ -const { getModule } = require('./module'); - -module.exports = () => { - const Module = getModule(); - return Module.cwrap('ffmpeg', 'number', ['number', 'number']); -}; diff --git a/src/util/module.js b/src/util/module.js deleted file mode 100755 index 3e7adf3..0000000 --- a/src/util/module.js +++ /dev/null @@ -1,7 +0,0 @@ -let Module = null; - -exports.setModule = (m) => { - Module = m; -}; - -exports.getModule = () => Module; diff --git a/src/util/str2ptr.js b/src/util/str2ptr.js deleted file mode 100755 index c3c8e88..0000000 --- a/src/util/str2ptr.js +++ /dev/null @@ -1,11 +0,0 @@ -const { getModule } = require('./module'); - -module.exports = (s) => { - const Module = getModule(); - const ptr = Module._malloc((s.length + 1) * Uint8Array.BYTES_PER_ELEMENT); - for (let i = 0; i < s.length; i += 1) { - Module.setValue(ptr + i, s.charCodeAt(i), 'i8'); - } - Module.setValue(ptr + s.length, 0, 'i8'); - return ptr; -}; diff --git a/src/util/strList2ptr.js b/src/util/strList2ptr.js deleted file mode 100755 index d5bd680..0000000 --- a/src/util/strList2ptr.js +++ /dev/null @@ -1,14 +0,0 @@ -const { getModule } = require('./module'); -const str2ptr = require('./str2ptr'); - -module.exports = (strList) => { - const Module = getModule(); - const listPtr = Module._malloc(strList.length * Uint32Array.BYTES_PER_ELEMENT); - - strList.forEach((s, idx) => { - const strPtr = str2ptr(s); - Module.setValue(listPtr + (4 * idx), strPtr, 'i32'); - }); - - return listPtr; -}; diff --git a/src/utils/getId.js b/src/utils/getId.js new file mode 100644 index 0000000..8c2b119 --- /dev/null +++ b/src/utils/getId.js @@ -0,0 +1,3 @@ +module.exports = (prefix, cnt) => ( + `${prefix}-${cnt}-${Math.random().toString(16).slice(3, 8)}` +); diff --git a/src/utils/log.js b/src/utils/log.js new file mode 100644 index 0000000..4faac26 --- /dev/null +++ b/src/utils/log.js @@ -0,0 +1,9 @@ +let logging = false; + +exports.logging = logging; + +exports.setLogging = (_logging) => { + logging = _logging; +}; + +exports.log = (...args) => (logging ? console.log.apply(this, args) : null); diff --git a/src/worker-script/browser/.eslintrc b/src/worker-script/browser/.eslintrc new file mode 100644 index 0000000..e88da2e --- /dev/null +++ b/src/worker-script/browser/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "worker": true + } +} diff --git a/src/worker-script/browser/getCore.js b/src/worker-script/browser/getCore.js new file mode 100644 index 0000000..521d008 --- /dev/null +++ b/src/worker-script/browser/getCore.js @@ -0,0 +1,6 @@ +module.exports = (corePath) => { + if (typeof global.Module === 'undefined') { + global.importScripts(corePath); + } + return global.Module; +}; diff --git a/src/worker-script/browser/index.js b/src/worker-script/browser/index.js new file mode 100644 index 0000000..0ce1497 --- /dev/null +++ b/src/worker-script/browser/index.js @@ -0,0 +1,10 @@ +const worker = require('../'); +const getCore = require('./getCore'); + +global.addEventListener('message', ({ data }) => { + worker.dispatchHandlers(data, (obj) => postMessage(obj)); +}); + +worker.setAdapter({ + getCore, +}); diff --git a/src/constants/defaultArgs.js b/src/worker-script/constants/defaultArgs.js similarity index 100% rename from src/constants/defaultArgs.js rename to src/worker-script/constants/defaultArgs.js diff --git a/src/worker-script/index.js b/src/worker-script/index.js new file mode 100644 index 0000000..ac86ca8 --- /dev/null +++ b/src/worker-script/index.js @@ -0,0 +1,83 @@ +require('regenerator-runtime/runtime'); +const defaultArgs = require('./constants/defaultArgs'); + +let Module = null; +let adapter = null; +let ffmpeg = null; + +const str2ptr = (s) => { + const ptr = Module._malloc((s.length + 1) * Uint8Array.BYTES_PER_ELEMENT); + for (let i = 0; i < s.length; i += 1) { + Module.setValue(ptr + i, s.charCodeAt(i), 'i8'); + } + Module.setValue(ptr + s.length, 0, 'i8'); + return ptr; +}; + +const strList2ptr = (strList) => { + const listPtr = Module._malloc(strList.length * Uint32Array.BYTES_PER_ELEMENT); + + strList.forEach((s, idx) => { + const strPtr = str2ptr(s); + Module.setValue(listPtr + (4 * idx), strPtr, 'i32'); + }); + + return listPtr; +}; + +const load = ({ payload: { options: { corePath } } }, res) => { + if (Module == null) { + const Core = adapter.getCore(corePath); + Core() + .then((_Module) => { + Module = _Module; + ffmpeg = Module.cwrap('ffmpeg', 'number', ['number', 'number']); + res.resolve(true); + }); + } else { + res.resolve(true); + } +}; + +const transcode = ({ + payload: { + media, + outputExt, + options = '', + }, +}, res) => { + const data = Uint8Array.from({ ...media, length: Object.keys(media).length }); + const iPath = 'media'; + const oPath = `media.${outputExt}`; + const args = [...defaultArgs, ...`${options} -i ${iPath} ${oPath}`.trim().split(' ')]; + Module.FS.writeFile(iPath, data); + ffmpeg(args.length, strList2ptr(args)); + res.resolve(Module.FS.readFile(oPath)); +}; + +exports.dispatchHandlers = (packet, send) => { + const res = (status, data) => { + send({ + ...packet, + status, + data, + }); + }; + res.resolve = res.bind(this, 'resolve'); + res.reject = res.bind(this, 'reject'); + res.progress = res.bind(this, 'progress'); + + try { + ({ + load, + transcode, + })[packet.action](packet, res); + } catch (err) { + /** Prepare exception to travel through postMessage */ + res.reject(err.toString()); + } +}; + +exports.setAdapter = (_adapter) => { + adapter = _adapter; +}; diff --git a/src/worker-script/node/getCore.js b/src/worker-script/node/getCore.js new file mode 100644 index 0000000..af4bb5f --- /dev/null +++ b/src/worker-script/node/getCore.js @@ -0,0 +1,8 @@ +let FFmpegCore = null; + +module.exports = () => { + if (FFmpegCore === null) { + FFmpegCore = require('@ffmpeg/core'); + } + return FFmpegCore; +}; diff --git a/src/worker-script/node/index.js b/src/worker-script/node/index.js new file mode 100644 index 0000000..f59ff58 --- /dev/null +++ b/src/worker-script/node/index.js @@ -0,0 +1,10 @@ +const worker = require('../'); +const getCore = require('./getCore'); + +process.on('message', (packet) => { + worker.dispatchHandlers(packet, (obj) => process.send(obj)); +}); + +worker.setAdapter({ + getCore, +}); diff --git a/src/worker/browser/defaultOptions.js b/src/worker/browser/defaultOptions.js new file mode 100644 index 0000000..55146c0 --- /dev/null +++ b/src/worker/browser/defaultOptions.js @@ -0,0 +1,14 @@ +const resolveURL = require('resolve-url'); +const { version, dependencies } = require('../../../package.json'); +const defaultOptions = require('../../constants/defaultOptions'); + +/* + * Default options for browser worker + */ +module.exports = { + ...defaultOptions, + workerPath: (typeof process !== 'undefined' && process.env.FFMPEG_ENV === 'development') + ? resolveURL(`/dist/worker.dev.js?nocache=${Math.random().toString(36).slice(3)}`) + : `https://unpkg.com/@ffmpeg/ffmpeg@v${version}/dist/worker.min.js`, + corePath: `https://unpkg.com/@ffmpeg/core@v${dependencies['@ffmpeg/core'].substring(1)}/ffmpeg-core.js`, +}; diff --git a/src/worker/browser/index.js b/src/worker/browser/index.js new file mode 100644 index 0000000..ab562a0 --- /dev/null +++ b/src/worker/browser/index.js @@ -0,0 +1,24 @@ +/** + * + * Tesseract Worker adapter for browser + * + * @fileoverview Tesseract Worker adapter for browser + * @author Kevin Kwok + * @author Guillermo Webster + * @author Jerome Wu + */ +const defaultOptions = require('./defaultOptions'); +const spawnWorker = require('./spawnWorker'); +const terminateWorker = require('./terminateWorker'); +const onMessage = require('./onMessage'); +const send = require('./send'); +const loadMedia = require('./loadMedia'); + +module.exports = { + defaultOptions, + spawnWorker, + terminateWorker, + onMessage, + send, + loadMedia, +}; diff --git a/src/worker/browser/loadMedia.js b/src/worker/browser/loadMedia.js new file mode 100644 index 0000000..818bbcc --- /dev/null +++ b/src/worker/browser/loadMedia.js @@ -0,0 +1,46 @@ +const resolveURL = require('resolve-url'); + +/** + * readFromBlobOrFile + * + * @name readFromBlobOrFile + * @function + * @access private + */ +const readFromBlobOrFile = (blob) => ( + new Promise((resolve, reject) => { + const fileReader = new FileReader(); + fileReader.onload = () => { + resolve(fileReader.result); + }; + fileReader.onerror = ({ target: { error: { code } } }) => { + reject(Error(`File could not be read! Code=${code}`)); + }; + fileReader.readAsArrayBuffer(blob); + }) +); + +const loadMedia = async (image) => { + let data = image; + if (typeof image === 'undefined') { + return 'undefined'; + } + + if (typeof image === 'string') { + // Base64 Media + if (/data:image\/([a-zA-Z]*);base64,([^"]*)/.test(image)) { + data = atob(image.split(',')[1]) + .split('') + .map((c) => c.charCodeAt(0)); + } else { + const res = await fetch(resolveURL(image)); + data = res.arrayBuffer(); + } + } else if (image instanceof File || image instanceof Blob) { + data = await readFromBlobOrFile(image); + } + + return new Uint8Array(data); +}; + +module.exports = loadMedia; diff --git a/src/worker/browser/onMessage.js b/src/worker/browser/onMessage.js new file mode 100644 index 0000000..ec74783 --- /dev/null +++ b/src/worker/browser/onMessage.js @@ -0,0 +1,5 @@ +module.exports = (worker, handler) => { + worker.onmessage = ({ data }) => { // eslint-disable-line + handler(data); + }; +}; diff --git a/src/worker/browser/send.js b/src/worker/browser/send.js new file mode 100644 index 0000000..88f8aaf --- /dev/null +++ b/src/worker/browser/send.js @@ -0,0 +1,10 @@ +/** + * send + * + * @name send + * @function send packet to worker and create a job + * @access public + */ +module.exports = async (worker, packet) => { + worker.postMessage(packet); +}; diff --git a/src/worker/browser/spawnWorker.js b/src/worker/browser/spawnWorker.js new file mode 100644 index 0000000..5604aa4 --- /dev/null +++ b/src/worker/browser/spawnWorker.js @@ -0,0 +1,10 @@ +/** + * spawnWorker + * + * @name spawnWorker + * @function create a new Worker in browser + * @access public + */ +module.exports = ({ workerPath }) => ( + new Worker(workerPath) +); diff --git a/src/worker/browser/terminateWorker.js b/src/worker/browser/terminateWorker.js new file mode 100644 index 0000000..753a3fd --- /dev/null +++ b/src/worker/browser/terminateWorker.js @@ -0,0 +1,10 @@ +/** + * terminateWorker + * + * @name terminateWorker + * @function terminate worker + * @access public + */ +module.exports = (worker) => { + worker.terminate(); +}; diff --git a/src/worker/node/defaultOptions.js b/src/worker/node/defaultOptions.js new file mode 100644 index 0000000..053c1e3 --- /dev/null +++ b/src/worker/node/defaultOptions.js @@ -0,0 +1,10 @@ +const path = require('path'); +const defaultOptions = require('../../constants/defaultOptions'); + +/* + * Default options for node worker + */ +module.exports = { + ...defaultOptions, + workerPath: path.join(__dirname, '..', '..', 'worker-script', 'node', 'index.js'), +}; diff --git a/src/worker/node/index.js b/src/worker/node/index.js new file mode 100644 index 0000000..1911335 --- /dev/null +++ b/src/worker/node/index.js @@ -0,0 +1,24 @@ +/** + * + * Tesseract Worker impl. for node (using child_process) + * + * @fileoverview Tesseract Worker impl. for node + * @author Kevin Kwok + * @author Guillermo Webster + * @author Jerome Wu + */ +const defaultOptions = require('./defaultOptions'); +const spawnWorker = require('./spawnWorker'); +const terminateWorker = require('./terminateWorker'); +const onMessage = require('./onMessage'); +const send = require('./send'); +const loadMedia = require('./loadMedia'); + +module.exports = { + defaultOptions, + spawnWorker, + terminateWorker, + onMessage, + send, + loadMedia, +}; diff --git a/src/worker/node/loadMedia.js b/src/worker/node/loadMedia.js new file mode 100644 index 0000000..f30fc09 --- /dev/null +++ b/src/worker/node/loadMedia.js @@ -0,0 +1,28 @@ +const util = require('util'); +const fs = require('fs'); +const fetch = require('node-fetch'); +const isURL = require('is-url'); + +const readFile = util.promisify(fs.readFile); + +module.exports = async (media) => { + let data = media; + if (typeof media === 'undefined') { + return media; + } + + if (typeof media === 'string') { + if (isURL(media) || media.startsWith('chrome-extension://') || media.startsWith('file://')) { + const res = await fetch(media); + data = res.arrayBuffer(); + } else if (/data:media\/([a-zA-Z]*);base64,([^"]*)/.test(media)) { + data = Buffer.from(media.split(',')[1], 'base64'); + } else { + data = await readFile(media); + } + } else if (Buffer.isBuffer(media)) { + data = media; + } + + return new Uint8Array(data); +}; diff --git a/src/worker/node/onMessage.js b/src/worker/node/onMessage.js new file mode 100644 index 0000000..7100e71 --- /dev/null +++ b/src/worker/node/onMessage.js @@ -0,0 +1,3 @@ +module.exports = (worker, handler) => { + worker.on('message', handler); +}; diff --git a/src/worker/node/send.js b/src/worker/node/send.js new file mode 100644 index 0000000..783c6e1 --- /dev/null +++ b/src/worker/node/send.js @@ -0,0 +1,10 @@ +/** + * send + * + * @name send + * @function send packet to worker and create a job + * @access public + */ +module.exports = (worker, packet) => { + worker.send(packet); +}; diff --git a/src/worker/node/spawnWorker.js b/src/worker/node/spawnWorker.js new file mode 100644 index 0000000..e3c5d89 --- /dev/null +++ b/src/worker/node/spawnWorker.js @@ -0,0 +1,12 @@ +const { fork } = require('child_process'); + +/** + * spawnWorker + * + * @name spawnWorker + * @function fork a new process in node + * @access public + */ +module.exports = ({ workerPath }) => ( + fork(workerPath) +); diff --git a/src/worker/node/terminateWorker.js b/src/worker/node/terminateWorker.js new file mode 100644 index 0000000..0e8b67e --- /dev/null +++ b/src/worker/node/terminateWorker.js @@ -0,0 +1,10 @@ +/** + * terminateWorker + * + * @name terminateWorker + * @function kill worker + * @access public + */ +module.exports = (worker) => { + worker.kill(); +};