fix: Recipe Keeper Errors and Other Safari Issues (#3712)

Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
Michael Genson 2024-06-07 10:35:36 -05:00 committed by GitHub
parent eab7c0d9e5
commit 4634ad5666
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 101 additions and 31 deletions

View File

@ -132,14 +132,14 @@ export default defineComponent({
text: i18n.tc("migration.plantoeat.title"),
value: MIGRATIONS.plantoeat,
},
{
text: i18n.tc("migration.tandoor.title"),
value: MIGRATIONS.tandoor,
},
{
text: i18n.tc("migration.recipekeeper.title"),
value: MIGRATIONS.recipekeeper,
},
{
text: i18n.tc("migration.tandoor.title"),
value: MIGRATIONS.tandoor,
},
];
const _content = {
[MIGRATIONS.mealie]: {
@ -312,6 +312,26 @@ export default defineComponent({
}
],
},
[MIGRATIONS.recipekeeper]: {
text: i18n.tc("migration.recipekeeper.description-long"),
acceptedFileType: ".zip",
tree: [
{
id: 1,
icon: $globals.icons.zip,
name: "recipekeeperhtml.zip",
children: [
{ id: 2, name: "recipes.html", icon: $globals.icons.codeJson },
{ id: 3, name: "images", icon: $globals.icons.folderOutline,
children: [
{ id: 4, name: "image1.jpg", icon: $globals.icons.fileImage },
{ id: 5, name: "image2.jpg", icon: $globals.icons.fileImage },
]
},
],
}
],
},
[MIGRATIONS.tandoor]: {
text: i18n.tc("migration.tandoor.description-long"),
acceptedFileType: ".zip",
@ -352,26 +372,6 @@ export default defineComponent({
}
],
},
[MIGRATIONS.recipekeeper]: {
text: i18n.tc("migration.recipekeeper.description-long"),
acceptedFileType: ".zip",
tree: [
{
id: 1,
icon: $globals.icons.zip,
name: "recipekeeperhtml.zip",
children: [
{ id: 2, name: "recipes.html", icon: $globals.icons.codeJson },
{ id: 3, name: "images", icon: $globals.icons.folderOutline,
children: [
{ id: 4, name: "image1.jpeg", icon: $globals.icons.fileImage },
{ id: 5, name: "image2.jpeg", icon: $globals.icons.fileImage },
]
},
],
}
],
},
};
function setFileObject(fileObject: File) {

View File

@ -74,6 +74,28 @@ class BaseMigrator(BaseService):
super().__init__()
@classmethod
def get_zip_base_path(cls, path: Path) -> Path:
# Safari mangles our ZIP structure and adds a "__MACOSX" directory at the root along with
# an arbitrarily-named directory containing the actual contents. So, if we find a dunder directory
# at the root (i.e. __MACOSX) we traverse down the first non-dunder directory and assume this is the base.
# We assume migration exports never contain a directory that starts with "__".
normal_dirs: list[Path] = []
dunder_dirs: list[Path] = []
for dir in path.iterdir():
if not dir.is_dir():
continue
if dir.name.startswith("__"):
dunder_dirs.append(dir)
else:
normal_dirs.append(dir)
if len(normal_dirs) == 1 and len(dunder_dirs) == 1:
return normal_dirs[0]
else:
return path
def _migrate(self) -> None:
raise NotImplementedError

View File

@ -20,12 +20,24 @@ class ChowdownMigrator(BaseMigrator):
MigrationAlias(key="tags", alias="tags", func=split_by_comma),
]
@classmethod
def get_zip_base_path(cls, path: Path) -> Path:
potential_path = super().get_zip_base_path(path)
if path == potential_path:
return path
# make sure we didn't accidentally open a recipe dir
if (potential_path / "recipe.json").exists():
return path
else:
return potential_path
def _migrate(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
temp_path = Path(tmpdir)
temp_path = self.get_zip_base_path(Path(tmpdir))
chow_dir = next(temp_path.iterdir())
image_dir = temp_path.joinpath(chow_dir, "images")

View File

@ -86,7 +86,7 @@ class CopyMeThatMigrator(BaseMigrator):
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
source_dir = Path(tmpdir)
source_dir = self.get_zip_base_path(Path(tmpdir))
recipes_as_dicts: list[dict] = []
for recipes_data_file in source_dir.glob("*.html"):

View File

@ -25,6 +25,18 @@ class MealieAlphaMigrator(BaseMigrator):
MigrationAlias(key="tags", alias="tags", func=split_by_comma),
]
@classmethod
def get_zip_base_path(cls, path: Path) -> Path:
potential_path = super().get_zip_base_path(path)
if path == potential_path:
return path
# make sure we didn't accidentally open the "recipes" dir
if potential_path.name == "recipes":
return path
else:
return potential_path
def _convert_to_new_schema(self, recipe: dict) -> Recipe:
if recipe.get("categories", False):
recipe["recipeCategory"] = recipe.get("categories")
@ -55,7 +67,7 @@ class MealieAlphaMigrator(BaseMigrator):
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
temp_path = Path(tmpdir)
temp_path = self.get_zip_base_path(Path(tmpdir))
recipe_lookup: dict[str, Path] = {}
recipes: list[Recipe] = []

View File

@ -57,6 +57,18 @@ class NextcloudMigrator(BaseMigrator):
MigrationAlias(key="performTime", alias="cookTime", func=parse_iso8601_duration),
]
@classmethod
def get_zip_base_path(cls, path: Path) -> Path:
potential_path = super().get_zip_base_path(path)
if path == potential_path:
return path
# make sure we didn't accidentally open a recipe dir
if (potential_path / "recipe.json").exists():
return path
else:
return potential_path
def _migrate(self) -> None:
# Unzip File into temp directory
@ -65,7 +77,8 @@ class NextcloudMigrator(BaseMigrator):
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
potential_recipe_dirs = glob_walker(Path(tmpdir), glob_str="**/[!.]*.json", return_parent=True)
base_dir = self.get_zip_base_path(Path(tmpdir))
potential_recipe_dirs = glob_walker(base_dir, glob_str="**/[!.]*.json", return_parent=True)
nextcloud_dirs = {y.slug: y for x in potential_recipe_dirs if (y := NextcloudDir.from_dir(x))}
all_recipes = []

