added basic AI import and improved display for external recipes

This commit is contained in:
vabene1111
2025-08-16 15:08:25 +02:00
parent 4bd3da451d
commit 86fc4aa2d0
39 changed files with 181 additions and 49 deletions

View File

@@ -1768,7 +1768,7 @@ class RecipeFromSourceResponseSerializer(serializers.Serializer):
class AiImportSerializer(serializers.Serializer): class AiImportSerializer(serializers.Serializer):
file = serializers.FileField(allow_null=True) file = serializers.FileField(allow_null=True)
text = serializers.CharField(allow_null=True, allow_blank=True) text = serializers.CharField(allow_null=True, allow_blank=True)
recipe_id = serializers.CharField(allow_null=True, allow_blank=True)
class ExportRequestSerializer(serializers.Serializer): class ExportRequestSerializer(serializers.Serializer):
type = serializers.CharField() type = serializers.CharField()

View File

@@ -1891,6 +1891,12 @@ class AiImportView(APIView):
messages = [] messages = []
uploaded_file = serializer.validated_data['file'] uploaded_file = serializer.validated_data['file']
if serializer.validated_data['recipe_id']:
if recipe := Recipe.objects.filter(id=serializer.validated_data['recipe_id']).first():
if recipe.file_path:
uploaded_file = get_recipe_provider(recipe).get_file(recipe)
if uploaded_file: if uploaded_file:
base64type = None base64type = None
try: try:

View File

