puzzle/src/server/Images.ts

174 lines
4.5 KiB
TypeScript
Raw Normal View History

import sizeOf from 'image-size'
import fs from 'fs'
import exif from 'exif'
import sharp from 'sharp'
2021-05-17 00:27:47 +02:00
import {UPLOAD_DIR, UPLOAD_URL} from './Dirs'
2021-05-22 01:51:44 +02:00
import Db from './Db'
2021-05-17 00:27:47 +02:00
const resizeImage = async (filename: string) => {
if (!filename.toLowerCase().match(/\.(jpe?g|webp|png)$/)) {
return
}
const imagePath = `${UPLOAD_DIR}/${filename}`
const imageOutPath = `${UPLOAD_DIR}/r/${filename}`
const orientation = await getExifOrientation(imagePath)
let sharpImg = sharp(imagePath, { failOnError: false })
// when image is rotated to the left or right, switch width/height
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
if (orientation === 6) {
sharpImg = sharpImg.rotate()
} else if (orientation === 3) {
sharpImg = sharpImg.rotate().rotate()
} else if (orientation === 8) {
sharpImg = sharpImg.rotate().rotate().rotate()
}
const sizes = [
[150, 100],
[375, 210],
]
for (let [w,h] of sizes) {
console.log(w, h, imagePath)
await sharpImg.resize(w, h, { fit: 'contain' }).toFile(`${imageOutPath}-${w}x${h}.webp`)
}
}
2021-05-17 00:27:47 +02:00
async function getExifOrientation(imagePath: string) {
return new Promise((resolve, reject) => {
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
if (error) {
resolve(0)
} else {
resolve(exifData.image.Orientation)
}
})
})
}
2021-05-22 15:48:13 +02:00
const getTags = (db: Db, imageId: number) => {
2021-05-22 01:51:44 +02:00
const query = `
select * from categories c
inner join image_x_category ixc on c.id = ixc.category_id
where ixc.image_id = ?`
return db._getMany(query, [imageId])
}
const imageFromDb = (db: Db, imageId: number) => {
const i = db.get('images', { id: imageId })
return {
id: i.id,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
2021-05-22 15:48:13 +02:00
tags: getTags(db, i.id) as any[],
2021-05-22 01:51:44 +02:00
created: i.created * 1000,
}
}
2021-05-22 15:48:13 +02:00
const allImagesFromDb = (db: Db, tagSlugs: string[], sort: string) => {
2021-05-22 01:51:44 +02:00
const sortMap = {
alpha_asc: [{filename: 1}],
alpha_desc: [{filename: -1}],
date_asc: [{created: 1}],
date_desc: [{created: -1}],
} as Record<string, any>
// TODO: .... clean up
const wheresRaw: Record<string, any> = {}
2021-05-22 15:48:13 +02:00
if (tagSlugs.length > 0) {
const c = db.getMany('categories', {slug: {'$in': tagSlugs}})
2021-05-22 01:51:44 +02:00
if (!c) {
return []
}
2021-05-22 15:48:13 +02:00
const where = db._buildWhere({
'category_id': {'$in': c.map(x => x.id)}
})
2021-05-22 01:51:44 +02:00
const ids = db._getMany(`
select i.id from image_x_category ixc
2021-05-22 15:48:13 +02:00
inner join images i on i.id = ixc.image_id ${where.sql};
`, where.values).map(img => img.id)
2021-05-22 01:51:44 +02:00
if (ids.length === 0) {
return []
}
wheresRaw['id'] = {'$in': ids}
}
const images = db.getMany('images', wheresRaw, sortMap[sort])
return images.map(i => ({
id: i.id as number,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
2021-05-22 15:48:13 +02:00
tags: getTags(db, i.id) as any[],
2021-05-22 01:51:44 +02:00
created: i.created * 1000,
}))
}
2021-05-22 15:48:13 +02:00
const allImagesFromDisk = (tags: string[], sort: string) => {
2021-05-21 00:43:02 +02:00
let images = fs.readdirSync(UPLOAD_DIR)
.filter(f => f.toLowerCase().match(/\.(jpe?g|webp|png)$/))
.map(f => ({
2021-05-22 01:51:44 +02:00
id: 0,
filename: f,
file: `${UPLOAD_DIR}/${f}`,
2021-05-04 09:48:14 +02:00
url: `${UPLOAD_URL}/${encodeURIComponent(f)}`,
2021-05-22 01:51:44 +02:00
title: f.replace(/\.[a-z]+$/, ''),
2021-05-22 15:48:13 +02:00
tags: [] as any[],
2021-05-22 01:51:44 +02:00
created: fs.statSync(`${UPLOAD_DIR}/${f}`).mtime.getTime(),
}))
2021-05-21 00:43:02 +02:00
switch (sort) {
case 'alpha_asc':
images = images.sort((a, b) => {
return a.file > b.file ? 1 : -1
})
break;
case 'alpha_desc':
images = images.sort((a, b) => {
return a.file < b.file ? 1 : -1
})
break;
case 'date_asc':
images = images.sort((a, b) => {
2021-05-22 01:51:44 +02:00
return a.created > b.created ? 1 : -1
2021-05-21 00:43:02 +02:00
})
break;
case 'date_desc':
default:
images = images.sort((a, b) => {
2021-05-22 01:51:44 +02:00
return a.created < b.created ? 1 : -1
2021-05-21 00:43:02 +02:00
})
break;
}
return images
}
2021-05-17 00:27:47 +02:00
async function getDimensions(imagePath: string) {
let dimensions = sizeOf(imagePath)
const orientation = await getExifOrientation(imagePath)
// when image is rotated to the left or right, switch width/height
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
if (orientation === 6 || orientation === 8) {
return {
width: dimensions.height,
height: dimensions.width,
}
}
return dimensions
}
export default {
2021-05-22 01:51:44 +02:00
allImagesFromDisk,
imageFromDb,
allImagesFromDb,
resizeImage,
getDimensions,
}