mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
move images to /recipes/{slug}/images
This commit is contained in:
parent
38d704a5bc
commit
0a6a29a004
15 changed files with 121 additions and 128 deletions
|
@ -10,8 +10,8 @@
|
||||||
encode gzip
|
encode gzip
|
||||||
uri strip_suffix /
|
uri strip_suffix /
|
||||||
|
|
||||||
handle_path /api/recipes/image/* {
|
handle_path /api/recipes/media/* {
|
||||||
root * /app/data/img/
|
root * /app/data/recipes/
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ const recipeURLs = {
|
||||||
recipe: slug => prefix + slug,
|
recipe: slug => prefix + slug,
|
||||||
update: slug => prefix + slug,
|
update: slug => prefix + slug,
|
||||||
delete: slug => prefix + slug,
|
delete: slug => prefix + slug,
|
||||||
|
createAsset: slug => `${prefix}media/${slug}/assets`,
|
||||||
recipeImage: slug => `${prefix}${slug}/image`,
|
recipeImage: slug => `${prefix}${slug}/image`,
|
||||||
updateImage: slug => `${prefix}${slug}/image`,
|
updateImage: slug => `${prefix}${slug}/image`,
|
||||||
createAsset: slug => `${prefix}${slug}/asset`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const recipeAPI = {
|
export const recipeAPI = {
|
||||||
|
@ -84,7 +84,7 @@ export const recipeAPI = {
|
||||||
fd.append("extension", fileObject.name.split(".").pop());
|
fd.append("extension", fileObject.name.split(".").pop());
|
||||||
fd.append("name", name);
|
fd.append("name", name);
|
||||||
fd.append("icon", icon);
|
fd.append("icon", icon);
|
||||||
let response = apiReq.post(recipeURLs.createAsset(recipeSlug), fd);
|
const response = apiReq.post(recipeURLs.createAsset(recipeSlug), fd);
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -135,14 +135,14 @@ export const recipeAPI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeImage(recipeSlug) {
|
recipeImage(recipeSlug) {
|
||||||
return `/api/recipes/image/${recipeSlug}/original.webp`;
|
return `/api/recipes/media/${recipeSlug}/image/original.webp`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeSmallImage(recipeSlug) {
|
recipeSmallImage(recipeSlug) {
|
||||||
return `/api/recipes/image/${recipeSlug}/min-original.webp`;
|
return `/api/recipes/media/${recipeSlug}/image/min-original.webp`;
|
||||||
},
|
},
|
||||||
|
|
||||||
recipeTinyImage(recipeSlug) {
|
recipeTinyImage(recipeSlug) {
|
||||||
return `/api/recipes/image/${recipeSlug}/tiny-original.webp`;
|
return `/api/recipes/media/${recipeSlug}/image/tiny-original.webp`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
v-if="!edit"
|
v-if="!edit"
|
||||||
color="primary"
|
color="primary"
|
||||||
icon
|
icon
|
||||||
:href="`/api/recipes/${slug}/asset?file_name=${item.fileName}`"
|
:href="`/api/recipes/media/${slug}/assets/${item.fileName}`"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
top
|
top
|
||||||
>
|
>
|
||||||
|
@ -135,7 +135,7 @@ export default {
|
||||||
this.value.splice(index, 1);
|
this.value.splice(index, 1);
|
||||||
},
|
},
|
||||||
copyLink(name, fileName) {
|
copyLink(name, fileName) {
|
||||||
const copyText = ``;
|
const copyText = ``;
|
||||||
navigator.clipboard.writeText(copyText).then(
|
navigator.clipboard.writeText(copyText).then(
|
||||||
() => console.log("Copied", copyText),
|
() => console.log("Copied", copyText),
|
||||||
() => console.log("Copied Failed", copyText)
|
() => console.log("Copied Failed", copyText)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import shutil
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
|
from mealie.core.root_logger import get_logger
|
||||||
from mealie.core.security import create_file_token
|
from mealie.core.security import create_file_token
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
|
@ -12,6 +13,7 @@ from mealie.services.backups.exports import backup_all
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
|
router = APIRouter(prefix="/api/backups", tags=["Backups"], dependencies=[Depends(get_current_user)])
|
||||||
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/available", response_model=Imports)
|
@router.get("/available", response_model=Imports)
|
||||||
|
@ -44,7 +46,8 @@ def export_database(data: BackupJob, session: Session = Depends(generate_session
|
||||||
export_groups=data.options.groups,
|
export_groups=data.options.groups,
|
||||||
)
|
)
|
||||||
return {"export_path": export_path}
|
return {"export_path": export_path}
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ def get_todays_image(session: Session = Depends(generate_session), group_name: s
|
||||||
recipe = get_todays_meal(session, group_in_db)
|
recipe = get_todays_meal(session, group_in_db)
|
||||||
|
|
||||||
if recipe:
|
if recipe:
|
||||||
recipe_image = image.read_image(recipe.slug, image_type=image.IMG_OPTIONS.ORIGINAL_IMAGE)
|
recipe_image = recipe.image_dir.joinpath(image.ImageOptions.ORIGINAL_IMAGE)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
if recipe_image:
|
if recipe_image:
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_assets, recipe_crud_routes, tag_routes
|
from mealie.routes.recipe import all_recipe_routes, category_routes, recipe_crud_routes, recipe_media, tag_routes
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
router.include_router(all_recipe_routes.router)
|
router.include_router(all_recipe_routes.router)
|
||||||
router.include_router(recipe_crud_routes.router)
|
router.include_router(recipe_crud_routes.router)
|
||||||
router.include_router(recipe_assets.router)
|
router.include_router(recipe_media.router)
|
||||||
router.include_router(category_routes.router)
|
router.include_router(category_routes.router)
|
||||||
router.include_router(tag_routes.router)
|
router.include_router(tag_routes.router)
|
||||||
|
|
|
@ -4,7 +4,8 @@ from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.recipe import Recipe, RecipeURLIn
|
from mealie.schema.recipe import Recipe, RecipeURLIn
|
||||||
from mealie.services.image.image import delete_image, rename_image, scrape_image, write_image
|
from mealie.services.image.image import scrape_image, write_image
|
||||||
|
from mealie.services.recipe.asset import check_asset
|
||||||
from mealie.services.scraper.scraper import create_from_url
|
from mealie.services.scraper.scraper import create_from_url
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ def update_recipe(
|
||||||
print(recipe.assets)
|
print(recipe.assets)
|
||||||
|
|
||||||
if recipe_slug != recipe.slug:
|
if recipe_slug != recipe.slug:
|
||||||
rename_image(original_slug=recipe_slug, new_slug=recipe.slug)
|
check_asset(original_slug=recipe_slug, recipe=recipe)
|
||||||
|
|
||||||
return recipe
|
return recipe
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ def patch_recipe(
|
||||||
session, recipe_slug, new_data=data.dict(exclude_unset=True, exclude_defaults=True)
|
session, recipe_slug, new_data=data.dict(exclude_unset=True, exclude_defaults=True)
|
||||||
)
|
)
|
||||||
if recipe_slug != recipe.slug:
|
if recipe_slug != recipe.slug:
|
||||||
rename_image(original_slug=recipe_slug, new_slug=recipe.slug)
|
check_asset(original_slug=recipe_slug, recipe=recipe)
|
||||||
|
|
||||||
return recipe
|
return recipe
|
||||||
|
|
||||||
|
@ -91,7 +92,6 @@ def delete_recipe(
|
||||||
|
|
||||||
try:
|
try:
|
||||||
delete_data = db.recipes.delete(session, recipe_slug)
|
delete_data = db.recipes.delete(session, recipe_slug)
|
||||||
delete_image(recipe_slug)
|
|
||||||
|
|
||||||
return delete_data
|
return delete_data
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -3,7 +3,6 @@ from enum import Enum
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, status
|
||||||
from fastapi.datastructures import UploadFile
|
from fastapi.datastructures import UploadFile
|
||||||
from mealie.core.config import app_dirs
|
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
|
@ -12,7 +11,7 @@ from slugify import slugify
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from starlette.responses import FileResponse
|
from starlette.responses import FileResponse
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/recipes", tags=["Recipe Media"])
|
router = APIRouter(prefix="/api/recipes/media", tags=["Recipe Media"])
|
||||||
|
|
||||||
|
|
||||||
class ImageType(str, Enum):
|
class ImageType(str, Enum):
|
||||||
|
@ -21,11 +20,12 @@ class ImageType(str, Enum):
|
||||||
tiny = "tiny-original.webp"
|
tiny = "tiny-original.webp"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/image/{recipe_slug}/{file_name}")
|
@router.get("/{recipe_slug}/image/{file_name}")
|
||||||
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.original):
|
||||||
"""Takes in a recipe slug, returns the static image. This route is proxied in the docker image
|
"""Takes in a recipe slug, returns the static image. This route is proxied in the docker image
|
||||||
and should not hit the API in production"""
|
and should not hit the API in production"""
|
||||||
recipe_image = app_dirs.IMG_DIR.joinpath(recipe_slug, file_name.value)
|
recipe_image = Recipe(slug=recipe_slug).image_dir.joinpath(file_name.value)
|
||||||
|
|
||||||
if recipe_image:
|
if recipe_image:
|
||||||
return FileResponse(recipe_image)
|
return FileResponse(recipe_image)
|
||||||
else:
|
else:
|
||||||
|
@ -33,13 +33,17 @@ async def get_recipe_img(recipe_slug: str, file_name: ImageType = ImageType.orig
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{recipe_slug}/assets/{file_name}")
|
@router.get("/{recipe_slug}/assets/{file_name}")
|
||||||
async def get_recipe_asset(recipe_slug, file_name: str):
|
async def get_recipe_asset(recipe_slug: str, file_name: str):
|
||||||
""" Returns a recipe asset """
|
""" Returns a recipe asset """
|
||||||
file = app_dirs.RECIPE_DATA_DIR.joinpath(recipe_slug, file_name)
|
file = Recipe(slug=recipe_slug).asset_dir.joinpath(file_name)
|
||||||
return FileResponse(file)
|
|
||||||
|
try:
|
||||||
|
return FileResponse(file)
|
||||||
|
except Exception:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{recipe_slug}/asset", response_model=RecipeAsset)
|
@router.post("/{recipe_slug}/assets", response_model=RecipeAsset)
|
||||||
def upload_recipe_asset(
|
def upload_recipe_asset(
|
||||||
recipe_slug: str,
|
recipe_slug: str,
|
||||||
name: str = Form(...),
|
name: str = Form(...),
|
||||||
|
@ -52,8 +56,7 @@ def upload_recipe_asset(
|
||||||
""" Upload a file to store as a recipe asset """
|
""" Upload a file to store as a recipe asset """
|
||||||
file_name = slugify(name) + "." + extension
|
file_name = slugify(name) + "." + extension
|
||||||
asset_in = RecipeAsset(name=name, icon=icon, file_name=file_name)
|
asset_in = RecipeAsset(name=name, icon=icon, file_name=file_name)
|
||||||
dest = app_dirs.RECIPE_DATA_DIR.joinpath(recipe_slug, file_name)
|
dest = Recipe(slug=recipe_slug).asset_dir.joinpath(file_name)
|
||||||
dest.parent.mkdir(exist_ok=True, parents=True)
|
|
||||||
|
|
||||||
with dest.open("wb") as buffer:
|
with dest.open("wb") as buffer:
|
||||||
shutil.copyfileobj(file.file, buffer)
|
shutil.copyfileobj(file.file, buffer)
|
|
@ -1,7 +1,9 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from fastapi_camelcase import CamelModel
|
from fastapi_camelcase import CamelModel
|
||||||
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.models.recipe.recipe import RecipeModel
|
from mealie.db.models.recipe.recipe import RecipeModel
|
||||||
from pydantic import BaseModel, Field, validator
|
from pydantic import BaseModel, Field, validator
|
||||||
from pydantic.utils import GetterDict
|
from pydantic.utils import GetterDict
|
||||||
|
@ -58,8 +60,8 @@ class Nutrition(CamelModel):
|
||||||
|
|
||||||
class RecipeSummary(CamelModel):
|
class RecipeSummary(CamelModel):
|
||||||
id: Optional[int]
|
id: Optional[int]
|
||||||
name: str
|
name: Optional[str]
|
||||||
slug: Optional[str] = ""
|
slug: str = ""
|
||||||
image: Optional[Any]
|
image: Optional[Any]
|
||||||
|
|
||||||
description: Optional[str]
|
description: Optional[str]
|
||||||
|
@ -98,6 +100,28 @@ class Recipe(RecipeSummary):
|
||||||
org_url: Optional[str] = Field(None, alias="orgURL")
|
org_url: Optional[str] = Field(None, alias="orgURL")
|
||||||
extras: Optional[dict] = {}
|
extras: Optional[dict] = {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def directory_from_slug(slug) -> Path:
|
||||||
|
return app_dirs.RECIPE_DATA_DIR.joinpath(slug)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def directory(self) -> Path:
|
||||||
|
dir = app_dirs.RECIPE_DATA_DIR.joinpath(self.slug)
|
||||||
|
dir.mkdir(exist_ok=True, parents=True)
|
||||||
|
return dir
|
||||||
|
|
||||||
|
@property
|
||||||
|
def asset_dir(self) -> Path:
|
||||||
|
dir = self.directory.joinpath("assets")
|
||||||
|
dir.mkdir(exist_ok=True, parents=True)
|
||||||
|
return dir
|
||||||
|
|
||||||
|
@property
|
||||||
|
def image_dir(self) -> Path:
|
||||||
|
dir = self.directory.joinpath("images")
|
||||||
|
dir.mkdir(exist_ok=True, parents=True)
|
||||||
|
return dir
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
@ -140,6 +164,8 @@ class Recipe(RecipeSummary):
|
||||||
|
|
||||||
@validator("slug", always=True, pre=True)
|
@validator("slug", always=True, pre=True)
|
||||||
def validate_slug(slug: str, values):
|
def validate_slug(slug: str, values):
|
||||||
|
if not values["name"]:
|
||||||
|
return slug
|
||||||
name: str = values["name"]
|
name: str = values["name"]
|
||||||
calc_slug: str = slugify(name)
|
calc_slug: str = slugify(name)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ class ExportDatabase:
|
||||||
export_tag = datetime.now().strftime("%Y-%b-%d")
|
export_tag = datetime.now().strftime("%Y-%b-%d")
|
||||||
|
|
||||||
self.main_dir = app_dirs.TEMP_DIR.joinpath(export_tag)
|
self.main_dir = app_dirs.TEMP_DIR.joinpath(export_tag)
|
||||||
self.img_dir = self.main_dir.joinpath("images")
|
self.recipes = self.main_dir.joinpath("recipes")
|
||||||
self.templates_dir = self.main_dir.joinpath("templates")
|
self.templates_dir = self.main_dir.joinpath("templates")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -43,7 +43,7 @@ class ExportDatabase:
|
||||||
|
|
||||||
required_dirs = [
|
required_dirs = [
|
||||||
self.main_dir,
|
self.main_dir,
|
||||||
self.img_dir,
|
self.recipes,
|
||||||
self.templates_dir,
|
self.templates_dir,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -67,10 +67,10 @@ class ExportDatabase:
|
||||||
with open(out_file, "w") as f:
|
with open(out_file, "w") as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
def export_images(self):
|
def export_recipe_dirs(self):
|
||||||
shutil.copytree(app_dirs.IMG_DIR, self.img_dir, dirs_exist_ok=True)
|
shutil.copytree(app_dirs.RECIPE_DATA_DIR, self.recipes, dirs_exist_ok=True)
|
||||||
|
|
||||||
def export_items(self, items: list[BaseModel], folder_name: str, export_list=True):
|
def export_items(self, items: list[BaseModel], folder_name: str, export_list=True, slug_folder=False):
|
||||||
items = [x.dict() for x in items]
|
items = [x.dict() for x in items]
|
||||||
out_dir = self.main_dir.joinpath(folder_name)
|
out_dir = self.main_dir.joinpath(folder_name)
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
@ -79,8 +79,9 @@ class ExportDatabase:
|
||||||
ExportDatabase._write_json_file(items, out_dir.joinpath(f"{folder_name}.json"))
|
ExportDatabase._write_json_file(items, out_dir.joinpath(f"{folder_name}.json"))
|
||||||
else:
|
else:
|
||||||
for item in items:
|
for item in items:
|
||||||
filename = sanitize_filename(f"{item.get('name')}.json")
|
final_dest = out_dir if not slug_folder else out_dir.joinpath(item.get("slug"))
|
||||||
ExportDatabase._write_json_file(item, out_dir.joinpath(filename))
|
filename = sanitize_filename(f"{item.get('slug')}.json")
|
||||||
|
ExportDatabase._write_json_file(item, final_dest.joinpath(filename))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _write_json_file(data: Union[dict, list], out_file: Path):
|
def _write_json_file(data: Union[dict, list], out_file: Path):
|
||||||
|
@ -121,9 +122,9 @@ def backup_all(
|
||||||
|
|
||||||
if export_recipes:
|
if export_recipes:
|
||||||
all_recipes = db.recipes.get_all(session)
|
all_recipes = db.recipes.get_all(session)
|
||||||
db_export.export_items(all_recipes, "recipes", export_list=False)
|
db_export.export_recipe_dirs()
|
||||||
|
db_export.export_items(all_recipes, "recipes", export_list=False, slug_folder=True)
|
||||||
db_export.export_templates(all_recipes)
|
db_export.export_templates(all_recipes)
|
||||||
db_export.export_images()
|
|
||||||
|
|
||||||
if export_settings:
|
if export_settings:
|
||||||
all_settings = db.settings.get_all(session)
|
all_settings = db.settings.get_all(session)
|
||||||
|
|
|
@ -2,7 +2,7 @@ import json
|
||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, List
|
from typing import Callable
|
||||||
|
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.database import db
|
from mealie.db.database import db
|
||||||
|
@ -49,7 +49,7 @@ class ImportDatabase:
|
||||||
def import_recipes(self):
|
def import_recipes(self):
|
||||||
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
||||||
imports = []
|
imports = []
|
||||||
successful_imports = []
|
successful_imports = {}
|
||||||
|
|
||||||
recipes = ImportDatabase.read_models_file(
|
recipes = ImportDatabase.read_models_file(
|
||||||
file_path=recipe_dir, model=Recipe, single_file=False, migrate=ImportDatabase._recipe_migration
|
file_path=recipe_dir, model=Recipe, single_file=False, migrate=ImportDatabase._recipe_migration
|
||||||
|
@ -68,7 +68,7 @@ class ImportDatabase:
|
||||||
)
|
)
|
||||||
|
|
||||||
if import_status.status:
|
if import_status.status:
|
||||||
successful_imports.append(recipe.slug)
|
successful_imports.update({recipe.slug: recipe})
|
||||||
|
|
||||||
imports.append(import_status)
|
imports.append(import_status)
|
||||||
|
|
||||||
|
@ -105,15 +105,21 @@ class ImportDatabase:
|
||||||
|
|
||||||
return recipe_dict
|
return recipe_dict
|
||||||
|
|
||||||
def _import_images(self, successful_imports: List[str]):
|
def _import_images(self, successful_imports: list[Recipe]):
|
||||||
image_dir = self.import_dir.joinpath("images")
|
image_dir = self.import_dir.joinpath("images")
|
||||||
for image in image_dir.iterdir():
|
|
||||||
if image.stem in successful_imports:
|
if image_dir.exists():
|
||||||
if image.is_dir():
|
for image in image_dir.iterdir():
|
||||||
dest = app_dirs.IMG_DIR.joinpath(image.stem)
|
item: Recipe = successful_imports.get(image.stem)
|
||||||
shutil.copytree(image, dest, dirs_exist_ok=True)
|
|
||||||
if image.is_file():
|
if item:
|
||||||
shutil.copy(image, app_dirs.IMG_DIR)
|
dest_dir = item.image_dir
|
||||||
|
|
||||||
|
if image.is_dir():
|
||||||
|
shutil.copytree(image, dest_dir, dirs_exist_ok=True)
|
||||||
|
|
||||||
|
if image.is_file():
|
||||||
|
shutil.copy(image, dest_dir)
|
||||||
|
|
||||||
minify.migrate_images()
|
minify.migrate_images()
|
||||||
|
|
||||||
|
@ -227,7 +233,7 @@ class ImportDatabase:
|
||||||
return [model(**g) for g in file_data]
|
return [model(**g) for g in file_data]
|
||||||
|
|
||||||
all_models = []
|
all_models = []
|
||||||
for file in file_path.glob("*.json"):
|
for file in file_path.glob("**/*.json"):
|
||||||
with open(file, "r") as f:
|
with open(file, "r") as f:
|
||||||
file_data = json.loads(f.read())
|
file_data = json.loads(f.read())
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from mealie.core import root_logger
|
from mealie.core import root_logger
|
||||||
from mealie.core.config import app_dirs
|
from mealie.schema.recipe import Recipe
|
||||||
from mealie.services.image import minify
|
from mealie.services.image import minify
|
||||||
|
|
||||||
logger = root_logger.get_logger()
|
logger = root_logger.get_logger()
|
||||||
|
@ -20,47 +20,11 @@ class ImageOptions:
|
||||||
IMG_OPTIONS = ImageOptions()
|
IMG_OPTIONS = ImageOptions()
|
||||||
|
|
||||||
|
|
||||||
def read_image(recipe_slug: str, image_type: str = "original") -> Path:
|
|
||||||
"""returns the path to the image file for the recipe base of image_type
|
|
||||||
|
|
||||||
Args:
|
|
||||||
recipe_slug (str): Recipe Slug
|
|
||||||
image_type (str, optional): Glob Style Matcher "original*" | "min-original* | "tiny-original*"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Path: [description]
|
|
||||||
"""
|
|
||||||
recipe_slug = recipe_slug.split(".")[0] # Incase of File Name
|
|
||||||
recipe_image_dir = app_dirs.IMG_DIR.joinpath(recipe_slug)
|
|
||||||
|
|
||||||
for file in recipe_image_dir.glob(image_type):
|
|
||||||
return file
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def rename_image(original_slug, new_slug) -> Path:
|
|
||||||
current_path = app_dirs.IMG_DIR.joinpath(original_slug)
|
|
||||||
new_path = app_dirs.IMG_DIR.joinpath(new_slug)
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_path = current_path.rename(new_path)
|
|
||||||
except FileNotFoundError:
|
|
||||||
logger.error(f"Image Directory {original_slug} Doesn't Exist")
|
|
||||||
|
|
||||||
return new_path
|
|
||||||
|
|
||||||
|
|
||||||
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
||||||
try:
|
image_dir = Recipe(slug=recipe_slug).image_dir
|
||||||
delete_image(recipe_slug)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
image_dir = Path(app_dirs.IMG_DIR.joinpath(f"{recipe_slug}"))
|
|
||||||
image_dir.mkdir(exist_ok=True, parents=True)
|
|
||||||
extension = extension.replace(".", "")
|
extension = extension.replace(".", "")
|
||||||
image_path = image_dir.joinpath(f"original.{extension}")
|
image_path = image_dir.joinpath(f"original.{extension}")
|
||||||
|
image_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
if isinstance(file_data, Path):
|
if isinstance(file_data, Path):
|
||||||
shutil.copy2(file_data, image_path)
|
shutil.copy2(file_data, image_path)
|
||||||
|
@ -77,12 +41,6 @@ def write_image(recipe_slug: str, file_data: bytes, extension: str) -> Path:
|
||||||
return image_path
|
return image_path
|
||||||
|
|
||||||
|
|
||||||
def delete_image(recipe_slug: str) -> str:
|
|
||||||
recipe_slug = recipe_slug.split(".")[0]
|
|
||||||
for file in app_dirs.IMG_DIR.glob(f"{recipe_slug}*"):
|
|
||||||
return shutil.rmtree(file)
|
|
||||||
|
|
||||||
|
|
||||||
def scrape_image(image_url: str, slug: str) -> Path:
|
def scrape_image(image_url: str, slug: str) -> Path:
|
||||||
if isinstance(image_url, str): # Handles String Types
|
if isinstance(image_url, str): # Handles String Types
|
||||||
image_url = image_url
|
image_url = image_url
|
||||||
|
@ -96,7 +54,7 @@ def scrape_image(image_url: str, slug: str) -> Path:
|
||||||
image_url = image_url.get("url")
|
image_url = image_url.get("url")
|
||||||
|
|
||||||
filename = slug + "." + image_url.split(".")[-1]
|
filename = slug + "." + image_url.split(".")[-1]
|
||||||
filename = app_dirs.IMG_DIR.joinpath(filename)
|
filename = Recipe(slug=slug).image_dir.joinpath(filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(image_url, stream=True)
|
r = requests.get(image_url, stream=True)
|
||||||
|
|
|
@ -4,10 +4,8 @@ from pathlib import Path
|
||||||
|
|
||||||
from mealie.core import root_logger
|
from mealie.core import root_logger
|
||||||
from mealie.core.config import app_dirs
|
from mealie.core.config import app_dirs
|
||||||
from mealie.db.database import db
|
from mealie.schema.recipe import Recipe
|
||||||
from mealie.db.db_setup import create_session
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from sqlalchemy.orm.session import Session
|
|
||||||
|
|
||||||
logger = root_logger.get_logger()
|
logger = root_logger.get_logger()
|
||||||
|
|
||||||
|
@ -20,11 +18,7 @@ class ImageSizes:
|
||||||
|
|
||||||
|
|
||||||
def get_image_sizes(org_img: Path, min_img: Path, tiny_img: Path) -> ImageSizes:
|
def get_image_sizes(org_img: Path, min_img: Path, tiny_img: Path) -> ImageSizes:
|
||||||
return ImageSizes(
|
return ImageSizes(org=sizeof_fmt(org_img), min=sizeof_fmt(min_img), tiny=sizeof_fmt(tiny_img))
|
||||||
org=sizeof_fmt(org_img),
|
|
||||||
min=sizeof_fmt(min_img),
|
|
||||||
tiny=sizeof_fmt(tiny_img),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def minify_image(image_file: Path) -> ImageSizes:
|
def minify_image(image_file: Path) -> ImageSizes:
|
||||||
|
@ -110,28 +104,9 @@ def move_all_images():
|
||||||
if new_file.is_file():
|
if new_file.is_file():
|
||||||
new_file.unlink()
|
new_file.unlink()
|
||||||
image_file.rename(new_file)
|
image_file.rename(new_file)
|
||||||
|
if image_file.is_dir():
|
||||||
|
slug = image_file.name
|
||||||
def validate_slugs_in_database(session: Session = None):
|
image_file.rename(Recipe(slug=slug).image_dir)
|
||||||
def check_image_path(image_name: str, slug_path: str) -> bool:
|
|
||||||
existing_path: Path = app_dirs.IMG_DIR.joinpath(image_name)
|
|
||||||
slug_path: Path = app_dirs.IMG_DIR.joinpath(slug_path)
|
|
||||||
|
|
||||||
if existing_path.is_dir():
|
|
||||||
slug_path.rename(existing_path)
|
|
||||||
else:
|
|
||||||
logger.info("No Image Found")
|
|
||||||
|
|
||||||
session = session or create_session()
|
|
||||||
all_recipes = db.recipes.get_all(session)
|
|
||||||
|
|
||||||
slugs_and_images = [(x.slug, x.image) for x in all_recipes]
|
|
||||||
|
|
||||||
for slug, image in slugs_and_images:
|
|
||||||
image_slug = image.split(".")[0] # Remove Extension
|
|
||||||
if slug != image_slug:
|
|
||||||
logger.info(f"{slug}, Doesn't Match '{image_slug}'")
|
|
||||||
check_image_path(image, slug)
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_images():
|
def migrate_images():
|
||||||
|
@ -139,7 +114,7 @@ def migrate_images():
|
||||||
|
|
||||||
move_all_images()
|
move_all_images()
|
||||||
|
|
||||||
for image in app_dirs.IMG_DIR.glob("*/original.*"):
|
for image in app_dirs.RECIPE_DATA_DIR.glob("**/original.*"):
|
||||||
|
|
||||||
minify_image(image)
|
minify_image(image)
|
||||||
|
|
||||||
|
@ -148,4 +123,3 @@ def migrate_images():
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
migrate_images()
|
migrate_images()
|
||||||
validate_slugs_in_database()
|
|
||||||
|
|
0
mealie/services/recipe/__init__.py
Normal file
0
mealie/services/recipe/__init__.py
Normal file
22
mealie/services/recipe/asset.py
Normal file
22
mealie/services/recipe/asset.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from mealie.core.config import app_dirs
|
||||||
|
from mealie.core.root_logger import get_logger
|
||||||
|
from mealie.schema.recipe import Recipe
|
||||||
|
|
||||||
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
def check_asset(original_slug, recipe: Recipe) -> Path:
|
||||||
|
if original_slug == recipe.slug:
|
||||||
|
return recipe.assets
|
||||||
|
|
||||||
|
current_dir = app_dirs.RECIPE_DATA_DIR.joinpath(original_slug)
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_dir.rename(recipe.directory)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logger.error(f"Recipe Directory not Found: {original_slug}")
|
||||||
|
logger.info(f"Renaming Recipe Directory: {original_slug} -> {recipe.slug}")
|
||||||
|
|
||||||
|
return current_dir.absolute()
|
Loading…
Add table
Add a link
Reference in a new issue