alchemy-media 0.9.0-alpha.4 → 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 +14 -0
- package/README.md +12 -18
- package/assets/stylesheets/media_elements/_file.scss +12 -0
- package/config/settings.js +0 -27
- package/controller/media_files_controller.js +2 -3
- package/lib/media_types/image_media_type.js +57 -60
- package/package.json +4 -5
- package/view/element/al_file_selection.hwk +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
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
|
+
|
|
9
|
+
## 0.9.0-alpha.5 (2025-07-10)
|
|
10
|
+
|
|
11
|
+
* Add `size` to the media file chooser
|
|
12
|
+
* Allow using filters in the table
|
|
13
|
+
* Limit the height of the media file chooser
|
|
14
|
+
|
|
1
15
|
## 0.9.0-alpha.4 (2024-10-10)
|
|
2
16
|
|
|
3
17
|
* Make `MediaRaw#addFile(url)` support data uris too
|
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
|
-
|
|
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
|
-
|
|
69
|
+
For placeholder image generation, you'll need the canvas dependencies:
|
|
77
70
|
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
# Arch Linux
|
|
79
|
+
sudo pacman -S cairo pango giflib libjpeg-turbo librsvg
|
|
80
|
+
```
|
|
87
81
|
|
|
88
|
-
This plugin
|
|
82
|
+
This plugin uses exiv2 for reading image metadata:
|
|
89
83
|
|
|
90
|
-
apt-get install
|
|
84
|
+
apt-get install exiv2 libexiv2-dev
|
|
@@ -133,4 +133,16 @@ al-field[field-type="file"] {
|
|
|
133
133
|
margin: auto;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.alchemy-file-selection-dialog {
|
|
139
|
+
.alchemy-file-selection-wrapper {
|
|
140
|
+
max-height: 85vh;
|
|
141
|
+
|
|
142
|
+
al-table {
|
|
143
|
+
max-height: 76vh;
|
|
144
|
+
margin-bottom: 1rem;
|
|
145
|
+
overflow: auto;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
136
148
|
}
|
package/config/settings.js
CHANGED
|
@@ -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
|
}
|
|
@@ -460,13 +460,12 @@ MediaFiles.setAction(async function recordsource(conduit) {
|
|
|
460
460
|
continue;
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
|
|
464
|
-
crit.where(key).equals(val);
|
|
463
|
+
crit.where(key).matchesFilter(val);
|
|
465
464
|
}
|
|
466
465
|
}
|
|
467
466
|
|
|
468
467
|
let records = await model.find('all', crit);
|
|
469
|
-
|
|
468
|
+
|
|
470
469
|
conduit.end({
|
|
471
470
|
records : records
|
|
472
471
|
});
|
|
@@ -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
|
-
|
|
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
|
|
454
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
460
|
+
// Use veronica for resize and WebP conversion
|
|
461
|
+
let resizeOptions = {
|
|
462
|
+
type: options.type,
|
|
463
|
+
quality: 80
|
|
464
|
+
};
|
|
481
465
|
|
|
482
|
-
if (
|
|
483
|
-
|
|
466
|
+
if (options.width) {
|
|
467
|
+
resizeOptions.maxWidth = options.width;
|
|
484
468
|
}
|
|
485
469
|
|
|
486
|
-
|
|
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] =
|
|
493
|
-
callback(null,
|
|
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
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
|
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.
|
|
11
|
-
"
|
|
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" : ">=
|
|
18
|
+
"node" : ">=18.17.0"
|
|
20
19
|
}
|
|
21
20
|
}
|
|
@@ -15,6 +15,14 @@ fieldset.addField('_id', {
|
|
|
15
15
|
|
|
16
16
|
fieldset.addField('filename');
|
|
17
17
|
fieldset.addField('type');
|
|
18
|
+
fieldset.addField('MediaRaw.size');
|
|
19
|
+
|
|
20
|
+
if (!filters) {
|
|
21
|
+
filters = {
|
|
22
|
+
'MediaRaw.size': '>500000'
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
%>
|
|
19
27
|
|
|
20
28
|
<div class="alchemy-file-selection-wrapper">
|