@jrmc/adonis-attachment 3.3.0-beta.2 → 3.3.0-beta.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/README.md +3 -3
- package/build/src/adapters/blurhash.d.ts +4 -0
- package/build/src/adapters/blurhash.js +6 -0
- package/build/src/attachments/attachment.js +12 -5
- package/build/src/attachments/attachment_base.d.ts +3 -1
- package/build/src/attachments/attachment_base.js +35 -3
- package/build/src/converter_manager.js +21 -9
- package/build/src/services/record_with_attachment.js +6 -4
- package/build/src/types/attachment.d.ts +4 -2
- package/build/src/types/converter.d.ts +29 -29
- package/build/src/utils/helpers.d.ts +2 -1
- package/build/src/utils/helpers.js +13 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
This package is currently development and will replace [attachment-advanced](https://github.com/batosai/attachment-advanced) for AdonisJS 6.
|
|
4
4
|
|
|
5
|
-
Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-kit)
|
|
6
|
-
|
|
7
5
|
## Links
|
|
8
6
|
|
|
9
7
|
[View documentation](https://adonis-attachment.jrmc.dev/)
|
|
@@ -12,7 +10,7 @@ Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-
|
|
|
12
10
|
|
|
13
11
|
[Discord](https://discord.gg/89eMn2vB)
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-kit)
|
|
16
14
|
|
|
17
15
|
## Roadmap
|
|
18
16
|
|
|
@@ -21,9 +19,11 @@ Project sample : [adonis-starter-kit](https://github.com/batosai/adonis-starter-
|
|
|
21
19
|
- [x] attachment file by path
|
|
22
20
|
- [x] attachment file by url
|
|
23
21
|
- [x] attachment file by stream
|
|
22
|
+
- [x] attachment files
|
|
24
23
|
- [x] save meta data
|
|
25
24
|
- [x] variantes
|
|
26
25
|
- [x] images
|
|
26
|
+
- [x] [blurhash](https://blurha.sh/)
|
|
27
27
|
- [x] documents thumbnail
|
|
28
28
|
- [x] videos thumbnail
|
|
29
29
|
- [ ] command regenerate
|
|
@@ -45,7 +45,7 @@ export class Attachment extends AttachmentBase {
|
|
|
45
45
|
const attributes = {
|
|
46
46
|
...meta,
|
|
47
47
|
key,
|
|
48
|
-
folder: path.join(this.
|
|
48
|
+
folder: path.join(this.folder, 'variants', this.name),
|
|
49
49
|
};
|
|
50
50
|
const variant = new Variant(this.drive, attributes, input);
|
|
51
51
|
variant.setOptions(this.options);
|
|
@@ -58,17 +58,20 @@ export class Attachment extends AttachmentBase {
|
|
|
58
58
|
getVariant(variantName) {
|
|
59
59
|
return this.variants?.find((v) => v.key === variantName);
|
|
60
60
|
}
|
|
61
|
-
getUrl(variantName) {
|
|
61
|
+
async getUrl(variantName) {
|
|
62
62
|
if (variantName) {
|
|
63
63
|
const variant = this.getVariant(variantName);
|
|
64
64
|
if (variant) {
|
|
65
65
|
variant.setOptions(this.options);
|
|
66
|
-
|
|
66
|
+
const url = await variant.getUrl();
|
|
67
|
+
if (url) {
|
|
68
|
+
return url;
|
|
69
|
+
}
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
return super.getUrl();
|
|
70
73
|
}
|
|
71
|
-
getSignedUrl(variantNameOrOptions, signedUrlOptions) {
|
|
74
|
+
async getSignedUrl(variantNameOrOptions, signedUrlOptions) {
|
|
72
75
|
let variantName;
|
|
73
76
|
let options = signedUrlOptions;
|
|
74
77
|
if (typeof variantNameOrOptions === 'string') {
|
|
@@ -81,7 +84,10 @@ export class Attachment extends AttachmentBase {
|
|
|
81
84
|
const variant = this.getVariant(variantName);
|
|
82
85
|
if (variant) {
|
|
83
86
|
variant.setOptions(this.options);
|
|
84
|
-
|
|
87
|
+
const url = variant.getSignedUrl(options);
|
|
88
|
+
if (url) {
|
|
89
|
+
return url;
|
|
90
|
+
}
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
return super.getSignedUrl(options);
|
|
@@ -129,6 +135,7 @@ export class Attachment extends AttachmentBase {
|
|
|
129
135
|
mimeType: v.mimeType,
|
|
130
136
|
meta: v.meta,
|
|
131
137
|
size: v.size,
|
|
138
|
+
blurhash: v.blurhash,
|
|
132
139
|
};
|
|
133
140
|
});
|
|
134
141
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
+
import type { LucidRow } from '@adonisjs/lucid/types/model';
|
|
7
8
|
import type { DriveService, SignedURLOptions } from '@adonisjs/drive/types';
|
|
8
9
|
import type { LucidOptions, AttachmentBaseAttributes, AttachmentBase as AttachmentBaseInterface } from '../types/attachment.js';
|
|
9
10
|
import type { Exif, Input } from '../types/input.js';
|
|
@@ -17,7 +18,7 @@ export declare class AttachmentBase implements AttachmentBaseInterface {
|
|
|
17
18
|
meta?: Exif;
|
|
18
19
|
originalPath?: string;
|
|
19
20
|
url?: string;
|
|
20
|
-
options
|
|
21
|
+
options: LucidOptions;
|
|
21
22
|
constructor(drive: DriveService, attributes: AttachmentBaseAttributes, input?: Input);
|
|
22
23
|
/**
|
|
23
24
|
* Getters
|
|
@@ -32,6 +33,7 @@ export declare class AttachmentBase implements AttachmentBaseInterface {
|
|
|
32
33
|
getUrl(): Promise<string>;
|
|
33
34
|
getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
34
35
|
setOptions(options: LucidOptions): this;
|
|
36
|
+
makeFolder(record?: LucidRow): this;
|
|
35
37
|
/**
|
|
36
38
|
*
|
|
37
39
|
*/
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
+
import string from '@adonisjs/core/helpers/string';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
import { cuid } from '@adonisjs/core/helpers';
|
|
9
10
|
import { defaultOptionsDecorator } from '../utils/default_values.js';
|
|
11
|
+
import { extractPathParameters } from '../utils/helpers.js';
|
|
10
12
|
export class AttachmentBase {
|
|
11
13
|
drive;
|
|
12
14
|
input;
|
|
@@ -27,6 +29,7 @@ export class AttachmentBase {
|
|
|
27
29
|
this.mimeType = attributes.mimeType;
|
|
28
30
|
this.originalPath = attributes.path;
|
|
29
31
|
this.#folder = attributes.folder;
|
|
32
|
+
this.setOptions({ folder: attributes.folder });
|
|
30
33
|
if (attributes.name) {
|
|
31
34
|
this.#name = attributes.name;
|
|
32
35
|
}
|
|
@@ -43,12 +46,20 @@ export class AttachmentBase {
|
|
|
43
46
|
return this.#name;
|
|
44
47
|
}
|
|
45
48
|
get folder() {
|
|
46
|
-
if (this
|
|
47
|
-
return this
|
|
49
|
+
if (this.#folder) {
|
|
50
|
+
return this.#folder;
|
|
51
|
+
}
|
|
52
|
+
if (typeof this.options.folder === 'string') {
|
|
53
|
+
const parameters = extractPathParameters(this.options.folder);
|
|
54
|
+
if (!parameters.length) {
|
|
55
|
+
return this.options.folder;
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
|
-
return this.#folder;
|
|
50
58
|
}
|
|
51
59
|
get path() {
|
|
60
|
+
if (!this.folder && this.originalPath) {
|
|
61
|
+
return this.originalPath;
|
|
62
|
+
}
|
|
52
63
|
return path.join(this.folder, this.name);
|
|
53
64
|
}
|
|
54
65
|
/**
|
|
@@ -70,6 +81,27 @@ export class AttachmentBase {
|
|
|
70
81
|
};
|
|
71
82
|
return this;
|
|
72
83
|
}
|
|
84
|
+
makeFolder(record) {
|
|
85
|
+
if (typeof this.options.folder === 'function' && record) {
|
|
86
|
+
this.#folder = this.options.folder(record);
|
|
87
|
+
}
|
|
88
|
+
else if (this.options.folder) {
|
|
89
|
+
this.#folder = this.options.folder;
|
|
90
|
+
}
|
|
91
|
+
if (this.#folder && record) {
|
|
92
|
+
const parameters = extractPathParameters(this.#folder);
|
|
93
|
+
if (parameters) {
|
|
94
|
+
parameters.forEach((parameter) => {
|
|
95
|
+
const attribute = record.$attributes[parameter];
|
|
96
|
+
if (typeof attribute === 'string') {
|
|
97
|
+
const name = string.slug(string.noCase(string.escapeHTML(attribute.toLowerCase())));
|
|
98
|
+
this.#folder = this.#folder?.replace(`:${parameter}`, name);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
73
105
|
/**
|
|
74
106
|
*
|
|
75
107
|
*/
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import logger from '@adonisjs/core/services/logger';
|
|
1
2
|
import string from '@adonisjs/core/helpers/string';
|
|
2
3
|
import db from '@adonisjs/lucid/services/db';
|
|
3
4
|
import attachmentManager from '../services/main.js';
|
|
4
5
|
import * as errors from './errors.js';
|
|
5
|
-
import {
|
|
6
|
+
import { imageToBlurhash } from './utils/helpers.js';
|
|
6
7
|
export class ConverterManager {
|
|
7
8
|
#record;
|
|
8
9
|
#attributeName;
|
|
@@ -20,11 +21,11 @@ export class ConverterManager {
|
|
|
20
21
|
const id = this.#record.model.$attributes['id'];
|
|
21
22
|
const data = {};
|
|
22
23
|
if (this.#options.variants) {
|
|
23
|
-
for (const option of this.#options.variants) {
|
|
24
|
+
for await (const option of this.#options.variants) {
|
|
24
25
|
const converter = (await attachmentManager.getConverter(option));
|
|
25
26
|
if (attachments && converter) {
|
|
26
|
-
for (
|
|
27
|
-
const input =
|
|
27
|
+
for await (const attachment of attachments) {
|
|
28
|
+
const input = attachment.input;
|
|
28
29
|
const output = await converter.handle({
|
|
29
30
|
input,
|
|
30
31
|
options: converter.options,
|
|
@@ -32,10 +33,21 @@ export class ConverterManager {
|
|
|
32
33
|
if (output === undefined) {
|
|
33
34
|
throw new errors.E_CANNOT_PATH_BY_CONVERTER();
|
|
34
35
|
}
|
|
35
|
-
const variant = await
|
|
36
|
+
const variant = await attachment.createVariant(option, output);
|
|
36
37
|
if (converter.options.blurhash) {
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if ((typeof converter.options.blurhash !== 'boolean' &&
|
|
39
|
+
converter.options.blurhash.enabled === true) ||
|
|
40
|
+
converter.options.blurhash === true) {
|
|
41
|
+
try {
|
|
42
|
+
const options = typeof converter.options.blurhash !== 'boolean'
|
|
43
|
+
? converter.options.blurhash
|
|
44
|
+
: undefined;
|
|
45
|
+
variant.blurhash = await imageToBlurhash(variant.input, options);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logger.error(error.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
39
51
|
}
|
|
40
52
|
await attachmentManager.save(variant);
|
|
41
53
|
}
|
|
@@ -58,10 +70,10 @@ export class ConverterManager {
|
|
|
58
70
|
});
|
|
59
71
|
try {
|
|
60
72
|
await trx.query().from(Model.table).where('id', id).update(data);
|
|
61
|
-
return
|
|
73
|
+
return trx.commit();
|
|
62
74
|
}
|
|
63
75
|
catch (error) {
|
|
64
|
-
return
|
|
76
|
+
return trx.rollback();
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
79
|
}
|
|
@@ -57,7 +57,7 @@ export default class Record {
|
|
|
57
57
|
* file.
|
|
58
58
|
*/
|
|
59
59
|
if (newAttachments[i]) {
|
|
60
|
-
newAttachments[i].setOptions(options);
|
|
60
|
+
newAttachments[i].setOptions(options).makeFolder(this.#model);
|
|
61
61
|
this.#model.$attachments.attached.push(newAttachments[i]);
|
|
62
62
|
/**
|
|
63
63
|
* Also write the file to the disk right away
|
|
@@ -106,7 +106,7 @@ export default class Record {
|
|
|
106
106
|
* For all properties Attachment
|
|
107
107
|
* Launch async generation variants
|
|
108
108
|
*/
|
|
109
|
-
await
|
|
109
|
+
for await (const name of attachmentAttributeNames) {
|
|
110
110
|
const record = this;
|
|
111
111
|
attachmentManager.queue.push({
|
|
112
112
|
name: `${this.#model.constructor.name}-${name}`,
|
|
@@ -124,7 +124,7 @@ export default class Record {
|
|
|
124
124
|
}
|
|
125
125
|
},
|
|
126
126
|
});
|
|
127
|
-
}
|
|
127
|
+
}
|
|
128
128
|
}
|
|
129
129
|
async detach() {
|
|
130
130
|
const attachmentAttributeNames = this.#getDirtyAttributeNamesOfAttachment();
|
|
@@ -214,7 +214,9 @@ export default class Record {
|
|
|
214
214
|
const dirtyValue = this.#model.$dirty[name];
|
|
215
215
|
const originalValue = this.#model.$original[name]; // if dirtyValue is null, check original type
|
|
216
216
|
const isDirtyAttachment = dirtyValue instanceof Attachment ||
|
|
217
|
-
(Array.isArray(dirtyValue) &&
|
|
217
|
+
(Array.isArray(dirtyValue) &&
|
|
218
|
+
dirtyValue.every((item) => item instanceof Attachment) &&
|
|
219
|
+
dirtyValue.length);
|
|
218
220
|
const isOriginalAttachment = originalValue instanceof Attachment ||
|
|
219
221
|
(Array.isArray(originalValue) && originalValue.every((item) => item instanceof Attachment));
|
|
220
222
|
return isDirtyAttachment || isOriginalAttachment;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
|
|
6
6
|
*/
|
|
7
|
+
import type { LucidRow } from '@adonisjs/lucid/types/model';
|
|
7
8
|
import type { DriveService } from '@adonisjs/drive/types';
|
|
8
9
|
import type { Exif, Input } from './input.js';
|
|
9
10
|
import type { Disk } from '@adonisjs/drive';
|
|
@@ -21,7 +22,8 @@ export type AttachmentBase = {
|
|
|
21
22
|
meta?: Exif;
|
|
22
23
|
originalPath?: string;
|
|
23
24
|
url?: string;
|
|
24
|
-
options
|
|
25
|
+
options: LucidOptions;
|
|
26
|
+
makeFolder(record?: LucidRow): void;
|
|
25
27
|
getDisk(): Disk;
|
|
26
28
|
getUrl(): Promise<string>;
|
|
27
29
|
getSignedUrl(signedUrlOptions?: SignedURLOptions): Promise<string>;
|
|
@@ -46,7 +48,7 @@ export type Variant = AttachmentBase & {
|
|
|
46
48
|
};
|
|
47
49
|
export type LucidOptions = {
|
|
48
50
|
disk?: string;
|
|
49
|
-
folder?: string;
|
|
51
|
+
folder?: string | ((record?: LucidRow) => string);
|
|
50
52
|
preComputeUrl?: boolean;
|
|
51
53
|
variants?: (keyof AttachmentVariants)[];
|
|
52
54
|
rename?: boolean;
|
|
@@ -24,47 +24,47 @@ export type ConverterAttributes = {
|
|
|
24
24
|
};
|
|
25
25
|
export type BlurhashOptions = {
|
|
26
26
|
enabled: boolean;
|
|
27
|
-
componentX:
|
|
28
|
-
componentY:
|
|
27
|
+
componentX: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
28
|
+
componentY: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
29
29
|
};
|
|
30
30
|
type jpeg = {
|
|
31
31
|
format: 'jpeg';
|
|
32
32
|
options: {
|
|
33
33
|
quality?: number;
|
|
34
|
-
progressive?:
|
|
34
|
+
progressive?: boolean;
|
|
35
35
|
chromaSubsampling?: string;
|
|
36
|
-
optimiseCoding?:
|
|
37
|
-
optimizeCoding?:
|
|
38
|
-
mozjpeg?:
|
|
39
|
-
trellisQuantisation?:
|
|
40
|
-
overshootDeringing?:
|
|
41
|
-
optimiseScans?:
|
|
42
|
-
optimizeScans?:
|
|
36
|
+
optimiseCoding?: boolean;
|
|
37
|
+
optimizeCoding?: boolean;
|
|
38
|
+
mozjpeg?: boolean;
|
|
39
|
+
trellisQuantisation?: boolean;
|
|
40
|
+
overshootDeringing?: boolean;
|
|
41
|
+
optimiseScans?: boolean;
|
|
42
|
+
optimizeScans?: boolean;
|
|
43
43
|
quantisationTable?: number;
|
|
44
44
|
quantizationTable?: number;
|
|
45
|
-
force?:
|
|
45
|
+
force?: boolean;
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
type png = {
|
|
49
49
|
format: 'png';
|
|
50
50
|
options: {
|
|
51
51
|
quality?: number;
|
|
52
|
-
progressive?:
|
|
52
|
+
progressive?: boolean;
|
|
53
53
|
compressionLevel?: number;
|
|
54
|
-
adaptiveFiltering?:
|
|
55
|
-
palette?:
|
|
54
|
+
adaptiveFiltering?: boolean;
|
|
55
|
+
palette?: boolean;
|
|
56
56
|
effort?: number;
|
|
57
57
|
colours?: number;
|
|
58
58
|
colors?: number;
|
|
59
59
|
dither?: number;
|
|
60
|
-
force?:
|
|
60
|
+
force?: boolean;
|
|
61
61
|
};
|
|
62
62
|
};
|
|
63
63
|
type gif = {
|
|
64
64
|
format: 'gif';
|
|
65
65
|
options: {
|
|
66
|
-
reuse?:
|
|
67
|
-
progressive?:
|
|
66
|
+
reuse?: boolean;
|
|
67
|
+
progressive?: boolean;
|
|
68
68
|
colours?: number;
|
|
69
69
|
colors?: number;
|
|
70
70
|
effort?: number;
|
|
@@ -73,7 +73,7 @@ type gif = {
|
|
|
73
73
|
interPaletteMaxError?: number;
|
|
74
74
|
loop?: number;
|
|
75
75
|
delay?: number | number[];
|
|
76
|
-
force?:
|
|
76
|
+
force?: boolean;
|
|
77
77
|
};
|
|
78
78
|
};
|
|
79
79
|
type webp = {
|
|
@@ -81,23 +81,23 @@ type webp = {
|
|
|
81
81
|
options: {
|
|
82
82
|
quality?: number;
|
|
83
83
|
alphaQuality?: number;
|
|
84
|
-
lossless?:
|
|
85
|
-
nearLossless?:
|
|
86
|
-
smartSubsample?:
|
|
84
|
+
lossless?: boolean;
|
|
85
|
+
nearLossless?: boolean;
|
|
86
|
+
smartSubsample?: boolean;
|
|
87
87
|
preset?: string;
|
|
88
88
|
effort?: number;
|
|
89
89
|
loop?: number;
|
|
90
90
|
delay?: number | number[];
|
|
91
|
-
minSize?:
|
|
92
|
-
mixed?:
|
|
93
|
-
force?:
|
|
91
|
+
minSize?: boolean;
|
|
92
|
+
mixed?: boolean;
|
|
93
|
+
force?: boolean;
|
|
94
94
|
};
|
|
95
95
|
};
|
|
96
96
|
type avif = {
|
|
97
97
|
format: 'avif';
|
|
98
98
|
options: {
|
|
99
99
|
quality?: number;
|
|
100
|
-
lossless?:
|
|
100
|
+
lossless?: boolean;
|
|
101
101
|
effort?: number;
|
|
102
102
|
chromaSubsampling?: string;
|
|
103
103
|
bitdepth?: number;
|
|
@@ -108,7 +108,7 @@ type heif = {
|
|
|
108
108
|
options: {
|
|
109
109
|
compression?: string;
|
|
110
110
|
quality?: number;
|
|
111
|
-
lossless?:
|
|
111
|
+
lossless?: boolean;
|
|
112
112
|
effort?: number;
|
|
113
113
|
chromaSubsampling?: string;
|
|
114
114
|
bitdepth?: number;
|
|
@@ -127,9 +127,9 @@ export type ConverterOptions = {
|
|
|
127
127
|
alpha: number;
|
|
128
128
|
};
|
|
129
129
|
kernel?: string;
|
|
130
|
-
withoutEnlargement?:
|
|
131
|
-
withoutReduction?:
|
|
132
|
-
fastShrinkOnLoad?:
|
|
130
|
+
withoutEnlargement?: boolean;
|
|
131
|
+
withoutReduction?: boolean;
|
|
132
|
+
fastShrinkOnLoad?: boolean;
|
|
133
133
|
};
|
|
134
134
|
format?: 'jpeg' | 'jpg' | 'png' | 'gif' | 'webp' | 'avif' | 'heif' | 'tiff' | 'raw' | jpeg | png | gif | webp | avif | heif;
|
|
135
135
|
blurhash?: boolean | BlurhashOptions;
|
|
@@ -12,4 +12,5 @@ export declare function bufferToTempFile(input: Buffer): Promise<string>;
|
|
|
12
12
|
export declare function streamToTempFile(input: NodeJS.ReadableStream): Promise<string>;
|
|
13
13
|
export declare function downloadToTempFile(input: URL): Promise<string>;
|
|
14
14
|
export declare function isBase64(str: string): boolean;
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function imageToBlurhash(input: Input, options?: BlurhashOptions): Promise<string>;
|
|
16
|
+
export declare function extractPathParameters(path: string): string[];
|
|
@@ -11,7 +11,7 @@ import fs from 'node:fs/promises';
|
|
|
11
11
|
import { pipeline } from 'node:stream';
|
|
12
12
|
import { promisify } from 'node:util';
|
|
13
13
|
import { createWriteStream } from 'node:fs';
|
|
14
|
-
import
|
|
14
|
+
import BlurhashAdapter from '../adapters/blurhash.js';
|
|
15
15
|
import * as errors from '../errors.js';
|
|
16
16
|
const streamPipeline = promisify(pipeline);
|
|
17
17
|
export function cleanObject(obj) {
|
|
@@ -92,7 +92,7 @@ export function isBase64(str) {
|
|
|
92
92
|
return false;
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
export function
|
|
95
|
+
export function imageToBlurhash(input, options) {
|
|
96
96
|
const { componentX, componentY } = options || { componentX: 4, componentY: 4 };
|
|
97
97
|
return new Promise(async (resolve, reject) => {
|
|
98
98
|
try {
|
|
@@ -102,12 +102,20 @@ export function encodeImageToBlurhash(input, options) {
|
|
|
102
102
|
.raw()
|
|
103
103
|
.ensureAlpha()
|
|
104
104
|
.toBuffer({ resolveWithObject: true });
|
|
105
|
-
|
|
105
|
+
const blurhash = BlurhashAdapter.encode(new Uint8ClampedArray(pixels), metadata.width, metadata.height, componentX, componentY);
|
|
106
|
+
return resolve(blurhash);
|
|
106
107
|
}
|
|
107
108
|
catch (error) {
|
|
108
|
-
console.log('--------------');
|
|
109
|
-
console.log(error);
|
|
110
109
|
return reject(error);
|
|
111
110
|
}
|
|
112
111
|
});
|
|
113
112
|
}
|
|
113
|
+
export function extractPathParameters(path) {
|
|
114
|
+
const paramRegex = /:(\w+)/g;
|
|
115
|
+
const parameters = [];
|
|
116
|
+
let match;
|
|
117
|
+
while ((match = paramRegex.exec(path)) !== null) {
|
|
118
|
+
parameters.push(match[1]);
|
|
119
|
+
}
|
|
120
|
+
return parameters;
|
|
121
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrmc/adonis-attachment",
|
|
3
|
-
"version": "3.3.0-beta.
|
|
3
|
+
"version": "3.3.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Turn any field on your Lucid model to an attachment data type",
|
|
6
6
|
"engines": {
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"luxon": "^3.5.0",
|
|
97
97
|
"prettier": "^3.4.2",
|
|
98
98
|
"sharp": "^0.33.5",
|
|
99
|
+
"sinon": "^19.0.2",
|
|
99
100
|
"ts-node": "^10.9.2",
|
|
100
101
|
"typescript": "^5.7.3",
|
|
101
102
|
"vitepress": "^1.5.0"
|