Pre-populate upload form with AI data
This commit is contained in:
parent
47fe1cf383
commit
e9d3c19c40
@ -15,6 +15,7 @@ import {
|
||||
import ErrorNote from '@/components/ErrorNote';
|
||||
import { getRecipeTitleForData } from '@/photo/query';
|
||||
import { getAlbumsWithMeta } from '@/album/query';
|
||||
import { addAiTextToFormData } from '@/photo/ai/server';
|
||||
|
||||
export const maxDuration = 60;
|
||||
|
||||
@ -29,7 +30,7 @@ export default async function UploadPage({ params, searchParams }: Params) {
|
||||
|
||||
const {
|
||||
blobId,
|
||||
formDataFromExif,
|
||||
formDataFromExif: _formDataFromExif,
|
||||
imageResizedBase64: imageThumbnailBase64,
|
||||
shouldStripGpsData,
|
||||
error,
|
||||
@ -40,7 +41,7 @@ export default async function UploadPage({ params, searchParams }: Params) {
|
||||
});
|
||||
|
||||
const isDataMissing =
|
||||
!formDataFromExif ||
|
||||
!_formDataFromExif ||
|
||||
(AI_CONTENT_GENERATION_ENABLED && !imageThumbnailBase64);
|
||||
|
||||
if (isDataMissing && !error) {
|
||||
@ -54,17 +55,22 @@ export default async function UploadPage({ params, searchParams }: Params) {
|
||||
uniqueRecipes,
|
||||
uniqueFilms,
|
||||
recipeTitle,
|
||||
formDataFromExif,
|
||||
] = await Promise.all([
|
||||
getAlbumsWithMeta(),
|
||||
getUniqueTagsCached(),
|
||||
getUniqueRecipesCached(),
|
||||
getUniqueFilmsCached(),
|
||||
formDataFromExif?.recipeData && formDataFromExif.film
|
||||
_formDataFromExif?.recipeData && _formDataFromExif.film
|
||||
? getRecipeTitleForData(
|
||||
formDataFromExif.recipeData,
|
||||
formDataFromExif.film,
|
||||
_formDataFromExif.recipeData,
|
||||
_formDataFromExif.film,
|
||||
)
|
||||
: undefined,
|
||||
addAiTextToFormData(
|
||||
_formDataFromExif,
|
||||
imageThumbnailBase64,
|
||||
),
|
||||
]);
|
||||
|
||||
const hasAiTextGeneration = AI_CONTENT_GENERATION_ENABLED;
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
generateLocalNaivePostgresString,
|
||||
generateLocalPostgresString,
|
||||
} from '@/utility/date';
|
||||
import { pathForAdminUploadUrl } from '@/app/path';
|
||||
import { PATH_ADMIN_PHOTOS } from '@/app/path';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { ComponentProps, useState } from 'react';
|
||||
import IconAddUpload from '@/components/icons/IconAddUpload';
|
||||
@ -42,7 +42,7 @@ export default function AddUploadButton({
|
||||
})
|
||||
.then(() => {
|
||||
if (shouldRedirectToAdminPhotos) {
|
||||
router.push(pathForAdminUploadUrl(url));
|
||||
router.push(PATH_ADMIN_PHOTOS);
|
||||
} else {
|
||||
onAddFinish?.(true);
|
||||
setIsAddingLocal(false);
|
||||
|
||||
@ -2,9 +2,12 @@ import { generateOpenAiImageQuery } from '@/platforms/openai';
|
||||
import {
|
||||
AiAutoGeneratedField,
|
||||
getAiImageQuery,
|
||||
getAiTextFieldsToGenerate,
|
||||
parseTitleAndCaption,
|
||||
} from '.';
|
||||
import { getUniqueTags } from '@/photo/query';
|
||||
import { AI_TEXT_AUTO_GENERATED_FIELDS } from '@/app/config';
|
||||
import { PhotoFormData } from '../form';
|
||||
|
||||
export const generateAiImageQueries = async (
|
||||
imageBase64?: string,
|
||||
@ -26,53 +29,63 @@ export const generateAiImageQueries = async (
|
||||
|
||||
try {
|
||||
if (imageBase64) {
|
||||
if (
|
||||
const shouldGenerateTitleAndCaption =
|
||||
textFieldsToGenerate.includes('title') &&
|
||||
textFieldsToGenerate.includes('caption')
|
||||
) {
|
||||
const titleAndCaption = await generateOpenAiImageQuery(
|
||||
textFieldsToGenerate.includes('caption');
|
||||
const shouldGenerateTitle =
|
||||
!shouldGenerateTitleAndCaption &&
|
||||
textFieldsToGenerate.includes('title');
|
||||
const shouldGenerateCaption =
|
||||
!shouldGenerateTitleAndCaption &&
|
||||
textFieldsToGenerate.includes('caption');
|
||||
const shouldGenerateTags = textFieldsToGenerate.includes('tags');
|
||||
const shouldGenerateSemantic = textFieldsToGenerate.includes('semantic');
|
||||
|
||||
const [
|
||||
titleAndCaption,
|
||||
_title,
|
||||
_caption,
|
||||
_tags,
|
||||
_semanticDescription,
|
||||
] = await Promise.all([
|
||||
shouldGenerateTitleAndCaption ? generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('title-and-caption'),
|
||||
isBatch,
|
||||
);
|
||||
if (titleAndCaption) {
|
||||
const titleAndCaptionParsed = parseTitleAndCaption(titleAndCaption);
|
||||
title = titleAndCaptionParsed.title;
|
||||
caption = titleAndCaptionParsed.caption;
|
||||
}
|
||||
} else {
|
||||
if (textFieldsToGenerate.includes('title')) {
|
||||
title = await generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('title', undefined, existingTitle),
|
||||
isBatch,
|
||||
);
|
||||
}
|
||||
if (textFieldsToGenerate.includes('caption')) {
|
||||
caption = await generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('caption'),
|
||||
isBatch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (textFieldsToGenerate.includes('tags')) {
|
||||
const existingTags = await getUniqueTags();
|
||||
tags = await generateOpenAiImageQuery(
|
||||
): undefined,
|
||||
shouldGenerateTitle ? generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('tags', existingTags),
|
||||
getAiImageQuery('title', undefined, existingTitle),
|
||||
isBatch,
|
||||
);
|
||||
}
|
||||
|
||||
if (textFieldsToGenerate.includes('semantic')) {
|
||||
semanticDescription = await generateOpenAiImageQuery(
|
||||
): undefined,
|
||||
shouldGenerateCaption ? generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('caption'),
|
||||
isBatch,
|
||||
): undefined,
|
||||
shouldGenerateTags ? getUniqueTags()
|
||||
.then(existingTags => generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('tags', existingTags),
|
||||
isBatch,
|
||||
)): undefined,
|
||||
shouldGenerateSemantic ? generateOpenAiImageQuery(
|
||||
imageBase64,
|
||||
getAiImageQuery('description-small'),
|
||||
isBatch,
|
||||
);
|
||||
): undefined,
|
||||
]);
|
||||
|
||||
if (titleAndCaption) {
|
||||
const titleAndCaptionParsed = parseTitleAndCaption(titleAndCaption);
|
||||
title = titleAndCaptionParsed.title;
|
||||
caption = titleAndCaptionParsed.caption;
|
||||
} else {
|
||||
title = _title;
|
||||
caption = _caption;
|
||||
}
|
||||
tags = _tags;
|
||||
semanticDescription = _semanticDescription;
|
||||
}
|
||||
} catch (e: any) {
|
||||
error = e.message;
|
||||
@ -87,3 +100,34 @@ export const generateAiImageQueries = async (
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export const addAiTextToFormData = async (
|
||||
formData: Partial<PhotoFormData> = {},
|
||||
imageBase64?: string,
|
||||
title?: string,
|
||||
tags?: string,
|
||||
): Promise<Partial<PhotoFormData>> => {
|
||||
const {
|
||||
title: aiTitle,
|
||||
caption: aiCaption,
|
||||
tags: aiTags,
|
||||
semanticDescription,
|
||||
} = await generateAiImageQueries(
|
||||
imageBase64,
|
||||
getAiTextFieldsToGenerate(
|
||||
AI_TEXT_AUTO_GENERATED_FIELDS,
|
||||
Boolean(title || formData?.title),
|
||||
Boolean(formData?.caption),
|
||||
Boolean(tags || formData?.tags),
|
||||
),
|
||||
title || formData?.title,
|
||||
);
|
||||
|
||||
return {
|
||||
...formData,
|
||||
title: formData?.title || aiTitle,
|
||||
caption: formData?.caption || aiCaption,
|
||||
tags: formData?.tags || aiTags,
|
||||
semanticDescription,
|
||||
};
|
||||
};
|
||||
|
||||
@ -6,8 +6,8 @@ import { AI_AUTO_GENERATED_FIELDS_ALL, AiAutoGeneratedField } from '.';
|
||||
export type AiContent = ReturnType<typeof useAiImageQueries>;
|
||||
|
||||
export default function useAiImageQueries(
|
||||
textFieldsToAutoGenerate: AiAutoGeneratedField[] = [],
|
||||
imageBase64?: string,
|
||||
textFieldsToAutoGenerate: AiAutoGeneratedField[] = [],
|
||||
) {
|
||||
const [
|
||||
requestTitleCaption,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { PhotoFormData, formHasExistingAiTextContent } from '.';
|
||||
import useAiImageQueries from '../ai/useAiImageQueries';
|
||||
import { AiAutoGeneratedField, getAiTextFieldsToGenerate } from '../ai';
|
||||
import { AiAutoGeneratedField } from '../ai';
|
||||
|
||||
export default function usePhotoFormParent({
|
||||
photoForm,
|
||||
@ -24,24 +24,7 @@ export default function usePhotoFormParent({
|
||||
);
|
||||
}, []);
|
||||
|
||||
// Don't auto-generate titles when they can be captured from EXIF data
|
||||
const textFieldsToAutoGenerate = useMemo(() =>
|
||||
getAiTextFieldsToGenerate(
|
||||
_textFieldsToAutoGenerate,
|
||||
Boolean(photoForm?.title),
|
||||
Boolean(photoForm?.caption),
|
||||
Boolean(photoForm?.tags),
|
||||
), [
|
||||
_textFieldsToAutoGenerate,
|
||||
photoForm?.title,
|
||||
photoForm?.caption,
|
||||
photoForm?.tags,
|
||||
]);
|
||||
|
||||
const aiContent = useAiImageQueries(
|
||||
textFieldsToAutoGenerate,
|
||||
imageThumbnailBase64,
|
||||
);
|
||||
const aiContent = useAiImageQueries(imageThumbnailBase64);
|
||||
|
||||
return {
|
||||
pending,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user