Merge pull request #106 from sambecker/lens-make-model
Add camera lenses to model
This commit is contained in:
commit
701055a35b
47
src/lens/index.ts
Normal file
47
src/lens/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Photo } from '@/photo';
|
||||
import { parameterize } from '@/utility/string';
|
||||
|
||||
const LENS_PLACEHOLDER: Lens = { make: 'Lens', model: 'Model' };
|
||||
|
||||
export type Lens = {
|
||||
make: string
|
||||
model: string
|
||||
};
|
||||
|
||||
export interface LensProps {
|
||||
params: Lens
|
||||
}
|
||||
|
||||
export interface PhotoLensProps {
|
||||
params: Lens & { photoId: string }
|
||||
}
|
||||
|
||||
export type LensWithCount = {
|
||||
lensKey: string
|
||||
lens: Lens
|
||||
count: number
|
||||
}
|
||||
|
||||
export type Lenses = LensWithCount[];
|
||||
|
||||
export const createLensKey = ({ make, model }: Lens) =>
|
||||
parameterize(`${make}-${model}`, true);
|
||||
|
||||
export const getLensFromParams = ({
|
||||
make,
|
||||
model,
|
||||
}: {
|
||||
make: string,
|
||||
model: string,
|
||||
}): Lens => ({
|
||||
make: parameterize(make, true),
|
||||
model: parameterize(model, true),
|
||||
});
|
||||
|
||||
export const lensFromPhoto = (
|
||||
photo: Photo | undefined,
|
||||
fallback?: Lens,
|
||||
): Lens =>
|
||||
photo?.lensMake && photo?.lensModel
|
||||
? { make: photo.lensMake, model: photo.lensModel }
|
||||
: fallback ?? LENS_PLACEHOLDER;
|
||||
@ -15,6 +15,7 @@ import {
|
||||
getPhotosMostRecentUpdate,
|
||||
getPhotosMeta,
|
||||
getUniqueFocalLengths,
|
||||
getUniqueLenses,
|
||||
} from '@/photo/db/query';
|
||||
import { GetPhotosOptions } from './db';
|
||||
import { parseCachedPhotoDates, parseCachedPhotosDates } from '@/photo';
|
||||
@ -30,6 +31,7 @@ import {
|
||||
PREFIX_TAG,
|
||||
pathForPhoto,
|
||||
} from '@/site/paths';
|
||||
import { createLensKey } from '@/lens';
|
||||
|
||||
// Table key
|
||||
const KEY_PHOTOS = 'photos';
|
||||
@ -37,6 +39,7 @@ const KEY_PHOTO = 'photo';
|
||||
// Field keys
|
||||
const KEY_TAGS = 'tags';
|
||||
const KEY_CAMERAS = 'cameras';
|
||||
const KEY_LENSES = 'lenses';
|
||||
const KEY_FILM_SIMULATIONS = 'film-simulations';
|
||||
const KEY_FOCAL_LENGTHS = 'focal-lengths';
|
||||
// Type keys
|
||||
@ -54,6 +57,10 @@ const getPhotosCacheKeyForOption = (
|
||||
const value = options[option];
|
||||
return value ? `${option}-${createCameraKey(value)}` : null;
|
||||
}
|
||||
case 'lens': {
|
||||
const value = options[option];
|
||||
return value ? `${option}-${createLensKey(value)}` : null;
|
||||
}
|
||||
case 'takenBefore':
|
||||
case 'takenAfterInclusive': {
|
||||
const value = options[option];
|
||||
@ -192,6 +199,12 @@ export const getUniqueCamerasCached =
|
||||
[KEY_PHOTOS, KEY_CAMERAS]
|
||||
);
|
||||
|
||||
export const getUniqueLensesCached =
|
||||
unstable_cache(
|
||||
getUniqueLenses,
|
||||
[KEY_PHOTOS, KEY_LENSES]
|
||||
);
|
||||
|
||||
export const getUniqueFilmSimulationsCached =
|
||||
unstable_cache(
|
||||
getUniqueFilmSimulations,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Camera } from '@/camera';
|
||||
import { Lens } from '@/lens';
|
||||
import { FilmSimulation } from '@/simulation';
|
||||
import { PRIORITY_ORDER_ENABLED } from '@/site/config';
|
||||
import { parameterize } from '@/utility/string';
|
||||
@ -13,6 +14,7 @@ export type GetPhotosOptions = {
|
||||
query?: string
|
||||
tag?: string
|
||||
camera?: Camera
|
||||
lens?: Lens
|
||||
simulation?: FilmSimulation
|
||||
focal?: number
|
||||
takenBefore?: Date
|
||||
@ -31,6 +33,7 @@ export const getWheresFromOptions = (
|
||||
query,
|
||||
tag,
|
||||
camera,
|
||||
lens,
|
||||
simulation,
|
||||
focal,
|
||||
} = options;
|
||||
@ -71,6 +74,12 @@ export const getWheresFromOptions = (
|
||||
wheresValues.push(parameterize(camera.make, true));
|
||||
wheresValues.push(parameterize(camera.model, true));
|
||||
}
|
||||
if (lens) {
|
||||
wheres.push(`LOWER(REPLACE(lens_make, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheres.push(`LOWER(REPLACE(lens_model, ' ', '-'))=$${valuesIndex++}`);
|
||||
wheresValues.push(parameterize(lens.make, true));
|
||||
wheresValues.push(parameterize(lens.model, true));
|
||||
}
|
||||
if (simulation) {
|
||||
wheres.push(`film_simulation=$${valuesIndex++}`);
|
||||
wheresValues.push(simulation);
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
} from '.';
|
||||
import { getWheresFromOptions } from '.';
|
||||
import { FocalLengths } from '@/focal';
|
||||
import { Lenses, createLensKey } from '@/lens';
|
||||
|
||||
const createPhotosTable = () =>
|
||||
sql`
|
||||
@ -39,6 +40,8 @@ const createPhotosTable = () =>
|
||||
model VARCHAR(255),
|
||||
focal_length SMALLINT,
|
||||
focal_length_in_35mm_format SMALLINT,
|
||||
lens_make VARCHAR(255),
|
||||
lens_model VARCHAR(255),
|
||||
f_number REAL,
|
||||
iso SMALLINT,
|
||||
exposure_time DOUBLE PRECISION,
|
||||
@ -65,6 +68,15 @@ const runMigration01 = () =>
|
||||
ADD COLUMN IF NOT EXISTS semantic_description TEXT
|
||||
`;
|
||||
|
||||
// Migration 02
|
||||
const MIGRATION_FIELDS_02 = ['lens_make', 'lens_model'];
|
||||
const runMigration02 = () =>
|
||||
sql`
|
||||
ALTER TABLE photos
|
||||
ADD COLUMN IF NOT EXISTS lens_make VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS lens_model VARCHAR(255)
|
||||
`;
|
||||
|
||||
// Wrapper for most queries for JIT table creation/migration running
|
||||
const safelyQueryPhotos = async <T>(
|
||||
callback: () => Promise<T>,
|
||||
@ -84,6 +96,13 @@ const safelyQueryPhotos = async <T>(
|
||||
console.log('Running migration 01 ...');
|
||||
await runMigration01();
|
||||
result = await callback();
|
||||
} else if (MIGRATION_FIELDS_02.some(field => new RegExp(
|
||||
`column "${field}" of relation "photos" does not exist`,
|
||||
'i',
|
||||
).test(e.message))) {
|
||||
console.log('Running migration 02 ...');
|
||||
await runMigration02();
|
||||
result = await callback();
|
||||
} else if (/relation "photos" does not exist/i.test(e.message)) {
|
||||
// If the table does not exist, create it
|
||||
console.log('Creating photos table ...');
|
||||
@ -131,6 +150,8 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
model,
|
||||
focal_length,
|
||||
focal_length_in_35mm_format,
|
||||
lens_make,
|
||||
lens_model,
|
||||
f_number,
|
||||
iso,
|
||||
exposure_time,
|
||||
@ -158,6 +179,8 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
${photo.model},
|
||||
${photo.focalLength},
|
||||
${photo.focalLengthIn35MmFormat},
|
||||
${photo.lensMake},
|
||||
${photo.lensModel},
|
||||
${photo.fNumber},
|
||||
${photo.iso},
|
||||
${photo.exposureTime},
|
||||
@ -188,6 +211,8 @@ export const updatePhoto = (photo: PhotoDbInsert) =>
|
||||
model=${photo.model},
|
||||
focal_length=${photo.focalLength},
|
||||
focal_length_in_35mm_format=${photo.focalLengthIn35MmFormat},
|
||||
lens_make=${photo.lensMake},
|
||||
lens_model=${photo.lensModel},
|
||||
f_number=${photo.fNumber},
|
||||
iso=${photo.iso},
|
||||
exposure_time=${photo.exposureTime},
|
||||
@ -270,6 +295,24 @@ export const getUniqueCameras = async () =>
|
||||
})))
|
||||
, 'getUniqueCameras');
|
||||
|
||||
export const getUniqueLenses = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT lens_make||' '||lens_model as lens,
|
||||
lens_make, lens_model, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE
|
||||
AND trim(lens_make) <> ''
|
||||
AND trim(lens_model) <> ''
|
||||
GROUP BY lens_make, lens_model
|
||||
ORDER BY lens ASC
|
||||
`.then(({ rows }): Lenses => rows
|
||||
.map(({ lens_make: make, lens_model: model, count }) => ({
|
||||
lensKey: createLensKey({ make, model }),
|
||||
lens: { make, model },
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueCameras');
|
||||
|
||||
export const getUniqueFilmSimulations = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT film_simulation, COUNT(*)
|
||||
|
||||
@ -103,6 +103,8 @@ const FORM_METADATA = (
|
||||
},
|
||||
focalLength: { label: 'focal length' },
|
||||
focalLengthIn35MmFormat: { label: 'focal length 35mm-equivalent' },
|
||||
lensMake: { label: 'lens make' },
|
||||
lensModel: { label: 'lens model' },
|
||||
fNumber: { label: 'aperture' },
|
||||
iso: { label: 'ISO' },
|
||||
exposureTime: { label: 'exposure time' },
|
||||
@ -195,6 +197,8 @@ export const convertExifToFormData = (
|
||||
model: data.tags?.Model,
|
||||
focalLength: data.tags?.FocalLength?.toString(),
|
||||
focalLengthIn35MmFormat: data.tags?.FocalLengthIn35mmFormat?.toString(),
|
||||
lensMake: data.tags?.LensMake,
|
||||
lensModel: data.tags?.LensModel,
|
||||
fNumber: data.tags?.FNumber?.toString(),
|
||||
iso: data.tags?.ISO?.toString(),
|
||||
exposureTime: data.tags?.ExposureTime?.toString(),
|
||||
|
||||
@ -48,6 +48,8 @@ export interface PhotoExif {
|
||||
model?: string
|
||||
focalLength?: number
|
||||
focalLengthIn35MmFormat?: number
|
||||
lensMake?: string
|
||||
lensModel?: string
|
||||
fNumber?: number
|
||||
iso?: number
|
||||
exposureTime?: number
|
||||
|
||||
@ -40,7 +40,7 @@ export const extractImageDataFromBlobPath = async (
|
||||
const extension = getExtensionFromStorageUrl(url);
|
||||
|
||||
const fileBytes = blobPath
|
||||
? await fetch(url).then(res => res.arrayBuffer())
|
||||
? await fetch(url, { cache: 'no-store' }).then(res => res.arrayBuffer())
|
||||
: undefined;
|
||||
|
||||
let exifData: ExifData | undefined;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user