@@ -1,18 +1,24 @@
<template> <template>
<v-card class="mt-1 h-100">
<iframe width="100%" height="700px" :src="externalUrl" v-if="isPdf"></iframe>
<v-img :src="externalUrl" v-if="isImage"></v-img> <v-expansion-panels v-model="panelState">
</v-card> <v-expansion-panel value="show">
<v-expansion-panel-title>{{ $t('ExternalRecipe') }}</v-expansion-panel-title>
<v-expansion-panel-text>
<v-card class="mt-1 h-100">
<iframe width="100%" height="700px" :src="externalUrl" v-if="isPdf"></iframe>
<v-img :src="externalUrl" v-if="isImage"></v-img>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, PropType} from "vue"; import {computed, onMounted, PropType, ref} from "vue";
import {Recipe} from "@/openapi"; import {Recipe} from "@/openapi";
import {useDjangoUrls} from "@/composables/useDjangoUrls"; import {useDjangoUrls} from "@/composables/useDjangoUrls";
import {useUrlSearchParams} from "@vueuse/core"; import {useUrlSearchParams} from "@vueuse/core";
const props = defineProps({ const props = defineProps({
recipe: {type: {} as PropType<Recipe>, required: true} recipe: {type: {} as PropType<Recipe>, required: true}
}) })
@@ -20,6 +26,15 @@ const props = defineProps({
const params = useUrlSearchParams('history') const params = useUrlSearchParams('history')
const {getDjangoUrl} = useDjangoUrls() const {getDjangoUrl} = useDjangoUrls()
const panelState = ref('')
onMounted(() => {
// open panel by default if recipe has not been converted to internal yet
if (!props.recipe.internal) {
panelState.value = 'show'
}
})
/** /**
* determines if the file is a PDF based on the path * determines if the file is a PDF based on the path
*/ */

View File

@@ -17,8 +17,7 @@
<recipe-image <recipe-image
max-height="25vh" max-height="25vh"
:recipe="recipe" :recipe="recipe"
v-if="recipe.internal" v-if="recipe.image != undefined">
>
</recipe-image> </recipe-image>
<v-card> <v-card>
@@ -36,31 +35,30 @@
</v-card> </v-card>
</v-card> </v-card>
<template v-if="recipe.internal"> <!-- only display values if not all are default (e.g. for external recipes) -->
<v-card class="mt-1"> <v-card class="mt-1" v-if="recipe.workingTime != 0 || recipe.waitingTime != 0 || recipe.servings != 1">
<v-container> <v-container>
<v-row class="text-center text-body-2"> <v-row class="text-center text-body-2">
<v-col class="pt-1 pb-1"> <v-col class="pt-1 pb-1">
<i class="fas fa-cogs fa-fw mr-1"></i> {{ recipe.workingTime }} min<br/> <i class="fas fa-cogs fa-fw mr-1"></i> {{ recipe.workingTime }} min<br/>
<div class="text-grey">{{ $t('WorkingTime') }}</div> <div class="text-grey">{{ $t('WorkingTime') }}</div>
</v-col> </v-col>
<v-col class="pt-1 pb-1"> <v-col class="pt-1 pb-1">
<div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ recipe.waitingTime }} min</div> <div><i class="fas fa-hourglass-half fa-fw mr-1"></i> {{ recipe.waitingTime }} min</div>
<div class="text-grey">{{ $t('WaitingTime') }}</div> <div class="text-grey">{{ $t('WaitingTime') }}</div>
</v-col> </v-col>
<v-col class="pt-1 pb-1"> <v-col class="pt-1 pb-1">
<div class="cursor-pointer"> <div class="cursor-pointer">
<i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/> <i class="fas fa-sort-numeric-up fa-fw mr-1"></i> {{ servings }} <br/>
<div class="text-grey"><span v-if="recipe.servingsText">{{ recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div> <div class="text-grey"><span v-if="recipe.servingsText">{{ recipe.servingsText }}</span><span v-else>{{ $t('Servings') }}</span></div>
<number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings"> <number-scaler-dialog :number="servings" @confirm="(s: number) => {servings = s}" title="Servings">
</number-scaler-dialog> </number-scaler-dialog>
</div> </div>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card> </v-card>
</template>
</template> </template>
<!-- Desktop horizontal layout --> <!-- Desktop horizontal layout -->
<template class="d-none d-lg-block"> <template class="d-none d-lg-block">
@@ -69,8 +67,7 @@
<recipe-image <recipe-image
:rounded="true" :rounded="true"
max-height="40vh" max-height="40vh"
:recipe="recipe" :recipe="recipe">
v-if="recipe.internal">
</recipe-image> </recipe-image>
</v-col> </v-col>
<v-col cols="4"> <v-col cols="4">
@@ -78,7 +75,8 @@
<v-card-text class="flex-grow-1"> <v-card-text class="flex-grow-1">
<div class="d-flex"> <div class="d-flex">
<h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1> <h1 class="flex-column flex-grow-1">{{ recipe.name }}</h1>
<recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated" class="flex-column mb-auto mt-2 float-right"></recipe-context-menu> <recipe-context-menu :recipe="recipe" v-if="useUserPreferenceStore().isAuthenticated"
class="flex-column mb-auto mt-2 float-right"></recipe-context-menu>
</div> </div>
<p> <p>
{{ $t('created_by') }} {{ recipe.createdBy.displayName }} ({{ DateTime.fromJSDate(recipe.createdAt).toLocaleString(DateTime.DATE_SHORT) }}) {{ $t('created_by') }} {{ recipe.createdBy.displayName }} ({{ DateTime.fromJSDate(recipe.createdAt).toLocaleString(DateTime.DATE_SHORT) }})
@@ -118,20 +116,27 @@
</v-row> </v-row>
</template> </template>
<template v-if="!recipe.internal"> <template v-if="recipe.filePath">
<external-recipe-viewer :recipe="recipe"></external-recipe-viewer> <external-recipe-viewer class="mt-2" :recipe="recipe"></external-recipe-viewer>
</template>
<template v-else>
<v-card class="mt-1" v-if="recipe.steps.length > 1 && recipe.showIngredientOverview">
<steps-overview :steps="recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
</v-card>
<v-card class="mt-1" v-for="(step, index) in recipe.steps" :key="step.id"> <v-card :title="$t('AI')" prepend-icon="$ai" @click="aiConvertRecipe()" :loading="fileApiLoading || loading" :disabled="fileApiLoading || loading"
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view> v-if="!recipe.internal">
<v-card-text>
Convert the recipe using AI
</v-card-text>
</v-card> </v-card>
</template> </template>
<property-view v-model="recipe" :servings="servings" v-if="recipe.internal"></property-view> <v-card class="mt-1" v-if="recipe.steps.length > 1 && recipe.showIngredientOverview">
<steps-overview :steps="recipe.steps" :ingredient-factor="ingredientFactor"></steps-overview>
</v-card>
<v-card class="mt-1" v-for="(step, index) in recipe.steps" :key="step.id">
<step-view v-model="recipe.steps[index]" :step-number="index+1" :ingredientFactor="ingredientFactor"></step-view>
</v-card>
<property-view v-model="recipe" :servings="servings"></property-view>
<v-card class="mt-2"> <v-card class="mt-2">
<v-card-text> <v-card-text>
@@ -181,7 +186,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue' import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'
import {Recipe} from "@/openapi" import {ApiApi, Recipe} from "@/openapi"
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue" import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue"
import StepsOverview from "@/components/display/StepsOverview.vue"; import StepsOverview from "@/components/display/StepsOverview.vue";
import RecipeActivity from "@/components/display/RecipeActivity.vue"; import RecipeActivity from "@/components/display/RecipeActivity.vue";
@@ -189,23 +194,33 @@ import RecipeContextMenu from "@/components/inputs/RecipeContextMenu.vue";
import KeywordsComponent from "@/components/display/KeywordsBar.vue"; import KeywordsComponent from "@/components/display/KeywordsBar.vue";
import RecipeImage from "@/components/display/RecipeImage.vue"; import RecipeImage from "@/components/display/RecipeImage.vue";
import ExternalRecipeViewer from "@/components/display/ExternalRecipeViewer.vue"; import ExternalRecipeViewer from "@/components/display/ExternalRecipeViewer.vue";
import {useMediaQuery, useWakeLock} from "@vueuse/core"; import {useWakeLock} from "@vueuse/core";
import StepView from "@/components/display/StepView.vue"; import StepView from "@/components/display/StepView.vue";
import {DateTime} from "luxon"; import {DateTime} from "luxon";
import PropertyView from "@/components/display/PropertyView.vue"; import PropertyView from "@/components/display/PropertyView.vue";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore.ts";
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore.ts";
import {useFileApi} from "@/composables/useFileApi.ts";
const {request, release} = useWakeLock() const {request, release} = useWakeLock()
const {doAiImport, fileApiLoading} = useFileApi()
const loading = ref(false)
const recipe = defineModel<Recipe>({required: true}) const recipe = defineModel<Recipe>({required: true})
const servings = ref(1) const servings = ref(1)
const showFullRecipeName = ref(false) const showFullRecipeName = ref(false)
/**
* factor for multiplying ingredient amounts based on recipe base servings and user selected servings
*/
const ingredientFactor = computed(() => { const ingredientFactor = computed(() => {
return servings.value / ((recipe.value.servings != undefined) ? recipe.value.servings : 1) return servings.value / ((recipe.value.servings != undefined) ? recipe.value.servings : 1)
}) })
/**
* change servings when recipe servings are changed
*/
watch(() => recipe.value.servings, () => { watch(() => recipe.value.servings, () => {
if (recipe.value.servings) { if (recipe.value.servings) {
servings.value = recipe.value.servings servings.value = recipe.value.servings
@@ -222,6 +237,42 @@ onBeforeUnmount(() => {
release() release()
}) })
/**
* converts the recipe into an internal recipe using AI
*/
function aiConvertRecipe() {
let api = new ApiApi()
doAiImport(null, '', recipe.value.id!).then(r => {
if (r.recipe) {
recipe.value.internal = true
recipe.value.steps = r.recipe.steps
recipe.value.keywords = r.recipe.keywords
recipe.value.servings = r.recipe.servings
recipe.value.servingsText = r.recipe.servingsText
recipe.value.workingTime = r.recipe.workingTime
recipe.value.waitingTime = r.recipe.waitingTime
loading.value = true
api.apiRecipeUpdate({id: recipe.value.id!, recipe: recipe.value}).then(r => {
recipe.value = r
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
loading.value = false
})
} else {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -84,8 +84,9 @@ export function useFileApi() {
* uploads the given file to the image recognition endpoint * uploads the given file to the image recognition endpoint
* @param file file object to upload * @param file file object to upload
* @param text text to import * @param text text to import
* @param recipeId id of a recipe to use as import base (for external recipes
*/ */
function doAiImport(file: File | null, text: string = '') { function doAiImport(file: File | null, text: string = '', recipeId: string = '') {
let formData = new FormData() let formData = new FormData()
if (file != null) { if (file != null) {
@@ -94,6 +95,8 @@ export function useFileApi() {
formData.append('file', '') formData.append('file', '')
} }
formData.append('text', text) formData.append('text', text)
formData.append('recipe_id', recipeId)
fileApiLoading.value = true
return fetch(getDjangoUrl(`api/ai-import/`), { return fetch(getDjangoUrl(`api/ai-import/`), {
method: 'POST', method: 'POST',

View File

@@ -80,6 +80,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "", "External": "",
"ExternalRecipe": "",
"External_Recipe_Image": "", "External_Recipe_Image": "",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "", "Failure": "",

View File

@@ -77,6 +77,7 @@
"Export_Supported": "Поддържа се експорт", "Export_Supported": "Поддържа се експорт",
"Export_To_ICal": "Експортиране на .ics", "Export_To_ICal": "Експортиране на .ics",
"External": "Външен", "External": "Външен",
"ExternalRecipe": "",
"External_Recipe_Image": "Външно изображение на рецептата", "External_Recipe_Image": "Външно изображение на рецептата",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "Неуспешно", "Failure": "Неуспешно",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "Exportació suportada", "Export_Supported": "Exportació suportada",
"Export_To_ICal": "Exportar .ics", "Export_To_ICal": "Exportar .ics",
"External": "Extern", "External": "Extern",
"ExternalRecipe": "",
"External_Recipe_Image": "Imatge externa de la recepta", "External_Recipe_Image": "Imatge externa de la recepta",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "Base de dades FDC ID", "FDC_ID_help": "Base de dades FDC ID",

View File

@@ -115,6 +115,7 @@
"Export_Supported": "Export podporován", "Export_Supported": "Export podporován",
"Export_To_ICal": "Export ovat .ics", "Export_To_ICal": "Export ovat .ics",
"External": "Externí", "External": "Externí",
"ExternalRecipe": "",
"External_Recipe_Image": "Externí obrázek receptu", "External_Recipe_Image": "Externí obrázek receptu",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "ID v databázi FDC", "FDC_ID_help": "ID v databázi FDC",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "Eksport understøttet", "Export_Supported": "Eksport understøttet",
"Export_To_ICal": "Eksporter .ics", "Export_To_ICal": "Eksporter .ics",
"External": "Ekstern", "External": "Ekstern",
"ExternalRecipe": "",
"External_Recipe_Image": "Eksternt billede af opskrift", "External_Recipe_Image": "Eksternt billede af opskrift",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "FDC database ID", "FDC_ID_help": "FDC database ID",

View File

@@ -170,6 +170,7 @@
"Export_Supported": "Exportieren wird unterstützt", "Export_Supported": "Exportieren wird unterstützt",
"Export_To_ICal": "Export als .ics", "Export_To_ICal": "Export als .ics",
"External": "Extern", "External": "Extern",
"ExternalRecipe": "Externes Rezept",
"ExternalRecipeImport": "Externer Rezeptimport", "ExternalRecipeImport": "Externer Rezeptimport",
"ExternalRecipeImportHelp": "Dateien die in überwachten Ordnern auf externen Speichern gefunden werden, werden nicht sofort als Rezept importiert, sondern zunächst als Rezeptimport zwischengespeichert. Hier können gefundene Rezepte schnell und einfach editiert werden, bevor Sie in die Sammlung aufgenommen werden ", "ExternalRecipeImportHelp": "Dateien die in überwachten Ordnern auf externen Speichern gefunden werden, werden nicht sofort als Rezept importiert, sondern zunächst als Rezeptimport zwischengespeichert. Hier können gefundene Rezepte schnell und einfach editiert werden, bevor Sie in die Sammlung aufgenommen werden ",
"ExternalStorage": "Externer Speicher", "ExternalStorage": "Externer Speicher",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "Υποστηρίζεται εξαγωγή", "Export_Supported": "Υποστηρίζεται εξαγωγή",
"Export_To_ICal": "Εξαγωγή .ics", "Export_To_ICal": "Εξαγωγή .ics",
"External": "Εξωτερική", "External": "Εξωτερική",
"ExternalRecipe": "",
"External_Recipe_Image": "Εξωτερική εικόνα συνταγής", "External_Recipe_Image": "Εξωτερική εικόνα συνταγής",
"FDC_ID": "Ταυτότητα FDC", "FDC_ID": "Ταυτότητα FDC",
"FDC_ID_help": "Ταυτότητα βάσης δεδομένων FDC", "FDC_ID_help": "Ταυτότητα βάσης δεδομένων FDC",

View File

@@ -168,6 +168,7 @@
"Export_Supported": "Export supported", "Export_Supported": "Export supported",
"Export_To_ICal": "Export .ics", "Export_To_ICal": "Export .ics",
"External": "External", "External": "External",
"ExternalRecipe": "External Recipe",
"ExternalRecipeImport": "External recipe import", "ExternalRecipeImport": "External recipe import",
"ExternalRecipeImportHelp": "Files in synced folders on external storages are not imported directly but temporarily saved as external import recipes. Here you can quickly view and edit newly found files before they are moved to the main collection. ", "ExternalRecipeImportHelp": "Files in synced folders on external storages are not imported directly but temporarily saved as external import recipes. Here you can quickly view and edit newly found files before they are moved to the main collection. ",
"ExternalStorage": "External storage", "ExternalStorage": "External storage",

View File

@@ -166,6 +166,7 @@
"Export_Supported": "Exportación soportada", "Export_Supported": "Exportación soportada",
"Export_To_ICal": "Exportar .ics", "Export_To_ICal": "Exportar .ics",
"External": "Externo", "External": "Externo",
"ExternalRecipe": "",
"ExternalRecipeImport": "Importación externa de recetas", "ExternalRecipeImport": "Importación externa de recetas",
"ExternalRecipeImportHelp": "Los archivos en carpetas sincronizadas en almacenamientos externos no se importan directamente, en su lugar son guardados temporalmente como recetas de importación externa. Aquí puedes ver y editar rápidamente los archivos recién encontrados antes de que se muevan a la colección principal. ", "ExternalRecipeImportHelp": "Los archivos en carpetas sincronizadas en almacenamientos externos no se importan directamente, en su lugar son guardados temporalmente como recetas de importación externa. Aquí puedes ver y editar rápidamente los archivos recién encontrados antes de que se muevan a la colección principal. ",
"ExternalStorage": "Almacenamiento externo", "ExternalStorage": "Almacenamiento externo",

View File

@@ -113,6 +113,7 @@
"Export_Supported": "Vienti tuettu", "Export_Supported": "Vienti tuettu",
"Export_To_ICal": "Vie .ics", "Export_To_ICal": "Vie .ics",
"External": "Ulkoinen", "External": "Ulkoinen",
"ExternalRecipe": "",
"External_Recipe_Image": "Ulkoinen reseptin kuva", "External_Recipe_Image": "Ulkoinen reseptin kuva",
"FDC_ID": "FDC -tunnus", "FDC_ID": "FDC -tunnus",
"FDC_ID_help": "FDC tietokanta tunnus", "FDC_ID_help": "FDC tietokanta tunnus",

View File

@@ -169,6 +169,7 @@
"Export_Supported": "Exportation prise en charge", "Export_Supported": "Exportation prise en charge",
"Export_To_ICal": "Exporter .ics", "Export_To_ICal": "Exporter .ics",
"External": "Externe", "External": "Externe",
"ExternalRecipe": "",
"ExternalRecipeImport": "Importation d'une recette externe", "ExternalRecipeImport": "Importation d'une recette externe",
"ExternalRecipeImportHelp": "Les fichiers des dossiers synchronisés sur des stockages externes ne sont pas importés directement, mais enregistrés temporairement comme recettes d'importation externe. Vous pouvez ainsi visualiser et modifier rapidement les fichiers nouvellement trouvés avant leur transfert vers la collection principale. ", "ExternalRecipeImportHelp": "Les fichiers des dossiers synchronisés sur des stockages externes ne sont pas importés directement, mais enregistrés temporairement comme recettes d'importation externe. Vous pouvez ainsi visualiser et modifier rapidement les fichiers nouvellement trouvés avant leur transfert vers la collection principale. ",
"ExternalStorage": "Stockage externe", "ExternalStorage": "Stockage externe",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "ייצוא נתמך", "Export_Supported": "ייצוא נתמך",
"Export_To_ICal": "ייצא .ics", "Export_To_ICal": "ייצא .ics",
"External": "חיצוני", "External": "חיצוני",
"ExternalRecipe": "",
"External_Recipe_Image": "תמונת מתכון חיצונית", "External_Recipe_Image": "תמונת מתכון חיצונית",
"FDC_ID": "מספר FDC", "FDC_ID": "מספר FDC",
"FDC_ID_help": "מספר FDC", "FDC_ID_help": "מספר FDC",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "Izvoz podržan", "Export_Supported": "Izvoz podržan",
"Export_To_ICal": "Izvoz .ics", "Export_To_ICal": "Izvoz .ics",
"External": "Vanjski", "External": "Vanjski",
"ExternalRecipe": "",
"External_Recipe_Image": "Slika vanjskog recepta", "External_Recipe_Image": "Slika vanjskog recepta",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "FDC ID baze podataka", "FDC_ID_help": "FDC ID baze podataka",

View File

@@ -102,6 +102,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "Külső", "External": "Külső",
"ExternalRecipe": "",
"External_Recipe_Image": "Külső receptkép", "External_Recipe_Image": "Külső receptkép",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "Hiba", "Failure": "Hiba",

View File

@@ -35,6 +35,7 @@
"Energy": "", "Energy": "",
"Export": "", "Export": "",
"External": "", "External": "",
"ExternalRecipe": "",
"External_Recipe_Image": "", "External_Recipe_Image": "",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Fats": "", "Fats": "",

View File

@@ -91,6 +91,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "Luar", "External": "Luar",
"ExternalRecipe": "",
"External_Recipe_Image": "Gambar Resep Eksternal", "External_Recipe_Image": "Gambar Resep Eksternal",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "Kegagalan", "Failure": "Kegagalan",

View File

@@ -115,6 +115,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "", "External": "",
"ExternalRecipe": "",
"External_Recipe_Image": "", "External_Recipe_Image": "",
"FDC_ID": "", "FDC_ID": "",
"FDC_ID_help": "", "FDC_ID_help": "",

View File

@@ -169,6 +169,7 @@
"Export_Supported": "Esportazione supportata", "Export_Supported": "Esportazione supportata",
"Export_To_ICal": "Esporta .ics", "Export_To_ICal": "Esporta .ics",
"External": "Esterna", "External": "Esterna",
"ExternalRecipe": "",
"ExternalRecipeImport": "Importa ricetta esterna", "ExternalRecipeImport": "Importa ricetta esterna",
"ExternalRecipeImportHelp": "I file nelle cartelle sincronizzate su dispositivi di archiviazione esterni non vengono importati direttamente, ma salvati temporaneamente come ricette di importazione esterne. Qui è possibile visualizzare e modificare rapidamente i file appena trovati prima che vengano spostati nella raccolta principale. ", "ExternalRecipeImportHelp": "I file nelle cartelle sincronizzate su dispositivi di archiviazione esterni non vengono importati direttamente, ma salvati temporaneamente come ricette di importazione esterne. Qui è possibile visualizzare e modificare rapidamente i file appena trovati prima che vengano spostati nella raccolta principale. ",
"ExternalStorage": "Archiviazione esterna", "ExternalStorage": "Archiviazione esterna",

View File

@@ -104,6 +104,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "", "External": "",
"ExternalRecipe": "",
"External_Recipe_Image": "Išorinis recepto vaizdas", "External_Recipe_Image": "Išorinis recepto vaizdas",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "", "Failure": "",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "", "Export_To_ICal": "",
"External": "", "External": "",
"ExternalRecipe": "",
"External_Recipe_Image": "", "External_Recipe_Image": "",
"FDC_ID": "", "FDC_ID": "",
"FDC_ID_help": "", "FDC_ID_help": "",

View File

@@ -110,6 +110,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "Eksporter .ics", "Export_To_ICal": "Eksporter .ics",
"External": "Ekstern", "External": "Ekstern",
"ExternalRecipe": "",
"External_Recipe_Image": "Bilde av ekstern oppskrift", "External_Recipe_Image": "Bilde av ekstern oppskrift",
"FDC_ID_help": "FDC database-ID", "FDC_ID_help": "FDC database-ID",
"FETCH_ERROR": "", "FETCH_ERROR": "",

View File

@@ -170,6 +170,7 @@
"Export_Supported": "Export ondersteund", "Export_Supported": "Export ondersteund",
"Export_To_ICal": "Exporteer .ics", "Export_To_ICal": "Exporteer .ics",
"External": "Externe", "External": "Externe",
"ExternalRecipe": "",
"ExternalRecipeImport": "Externe receptimport", "ExternalRecipeImport": "Externe receptimport",
"ExternalRecipeImportHelp": "Bestanden in gesynchroniseerde mappen op externe opslag worden niet direct geïmporteerd, maar tijdelijk opgeslagen als externe receptimport. Hier kun je snel nieuw gevonden bestanden bekijken en bewerken voordat ze naar de hoofdcollectie worden verplaatst. ", "ExternalRecipeImportHelp": "Bestanden in gesynchroniseerde mappen op externe opslag worden niet direct geïmporteerd, maar tijdelijk opgeslagen als externe receptimport. Hier kun je snel nieuw gevonden bestanden bekijken en bewerken voordat ze naar de hoofdcollectie worden verplaatst. ",
"ExternalStorage": "Externe opslag", "ExternalStorage": "Externe opslag",

View File

@@ -142,6 +142,7 @@
"Export_Supported": "Eksportowanie wspierane", "Export_Supported": "Eksportowanie wspierane",
"Export_To_ICal": "Eksportuj .ics", "Export_To_ICal": "Eksportuj .ics",
"External": "Zewnętrzny", "External": "Zewnętrzny",
"ExternalRecipe": "",
"External_Recipe_Image": "Zewnętrzny obraz dla przepisu", "External_Recipe_Image": "Zewnętrzny obraz dla przepisu",
"FDC_ID": "Identyfikator FDC", "FDC_ID": "Identyfikator FDC",
"FDC_ID_help": "Identyfikator bazy FDC", "FDC_ID_help": "Identyfikator bazy FDC",

View File

@@ -89,6 +89,7 @@
"Export_As_ICal": "Exportar período atual para o formato ICal", "Export_As_ICal": "Exportar período atual para o formato ICal",
"Export_To_ICal": "Exportar .ics", "Export_To_ICal": "Exportar .ics",
"External": "Externo", "External": "Externo",
"ExternalRecipe": "",
"External_Recipe_Image": "Imagem da receita externa", "External_Recipe_Image": "Imagem da receita externa",
"FDC_ID": "ID FDC", "FDC_ID": "ID FDC",
"FDC_ID_help": "ID database FDC", "FDC_ID_help": "ID database FDC",

View File

@@ -168,6 +168,7 @@
"Export_Supported": "Exportação suportada", "Export_Supported": "Exportação suportada",
"Export_To_ICal": "Exportar .ics", "Export_To_ICal": "Exportar .ics",
"External": "Externo", "External": "Externo",
"ExternalRecipe": "",
"ExternalRecipeImport": "Importar receita externa", "ExternalRecipeImport": "Importar receita externa",
"ExternalRecipeImportHelp": "Arquivos em pastas sincronizadas em armazenamentos externos não são importados diretamente, mas salvos temporariamente como receitas de importação externa. Aqui, você pode visualizar e editar rapidamente os arquivos recém-encontrados antes que eles sejam movidos para a coleção principal. ", "ExternalRecipeImportHelp": "Arquivos em pastas sincronizadas em armazenamentos externos não são importados diretamente, mas salvos temporariamente como receitas de importação externa. Aqui, você pode visualizar e editar rapidamente os arquivos recém-encontrados antes que eles sejam movidos para a coleção principal. ",
"ExternalStorage": "Armazenamento externo", "ExternalStorage": "Armazenamento externo",

View File

@@ -98,6 +98,7 @@
"Export_Supported": "Export compatibil", "Export_Supported": "Export compatibil",
"Export_To_ICal": "Exportă .ics", "Export_To_ICal": "Exportă .ics",
"External": "Extern", "External": "Extern",
"ExternalRecipe": "",
"External_Recipe_Image": "Imagine rețetă externă", "External_Recipe_Image": "Imagine rețetă externă",
"FETCH_ERROR": "", "FETCH_ERROR": "",
"Failure": "Eșec", "Failure": "Eșec",

View File

@@ -169,6 +169,7 @@
"Export_Supported": "Экспорт поддерживается", "Export_Supported": "Экспорт поддерживается",
"Export_To_ICal": "Экспортировать .ics", "Export_To_ICal": "Экспортировать .ics",
"External": "Внешний", "External": "Внешний",
"ExternalRecipe": "",
"ExternalRecipeImport": "Импорт внешних рецептов", "ExternalRecipeImport": "Импорт внешних рецептов",
"ExternalRecipeImportHelp": "Файлы в синхронизируемых папках на внешних хранилищах не импортируются напрямую, а временно сохраняются как рецепты внешнего импорта. Здесь вы можете быстро просмотреть и отредактировать найденные файлы перед тем, как они будут перемещены в основную коллекцию. ", "ExternalRecipeImportHelp": "Файлы в синхронизируемых папках на внешних хранилищах не импортируются напрямую, а временно сохраняются как рецепты внешнего импорта. Здесь вы можете быстро просмотреть и отредактировать найденные файлы перед тем, как они будут перемещены в основную коллекцию. ",
"ExternalStorage": "Внешнее хранилище", "ExternalStorage": "Внешнее хранилище",

View File

@@ -169,6 +169,7 @@
"Export_Supported": "Izvoz podprt", "Export_Supported": "Izvoz podprt",
"Export_To_ICal": "Izvoz.ics", "Export_To_ICal": "Izvoz.ics",
"External": "Zunanje", "External": "Zunanje",
"ExternalRecipe": "",
"ExternalRecipeImport": "Uvoz zunanjih receptov", "ExternalRecipeImport": "Uvoz zunanjih receptov",
"ExternalRecipeImportHelp": "Datoteke v sinhroniziranih mapah na zunanjih shrambah se ne uvozijo neposredno, temveč se začasno shranijo kot recepti za zunanji uvoz. Tukaj si lahko hitro ogledate in uredite novo najdene datoteke, preden jih premaknete v glavno zbirko. ", "ExternalRecipeImportHelp": "Datoteke v sinhroniziranih mapah na zunanjih shrambah se ne uvozijo neposredno, temveč se začasno shranijo kot recepti za zunanji uvoz. Tukaj si lahko hitro ogledate in uredite novo najdene datoteke, preden jih premaknete v glavno zbirko. ",
"ExternalStorage": "Zunanji pomnilnik", "ExternalStorage": "Zunanji pomnilnik",

View File

@@ -153,6 +153,7 @@
"Export_Supported": "Export stöds", "Export_Supported": "Export stöds",
"Export_To_ICal": "Exportera .ics", "Export_To_ICal": "Exportera .ics",
"External": "Extern", "External": "Extern",
"ExternalRecipe": "",
"External_Recipe_Image": "Extern receptbild", "External_Recipe_Image": "Extern receptbild",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "FDC databas ID", "FDC_ID_help": "FDC databas ID",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "Desteklenen Dışa Aktarma", "Export_Supported": "Desteklenen Dışa Aktarma",
"Export_To_ICal": ".ics olarak dışa aktar", "Export_To_ICal": ".ics olarak dışa aktar",
"External": "Harici", "External": "Harici",
"ExternalRecipe": "",
"External_Recipe_Image": "Harici Tarif Resim", "External_Recipe_Image": "Harici Tarif Resim",
"FDC_ID": "FDC Kimlik", "FDC_ID": "FDC Kimlik",
"FDC_ID_help": "FDC veritabanı Kimlik", "FDC_ID_help": "FDC veritabanı Kimlik",

View File

@@ -99,6 +99,7 @@
"Export_Supported": "", "Export_Supported": "",
"Export_To_ICal": "Експортувати .ics", "Export_To_ICal": "Експортувати .ics",
"External": "Зовнішній", "External": "Зовнішній",
"ExternalRecipe": "",
"External_Recipe_Image": "Зображення Зовнішнього Рецепту", "External_Recipe_Image": "Зображення Зовнішнього Рецепту",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "Ідентифікатор Бази FDC", "FDC_ID_help": "Ідентифікатор Бази FDC",

View File

@@ -116,6 +116,7 @@
"Export_Supported": "导出支持", "Export_Supported": "导出支持",
"Export_To_ICal": "导出 .ics", "Export_To_ICal": "导出 .ics",
"External": "外部", "External": "外部",
"ExternalRecipe": "",
"External_Recipe_Image": "外部食谱图像", "External_Recipe_Image": "外部食谱图像",
"FDC_ID": "FDC ID", "FDC_ID": "FDC ID",
"FDC_ID_help": "FDC数据库ID", "FDC_ID_help": "FDC数据库ID",

View File

@@ -168,6 +168,7 @@
"Export_Supported": "支援匯出", "Export_Supported": "支援匯出",
"Export_To_ICal": "匯出到 iCal", "Export_To_ICal": "匯出到 iCal",
"External": "外部", "External": "外部",
"ExternalRecipe": "",
"ExternalRecipeImport": "外部食譜匯入", "ExternalRecipeImport": "外部食譜匯入",
"ExternalRecipeImportHelp": "外部儲存同步資料夾中的檔案不會直接匯入,而是暫時儲存為外部匯入食譜。在這裡您可以快速檢視和編輯新發現的檔案,然後再將它們移至主要收藏。 ", "ExternalRecipeImportHelp": "外部儲存同步資料夾中的檔案不會直接匯入,而是暫時儲存為外部匯入食譜。在這裡您可以快速檢視和編輯新發現的檔案,然後再將它們移至主要收藏。 ",
"ExternalStorage": "外部儲存", "ExternalStorage": "外部儲存",

View File

@@ -479,6 +479,7 @@ export interface ApiAccessTokenUpdateRequest {
export interface ApiAiImportCreateRequest { export interface ApiAiImportCreateRequest {
file: string | null; file: string | null;
text: string | null; text: string | null;
recipeId: string | null;
} }
export interface ApiAutoPlanCreateRequest { export interface ApiAutoPlanCreateRequest {
@@ -741,6 +742,7 @@ export interface ApiGroupRetrieveRequest {
export interface ApiImportCreateRequest { export interface ApiImportCreateRequest {
file: string | null; file: string | null;
text: string | null; text: string | null;
recipeId: string | null;
} }
export interface ApiImportLogCreateRequest { export interface ApiImportLogCreateRequest {
@@ -2087,6 +2089,13 @@ export class ApiApi extends runtime.BaseAPI {
); );
} }
if (requestParameters['recipeId'] == null) {
throw new runtime.RequiredError(
'recipeId',
'Required parameter "recipeId" was null or undefined when calling apiAiImportCreate().'
);
}
const queryParameters: any = {}; const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
@@ -2117,6 +2126,10 @@ export class ApiApi extends runtime.BaseAPI {
formParams.append('text', requestParameters['text'] as any); formParams.append('text', requestParameters['text'] as any);
} }
if (requestParameters['recipeId'] != null) {
formParams.append('recipe_id', requestParameters['recipeId'] as any);
}
const response = await this.request({ const response = await this.request({
path: `/api/ai-import/`, path: `/api/ai-import/`,
method: 'POST', method: 'POST',
@@ -4425,6 +4438,13 @@ export class ApiApi extends runtime.BaseAPI {
); );
} }
if (requestParameters['recipeId'] == null) {
throw new runtime.RequiredError(
'recipeId',
'Required parameter "recipeId" was null or undefined when calling apiImportCreate().'
);
}
const queryParameters: any = {}; const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
@@ -4455,6 +4475,10 @@ export class ApiApi extends runtime.BaseAPI {
formParams.append('text', requestParameters['text'] as any); formParams.append('text', requestParameters['text'] as any);
} }
if (requestParameters['recipeId'] != null) {
formParams.append('recipe_id', requestParameters['recipeId'] as any);
}
const response = await this.request({ const response = await this.request({
path: `/api/import/`, path: `/api/import/`,
method: 'POST', method: 'POST',