From 8e92c0c14ecf6604bb6a4da08dbf96598afd0ad2 Mon Sep 17 00:00:00 2001 From: Clovis Gauzy Date: Thu, 26 Nov 2020 17:58:42 +0100 Subject: [PATCH 1/3] Setups --- .gitignore | 3 ++ README.md | 113 ++++++++++++++++++++++++++++++++++++++++----------- package.json | 16 ++++++++ 3 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 .gitignore create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36aec13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/node_modules +*.lock +*.md.backup diff --git a/README.md b/README.md index 6bf90b0..e176b18 100644 --- a/README.md +++ b/README.md @@ -22,24 +22,15 @@ npm i --save rawtherapee yarn add rawtherapee ``` -## Simple Usage +## Usage -```js -const rawtherapee = require("rawtherapee"); +_Promise_ = rawtherapee(_url_ | _array_ `targets` [, _object_ `options`]) -rawtherapee("/somewhere/something.NEF").then((files) => { - // `files` is an array containing all processed file paths. - console.log(files.length, "files processed."); -}); -``` + * `targets`: files or directories + * `options`: simple options _Object_ ### Options -| option | format | values | default | -|!===|===|===|===| -| replace | _boolean_ | | | - - #### `replace` *default: `false`* @@ -51,12 +42,23 @@ Replace the existing output file. *default: `false`* +Process all raw and non raw formats, igroring GUI parameters. + #### `presets` *default: `['default']`* -'sidecar', 'sidecar-strict', '' +An array of pp3 presets files. + +Possible opions: + + * `'default'`: use the default preset selected in GUI. + * `'sidecar'`: use the sidecar file of each image if available + * `'sidecar-strict'`: like `'sidecar`, but return an error if there is no sidecar. + * ``: any pp3 file in your system + +`rawtherapee-cli` always use the neutral presets as base, then apply presets in the order you passed it. #### `ignoreBadPreset` @@ -66,16 +68,20 @@ Replace the existing output file. If set to `false`, any non-existing preset file passed to the `presets` parameter will throw an error. If set to `true`, those files will be ignored (not passed to `rawtherapi-cli`) and print message in `sterr` if the `DEBUG` environment variable is set to any value. + #### `output` -*default: `'/tmp/img'`* +*default: `'.'`* +File or directory where the processed files will be stored (directory must exists). #### `format` *default: `'jpg'`* +Possibles options: + `'jpg'`, `'png'` or `'tiff'` @@ -83,13 +89,16 @@ If set to `true`, those files will be ignored (not passed to `rawtherapi-cli`) a *default: `8`* -`16`, `16f` or `32` -Only for TIFF and PNG output formats. +Color depth of the output file. Only for TIFF and PNG formats. + +Possibles options: + +`8` or `16` #### `compression` -*default: `92`* +*default: `90`* Only for JPG output formats (PNG compression is hardcoded at 6 in `rawtherapi-cli`) @@ -98,9 +107,13 @@ Only for JPG output formats (PNG compression is hardcoded at 6 in `rawtherapi-cl _string_ or _int_ -*default:`'4:2:2'`* +*default:`2`* -`1`, `'4:2:0'`, `2`, `'4:4:4'`, `3` +Possibles options: + + * `'4:2:2'` or `1` + * `'4:2:0'` or `2` + * `'4:4:4'` or `3` #### `zip` @@ -109,7 +122,7 @@ _boolean_ *default: `false`* -Only for TIFF output format. +Use TIFF zip compression. #### `onChange` @@ -117,8 +130,62 @@ Only for TIFF output format. _function_ *default: `() => {}`* -Callback _function_ fired every time the status is updated (not based on `EventListener`) : +Callback _function_ fired every time the status is updated (not using `EventListeners`) : -Return a simple object who always contains `status` entry, and maybe `file` or `code`. +Return a simple object who always contains `status`, and maybe `file` or `code`. + +Status can be : + + * `start`: start running `rawtherapee-cli`. + * `skipped`: skip ignored `file` in a full directory process. + * `processing`: start processing `file`. + * `complete`: processing `file` completed. + * `idle`: `rawtherapee-cli` stop with `code` code. +### Examples + +```js +const rawtherapee = require('rawtherapee') + +rawtherapee('/somewhere/something.NEF') + .then((files) => { + console.log(files.length, 'files processed.') + }) +``` + + +```js +const fs = require('fs') +const rawtherapee = require('rawtherapee') + +const output = '/tmp/img' + +if (!fs.existsSync(output)) fs.mkdirSync(output) + +const onChange = (state) => { + switch (state.status) { + case 'complete': + console.log(state.file, 'process done.') + break + case 'skipped': + console.log(state.file, 'have been ignored.') + break + default: + console.log('Event', state.status, 'fired.', state) + } +} + +rawtherapee([ + '/somewhere/something.NEF', + '/somewhereelse/somethingelse.NEF' +], { + onChange, + output, + format: 'tiff', + depth: 16, + zip: true, + preset: ['sidecar'] +}) + .then((files) => console.log(files.length, 'files processed.')) +``` diff --git a/package.json b/package.json new file mode 100644 index 0000000..820baad --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "rawtherapee", + "version": "0.1.0", + "description": "rawtherapee-cli wrapper to process RAW images in NodeJs", + "repository": "https://github.com/clovfr/node-rawtherapee", + "author": "Clovis Gauzy", + "license": "BSD-3", + "private": true, + "main": "rawtherapee.js", + "scripts": { + "test": "standard" + }, + "devDependencies": { + "standard": "*" + } +} From f974a2793a4619032473d814520bb532b008d93c Mon Sep 17 00:00:00 2001 From: Clovis Gauzy Date: Thu, 26 Nov 2020 17:59:05 +0100 Subject: [PATCH 2/3] Rawtherapee wrapper --- rawtherapee.js | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 rawtherapee.js diff --git a/rawtherapee.js b/rawtherapee.js new file mode 100644 index 0000000..aa76d68 --- /dev/null +++ b/rawtherapee.js @@ -0,0 +1,175 @@ +const fs = require('fs') +const { spawn } = require('child_process') + +const defaultOptions = { + replace: false, + allFormats: false, + presets: ['default'], + ignoreBadPreset: false, + output: '.', + format: 'jpg', + depth: 8, + compression: 90, + subSampling: 2, + zip: false, + onChange: () => {} +} + +const o2cli = (options) => { + const params = ['-q'] + const { + replace, + allFormats, + presets, + ignoreBadPreset, + output, + format, + compression, + subSampling, + zip, + depth + } = options + if (replace) params.push('-Y') + if (allFormats) params.push('-a') + if (presets || presets.length) { + presets.forEach((preset) => { + switch (preset) { + case 'default': + return params.push('-d') + case 'sidecar': + return params.push('-s') + case 'sidecar-strict': + return params.push('-S') + default: + if (!fs.existsSync(preset)) { + const err = new Error(`Preset '${preset}' not found.`) + if (!ignoreBadPreset) throw err + else if (process.env.DEBUG) console.error(err) + } else { + params.push('-p') + params.push(preset) + } + } + }) + } + if (format) { + switch (format.toLowerCase()) { + case 'jpg': + case 'jpeg': + params.push(`-j${compression || ''}`) + if (subSampling) { + switch (subSampling) { + case '4:2:0': + case 1: + params.push('-js1') + break + case '4:2:2': + case 2: + params.push('-js2') + break + case '4:4:4': + case 3: + params.push('-js3') + break + default: + console.warn( + `Warning: subSampling '${subSampling}' is unknown. Option is ignored, 'rawtherapee-cli' will use his own default (4:2:2).` + ) + } + } + break + case 'png': + params.push('-n') + break + case 'tif': + case 'tiff': + params.push(`-t${zip && 'z'}`) + break + } + } + if (depth) params.push(`-b${depth}`) + if (output) { + params.push('-o') + params.push(output) + } + return params +} + +const rawtherapee = (file, options = {}) => { + options = { ...defaultOptions, ...options } + const { onChange } = options + return new Promise((resolve, reject) => { + let ckf = file + let filename + if (!Array.isArray(file)) ckf = [file] + if ((filename = ckf.find((fl) => !fs.existsSync(fl)))) { + return reject(new Error(`File ${filename} not found`)) + } + const params = o2cli(options) + params.push('-c') + params.push(file) + const rwtp = spawn('rawtherapee-cli', params) + + const files = [] + + rwtp.stdout.on('data', (buf) => { + const data = buf.toString('utf8') + let ipath = /Processing: (.+)$/im.exec(data) + if (ipath) { + if (files.length) { + onChange({ + status: 'complete', + file: files[files.length - 1] + }) + } + files.push(ipath[1]) + onChange({ + status: 'processing', + file: ipath[1] + }) + } + if (!ipath) { + ipath = /^RawTherapee, version ([0-9.]+), command line./im.exec(data) + if (ipath) { + onChange({ + status: 'start', + version: ipath[1] + }) + } + } + if (!ipath) { + ipath = /^"(.+)".*image skipped/im.exec(data) + if (ipath) { + onChange({ + status: 'skipped', + file: ipath[1] + }) + } + } + if (!ipath && process.env.DEBUG) console.warn('not handled', data) + }) + + rwtp.stderr.on('data', (data) => { + console.error(`stderr: ${data}`) + reject(new Error(data)) + }) + rwtp.on('error', (data) => { + console.error(`error: ${data}`) + reject(new Error(data)) + }) + + rwtp.on('close', (code) => { + if (code > 0) return reject(new Error(`rawtherapee-cli process exited with code ${code}`)) + if (files.length) { + onChange({ + status: 'complete', + file: files[files.length - 1] + }) + } + onChange({ status: 'idle', code }) + resolve(files) + }) + }) +} + +module.exports = rawtherapee From 20a9aa36b0a6b8921a558577fdaf77a82f124782 Mon Sep 17 00:00:00 2001 From: Clovis Gauzy Date: Sun, 29 Nov 2020 14:00:28 +0100 Subject: [PATCH 3/3] v1.0.0 --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 820baad..f0ba5bb 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,10 @@ { "name": "rawtherapee", - "version": "0.1.0", + "version": "1.0.0", "description": "rawtherapee-cli wrapper to process RAW images in NodeJs", "repository": "https://github.com/clovfr/node-rawtherapee", "author": "Clovis Gauzy", "license": "BSD-3", - "private": true, "main": "rawtherapee.js", "scripts": { "test": "standard"