138 lines
3.6 KiB
JavaScript
138 lines
3.6 KiB
JavaScript
import { PouchDB, TypeSpec } from '../services/db.js';
|
|
import { blobToArrayBuffer, deepAssign } from '../utils/conversion.js';
|
|
import { backgroundTask } from '../utils/event.js';
|
|
import { FileType } from './file.js';
|
|
import { error } from '../services/console.js';
|
|
|
|
class ImageSpec extends TypeSpec {
|
|
static async upload(blob) {
|
|
const f = await FileType.upload(blob, false);
|
|
const doc = await ImageType.getOrCreate({
|
|
digest: f.digest,
|
|
originalDate: f.lastModified,
|
|
importing: true,
|
|
width: 0,
|
|
height: 0,
|
|
sizes: {
|
|
full: FileType.getURL(f)
|
|
}
|
|
});
|
|
processImportables(doc);
|
|
return doc;
|
|
}
|
|
|
|
static getUniqueID(doc) {
|
|
return doc.digest.substr(0, 16);
|
|
}
|
|
|
|
static getSequence(doc) {
|
|
return new Date(doc.originalDate).getTime();
|
|
}
|
|
|
|
async delete(cascade = true) {
|
|
if (cascade) {
|
|
new Set(Object.keys(this.sizes)).forEach(async key => {
|
|
const f = await FileType.getDocFromURL(this.sizes[key]);
|
|
f.delete();
|
|
});
|
|
}
|
|
return await this.update({ _deleted: true });
|
|
}
|
|
//
|
|
// static validate(doc) {
|
|
// // TODO actually validate perhaps against a JSON schema
|
|
//
|
|
// const schema = {
|
|
// originalDate: t.REQUIRED_DATE,
|
|
// digest: t.REQUIRED_STRING,
|
|
// width: t.INTEGER,
|
|
// height: t.INTEGER,
|
|
// sizes: {
|
|
// type: 'object',
|
|
// properties: {
|
|
// full: t.REQUIRED_STRING,
|
|
// thumbnail: t.STRING,
|
|
// }
|
|
// },
|
|
// orientation: t.INTEGER,
|
|
// make: t.STRING,
|
|
// model: t.STRING,
|
|
// flash: t.BOOLEAN,
|
|
// iso: t.INTEGER,
|
|
// gps: {
|
|
// type: 'object',
|
|
// properties: {
|
|
// latitude: t.NUMBER,
|
|
// longitude: t.NUMBER,
|
|
// altitude: t.NUMBER,
|
|
// heading: t.NUMBER,
|
|
// }
|
|
// },
|
|
// tags: {
|
|
// type: "object",
|
|
// additionalProperties: t.BOOLEAN
|
|
// }
|
|
// };
|
|
// }
|
|
}
|
|
|
|
const processImportables = backgroundTask(async function _processImportables(image) {
|
|
const { _id, _rev, sizes, digest } = image;
|
|
const imageData = await FileType.getFromURL(image.sizes.full);
|
|
|
|
const img = new Image();
|
|
const imageProps = await new Promise(resolve => {
|
|
img.onload = () => {
|
|
resolve({ width: img.width, height: img.height });
|
|
URL.revokeObjectURL(img.src);
|
|
};
|
|
img.src = URL.createObjectURL(imageData);
|
|
});
|
|
|
|
if (new Set(['image/jpg', 'image/jpeg']).has(imageData.type)) {
|
|
const ExifParser = await import('exif-parser');
|
|
const buffer = await blobToArrayBuffer(imageData);
|
|
|
|
try {
|
|
const exifData = ExifParser.create(buffer).parse();
|
|
const { tags } = exifData;
|
|
const originalDate = new Date(
|
|
tags.DateTimeOriginal
|
|
? new Date(tags.DateTimeOriginal * 1000).toISOString()
|
|
: image.originalDate
|
|
).toISOString();
|
|
|
|
deepAssign(imageProps, {
|
|
originalDate,
|
|
width,
|
|
height,
|
|
orientation: tags.Orientation,
|
|
digest,
|
|
make: tags.Make,
|
|
model: tags.Model,
|
|
flash: !!tags.Flash,
|
|
iso: tags.ISO,
|
|
sizes,
|
|
gps: {
|
|
latitude: tags.GPSLatitude,
|
|
longitude: tags.GPSLongitude,
|
|
altitude: tags.GPSAltitude,
|
|
heading: tags.GPSImgDirection
|
|
}
|
|
});
|
|
} catch (e) {
|
|
error(e);
|
|
}
|
|
}
|
|
|
|
delete image.importing;
|
|
image.update(imageProps);
|
|
|
|
const module = await import('../context/generateThumbnails');
|
|
module.generateThumbnailForImage(image);
|
|
}, false);
|
|
|
|
export const ImageType = PouchDB.registerType('Image', ImageSpec);
|
|
|
|
ImageType.find({ importing: true }).then(results => results.forEach(processImportables));
|