@jrmc/adonis-attachment
Version:
Turn any field on your Lucid model to an attachment data type
136 lines (135 loc) • 4.38 kB
JavaScript
/**
* @jrmc/adonis-attachment
*
* @license MIT
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
*/
import fs from 'node:fs/promises';
import ExifReader from 'exifreader';
import logger from '@adonisjs/core/services/logger';
import { fileTypeFromBuffer, fileTypeFromFile } from 'file-type';
import { bufferToTempFile, cleanObject, use } from '../utils/helpers.js';
export default {
async exif(input, config) {
return exif(input, config);
},
};
const exif = async (input, config) => {
let fileType;
let buffer;
if (Buffer.isBuffer(input)) {
fileType = await fileTypeFromBuffer(input);
if (fileType?.mime.includes('image')) {
buffer = input;
}
}
else {
fileType = await fileTypeFromFile(input);
if (fileType?.mime.includes('image')) {
buffer = await fs.readFile(input);
}
}
if (fileType?.mime.includes('video')) {
return videoExif(input, config);
}
if (buffer && fileType?.mime.includes('image')) {
return imageExif(buffer);
}
return undefined;
};
async function imageExif(buffer) {
const tags = await ExifReader.load(buffer, { expanded: true });
const data = {};
if (tags.exif) {
if (tags.exif['DateTime'])
data.date = tags.exif['DateTime'].description;
if (tags.exif['Software'])
data.host = tags.exif['Software'].description;
if (tags.exif['PixelXDimension'] && tags.exif['PixelYDimension']) {
data.dimension = {
width: tags.exif['PixelXDimension'].value,
height: tags.exif['PixelYDimension'].value,
};
}
if (tags.exif['Orientation']) {
data.orientation = {
value: tags.exif['Orientation'].value,
description: tags.exif['Orientation'].description,
};
}
}
if (tags.gps) {
data.gps = {
latitude: tags.gps['Latitude'],
longitude: tags.gps['Longitude'],
altitude: tags.gps['Altitude'],
};
}
if (tags.png) {
if (tags.png['Image Width'] && tags.png['Image Height']) {
data.dimension = {
width: tags.png['Image Width'].value,
height: tags.png['Image Height'].value,
};
}
if (tags.png['Software'])
data.host = tags.png['Software'].description;
if (tags.png['Creation Time'])
data.date = tags.png['Creation Time'].description;
}
if (tags.pngFile) {
if (tags.pngFile['Image Width'] && tags.pngFile['Image Height']) {
data.dimension = {
width: tags.pngFile['Image Width'].value,
height: tags.pngFile['Image Height'].value,
};
}
}
if (tags.file) {
if (tags.file['Image Width'] && tags.file['Image Height']) {
data.dimension = {
width: tags.file['Image Width'].value,
height: tags.file['Image Height'].value,
};
}
}
if (tags.icc) {
if (tags.icc['Software'])
data.host = tags.icc['Software'].description;
if (tags.icc['Creation Time'])
data.date = tags.icc['Creation Time'].description;
if (tags.icc['Image Width'] && tags.icc['Image Height']) {
data.dimension = {
width: parseInt(tags.icc['Image Width'].value),
height: parseInt(tags.icc['Image Height'].value),
};
}
}
return cleanObject(data);
}
async function videoExif(input, config) {
return new Promise(async (resolve) => {
const ffmpeg = await use('fluent-ffmpeg');
let file = input;
if (Buffer.isBuffer(input)) {
file = await bufferToTempFile(input);
}
const ff = ffmpeg(file);
if (config.bin) {
if (config.bin.ffprobePath) {
ff.setFfprobePath(config.bin.ffprobePath);
}
}
ff.ffprobe(0, (err, data) => {
if (err) {
logger.error({ err });
}
resolve({
dimension: {
width: data.streams[0].width,
height: data.streams[0].height,
},
});
});
});
}