mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-23 06:45:22 -07:00
Add cookn migration
This commit is contained in:
parent
6381ac4c7f
commit
d5016030b6
53 changed files with 459 additions and 2 deletions
|
@ -22,6 +22,7 @@ Mealie supports importing recipes from a few other sources besides websites. Cur
|
|||
- Recipe Keeper
|
||||
- Copy Me That
|
||||
- My Recipe Box
|
||||
- DVO Cook'n X3
|
||||
|
||||
You can access these options on your installation at the `/group/migrations` page on your installation. If you'd like to see another source added, feel free to request so on Github.
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ Mealie is a self hosted recipe manager and meal planner with a RestAPI backend a
|
|||
- Copy Me That
|
||||
- Paprika
|
||||
- Tandoor Recipes
|
||||
- DVO Cook'n X3
|
||||
- Random Meal Plan generation
|
||||
- Advanced rule configuration to fine tune random recipes
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Maaltie kan resepte van DVO Cook'n x3 invoer. \nVoer 'n kookboek of spyskaart uit in die \"Cook'n\" -formaat, hernoem die uitvoeruitbreiding na .zip en laai dan die .zip hieronder op.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "مدير الوصفة",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "يمكن لوجبة الاستيراد وصفات من DVO Cook'n X3. \nقم بتصدير كتاب طبخ أو قائمة بتنسيق \"cook'n\" ، وقم بإعادة تسمية امتداد التصدير إلى .zip ، ثم قم بتحميل .zip أدناه.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Meadie може да импортира рецепти от DVO Cook'n X3. \nЕкспортирайте готварска книга или меню във формат \"Cook'n\", преименувайте разширението за експортиране на .zip, след което качете .zip по -долу.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie pot importar receptes de RecipeKeeper. Exporta les teves receptes en format zip, després puja el fitxer .zip aquí sota."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie pot importar receptes de DVO Cook'n X3. \nExporteu un llibre de cuina o un menú en el format \"Cook'n\", canvieu el nom de l'extensió d'exportació a .zip i, a continuació, pengeu el .zip següent.",
|
||||
"title": "Dvo cuin'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie může importovat recepty z Recipe Keeper. Exportujte recepty v zip formátu, poté nahrajte .zip soubor níže."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mearie může importovat recepty z DVO Cook'n X3. \nExportujte kuchařku nebo nabídku ve formátu „Cook'n“, přejmenujte rozšíření exportu na .zip a poté nahrajte níže uvedený .ZIP.",
|
||||
"title": "Dvo Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie kan importere opskrifter fra Recipe Keeper. Eksportér dine opskrifter i zip-format, og upload derefter .zip-filen nedenfor."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie kan importere opskrifter fra Dvo Cook'n X3. \nEksporter en kogebog eller menu i formatet \"Cook'n\", omdøb eksportforlængelsen til .zip, og upload derefter .zip nedenfor.",
|
||||
"title": "Dvo Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie kann Rezepte von Recipe Keeper importieren. Exportiere deine Rezepte im ZIP-Format und lade dann unten die .zip Datei hoch.\n"
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mahlzeit kann Rezepte von DVO Cook'n x3 importieren. \nExportieren Sie ein Kochbuch oder Menü im Format \"Cook'n\", benennen Sie die Export -Erweiterung in .zip um und laden Sie den unten stehenden .zip hoch.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Το Mealie μπορεί να εισάγει συνταγές από το Recipe Keeper. Εξάγετε τις συνταγές σας σε μορφή zip, στη συνέχεια, ανεβάστε το αρχείο .zip παρακάτω."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Το Mealie μπορεί να εισάγει συνταγές από το DVO Cook'n X3. \nΕξαγωγή βιβλίου μαγειρικής ή μενού στη μορφή \"Cook'n\", μετονομάστε την επέκταση εξαγωγής σε .zip και στη συνέχεια μεταφορτώστε το .zip παρακάτω.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie can import recipes from DVO Cook'n X3. Export a cookbook or menu in the \"Cook'n\" format, rename the export extension to .zip, then upload the .zip below.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -397,6 +397,10 @@
|
|||
"description-long": "Mealie can import recipes from Tandoor. Export your data in the \"Default\" format, then upload the .zip below.",
|
||||
"title": "Tandoor Recipes"
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie can import recipes from DVO Cook'n X3. Export a cookbook or menu in the \"Cook'n\" format, rename the export extension to .zip, then upload the .zip below.",
|
||||
"title": "DVO Cook'n X3"
|
||||
},
|
||||
"recipe-data-migrations": "Recipe Data Migrations",
|
||||
"recipe-data-migrations-explanation": "Recipes can be migrated from another supported application to Mealie. This is a great way to get started with Mealie.",
|
||||
"coming-from-another-application-or-an-even-older-version-of-mealie": "Coming from another application or an even older version of Mealie? Check out migrations and see if your data can be imported.",
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie puede importar recetas de Recipe Keeper. Exporta tus recetas en formato zip y luego sube el archivo a continuación."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie puede importar recetas de Dvo Cook'n X3. \nExporte un libro de cocina o menú en el formato \"Cook'n\", cambie el nombre de la extensión de exportación a .zip, luego cargue el .zip a continuación.",
|
||||
"title": "Dvo Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie saab importida retsepte Recipe Keeper-st. Ekspordi oma retsept .zip formaadis ning lae .zip fail allolevasse kasti."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Söögikord saab importida retsepte DVO Cook'n X3 -st. \nEksportige kokaraamat või menüü \"Cook'n\" vormingus, nimetage ekspordi laiend ümber .zip, seejärel laadige .zip üles allpool.",
|
||||
"title": "Dvo Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Reseptinsäilyttäjä",
|
||||
"description-long": "Mealieen voi tuoda reseptejä reseptinsäilyttäjästä. Tuo reseptit zip-muodossa ja sitten lataa zip-tiedosto alempaa."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mearie voi tuoda reseptejä DVO Cook'n X3: lta. \nVie keittokirja tai -valikko \"Cook'n\" -muodossa, nimeä vienti laajennus .zipiksi ja lataa sitten alla oleva .zip.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie peut importer des recettes depuis Recipe Keeper. Exportez vos recettes au format Zip, puis téléversez le fichier .zip ci-dessous."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Le repas peut importer des recettes à partir de DVO Cook'n x3. \nExportez un livre de cuisine ou un menu au format \"Cook'n\", renommez l'extension d'exportation vers .zip, puis téléchargez le .zip ci-dessous.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie peut importer des recettes depuis Recipe Keeper. Exportez vos recettes au format Zip, puis téléversez le fichier .zip ci-dessous."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Le repas peut importer des recettes à partir de DVO Cook'n x3. \nExportez un livre de cuisine ou un menu au format \"Cook'n\", renommez l'extension d'exportation vers .zip, puis téléchargez le .zip ci-dessous.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie peut importer des recettes depuis Recipe Keeper. Exportez vos recettes au format Zip, puis téléversez le fichier .zip ci-dessous."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Le repas peut importer des recettes à partir de DVO Cook'n x3. \nExportez un livre de cuisine ou un menu au format \"Cook'n\", renommez l'extension d'exportation vers .zip, puis téléchargez le .zip ci-dessous.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Garda Receitas",
|
||||
"description-long": "Mealie pode importar receitas do Garda Receitas. Exporta as túas receitas en formato zip e, a continuación, carga o ficheiro .zip abaixo."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "A comida pode importar receitas de DVO Cook'n X3. \nExportar un libro de receitas ou menú no formato \"Cook'n\", cambiar o nome da extensión de exportación a .zip e logo cargar o .zip a continuación.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "שומר המתכון",
|
||||
"description-long": "Mealie יכולה לייבא מתכונים מ-Recipe Keeper. יש לייצא את המתכונים בפורמט zip ולהעלות אותו כאן."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "SELEI יכול לייבא מתכונים מ- DVO COOK'N X3. \nייצא ספר בישול או תפריט בפורמט \"Cook'n\", שנה את שם סיומת הייצוא ל- .zip, ואז העלה את ה- .zip למטה.",
|
||||
"title": "DVO COOK'N X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Obloge može uvesti recepte iz DVO Cook'n X3. \nIzvezite kuharsku knjigu ili izbornik u formatu \"Cook'n\", preimenujte ekstenziju izvoza u .zip, a zatim prenesite .zip u nastavku.",
|
||||
"title": "Dvo kuhar x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "A Mealie képes recepteket importálni a Recipe Keeperből. Exportálja a receptjeit zip formátumban, majd töltse fel a .zip fájlt az oldal alján."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "A Mealie recepteket importálhat a DVO Cook'n X3 -ból. \nExportáljon szakácskönyvet vagy menüt a \"Cook'n\" formátumban, nevezze át az export kiterjesztést .zip -re, majd töltse fel az alábbi .zip -t.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie getur flutt inn uppskriftir frá DVO Cook'n x3. \nFlyttu út matreiðslubók eða valmynd á „Cook'n“ sniði, endurnefndu útflutningsframlenginguna í .zip og hlaðið síðan upp .zipinu hér að neðan.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie può importare ricette da Recipe Keeper. Esporta le tue ricette in formato zip, quindi carica lo .zip qui sotto."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie può importare ricette da Dvo Cook'n X3. \nEsporta un libro di cucina o un menu nel formato \"Cook'n\", rinomina l'estensione di esportazione in .Zip, quindi caricare il .zip di seguito.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "MealieはRecipe Keeperからレシピをインポートできます。レシピをzip形式でエクスポートし、以下に.zipファイルをアップロードしてください。"
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "MealieはDVO Cook'n X3からレシピをインポートできます。 \n「cook'n」形式でクックブックまたはメニューをエクスポートし、エクスポート拡張子の名前を.zipに変更してから、.zipを下にアップロードします。",
|
||||
"title": "dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "레시피 보관함",
|
||||
"description-long": "Mealie는 Recipe Keeper에서 레시피를 가져올 수 있습니다. 레시피를 zip 형식으로 내보낸 다음 아래의 .zip 파일을 업로드하세요."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Sealie는 DVO Cook'n X3에서 레시피를 수입 할 수 있습니다. \nCookbook 또는 메뉴를 \"Cook'n\"형식으로 내보내고 내보내기 확장자 이름을 .zip로 바꾸고 아래 .zip을 업로드하십시오.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Maitinimas gali importuoti receptus iš „DVO Cook'n X3“. \nEksportuokite kulinarijos knygą ar meniu „Cook'n“ formatu, pervardykite eksporto plėtinį į .zip, tada įkelkite .zip žemiau.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recepšu turētājs",
|
||||
"description-long": "Mealie var importēt receptes no Recipe Keeper. Eksportējiet savas receptes zip formātā, pēc tam augšupielādējiet zemāk esošo.zip failu."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie var importēt receptes no DVO Cook'n X3. \nEksportējiet pavārgrāmatu vai izvēlni formātā \"Cook'n\", pārdēvējiet eksporta paplašinājumu uz .zip, pēc tam augšupielādējiet zemāk esošo .zip.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie kan recepten importeren van Recipe Keeper. Exporteer de recepten als .zip. Dat bestand kan je hier dan uploaden."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Maaltijd kan recepten importeren van DVO Cook'n X3. \nExporteer een kookboek of menu in het formaat \"Cook'n\", hernoem de exportuitbreiding naar .zip en upload vervolgens de .zip hieronder.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Meali kan importere oppskrifter fra Recipe Keeper. Eksporter oppskrifter i zip-format, og last deretter opp .zip-filen nedenfor."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie kan importere oppskrifter fra DVO Cook'n X3. \nEksporter en kokebok eller meny i \"Cook'n\" -formatet, gi nytt navn til eksportforlengelsen til .zip, og last deretter opp .zip nedenfor.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie może importować przepisy z Recipe Keeper. Eksportuj przepisy w formacie zip, a następnie prześlij plik .zip poniżej."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Posiłek może importować przepisy z DVO Cook'N x3. \nWyeksportuj książkę kucharską lub menu w formacie „Cook'n”, zmień nazwę rozszerzenia eksportu na .zip, a następnie prześlij .zip poniżej.",
|
||||
"title": "DVO Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie pode importar receitas do Recipe Keeper. Exporte suas receitas no formato ZIP, então envie o arquivo abaixo."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "O Mealie pode importar receitas do DVO Cook'n X3. \nExport um livro de receitas ou menu no formato \"Cook'n\", renomeie a extensão de exportação para .zip e envie o .zip abaixo.",
|
||||
"title": "Dvo Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "O Mealie pode importar receitas do Recipe Keeper. Exporte as suas receitas em formato zip e, em seguida, carregue o ficheiro .zip abaixo."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "O Mealie pode importar receitas do DVO Cook'n X3. \nExport um livro de receitas ou menu no formato \"Cook'n\", renomeie a extensão de exportação para .zip e envie o .zip abaixo.",
|
||||
"title": "Dvo Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Oraganizator de rețete",
|
||||
"description-long": "Mealie poate importa rețete din \"Recipe Keeper\". Exportă rețetele tale în format ZIP, apoi încarcă fișierul .zip mai jos."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie poate importa rețete de la DVO Cook'n X3. \nExportați o carte de bucate sau un meniu în formatul „Cook'n”, redenumiți extensia de export în .zip, apoi încărcați .zip de mai jos.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie может импортировать рецепты из DVO Cook'n x3. \nЭкспортируйте поваренную книгу или меню в формате «Cook'n», переименовать расширение экспорта в .zip, затем загрузите .zip ниже.",
|
||||
"title": "Dvo Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Strážca receptov",
|
||||
"description-long": "Mealie dokáže importovať recepty z Recipe Keeper. Exportujte recepty vo formáte Zip a nižšie nahrajte zip súbor."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "SOMEIE môže importovať recepty z DVO Cook'n X3. \nExportujte kuchárku alebo ponuku vo formáte „Cook'n“, premenujte rozšírenie exportu na .zip a potom nahrajte nižšie.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie lahko uvozi recepte iz Recipe Keeper. Izvozi svoje recepte v .zip formatu, nato spodaj naloži .zip datoteko."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Meanie lahko uvozi recepte iz DVO Cook'n X3. \nIzvozite kuharsko knjigo ali meni v obliki \"Cook'n\", preimenujte razširitev izvoza v .zip in nato naložite spodaj .zip.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie can import recipes from DVO Cook'n X3. Export a cookbook or menu in the \"Cook'n\" format, rename the export extension to .zip, then upload the .zip below.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie kan importera recept från Recept Keeper. Exportera dina recept i zip-format, ladda sedan upp .zip-filen här under."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie kan importera recept från DVO Cook'n X3. \nExportera en kokbok eller meny i formatet \"Cook'n\", byta namn på exportförlängningen till .zip och ladda sedan upp .zip nedan.",
|
||||
"title": "Dvo Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie tarifleri Recipe Keeper'dan içe aktarabilir. Tariflerinizi zip formatında dışa aktarın, daha sonra .zip dosyasını aşağıdan yükleyin."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie, DVO Cook'n X3'ten tarifleri içe aktarabilir. \n\"Cook'n\" biçiminde bir yemek kitabı veya menüsü dışa aktarın, dışa aktarma uzantısını .zip'e yeniden adlandırın, ardından aşağıdaki .zip'i yükleyin.",
|
||||
"title": "Dvo Cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie може імпортувати рецепти з Recipe Keeper. Експортувати ваші рецепти у форматі zip, а потім відвантажте файл .zip нижче."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mearie може імпортувати рецепти з DVO Cook'n x3. \nЕкспортуйте кулінарну книгу або меню у форматі \"Cook'n\", перейменуйте розширення експорту на .zip, а потім завантажте .zip нижче.",
|
||||
"title": "Dvo cook'n x3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "Mealie có thể nhập công thức nấu ăn từ DVO Cook'n X3. \nXuất một cuốn sách nấu ăn hoặc menu ở định dạng \"Cook'n\", đổi tên phần mở rộng xuất sang .zip, sau đó tải lên .zip bên dưới.",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "食谱保存者",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "用餐可以从DVO Cook'n X3进口食谱。\n以“库克”格式导出食谱或菜单,将导出扩展名重命名为.zip,然后在下面上传.zip。",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -419,6 +419,10 @@
|
|||
"recipekeeper": {
|
||||
"title": "Recipe Keeper",
|
||||
"description-long": "Mealie can import recipes from Recipe Keeper. Export your recipes in zip format, then upload the .zip file below."
|
||||
},
|
||||
"cookn": {
|
||||
"description-long": "用餐可以從DVO Cook'n X3進口食譜。\n以“庫克”格式導出食譜或菜單,將導出擴展名重命名為.zip,然後在下面上傳.zip。",
|
||||
"title": "DVO Cook'n X3"
|
||||
}
|
||||
},
|
||||
"new-recipe": {
|
||||
|
|
|
@ -14,7 +14,8 @@ export type SupportedMigrations =
|
|||
| "tandoor"
|
||||
| "plantoeat"
|
||||
| "myrecipebox"
|
||||
| "recipekeeper";
|
||||
| "recipekeeper"
|
||||
| "cookn";
|
||||
|
||||
export interface CreateGroupPreferences {
|
||||
privateGroup?: boolean;
|
||||
|
|
|
@ -84,6 +84,7 @@ const MIGRATIONS = {
|
|||
plantoeat: "plantoeat",
|
||||
recipekeeper: "recipekeeper",
|
||||
tandoor: "tandoor",
|
||||
cookn: "cookn"
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -140,6 +141,10 @@ export default defineComponent({
|
|||
text: i18n.tc("migration.tandoor.title"),
|
||||
value: MIGRATIONS.tandoor,
|
||||
},
|
||||
{
|
||||
text: i18n.tc("migration.cookn.title"),
|
||||
value: MIGRATIONS.cookn,
|
||||
},
|
||||
];
|
||||
const _content = {
|
||||
[MIGRATIONS.mealie]: {
|
||||
|
@ -372,6 +377,35 @@ export default defineComponent({
|
|||
}
|
||||
],
|
||||
},
|
||||
[MIGRATIONS.cookn]: {
|
||||
text: i18n.tc("migration.cookn.description-long"),
|
||||
acceptedFileType: ".zip",
|
||||
tree: [
|
||||
{
|
||||
id: 1,
|
||||
icon: $globals.icons.zip,
|
||||
name: "cookn.zip",
|
||||
children: [
|
||||
{ id: 2, name: "temp_brand.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 3, name: "temp_chapter_desc.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 4, name: "temp_chapter.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 5, name: "temp_cookBook_desc.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 6, name: "temp_cookBook.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 7, name: "temp_food_brand.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 8, name: "temp_food_group.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 9, name: "temp_food.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 10, name: "temp_ingrediant.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 11, name: "temp_media.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 12, name: "temp_nutrient.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 13, name: "temp_recipe_desc.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 13, name: "temp_recipe.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 13, name: "temp_unit_equivalent.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 13, name: "temp_unit.dsv", icon: $globals.icons.codeJson },
|
||||
{ id: 13, name: "images", icon: $globals.icons.fileImage },
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
function setFileObject(fileObject: File) {
|
||||
|
|
|
@ -11,6 +11,7 @@ from mealie.schema.reports.reports import ReportSummary
|
|||
from mealie.services.migrations import (
|
||||
BaseMigrator,
|
||||
ChowdownMigrator,
|
||||
CooknMigrator,
|
||||
CopyMeThatMigrator,
|
||||
MealieAlphaMigrator,
|
||||
MyRecipeBoxMigrator,
|
||||
|
@ -59,6 +60,7 @@ class GroupMigrationController(BaseUserController):
|
|||
SupportedMigrations.plantoeat: PlanToEatMigrator,
|
||||
SupportedMigrations.myrecipebox: MyRecipeBoxMigrator,
|
||||
SupportedMigrations.recipekeeper: RecipeKeeperMigrator,
|
||||
SupportedMigrations.cookn: CooknMigrator,
|
||||
}
|
||||
|
||||
constructor = table.get(migration_type, None)
|
||||
|
|
|
@ -13,6 +13,7 @@ class SupportedMigrations(str, enum.Enum):
|
|||
plantoeat = "plantoeat"
|
||||
myrecipebox = "myrecipebox"
|
||||
recipekeeper = "recipekeeper"
|
||||
cookn = "cookn"
|
||||
|
||||
|
||||
class DataMigrationCreate(MealieModel):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from .chowdown import *
|
||||
from .cookn import *
|
||||
from .copymethat import *
|
||||
from .mealie_alpha import *
|
||||
from .myrecipebox import *
|
||||
|
|
239
mealie/services/migrations/cookn.py
Normal file
239
mealie/services/migrations/cookn.py
Normal file
|
@ -0,0 +1,239 @@
|
|||
import os
|
||||
import tempfile
|
||||
import zipfile
|
||||
from fractions import Fraction
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import RecipeIngredientBase
|
||||
|
||||
from ._migration_base import BaseMigrator
|
||||
from .utils.migration_helpers import import_image
|
||||
|
||||
|
||||
def _format_time(minutes: int) -> str:
|
||||
"""Formats time from minutes to a human-readable string."""
|
||||
hours, minutes = divmod(minutes, 60)
|
||||
parts = []
|
||||
if hours:
|
||||
parts.append(f"{hours} hour{'s' if hours > 1 else ''}")
|
||||
if minutes:
|
||||
parts.append(f"{minutes} minute{'s' if minutes > 1 else ''}")
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
def convert_to_float(value):
|
||||
try:
|
||||
value = value.strip() # Remove any surrounding spaces
|
||||
if " " in value: # Check for mixed fractions like "1 1/2"
|
||||
# Split into whole number and fraction
|
||||
whole, fraction = value.split(" ", 1)
|
||||
return float(whole) + float(Fraction(fraction))
|
||||
return float(Fraction(value)) # Convert fraction or whole number
|
||||
except (ValueError, ZeroDivisionError):
|
||||
return None # Return None for invalid values
|
||||
|
||||
|
||||
def extract_instructions(instructions: str) -> list[str]:
|
||||
"""Splits the instruction text into steps."""
|
||||
return instructions.split("\n") if instructions else []
|
||||
|
||||
|
||||
# def extract_ingredients(ingredient_data: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
# """Extracts ingredient details from parsed Cook'n ingredient data."""
|
||||
# ingredients = []
|
||||
# for ingredient in ingredient_data:
|
||||
# base_ingredient = RecipeIngredientBase(
|
||||
# quantity=ingredient.get("AMOUNT_QTY", "1"),
|
||||
# unit=ingredient.get("AMOUNT_UNIT"),
|
||||
# food=ingredient.get("INGREDIENT_FOOD_ID"),
|
||||
# )
|
||||
# ingredients.append({"title": None, "note": base_ingredient.display})
|
||||
# return ingredients
|
||||
|
||||
|
||||
class DSVParser:
|
||||
def __init__(self, directory: Path):
|
||||
self.directory = directory
|
||||
self.tables: dict[str, list[dict[str, Any]]] = {}
|
||||
self.load_files()
|
||||
|
||||
def load_files(self):
|
||||
"""Loads all .dsv files from the directory into lists of dictionaries."""
|
||||
for file in self.directory.glob("*.dsv"):
|
||||
with open(file, "rb") as f:
|
||||
file_contents = f.read().decode("utf-8", errors="ignore")
|
||||
|
||||
# Replace unique delimiters
|
||||
file_contents = file_contents.replace("||||", "\x06")
|
||||
file_contents = file_contents.replace("!@#%^&*()", "\x07")
|
||||
|
||||
# Manually parse rows
|
||||
rows = file_contents.strip().split("\x07")
|
||||
if not rows:
|
||||
continue # Skip empty files
|
||||
|
||||
# Extract header
|
||||
headers = rows[0].split("\x06")
|
||||
data = [dict(zip(headers, row.split("\x06"), strict=False)) for row in rows[1:] if row]
|
||||
|
||||
self.tables[file.stem] = data # Store parsed table
|
||||
|
||||
def query_by_id(self, table_name: str, column_name: str, ids: list, return_first_only=False):
|
||||
"""Returns rows from a specified table where column_name matches any of the provided IDs."""
|
||||
if table_name not in self.tables:
|
||||
raise ValueError(f"Table '{table_name}' not found.")
|
||||
|
||||
results = [row for row in self.tables[table_name] if row.get(column_name) in ids]
|
||||
|
||||
if len(results) == 0:
|
||||
results.append({})
|
||||
|
||||
if return_first_only and results:
|
||||
return results[0]
|
||||
return results
|
||||
|
||||
def get_table(self, table_name: str):
|
||||
"""Returns the entire table as a list of dictionaries."""
|
||||
if table_name not in self.tables:
|
||||
raise ValueError(f"Table '{table_name}' not found.")
|
||||
return self.tables[table_name]
|
||||
|
||||
def list_tables(self):
|
||||
"""Returns a list of available tables."""
|
||||
return list(self.tables.keys())
|
||||
|
||||
|
||||
class CooknMigrator(BaseMigrator):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.name = "cookn"
|
||||
self.key_aliases = []
|
||||
|
||||
def _process_recipe_document(self, _recipe_row, db) -> dict:
|
||||
recipe_data = {}
|
||||
|
||||
# Select db values
|
||||
_recipe_id = _recipe_row["ID"]
|
||||
_recipe_desc_row = db.query_by_id("temp_recipe_desc", "ID", [_recipe_id], return_first_only=True)
|
||||
_chapter_id = _recipe_desc_row["PARENT"]
|
||||
_chapter_row = db.query_by_id("temp_chapter_desc", "ID", [_chapter_id], return_first_only=True)
|
||||
_cookbook_id = _chapter_row["PARENT"]
|
||||
_cookbook_row = db.query_by_id("temp_cookBook_desc", "ID", [_cookbook_id], return_first_only=True)
|
||||
_media_row = db.query_by_id("temp_media", "ENTITY_ID", [_recipe_id], return_first_only=True)
|
||||
_media_id = _media_row.get("ID", "")
|
||||
|
||||
# Parse general recipe info
|
||||
cookbook = _cookbook_row.get("TITLE", "")
|
||||
chapter = _chapter_row.get("TITLE", "")
|
||||
name = _recipe_desc_row.get("TITLE", "")
|
||||
description = _recipe_desc_row.get("DESCRIPTION", "")
|
||||
serves = _recipe_row["SERVES"]
|
||||
prep_time = int(_recipe_row["PREPTIME"])
|
||||
cook_time = int(_recipe_row["COOKTIME"])
|
||||
|
||||
recipe_data["recipeCategory"] = [cookbook]
|
||||
recipe_data["tags"] = [chapter]
|
||||
recipe_data["name"] = name
|
||||
recipe_data["description"] = description
|
||||
recipe_data["recipeYield"] = serves
|
||||
recipe_data["prepTime"] = _format_time(prep_time)
|
||||
recipe_data["performTime"] = _format_time(cook_time)
|
||||
recipe_data["totalTime"] = _format_time(prep_time + cook_time)
|
||||
|
||||
# Parse and rename image
|
||||
|
||||
if _media_id != "":
|
||||
_media_type = _media_row["MEDIA_CONTENT_TYPE"]
|
||||
# Determine file extension based on media type
|
||||
_extension = _media_type.split("/")[-1]
|
||||
_old_image_path = os.path.join(db.directory, str(_media_id))
|
||||
new_image_path = f"{_old_image_path}.{_extension}"
|
||||
# Rename the file if it exists and has no extension
|
||||
if os.path.exists(_old_image_path) and not os.path.exists(new_image_path):
|
||||
os.rename(_old_image_path, new_image_path)
|
||||
if Path(new_image_path).exists():
|
||||
recipe_data["image"] = [new_image_path]
|
||||
|
||||
# Parse ingrediants
|
||||
ingredients = []
|
||||
_ingrediant_rows = db.query_by_id("temp_ingredient", "PARENT_ID", [_recipe_id])
|
||||
for _ingrediant_row in _ingrediant_rows:
|
||||
_unit_id = _ingrediant_row.get("AMOUNT_UNIT", "")
|
||||
_unit_row = db.query_by_id("temp_unit", "ID", [_unit_id], return_first_only=True)
|
||||
_food_id = _ingrediant_row.get("INGREDIENT_FOOD_ID", "")
|
||||
_food_row = db.query_by_id("temp_food", "ID", [_food_id], return_first_only=True)
|
||||
_brand_id = _ingrediant_row.get("BRAND_ID", "")
|
||||
_brand_row = db.query_by_id("temp_brand", "ID", [_brand_id], return_first_only=True)
|
||||
|
||||
amount = convert_to_float(_ingrediant_row.get("AMOUNT_QTY_STRING", "1"))
|
||||
unit = _unit_row.get("ABBREVIATION")
|
||||
|
||||
food_name_singluar = _food_row.get("NAME", "")
|
||||
food_name_plural = _food_row.get("PLURAL_NAME", "")
|
||||
if food_name_singluar != "" and food_name_plural != "":
|
||||
if unit is None:
|
||||
if amount is not None and amount > 1:
|
||||
food_name = _food_row.get("PLURAL_NAME", "")
|
||||
else:
|
||||
food_name = _food_row.get("NAME", "")
|
||||
else:
|
||||
food_name = _food_row.get("NAME", "")
|
||||
else:
|
||||
if food_name_singluar != "":
|
||||
food_name = food_name_singluar
|
||||
else:
|
||||
food_name = food_name_plural
|
||||
|
||||
if unit is None:
|
||||
unit = ""
|
||||
else:
|
||||
if amount is not None and amount > 1:
|
||||
unit = _unit_row.get("PLURAL_NAME")
|
||||
else:
|
||||
unit = _unit_row.get("NAME")
|
||||
|
||||
pre_qualifier = _ingrediant_row.get("PRE_QUALIFIER", "")
|
||||
if pre_qualifier == "[null]":
|
||||
pre_qualifier = ""
|
||||
post_qualifier = _ingrediant_row.get("POST_QUALIFIER", "")
|
||||
if post_qualifier == "[null]":
|
||||
post_qualifier = ""
|
||||
brand = _brand_row.get("NAME", "")
|
||||
if brand == "[null]":
|
||||
brand = ""
|
||||
|
||||
base_ingredient = RecipeIngredientBase(
|
||||
quantity=amount,
|
||||
unit=unit,
|
||||
food=pre_qualifier + " " + food_name + " " + post_qualifier + " " + brand,
|
||||
notes=None,
|
||||
)
|
||||
_display_order = int(_ingrediant_row.get("DISPLAY_ORDER", ""))
|
||||
ingredients.append({"title": None, "order": _display_order, "note": base_ingredient.display})
|
||||
ingredients = sorted(ingredients, key=lambda d: d["order"])
|
||||
recipe_data["recipeIngredient"] = ingredients
|
||||
|
||||
# Parse instructions
|
||||
recipe_data["recipeInstructions"] = extract_instructions(_recipe_row["INSTRUCTIONS"])
|
||||
|
||||
return recipe_data
|
||||
|
||||
def _migrate(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
with zipfile.ZipFile(self.archive) as zip_file:
|
||||
zip_file.extractall(tmpdir)
|
||||
|
||||
source_dir = self.get_zip_base_path(Path(tmpdir))
|
||||
db = DSVParser(source_dir)
|
||||
_recipe_table = db.get_table("temp_recipe")
|
||||
recipes_as_dicts = [self._process_recipe_document(_recipe_row, db) for _recipe_row in _recipe_table]
|
||||
|
||||
recipes = [self.clean_recipe_dictionary(x) for x in recipes_as_dicts]
|
||||
results = self.import_recipes_to_database(recipes)
|
||||
recipe_lookup = {r.slug: r for r in recipes}
|
||||
for slug, recipe_id, status in results:
|
||||
if status:
|
||||
r = recipe_lookup.get(slug)
|
||||
if r and r.image:
|
||||
import_image(r.image, recipe_id)
|
|
@ -46,6 +46,8 @@ migrations_myrecipebox = CWD / "migrations/myrecipebox.csv"
|
|||
|
||||
migrations_recipekeeper = CWD / "migrations/recipekeeper.zip"
|
||||
|
||||
migrations_cookn = CWD / "migrations/cookn.zip"
|
||||
|
||||
images_test_image_1 = CWD / "images/test-image-1.jpg"
|
||||
|
||||
images_test_image_2 = CWD / "images/test-image-2.png"
|
||||
|
|
|
@ -108,6 +108,12 @@ test_cases = [
|
|||
search_slug="zucchini-bread",
|
||||
nutrition_entries=set(),
|
||||
),
|
||||
MigrationTestData(
|
||||
typ=SupportedMigrations.cookn,
|
||||
archive=test_data.migrations_cookn,
|
||||
search_slug="zucchini-bread",
|
||||
nutrition_entries=set(),
|
||||
),
|
||||
]
|
||||
|
||||
test_ids = [
|
||||
|
@ -120,6 +126,7 @@ test_ids = [
|
|||
"plantoeat_archive",
|
||||
"myrecipebox_csv",
|
||||
"recipekeeper_archive",
|
||||
"cookn_archive",
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue