Begin storing image width + height
This commit is contained in:
parent
42bf07445c
commit
1cb3c4a22c
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -57,6 +57,7 @@
|
||||
"pushstate",
|
||||
"qaub",
|
||||
"Qrcode",
|
||||
"qrserver",
|
||||
"QRSTUVWXYZ",
|
||||
"ratelimit",
|
||||
"ratelimiter",
|
||||
|
||||
@ -81,6 +81,7 @@ const nextConfig: NextConfig = {
|
||||
remotePatterns,
|
||||
minimumCacheTTL: 31536000,
|
||||
},
|
||||
serverExternalPackages: ['exifr'],
|
||||
turbopack: {
|
||||
resolveAlias: {
|
||||
[LOCALE_ALIAS]: `@/${LOCALE_DYNAMIC}`,
|
||||
|
||||
@ -101,6 +101,15 @@ export const MIGRATIONS: Migration[] = [{
|
||||
DROP COLUMN IF EXISTS latitude,
|
||||
DROP COLUMN IF EXISTS longitude;
|
||||
`),
|
||||
}, {
|
||||
label: '09: Image Dimensions',
|
||||
table: 'photos',
|
||||
fields: ['width', 'height'],
|
||||
run: () => sql`
|
||||
ALTER TABLE photos
|
||||
ADD COLUMN IF NOT EXISTS width INTEGER,
|
||||
ADD COLUMN IF NOT EXISTS height INTEGER
|
||||
`,
|
||||
}];
|
||||
|
||||
export const migrationForError = (e: any) =>
|
||||
|
||||
@ -295,7 +295,10 @@ export default function PhotoForm({
|
||||
? <SmallDisclosure label="Optimized file set">
|
||||
<div className="space-y-1">
|
||||
{photoStorageUrls.map(({ url, size }) => {
|
||||
const { fileName } = getFileNamePartsFromStorageUrl(url);
|
||||
const {
|
||||
fileName,
|
||||
fileModifier,
|
||||
} = getFileNamePartsFromStorageUrl(url);
|
||||
return <div
|
||||
key={url}
|
||||
className="flex items-center gap-2"
|
||||
@ -307,7 +310,12 @@ export default function PhotoForm({
|
||||
>
|
||||
{fileName}
|
||||
</Link>
|
||||
<span className="text-dim">{size}</span>
|
||||
<span className="text-dim">
|
||||
{size}
|
||||
{/* Show dimensions for original file when available */}
|
||||
{!fileModifier && formData.width && formData.height &&
|
||||
` @ ${formData.width}×${formData.height}`}
|
||||
</span>
|
||||
</div>;
|
||||
})}
|
||||
</div>
|
||||
|
||||
@ -253,6 +253,18 @@ const FORM_METADATA = (
|
||||
label: 'blur data',
|
||||
readOnly: true,
|
||||
},
|
||||
width: {
|
||||
section: 'storage',
|
||||
label: 'width',
|
||||
readOnly: true,
|
||||
hideIfEmpty: true,
|
||||
},
|
||||
height: {
|
||||
section: 'storage',
|
||||
label: 'height',
|
||||
readOnly: true,
|
||||
hideIfEmpty: true,
|
||||
},
|
||||
aspectRatio: {
|
||||
section: 'storage',
|
||||
label: 'aspect ratio',
|
||||
@ -423,6 +435,12 @@ export const convertFormDataToPhotoDbInsert = (
|
||||
...photoForm.recipeTitle && {
|
||||
recipeTitle: parameterize(photoForm.recipeTitle),
|
||||
},
|
||||
width: photoForm.width
|
||||
? parseInt(photoForm.width)
|
||||
: undefined,
|
||||
height: photoForm.height
|
||||
? parseInt(photoForm.height)
|
||||
: undefined,
|
||||
// Convert form strings to numbers
|
||||
aspectRatio: photoForm.aspectRatio
|
||||
? roundToNumber(parseFloat(photoForm.aspectRatio), 6)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {
|
||||
getCompatibleExifValue,
|
||||
convertApertureValueToFNumber,
|
||||
getAspectRatioFromExif,
|
||||
getDimensionsFromExif,
|
||||
getOffsetFromExif,
|
||||
} from '@/utility/exif';
|
||||
import {
|
||||
@ -45,8 +45,14 @@ export const convertExifToFormData = (
|
||||
const dateTimeOriginal = getExifValue('DateTimeOriginal');
|
||||
const offset = getOffsetFromExif(exif, exifr);
|
||||
|
||||
const { width, height, aspectRatio } = getDimensionsFromExif(exif, exifr);
|
||||
|
||||
return {
|
||||
aspectRatio: getAspectRatioFromExif(exif).toString(),
|
||||
...width && height && {
|
||||
width: width.toString(),
|
||||
height: height.toString(),
|
||||
},
|
||||
aspectRatio: aspectRatio.toString(),
|
||||
make: getExifValue('Make'),
|
||||
model: getExifValue('Model'),
|
||||
focalLength: getExifValue('FocalLength')?.toString(),
|
||||
|
||||
@ -50,6 +50,8 @@ export const MAX_PHOTO_UPLOAD_SIZE_IN_BYTES = 50_000_000;
|
||||
|
||||
// Core EXIF data
|
||||
export interface PhotoExif {
|
||||
width?: number
|
||||
height?: number
|
||||
aspectRatio: number
|
||||
make?: string
|
||||
model?: string
|
||||
|
||||
@ -44,6 +44,8 @@ export const createPhotosTable = () =>
|
||||
id VARCHAR(8) PRIMARY KEY,
|
||||
url VARCHAR(255) NOT NULL,
|
||||
extension VARCHAR(255) NOT NULL,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
aspect_ratio REAL DEFAULT 1.5,
|
||||
blur_data TEXT,
|
||||
title VARCHAR(255),
|
||||
@ -85,6 +87,8 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
id,
|
||||
url,
|
||||
extension,
|
||||
width,
|
||||
height,
|
||||
aspect_ratio,
|
||||
blur_data,
|
||||
title,
|
||||
@ -118,6 +122,8 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
${photo.id},
|
||||
${photo.url},
|
||||
${photo.extension},
|
||||
${photo.width},
|
||||
${photo.height},
|
||||
${photo.aspectRatio},
|
||||
${photo.blurData},
|
||||
${photo.title},
|
||||
@ -155,6 +161,8 @@ export const updatePhoto = (photo: PhotoDbInsert) =>
|
||||
UPDATE photos SET
|
||||
url=${photo.url},
|
||||
extension=${photo.extension},
|
||||
width=${photo.width},
|
||||
height=${photo.height},
|
||||
aspect_ratio=${photo.aspectRatio},
|
||||
blur_data=${photo.blurData},
|
||||
title=${photo.title},
|
||||
|
||||
@ -67,13 +67,17 @@ export const getFileNamePartsFromStorageUrl = (url: string) => {
|
||||
fileName = '',
|
||||
fileNameBase = '',
|
||||
fileId = '',
|
||||
fileModifier = '',
|
||||
fileExtension = '',
|
||||
] = url.match(/^(.+)\/((-*[a-z0-9]+-*([a-z0-9-]+))\.([a-z]{1,4}))$/i) ?? [];
|
||||
] = url.match(
|
||||
/^(.+)\/((-*[a-z0-9]+-*([a-z0-9]+)-*([a-z0-9]+)*)\.([a-z]{1,4}))$/i,
|
||||
) ?? [];
|
||||
return {
|
||||
urlBase,
|
||||
fileName,
|
||||
fileNameBase,
|
||||
fileId,
|
||||
fileModifier,
|
||||
fileExtension,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { DEFAULT_ASPECT_RATIO } from '@/photo';
|
||||
import { OrientationTypes, type ExifData, ExifTags } from 'ts-exif-parser';
|
||||
|
||||
export const getCompatibleExifValue = (
|
||||
@ -19,12 +20,19 @@ export const getOffsetFromExif = (
|
||||
Object.values(exifr).find(isValueOffset)
|
||||
) as string | undefined;
|
||||
|
||||
export const getAspectRatioFromExif = (data: ExifData): number => {
|
||||
export const getDimensionsFromExif = (
|
||||
exif: ExifData,
|
||||
exifr: any,
|
||||
): {
|
||||
width: number | undefined
|
||||
height: number | undefined
|
||||
aspectRatio: number
|
||||
} => {
|
||||
// Using '||' operator to handle `Orientation` unexpectedly being '0'
|
||||
const orientation = data.tags?.Orientation || OrientationTypes.TOP_LEFT;
|
||||
const orientation = exif.tags?.Orientation || OrientationTypes.TOP_LEFT;
|
||||
|
||||
const width = data.imageSize?.width ?? 3.0;
|
||||
const height = data.imageSize?.height ?? 2.0;
|
||||
let width: number | undefined;
|
||||
let height: number | undefined;
|
||||
|
||||
switch (orientation) {
|
||||
case OrientationTypes.TOP_LEFT:
|
||||
@ -33,11 +41,25 @@ export const getAspectRatioFromExif = (data: ExifData): number => {
|
||||
case OrientationTypes.BOTTOM_LEFT:
|
||||
case OrientationTypes.LEFT_TOP:
|
||||
case OrientationTypes.RIGHT_BOTTOM:
|
||||
return width / height;
|
||||
width = exif.imageSize?.width || exifr.ImageWidth;
|
||||
height = exif.imageSize?.height || exifr.ImageHeight;
|
||||
break;
|
||||
case OrientationTypes.RIGHT_TOP:
|
||||
case OrientationTypes.LEFT_BOTTOM:
|
||||
return height / width;
|
||||
width = exif.imageSize?.height || exifr.ImageHeight;
|
||||
height = exif.imageSize?.width || exifr.ImageWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
const aspectRatio = width && height
|
||||
? width / height
|
||||
: DEFAULT_ASPECT_RATIO;
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
aspectRatio,
|
||||
};
|
||||
};
|
||||
|
||||
export const convertApertureValueToFNumber = (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user