alchemy-media 0.9.0-alpha.5 → 0.9.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.9.0 (2026-01-21)
2
+
3
+ * Update to veronica v0.3.0
4
+ * Requires Node.js >= 18.17.0
5
+ * Remove direct `sharp` dependency - all image processing now goes through veronica
6
+ * Remove `cwebp` binary requirement - veronica/sharp has native WebP support
7
+ * Remove `scratch_path` setting - veronica now uses single cache directory
8
+
1
9
  ## 0.9.0-alpha.5 (2025-07-10)
2
10
 
3
11
  * Add `size` to the media file chooser
package/README.md CHANGED
@@ -34,15 +34,9 @@ var options = {
34
34
  // The location of the exiv2 binary
35
35
  exiv2: '/usr/bin/exiv2',
36
36
 
37
- // The location of cwebp
38
- cwebp: '/usr/bin/cwebp',
39
-
40
37
  // Enable webp
41
38
  webp: true,
42
39
 
43
- // Temporary map for intermediate file changes
44
- scratch: path.resolve(PATH_TEMP, 'scratch'),
45
-
46
40
  // The cache map for resized images & thumbnails
47
41
  cache: path.resolve(PATH_TEMP, 'imagecache')
48
42
  };
@@ -70,21 +64,21 @@ The supplied width percentage isn't relative to the image itself, but to the use
70
64
 
71
65
  ## System dependencies
72
66
 
73
- You will have to install certain packages on your system,
74
- you can do so like this on Debian/Ubuntu
67
+ This plugin uses `sharp` for image processing, which comes with pre-built binaries for all major platforms (no system packages needed).
75
68
 
76
- Install the requirements of the `veronica` module:
69
+ For placeholder image generation, you'll need the canvas dependencies:
77
70
 
78
- apt-get install graphicsmagick webp libgif-dev libcairo2-dev libpango1.0-dev libjpeg-dev librsvg2-dev
71
+ ```bash
72
+ # Debian/Ubuntu
73
+ sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
79
74
 
80
- This plugin requires exiv2
81
-
82
- apt-get install exiv2 libexiv2-dev
75
+ # macOS
76
+ brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman
83
77
 
84
- This plugin requires libopencv packages
85
-
86
- apt-get install libopencv-dev
78
+ # Arch Linux
79
+ sudo pacman -S cairo pango giflib libjpeg-turbo librsvg
80
+ ```
87
81
 
88
- This plugin requires magick++
82
+ This plugin uses exiv2 for reading image metadata:
89
83
 
90
- apt-get install libmagick++-dev
84
+ apt-get install exiv2 libexiv2-dev
@@ -27,19 +27,6 @@ MEDIA_PLUGIN_GROUP.addSetting('file_storage_path', {
27
27
  description : 'Where to store uploaded files',
28
28
  });
29
29
 
30
- MEDIA_PLUGIN_GROUP.addSetting('scratch_path', {
31
- type : 'string',
32
- default : libpath.resolve(PATH_TEMP, 'scratch'),
33
- description : 'Temporary map for intermediate file changes',
34
- action : (value, value_instance) => {
35
-
36
- if (value) {
37
- alchemy.createDir(value);
38
- }
39
-
40
- createVeronicaInstance();
41
- },
42
- });
43
30
 