View File

@ -11,6 +11,17 @@ from .utils.migration_alias import MigrationAlias
from .utils.migration_helpers import import_image, parse_iso8601_duration
def clean_instructions(instructions: list[str]) -> list[str]:
try:
for i, instruction in enumerate(instructions):
if instruction.startswith(f"{i + 1}. "):
instructions[i] = instruction.removeprefix(f"{i + 1}. ")
return instructions
except Exception:
return instructions
def parse_recipe_div(recipe, image_path):
meta = {}
for item in recipe.find_all(lambda x: x.has_attr("itemprop")):
@ -59,7 +70,7 @@ class RecipeKeeperMigrator(BaseMigrator):
key="recipeIngredient",
alias="recipeIngredients",
),
MigrationAlias(key="recipeInstructions", alias="recipeDirections"),
MigrationAlias(key="recipeInstructions", alias="recipeDirections", func=clean_instructions),
MigrationAlias(key="performTime", alias="cookTime", func=parse_iso8601_duration),
MigrationAlias(key="prepTime", alias="prepTime", func=parse_iso8601_duration),
MigrationAlias(key="image", alias="photo0"),
@ -77,7 +88,7 @@ class RecipeKeeperMigrator(BaseMigrator):
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
source_dir = Path(tmpdir) / "recipekeeperhtml"
source_dir = self.get_zip_base_path(Path(tmpdir))
recipes_as_dicts: list[dict] = []
with open(source_dir / "recipes.html") as fp:

View File

@ -109,7 +109,7 @@ class TandoorMigrator(BaseMigrator):
with zipfile.ZipFile(self.archive) as zip_file:
zip_file.extractall(tmpdir)
source_dir = Path(tmpdir)
source_dir = self.get_zip_base_path(Path(tmpdir))
recipes_as_dicts: list[dict] = []
for i, recipe_zip_file in enumerate(source_dir.glob("*.zip")):