sotlas-api/photos.js

109 wiersze
4.0 KiB
JavaScript

const sharp = require('sharp')
const crypto = require('crypto')
const fs = require('fs')
const fsPromises = require('fs').promises
const exif = require('exif-reader')
const path = require('path')
const hasha = require('hasha')
const config = require('./config')
const db = require('./db')
module.exports = {
importPhoto: async function(filename, author) {
// Hash input file to determine filename
let hash = await hasha.fromFile(filename, {algorithm: 'sha256'})
let hashFilename = hash.substr(0, 32) + '.jpg'
let originalPath = config.photos.paths.original + '/' + hashFilename.substr(0, 2) + '/' + hashFilename
await fsPromises.mkdir(path.dirname(originalPath), {recursive: true})
let metadata = await getMetadata(filename)
if (metadata.format !== 'jpeg' && metadata.format != 'png' && metadata.format != 'heif') {
throw new Error('Bad input format, must be JPEG, PNG or HEIF')
}
await fsPromises.copyFile(filename, originalPath)
let photo = {
filename: hashFilename,
width: Math.round(metadata.width),
height: Math.round(metadata.height),
author,
uploadDate: new Date()
}
if (metadata.orientation && metadata.orientation >= 5) {
// Swap width/height
let tmp = photo.width
photo.width = photo.height
photo.height = tmp
}
if (metadata.exif) {
let exifParsed = exif(metadata.exif)
if (exifParsed) {
if (exifParsed.gps && exifParsed.gps.GPSLatitude && exifParsed.gps.GPSLongitude &&
(!exifParsed.gps.GPSStatus || exifParsed.gps.GPSStatus === 'A') &&
!isNaN(exifParsed.gps.GPSLatitude[0]) && !isNaN(exifParsed.gps.GPSLongitude[0])) {
photo.coordinates = {}
photo.coordinates.latitude = exifParsed.gps.GPSLatitude[0] + exifParsed.gps.GPSLatitude[1]/60 + exifParsed.gps.GPSLatitude[2]/3600
if (exifParsed.gps.GPSLatitudeRef === 'S') {
photo.coordinates.latitude = -photo.coordinates.latitude
}
photo.coordinates.longitude = exifParsed.gps.GPSLongitude[0] + exifParsed.gps.GPSLongitude[1]/60 + exifParsed.gps.GPSLongitude[2]/3600
if (exifParsed.gps.GPSLongitudeRef === 'W') {
photo.coordinates.longitude = -photo.coordinates.longitude
}
if (exifParsed.gps.GPSImgDirection && exifParsed.gps.GPSImgDirection >= 0 && exifParsed.gps.GPSImgDirection < 360) {
photo.direction = Math.round(exifParsed.gps.GPSImgDirection)
}
if (exifParsed.gps.GPSHPositioningError) {
photo.positioningError = Math.round(exifParsed.gps.GPSHPositioningError)
}
}
if (exifParsed.image && exifParsed.image.Make && exifParsed.image.Model) {
photo.camera = exifParsed.image.Make + ' ' + exifParsed.image.Model
}
if (exifParsed.exif) {
if (exifParsed.exif.DateTimeDigitized) {
photo.date = exifParsed.exif.DateTimeDigitized
} else if (exifParsed.exif.DateTimeOriginal) {
photo.date = exifParsed.exif.DateTimeOriginal
}
}
}
}
let mkdirTasks = []
let resizeTasks = []
Object.keys(config.photos.sizes).forEach(sizeDescr => {
let outPath = config.photos.paths[sizeDescr] + '/' + hashFilename.substr(0, 2) + '/' + hashFilename
mkdirTasks.push(fsPromises.mkdir(path.dirname(outPath), {recursive: true}))
resizeTasks.push(makeResized(originalPath, outPath, config.photos.sizes[sizeDescr].width, config.photos.sizes[sizeDescr].height))
})
await Promise.all(mkdirTasks)
await Promise.all(resizeTasks)
db.getDb().collection('uploads').insertOne({
uploadDate: new Date(),
type: 'photo',
filename: hashFilename,
author
})
return photo
}
}
function getMetadata(src) {
return sharp(src).metadata()
}
function makeResized(src, dst, maxWidth, maxHeight) {
return sharp(src).rotate().resize({ height: maxHeight, width: maxWidth, fit: 'inside' }).toFile(dst)
}