44
31
  MEDIA_PLUGIN_GROUP.addSetting('cache_path', {
45
32
  type : 'string',
@@ -72,24 +59,12 @@ MEDIA_PLUGIN_GROUP.addSetting('max_page_width', {
72
59
 
73
60
  const BINARIES = MEDIA_PLUGIN_GROUP.createGroup('binaries');
74
61
 
75
- BINARIES.addSetting('convert', {
76
- type : 'string',
77
- default : alchemy.findPathToBinarySync('convert'),
78
- description : 'The path to the `convert` binary',
79
- });
80
-
81
62
  BINARIES.addSetting('exiv2', {
82
63
  type : 'string',
83
64
  default : alchemy.findPathToBinarySync('exiv2'),
84
65
  description : 'The path to the `exiv2` binary',
85
66
  });
86
67
 
87
- BINARIES.addSetting('cwebp', {
88
- type : 'string',
89
- default : alchemy.findPathToBinarySync('cwebp'),
90
- description : 'The path to the `cwebp` binary',
91
- action : createVeronicaInstance,
92
- });
93
68
 
94
69
  /**
95
70
  * Recreate the Veronica instance
@@ -104,8 +79,6 @@ function createVeronicaInstance() {
104
79
  const media = alchemy.settings.plugins.media;
105
80
 
106
81
  alchemy.plugins.media.veronica = new Veronica({
107
- cwebp : media.binaries.cwebp,
108
- temp : media.scratch_path,
109
82
  cache : media.cache_path,
110
83
  });
111
84
  }
@@ -1,6 +1,5 @@
1
1
  var exiv2 = alchemy.use('@11ways/exiv2'),
2
2
  child = alchemy.use('child_process'),
3
- gm = alchemy.use('gm'),
4
3
  profiles = alchemy.shared('Media.profiles'),
5
4
  fs = alchemy.use('fs'),
6
5
  libpath = require('path'),
@@ -39,19 +38,6 @@ ImageMedia.setProperty(function exiv_path() {
39
38
  return alchemy.settings.plugins.media.binaries.exiv2;
40
39
  });
41
40
 
42
- /**
43
- * Add a property getter to the cwebp binary
44
- *
45
- * @author Jelle De Loecker <jelle@elevenways.be>
46
- * @since 0.9.0
47
- * @version 0.9.0
48
- *
49
- * @type {string}
50
- */
51
- ImageMedia.setProperty(function cwebp_path() {
52
- return alchemy.settings.plugins.media.binaries.cwebp;
53
- });
54
-
55
41
  /**
56
42
  * Get the hash algorithm
57
43
  *
@@ -342,9 +328,8 @@ ImageMedia.setMethod(function serve(conduit, record, options) {
342
328
 
343
329
  if (resizeOptions) {
344
330
 
345
- const cwebp_path = this.cwebp_path;
346
-
347
- if (this.supportsWebp(conduit) && cwebp_path) {
331
+ // Sharp has native WebP support, no external binary needed
332
+ if (this.supportsWebp(conduit)) {
348
333
  resizeOptions.type = 'webp';
349
334
  }
350
335
 
@@ -450,9 +435,8 @@ ImageMedia.setMethod(function placeholder(conduit, options) {
450
435
 
451
436
  ImageMedia.setMethod(function getSize(conduit, filePath, options, callback) {
452
437
 
453
- var tempName = filePath,
454
- resizeOptions = '',
455
- tempPath;
438
+ var that = this,
439
+ tempName = filePath;
456
440
 
457
441
  if (!options.type) {
458
442
  options.type = 'webp';
@@ -464,8 +448,6 @@ ImageMedia.setMethod(function getSize(conduit, filePath, options, callback) {
464
448
 
465
449
  if (options.height) {
466
450
  tempName += '-h' + options.height;
467
-
468
- resizeOptions += '-resize ' + options.width + ' ' + options.height;
469
451
  }
470
452
 
471
453
  tempName += '-' + options.type;
@@ -475,22 +457,27 @@ ImageMedia.setMethod(function getSize(conduit, filePath, options, callback) {
475
457
  return callback(null, cache[tempName]);
476
458
  }
477
459
 
478
- tempPath = PATH_TEMP + '/' + alchemy.ObjectId() + '.webp';
479
-
480
- const cwebp_path = this.cwebp_path;
460
+ // Use veronica for resize and WebP conversion
461
+ let resizeOptions = {
462
+ type: options.type,
463
+ quality: 80
464
+ };
481
465
 
482
- if (!cwebp_path) {
483
- return callback(new Error('Unable to get size: cwebp not found'));
466
+ if (options.width) {
467
+ resizeOptions.maxWidth = options.width;
484
468
  }
485
469
 
486
- child.exec(cwebp_path + ' ' + [resizeOptions, '-q 80', filePath, '-o', tempPath].join(' '), function(err, out) {
470
+ if (options.height) {
471
+ resizeOptions.maxHeight = options.height;
472
+ }
487
473
 
474
+ this.veronica.getResizedImage(filePath, resizeOptions, function(err, resizedPath) {
488
475
  if (err) {
489
476
  return callback(err);
490
477
  }
491
478
 
492
- cache[tempName] = tempPath;
493
- callback(null, tempPath);
479
+ cache[tempName] = resizedPath;
480
+ callback(null, resizedPath);
494
481
  });
495
482
  });
496
483
 
@@ -599,6 +586,10 @@ ImageMedia.setMethod(function getMetadata(filePath, base, info, extra, callback)
599
586
  return getInfo();
600
587
  }
601
588
 
589
+ if (!that.exiv_path) {
590
+ return getInfo();
591
+ }
592
+
602
593
  child.execFile(that.exiv_path, ['rm', tempFile], function afterStrip(err) {
603
594
 
604
595
  if (err) {
@@ -635,7 +626,7 @@ ImageMedia.setMethod(function getMetadata(filePath, base, info, extra, callback)
635
626
  });
636
627
 
637
628
  /**
638
- * Resize an image using imagemagick's convert
629
+ * Resize an image using veronica
639
630
  *
640
631
  * @author Jelle De Loecker <jelle@develry.be>
641
632
  * @since 0.0.1
@@ -643,11 +634,12 @@ ImageMedia.setMethod(function getMetadata(filePath, base, info, extra, callback)
643
634
  */
644
635
  ImageMedia.setMethod(function resize(source, target, width, height, callback) {
645
636
 
646
- let convertBin = alchemy.settings.plugins.media.convert;
647
-
648
- child.execFile(convertBin, [source, '-resize', width + 'x' + height, target], function(err) {
649
- callback(err);
650
- });
637
+ // Fit inside, maintain aspect ratio
638
+ this.veronica.resize(source, {
639
+ maxWidth: width,
640
+ maxHeight: height,
641
+ target: target
642
+ }, callback);
651
643
  });
652
644
 
653
645
  /**
@@ -662,6 +654,32 @@ ImageMedia.setMethod(function resize(source, target, width, height, callback) {
662
654
  */
663
655
  ImageMedia.setMethod(function getImageInfo(filePath, callback) {
664
656
 
657
+ const that = this;
658
+
659
+ // Helper to use veronica for basic image info
660
+ function useVeronica() {
661
+ that.veronica.image(filePath).size(function(err, size) {
662
+ if (err) {
663
+ return callback(err);
664
+ }
665
+
666
+ let info = {};
667
+ let extra = {};
668
+
669
+ info.width = size.width;
670
+ info.height = size.height;
671
+
672
+ extra.width = size.width;
673
+ extra.height = size.height;
674
+
675
+ callback(null, info, extra);
676
+ });
677
+ }
678
+
679
+ if (!this.exiv_path) {
680
+ return useVeronica();
681
+ }
682
+
665
683
  // Run exiv on the given file
666
684
  child.execFile(this.exiv_path, ['-pt', filePath], function afterStrip(err, out) {
667
685
 
@@ -675,30 +693,9 @@ ImageMedia.setMethod(function getImageInfo(filePath, callback) {
675
693
  // See what type of error was made
676
694
  if (err) {
677
695
 
678
- // No exif information found
679
- if (err.code == 253) {
680
-
681
- gm(filePath).size(function gotGmResponse(err, size) {
682
-
683
- if (err) {
684
- if (err.message && err.message.indexOf('spawn ENOENT')) {
685
- callback(new Error('Graphicsmagick needs to be installed!'));
686
- } else {
687
- callback(err);
688
- }
689
- return;
690
- }
691
-
692
- info.width = size.width;
693
- info.height = size.height;
694
-
695
- extra.width = size.width;
696
- extra.height = size.height;
697
-
698
- callback(null, info, extra);
699
- });
700
-
701
- return;
696
+ // No exif information found (253) or binary not found (ENOENT)
697
+ if (err.code == 253 || err.code === 'ENOENT') {
698
+ return useVeronica();
702
699
  }
703
700
 
704
701
  // Code 255 = file not found
package/package.json CHANGED
@@ -1,21 +1,20 @@
1
1
  {
2
2
  "name": "alchemy-media",
3
3
  "description": "The media plugin for Alchemy",
4
- "version": "0.9.0-alpha.5",
4
+ "version": "0.9.0",
5
5
  "repository": {
6
6
  "type" : "git",
7
7
  "url" : "https://github.com/11ways/alchemy-media.git"
8
8
  },
9
9
  "dependencies": {
10
- "@11ways/exiv2" : "~0.7.0",
11
- "gm" : "~1.23.1",
12
- "veronica" : "~0.2.2"
10
+ "@11ways/exiv2" : "~0.7.3",
11
+ "veronica" : "~0.3.0"
13
12
  },
14
13
  "peerDependencies": {
15
14
  "alchemymvc" : ">=1.4.0||>=1.4.0-alpha"
16
15
  },
17
16
  "license": "MIT",
18
17
  "engines": {
19
- "node" : ">=16.20.1"
18
+ "node" : ">=18.17.0"
20
19
  }
21
20
  }