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