@rosepetal/node-red-contrib-utils 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/nodes/io/save-file.html +22 -0
- package/nodes/io/save-file.js +18 -6
- package/package.json +1 -1
package/nodes/io/save-file.html
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
imageFormat: { value: "jpg" },
|
|
15
15
|
imageQuality: { value: 90 },
|
|
16
16
|
pngOptimize: { value: false },
|
|
17
|
+
webpLossless: { value: false },
|
|
18
|
+
webpSmartSubsample: { value: false },
|
|
19
|
+
webpEffort: { value: 4 },
|
|
17
20
|
jsonIndent: { value: 2 },
|
|
18
21
|
overwriteProtection: { value: "true" },
|
|
19
22
|
outputPath: { value: "savedFile" },
|
|
@@ -63,6 +66,7 @@
|
|
|
63
66
|
const imageFields = $(".savefile-image-options");
|
|
64
67
|
const jsonFields = $(".savefile-json-options");
|
|
65
68
|
const pngOnlyFields = $(".savefile-png-only");
|
|
69
|
+
const webpOnlyFields = $(".savefile-webp-only");
|
|
66
70
|
const qualityFields = $(".savefile-quality");
|
|
67
71
|
const imageSeparators = $(".savefile-image-separator");
|
|
68
72
|
const jsonSeparators = $(".savefile-json-separator");
|
|
@@ -80,6 +84,7 @@
|
|
|
80
84
|
const fmt = imageFormatField.val();
|
|
81
85
|
const showImage = fileTypeField.val() === 'image' || fileTypeField.val() === 'auto';
|
|
82
86
|
pngOnlyFields.toggle(showImage && fmt === 'png');
|
|
87
|
+
webpOnlyFields.toggle(showImage && fmt === 'webp');
|
|
83
88
|
qualityFields.toggle(showImage && fmt !== 'png');
|
|
84
89
|
}
|
|
85
90
|
|
|
@@ -169,6 +174,21 @@
|
|
|
169
174
|
<input type="checkbox" id="node-input-pngOptimize" style="margin-right: 5px;"> Optimize PNG
|
|
170
175
|
</label>
|
|
171
176
|
</div>
|
|
177
|
+
<div class="form-row savefile-image-options savefile-webp-only">
|
|
178
|
+
<label for="node-input-webpLossless"><i class="fa fa-lock"></i> WebP Lossless</label>
|
|
179
|
+
<input type="checkbox" id="node-input-webpLossless" style="display:inline-block; width:auto; vertical-align:baseline;">
|
|
180
|
+
<label for="node-input-webpLossless" style="width:auto; margin-left:5px;">Encode WebP without loss</label>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="form-row savefile-image-options savefile-webp-only">
|
|
183
|
+
<label for="node-input-webpSmartSubsample"><i class="fa fa-magic"></i> WebP Smart Subsample</label>
|
|
184
|
+
<input type="checkbox" id="node-input-webpSmartSubsample" style="display:inline-block; width:auto; vertical-align:baseline;">
|
|
185
|
+
<label for="node-input-webpSmartSubsample" style="width:auto; margin-left:5px;">Improve chroma downsampling (lossy WebP only)</label>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="form-row savefile-image-options savefile-webp-only">
|
|
188
|
+
<label for="node-input-webpEffort"><i class="fa fa-tachometer"></i> WebP Effort</label>
|
|
189
|
+
<input type="number" id="node-input-webpEffort" min="0" max="6" placeholder="4" style="width: 70px;">
|
|
190
|
+
<span style="margin-left: 10px; color: #666;">0-6 (higher = smaller file, slower)</span>
|
|
191
|
+
</div>
|
|
172
192
|
<div class="form-row savefile-image-options savefile-separator savefile-image-separator">
|
|
173
193
|
<hr>
|
|
174
194
|
</div>
|
|
@@ -225,6 +245,8 @@
|
|
|
225
245
|
<dd>Force saving as image, JSON, text, or binary, or let the node auto-detect from data/extension.</dd>
|
|
226
246
|
<dt>Image Format / Quality / Optimize PNG <span class="property-type">enum/number/bool</span></dt>
|
|
227
247
|
<dd>Applies when saving images. Raw image objects are validated and encoded with Sharp.</dd>
|
|
248
|
+
<dt>WebP Lossless / Smart Subsample / Effort <span class="property-type">bool/bool/number</span></dt>
|
|
249
|
+
<dd>WebP-specific encoding options. <b>Lossless</b> encodes without quality loss. <b>Smart Subsample</b> improves chroma downsampling for lossy WebP. <b>Effort</b> (0-6) controls the compression effort trade-off; higher values produce smaller files but encode more slowly.</dd>
|
|
228
250
|
<dt>JSON Indent <span class="property-type">number</span></dt>
|
|
229
251
|
<dd>Spacing for pretty-printed JSON output.</dd>
|
|
230
252
|
<dt>Overwrite <span class="property-type">enum</span></dt>
|
package/nodes/io/save-file.js
CHANGED
|
@@ -136,6 +136,10 @@ module.exports = function (RED) {
|
|
|
136
136
|
// Prepare data to write
|
|
137
137
|
const quality = parseInt(config.imageQuality, 10) || 90;
|
|
138
138
|
const pngOptimize = config.pngOptimize === true || config.pngOptimize === 'true';
|
|
139
|
+
const webpLossless = config.webpLossless === true || config.webpLossless === 'true';
|
|
140
|
+
const webpSmartSubsample = config.webpSmartSubsample === true || config.webpSmartSubsample === 'true';
|
|
141
|
+
const webpEffort = parseInt(config.webpEffort, 10);
|
|
142
|
+
const webpOptions = { lossless: webpLossless, smartSubsample: webpSmartSubsample, effort: webpEffort };
|
|
139
143
|
const jsonIndent = Number.isInteger(parseInt(config.jsonIndent, 10))
|
|
140
144
|
? parseInt(config.jsonIndent, 10)
|
|
141
145
|
: 2;
|
|
@@ -144,7 +148,7 @@ module.exports = function (RED) {
|
|
|
144
148
|
let isText = false;
|
|
145
149
|
|
|
146
150
|
if (fileType === 'image') {
|
|
147
|
-
payloadToWrite = await toImageBuffer(incoming, imageFormat, quality, pngOptimize, NodeUtils, node);
|
|
151
|
+
payloadToWrite = await toImageBuffer(incoming, imageFormat, quality, pngOptimize, webpOptions, NodeUtils, node);
|
|
148
152
|
} else if (fileType === 'json') {
|
|
149
153
|
if (typeof incoming === 'string') {
|
|
150
154
|
try {
|
|
@@ -341,7 +345,7 @@ async function toBinary(value) {
|
|
|
341
345
|
return Buffer.from(JSON.stringify(value ?? ''), 'utf8');
|
|
342
346
|
}
|
|
343
347
|
|
|
344
|
-
async function toImageBuffer(imageValue, format, quality, pngOptimize, NodeUtils, node) {
|
|
348
|
+
async function toImageBuffer(imageValue, format, quality, pngOptimize, webpOptions, NodeUtils, node) {
|
|
345
349
|
// Encoded buffer path
|
|
346
350
|
if (Buffer.isBuffer(imageValue) && !(imageValue.width && imageValue.height)) {
|
|
347
351
|
let inputFormat;
|
|
@@ -357,7 +361,7 @@ async function toImageBuffer(imageValue, format, quality, pngOptimize, NodeUtils
|
|
|
357
361
|
}
|
|
358
362
|
|
|
359
363
|
const sharpInstance = sharp(imageValue);
|
|
360
|
-
return encodeSharp(sharpInstance, format, quality, pngOptimize);
|
|
364
|
+
return encodeSharp(sharpInstance, format, quality, pngOptimize, webpOptions);
|
|
361
365
|
}
|
|
362
366
|
|
|
363
367
|
// Raw image path (uses validator for clear errors)
|
|
@@ -391,10 +395,10 @@ async function toImageBuffer(imageValue, format, quality, pngOptimize, NodeUtils
|
|
|
391
395
|
sharpInstance = sharpInstance.toColourspace('b-w');
|
|
392
396
|
}
|
|
393
397
|
|
|
394
|
-
return encodeSharp(sharpInstance, format, quality, pngOptimize);
|
|
398
|
+
return encodeSharp(sharpInstance, format, quality, pngOptimize, webpOptions);
|
|
395
399
|
}
|
|
396
400
|
|
|
397
|
-
function encodeSharp(sharpInstance, format, quality, pngOptimize) {
|
|
401
|
+
function encodeSharp(sharpInstance, format, quality, pngOptimize, webpOptions) {
|
|
398
402
|
if (format === 'png') {
|
|
399
403
|
const pngOptions = pngOptimize
|
|
400
404
|
? { compressionLevel: 9, palette: true }
|
|
@@ -402,7 +406,15 @@ function encodeSharp(sharpInstance, format, quality, pngOptimize) {
|
|
|
402
406
|
return sharpInstance.png(pngOptions).toBuffer();
|
|
403
407
|
}
|
|
404
408
|
if (format === 'webp') {
|
|
405
|
-
|
|
409
|
+
const opts = { quality };
|
|
410
|
+
if (webpOptions) {
|
|
411
|
+
if (webpOptions.lossless) opts.lossless = true;
|
|
412
|
+
if (webpOptions.smartSubsample) opts.smartSubsample = true;
|
|
413
|
+
if (typeof webpOptions.effort === 'number' && webpOptions.effort >= 0 && webpOptions.effort <= 6) {
|
|
414
|
+
opts.effort = webpOptions.effort;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return sharpInstance.webp(opts).toBuffer();
|
|
406
418
|
}
|
|
407
419
|
return sharpInstance.jpeg({ quality }).toBuffer();
|
|
408
420
|
}
|
package/package.json
CHANGED