Auto-label recognized recipes
This commit is contained in:
parent
b114bca43e
commit
7911cf1e2e
@ -15,7 +15,6 @@ import { GetPhotosOptions, areOptionsSensitive } from './db';
|
||||
import {
|
||||
FIELDS_TO_NOT_OVERWRITE_WITH_NULL_DATA_ON_SYNC,
|
||||
PhotoFormData,
|
||||
convertFormDataToPhotoDbInsert,
|
||||
convertPhotoToFormData,
|
||||
} from './form';
|
||||
import { redirect } from 'next/navigation';
|
||||
@ -34,7 +33,11 @@ import {
|
||||
PATH_ROOT,
|
||||
pathForPhoto,
|
||||
} from '@/app/paths';
|
||||
import { blurImageFromUrl, extractImageDataFromBlobPath } from './server';
|
||||
import {
|
||||
blurImageFromUrl,
|
||||
convertFormDataToPhotoDbInsertAndLookupRecipeTitle,
|
||||
extractImageDataFromBlobPath,
|
||||
} from './server';
|
||||
import { TAG_FAVS, isTagFavs } from '@/tag';
|
||||
import { convertPhotoToPhotoDbInsert, Photo } from '.';
|
||||
import { runAuthenticatedAdminServerAction } from '@/auth';
|
||||
@ -59,7 +62,8 @@ export const createPhotoAction = async (formData: FormData) =>
|
||||
const shouldStripGpsData = formData.get('shouldStripGpsData') === 'true';
|
||||
formData.delete('shouldStripGpsData');
|
||||
|
||||
const photo = convertFormDataToPhotoDbInsert(formData);
|
||||
const photo =
|
||||
await convertFormDataToPhotoDbInsertAndLookupRecipeTitle(formData);
|
||||
|
||||
const updatedUrl = await convertUploadToPhoto({
|
||||
urlOrigin: photo.url,
|
||||
@ -160,7 +164,8 @@ export const addAllUploadsAction = async ({
|
||||
if (updatedUrl) {
|
||||
const subheadFinal = 'Adding to database';
|
||||
streamUpdate(subheadFinal);
|
||||
const photo = convertFormDataToPhotoDbInsert(form);
|
||||
const photo =
|
||||
await convertFormDataToPhotoDbInsertAndLookupRecipeTitle(form);
|
||||
photo.url = updatedUrl;
|
||||
await insertPhoto(photo);
|
||||
addedUploadUrls.push(url);
|
||||
@ -185,7 +190,8 @@ export const addAllUploadsAction = async ({
|
||||
|
||||
export const updatePhotoAction = async (formData: FormData) =>
|
||||
runAuthenticatedAdminServerAction(async () => {
|
||||
const photo = convertFormDataToPhotoDbInsert(formData);
|
||||
const photo =
|
||||
await convertFormDataToPhotoDbInsertAndLookupRecipeTitle(formData);
|
||||
|
||||
let urlToDelete: string | undefined;
|
||||
if (photo.hidden && photo.url.includes(photo.id)) {
|
||||
@ -368,16 +374,17 @@ export const syncPhotoAction = async (photoId: string) =>
|
||||
}
|
||||
});
|
||||
|
||||
const photoFormDbInsert = convertFormDataToPhotoDbInsert({
|
||||
...formDataFromPhoto,
|
||||
...formDataFromExif,
|
||||
...!BLUR_ENABLED && { blurData: undefined },
|
||||
...!photo.title && { title: atTitle },
|
||||
...!photo.caption && { caption: aiCaption },
|
||||
...photo.tags.length === 0 && { tags: aiTags },
|
||||
...!photo.semanticDescription &&
|
||||
{ semanticDescription: aiSemanticDescription },
|
||||
});
|
||||
const photoFormDbInsert =
|
||||
await convertFormDataToPhotoDbInsertAndLookupRecipeTitle({
|
||||
...formDataFromPhoto,
|
||||
...formDataFromExif,
|
||||
...!BLUR_ENABLED && { blurData: undefined },
|
||||
...!photo.title && { title: atTitle },
|
||||
...!photo.caption && { caption: aiCaption },
|
||||
...photo.tags.length === 0 && { tags: aiTags },
|
||||
...!photo.semanticDescription &&
|
||||
{ semanticDescription: aiSemanticDescription },
|
||||
});
|
||||
|
||||
await updatePhoto(photoFormDbInsert)
|
||||
.then(async () => {
|
||||
|
||||
@ -187,7 +187,7 @@ export const insertPhoto = (photo: PhotoDbInsert) =>
|
||||
${photo.longitude},
|
||||
${photo.filmSimulation},
|
||||
${photo.recipeTitle},
|
||||
${JSON.stringify(photo.recipeData)},
|
||||
${photo.recipeData},
|
||||
${photo.priorityOrder},
|
||||
${photo.hidden},
|
||||
${photo.takenAt},
|
||||
@ -221,7 +221,7 @@ export const updatePhoto = (photo: PhotoDbInsert) =>
|
||||
longitude=${photo.longitude},
|
||||
film_simulation=${photo.filmSimulation},
|
||||
recipe_title=${photo.recipeTitle},
|
||||
recipe_data=${JSON.stringify(photo.recipeData)},
|
||||
recipe_data=${photo.recipeData},
|
||||
priority_order=${photo.priorityOrder || null},
|
||||
hidden=${photo.hidden},
|
||||
taken_at=${photo.takenAt},
|
||||
@ -357,6 +357,16 @@ export const getUniqueRecipes = async () =>
|
||||
})))
|
||||
, 'getUniqueRecipes');
|
||||
|
||||
export const getRecipeTitleForData = async (data: string | object) =>
|
||||
safelyQueryPhotos(() => query(
|
||||
// eslint-disable-next-line max-len
|
||||
'SELECT recipe_title FROM photos WHERE hidden IS NOT TRUE AND recipe_data = $1 LIMIT 1',
|
||||
// Legacy check on escaped, string-based JSON
|
||||
[typeof data === 'string' ? data : JSON.stringify(data)],
|
||||
)
|
||||
.then(({ rows }) => rows[0]?.recipe_title as string | undefined)
|
||||
, 'getRecipeTitleForData');
|
||||
|
||||
export const getUniqueFocalLengths = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT focal_length, COUNT(*)
|
||||
|
||||
@ -144,7 +144,10 @@ export const parsePhotoFromDb = (photoDbRaw: PhotoDb): Photo => {
|
||||
exposureCompensationFormatted:
|
||||
formatExposureCompensation(photoDb.exposureCompensation),
|
||||
recipeData: photoDb.recipeData
|
||||
? JSON.parse(photoDb.recipeData)
|
||||
// Legacy check on escaped, string-based JSON
|
||||
? typeof photoDb.recipeData === 'string'
|
||||
? JSON.parse(photoDb.recipeData)
|
||||
: photoDb.recipeData
|
||||
: undefined,
|
||||
takenAtNaiveFormatted:
|
||||
formatDateFromPostgresString(photoDb.takenAtNaive),
|
||||
|
||||
@ -2,7 +2,10 @@ import {
|
||||
getExtensionFromStorageUrl,
|
||||
getIdFromStorageUrl,
|
||||
} from '@/platforms/storage';
|
||||
import { convertExifToFormData } from '@/photo/form';
|
||||
import {
|
||||
convertExifToFormData,
|
||||
convertFormDataToPhotoDbInsert,
|
||||
} from '@/photo/form';
|
||||
import {
|
||||
getFujifilmSimulationFromMakerNote,
|
||||
} from '@/platforms/fujifilm/simulation';
|
||||
@ -19,6 +22,7 @@ import {
|
||||
FujifilmRecipe,
|
||||
getFujifilmRecipeFromMakerNote,
|
||||
} from '@/platforms/fujifilm/recipe';
|
||||
import { getRecipeTitleForData } from './db/query';
|
||||
const IMAGE_WIDTH_RESIZE = 200;
|
||||
const IMAGE_WIDTH_BLUR = 200;
|
||||
|
||||
@ -192,3 +196,18 @@ export const removeGpsData = async (image: ArrayBuffer) =>
|
||||
})
|
||||
.toFormat('jpeg', { quality: PRESERVE_ORIGINAL_UPLOADS ? 95 : 80 })
|
||||
.toBuffer();
|
||||
|
||||
export const convertFormDataToPhotoDbInsertAndLookupRecipeTitle =
|
||||
async (...args: Parameters<typeof convertFormDataToPhotoDbInsert>):
|
||||
Promise<ReturnType<typeof convertFormDataToPhotoDbInsert>> => {
|
||||
const photo = convertFormDataToPhotoDbInsert(...args);
|
||||
|
||||
if (photo.recipeData && !photo.recipeTitle) {
|
||||
const recipeTitle = await getRecipeTitleForData(photo.recipeData);
|
||||
if (recipeTitle) {
|
||||
photo.recipeTitle = recipeTitle;
|
||||
}
|
||||
}
|
||||
|
||||
return photo;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user