@jrmc/adonis-attachment
Version:
Turn any field on your Lucid model to an attachment data type
98 lines (97 loc) • 3.48 kB
JavaScript
/**
* @jrmc/adonis-attachment
*
* @license MIT
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
*/
import logger from '@adonisjs/core/services/logger';
import attachmentManager from '../../../services/main.js';
import { streamToTempFile } from '../../utils/helpers.js';
export default class VariantGeneratorService {
async generate({ attachments, options, filters }) {
const variants = [];
const variantKeys = this.#getVariantKeysToProcess(options, filters);
for (const key of variantKeys) {
const converter = await this.#getConverter(key);
if (!converter)
continue;
for (const attachment of attachments) {
const variant = await this.generateVariant({ key, attachment, converter });
if (variant) {
variants.push(variant);
}
}
}
return variants;
}
async generateVariant({ key, attachment, converter }) {
try {
const input = await this.#prepareInput(attachment);
const output = await this.#convertFile(input, converter);
if (!output) {
// throw new errors.E_CANNOT_PATH_BY_CONVERTER()
logger.warn(`Converter returned no output for key: ${key}`);
return null;
}
const variant = await attachment.createVariant(key, output);
await this.#processBlurhash(variant, converter);
await attachmentManager.write(variant);
return variant;
}
catch (error) {
logger.error(`Failed to generate variant ${key} for attachment: ${error.message}`);
return null;
}
}
#getVariantKeysToProcess(options, filters) {
if (!options.variants)
return [];
return options.variants.filter(key => filters?.variants === undefined ||
filters.variants.includes(key));
}
async #getConverter(key) {
try {
return await attachmentManager.getConverter(key);
}
catch (error) {
logger.error(`Failed to get converter for key ${key}: ${error.message}`);
return null;
}
}
async #prepareInput(attachment) {
if (attachment.input) {
return attachment.input;
}
const stream = await attachment.getStream();
return await streamToTempFile(stream);
}
async #convertFile(input, converter) {
if (!converter.handle) {
throw new Error('Converter handle method is required');
}
if (!converter.options) {
throw new Error('Converter options are required');
}
return await converter.handle({
input,
options: converter.options,
});
}
async #processBlurhash(variant, converter) {
const blurhashConfig = converter.options?.blurhash;
if (!blurhashConfig)
return;
const shouldGenerate = typeof blurhashConfig === 'boolean'
? blurhashConfig
: blurhashConfig.enabled === true;
if (!shouldGenerate)
return;
try {
const options = typeof blurhashConfig !== 'boolean' ? blurhashConfig : undefined;
await variant.generateBlurhash(options);
}
catch (error) {
logger.error(`Blurhash generation failed: ${error.message}`);
}
}
}