@jrmc/adonis-attachment
Version:
Turn any field on your Lucid model to an attachment data type
166 lines (165 loc) • 5.4 kB
JavaScript
/**
* @jrmc/adonis-attachment
*
* @license MIT
* @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
*/
import fs from 'node:fs/promises';
import ExifReader from 'exifreader';
import { fileTypeFromBuffer, fileTypeFromFile } from 'file-type';
import { bufferToTempFile, cleanObject } 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 (fileType?.mime.includes('pdf')) {
return pdfExif(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) {
const { default: FFmpeg } = await import('./ffmpeg.js');
return new Promise(async (resolve) => {
let file = input;
if (Buffer.isBuffer(input)) {
file = await bufferToTempFile(input);
}
const ffmpeg = new FFmpeg(file);
if (config.bin) {
if (config.bin.ffprobePath) {
ffmpeg.setFfprobePath(config.bin.ffprobePath);
}
}
const info = await ffmpeg.exif();
if (info.width && info.height && info.duration) {
resolve({
dimension: {
width: info.width,
height: info.height,
},
duration: info.duration,
videoCodec: info?.videoCodec,
audioCodec: info?.audioCodec,
});
}
});
}
async function pdfExif(input, config) {
const { default: Poppler } = await import('./poppler.js');
return new Promise(async (resolve) => {
let file = input;
if (Buffer.isBuffer(input)) {
file = await bufferToTempFile(input);
}
const poppler = new Poppler(file);
if (config.bin) {
if (config.bin.pdfinfoPath) {
poppler.setPdfInfoPath(config.bin.pdfinfoPath);
}
}
const info = await poppler.pdfInfo();
if (info.width && info.height && info.pages) {
resolve({
dimension: {
width: info.width,
height: info.height,
},
version: info?.version,
pages: info?.pages,
date: info?.creationDate,
});
}
});
}