added support for multiple image types

This commit is contained in:
vabene1111 2021-06-14 18:53:51 +02:00
parent c91fc096b3
commit bee1d717c5
12 changed files with 66 additions and 40 deletions

View File

@ -1,8 +1,11 @@
import os
import sys
from PIL import Image from PIL import Image
from io import BytesIO from io import BytesIO
def rescale_image(image_object, base_width=720): def rescale_image_jpeg(image_object, base_width=720):
img = Image.open(image_object) img = Image.open(image_object)
icc_profile = img.info.get('icc_profile') # remember color profile to not mess up colors icc_profile = img.info.get('icc_profile') # remember color profile to not mess up colors
width_percent = (base_width / float(img.size[0])) width_percent = (base_width / float(img.size[0]))
@ -13,3 +16,30 @@ def rescale_image(image_object, base_width=720):
img.save(img_bytes, 'JPEG', quality=75, optimize=True, icc_profile=icc_profile) img.save(img_bytes, 'JPEG', quality=75, optimize=True, icc_profile=icc_profile)
return img_bytes return img_bytes
def rescale_image_png(image_object, base_width=720):
basewidth = 720
wpercent = (basewidth / float(image_object.size[0]))
hsize = int((float(image_object.size[1]) * float(wpercent)))
img = image_object.resize((basewidth, hsize), Image.ANTIALIAS)
im_io = BytesIO()
img.save(im_io, 'PNG', quality=70)
return img
def get_filetype(name):
try:
return os.path.splitext(name)[1]
except:
return '.jpeg'
def handle_image(request, image_object, filetype='.jpeg'):
if sys.getsizeof(image_object) / 8 > 500:
if filetype == '.jpeg':
return rescale_image_jpeg(image_object), filetype
if filetype == '.png':
return rescale_image_png(image_object), filetype
return image_object, filetype

View File

@ -3,6 +3,7 @@ import re
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import parse, get_food, get_unit from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
@ -71,7 +72,7 @@ class Chowdown(Integration):
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
for z in import_zip.filelist: for z in import_zip.filelist:
if re.match(f'^images/{image}$', z.filename): if re.match(f'^images/{image}$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename))) self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
return recipe return recipe

View File

@ -1,9 +1,11 @@
import json import json
from io import BytesIO from io import BytesIO
from re import match
from zipfile import ZipFile from zipfile import ZipFile
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from cookbook.helper.image_processing import get_filetype
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.serializer import RecipeExportSerializer from cookbook.serializer import RecipeExportSerializer
@ -15,8 +17,9 @@ class Default(Integration):
recipe_string = recipe_zip.read('recipe.json').decode("utf-8") recipe_string = recipe_zip.read('recipe.json').decode("utf-8")
recipe = self.decode_recipe(recipe_string) recipe = self.decode_recipe(recipe_string)
if 'image.png' in recipe_zip.namelist(): images = list(filter(lambda v: match('image.*', v), recipe_zip.namelist()))
self.import_recipe_image(recipe, BytesIO(recipe_zip.read('image.png'))) if images:
self.import_recipe_image(recipe, BytesIO(recipe_zip.read(images[0])), filetype=get_filetype(images[0]))
return recipe return recipe
def decode_recipe(self, string): def decode_recipe(self, string):

View File

@ -45,7 +45,7 @@ class Domestica(Integration):
recipe.steps.add(step) recipe.steps.add(step)
if file['image'] != '': if file['image'] != '':
self.import_recipe_image(recipe, BytesIO(base64.b64decode(file['image'].replace('data:image/jpeg;base64,', '')))) self.import_recipe_image(recipe, BytesIO(base64.b64decode(file['image'].replace('data:image/jpeg;base64,', ''))), filetype='.jpeg')
return recipe return recipe

View File

@ -1,5 +1,6 @@
import datetime import datetime
import json import json
import os
import re import re
import uuid import uuid
from io import BytesIO, StringIO from io import BytesIO, StringIO
@ -12,6 +13,7 @@ from django.utils.translation import gettext as _
from django_scopes import scope from django_scopes import scope
from cookbook.forms import ImportExportBase from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype
from cookbook.models import Keyword, Recipe from cookbook.models import Keyword, Recipe
@ -59,7 +61,7 @@ class Integration:
recipe_zip_obj.writestr(filename, recipe_stream.getvalue()) recipe_zip_obj.writestr(filename, recipe_stream.getvalue())
recipe_stream.close() recipe_stream.close()
try: try:
recipe_zip_obj.writestr('image.png', r.image.file.read()) recipe_zip_obj.writestr(f'image{get_filetype(r.image.file.name)}', r.image.file.read())
except ValueError: except ValueError:
pass pass
@ -185,13 +187,14 @@ class Integration:
self.ignored_recipes.append(recipe.name) self.ignored_recipes.append(recipe.name)
@staticmethod @staticmethod
def import_recipe_image(recipe, image_file): def import_recipe_image(recipe, image_file, filetype='.jpeg'):
""" """
Adds an image to a recipe naming it correctly Adds an image to a recipe naming it correctly
:param recipe: Recipe object :param recipe: Recipe object
:param image_file: ByteIO stream containing the image :param image_file: ByteIO stream containing the image
:param filetype: type of file to write bytes to, default to .jpeg if unknown
""" """
recipe.image = File(image_file, name=f'{uuid.uuid4()}_{recipe.pk}.png') recipe.image = File(image_file, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}')
recipe.save() recipe.save()
def get_recipe_from_file(self, file): def get_recipe_from_file(self, file):

View File

@ -3,6 +3,7 @@ import re
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import parse, get_food, get_unit from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient from cookbook.models import Recipe, Step, Food, Unit, Ingredient
@ -49,7 +50,7 @@ class Mealie(Integration):
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
for z in import_zip.filelist: for z in import_zip.filelist:
if re.match(f'^images/{recipe_json["slug"]}.jpg$', z.filename): if re.match(f'^images/{recipe_json["slug"]}.jpg$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename))) self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
return recipe return recipe

View File

@ -3,6 +3,7 @@ import re
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from zipfile import ZipFile
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import parse, get_food, get_unit from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient from cookbook.models import Recipe, Step, Food, Unit, Ingredient
@ -51,7 +52,7 @@ class NextcloudCookbook(Integration):
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
for z in import_zip.filelist: for z in import_zip.filelist:
if re.match(f'^Recipes/{recipe.name}/full.jpg$', z.filename): if re.match(f'^Recipes/{recipe.name}/full.jpg$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename))) self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
return recipe return recipe

View File

@ -81,6 +81,6 @@ class Paprika(Integration):
recipe.steps.add(step) recipe.steps.add(step)
if recipe_json.get("photo_data", None): if recipe_json.get("photo_data", None):
self.import_recipe_image(recipe, BytesIO(base64.b64decode(recipe_json['photo_data']))) self.import_recipe_image(recipe, BytesIO(base64.b64decode(recipe_json['photo_data'])), filetype='.jpeg')
return recipe return recipe

View File

@ -7,6 +7,7 @@ from zipfile import ZipFile
import imghdr import imghdr
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import parse, get_food, get_unit from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
@ -113,17 +114,17 @@ class RecetteTek(Integration):
# Import the original image from the zip file, if we cannot do that, attempt to download it again. # Import the original image from the zip file, if we cannot do that, attempt to download it again.
try: try:
if file['pictures'][0] !='': if file['pictures'][0] != '':
image_file_name = file['pictures'][0].split('/')[-1] image_file_name = file['pictures'][0].split('/')[-1]
for f in self.files: for f in self.files:
if '.rtk' in f['name']: if '.rtk' in f['name']:
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
self.import_recipe_image(recipe, BytesIO(import_zip.read(image_file_name))) self.import_recipe_image(recipe, BytesIO(import_zip.read(image_file_name)), filetype=get_filetype(image_file_name))
else: else:
if file['originalPicture'] != '': if file['originalPicture'] != '':
response=requests.get(file['originalPicture']) response = requests.get(file['originalPicture'])
if imghdr.what(BytesIO(response.content)) != None: if imghdr.what(BytesIO(response.content)) != None:
self.import_recipe_image(recipe, BytesIO(response.content)) self.import_recipe_image(recipe, BytesIO(response.content), filetype=get_filetype(file['originalPicture']))
else: else:
raise Exception("Original image failed to download.") raise Exception("Original image failed to download.")
except Exception as e: except Exception as e:

View File

@ -70,7 +70,7 @@ class RecipeKeeper(Integration):
for f in self.files: for f in self.files:
if '.zip' in f['name']: if '.zip' in f['name']:
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
self.import_recipe_image(recipe, BytesIO(import_zip.read(file.find("img", class_="recipe-photo").get("src")))) self.import_recipe_image(recipe, BytesIO(import_zip.read(file.find("img", class_="recipe-photo").get("src"))), filetype='.jpeg')
except Exception as e: except Exception as e:
pass pass

View File

@ -26,6 +26,7 @@ from rest_framework.schemas.openapi import AutoSchema
from rest_framework.schemas.utils import is_list_view from rest_framework.schemas.utils import is_list_view
from rest_framework.viewsets import ViewSetMixin from rest_framework.viewsets import ViewSetMixin
from cookbook.helper.image_processing import handle_image
from cookbook.helper.ingredient_parser import parse from cookbook.helper.ingredient_parser import parse
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
CustomIsOwner, CustomIsShare, CustomIsOwner, CustomIsShare,
@ -394,16 +395,9 @@ class RecipeViewSet(viewsets.ModelViewSet):
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
img = Image.open(obj.image)
basewidth = 720 img, filetype = handle_image(request, obj.image)
wpercent = (basewidth / float(img.size[0])) obj.image = File(img, name=f'{uuid.uuid4()}_{obj.pk}{filetype}')
hsize = int((float(img.size[1]) * float(wpercent)))
img = img.resize((basewidth, hsize), Image.ANTIALIAS)
im_io = io.BytesIO()
img.save(im_io, 'PNG', quality=70)
obj.image = File(im_io, name=f'{uuid.uuid4()}_{obj.pk}.png')
obj.save() obj.save()
return Response(serializer.data) return Response(serializer.data)
@ -495,7 +489,6 @@ class BookmarkletImportViewSet(viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space).all() return self.queryset.filter(space=self.request.space).all()
class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin): class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = UserFile.objects queryset = UserFile.objects
serializer_class = UserFileSerializer serializer_class = UserFileSerializer

View File

@ -17,6 +17,7 @@ from PIL import Image, UnidentifiedImageError
from requests.exceptions import MissingSchema from requests.exceptions import MissingSchema
from cookbook.forms import BatchEditForm, SyncForm from cookbook.forms import BatchEditForm, SyncForm
from cookbook.helper.image_processing import handle_image
from cookbook.helper.permission_helper import group_required, has_group_permission from cookbook.helper.permission_helper import group_required, has_group_permission
from cookbook.helper.recipe_url_import import parse_cooktime from cookbook.helper.recipe_url_import import parse_cooktime
from cookbook.models import (Comment, Food, Ingredient, Keyword, Recipe, from cookbook.models import (Comment, Food, Ingredient, Keyword, Recipe,
@ -186,18 +187,10 @@ def import_url(request):
if 'image' in data and data['image'] != '' and data['image'] is not None: if 'image' in data and data['image'] != '' and data['image'] is not None:
try: try:
response = requests.get(data['image']) response = requests.get(data['image'])
img = Image.open(BytesIO(response.content))
# todo move image processing to dedicated function img, filetype = handle_image(request, response.content)
basewidth = 720
wpercent = (basewidth / float(img.size[0]))
hsize = int((float(img.size[1]) * float(wpercent)))
img = img.resize((basewidth, hsize), Image.ANTIALIAS)
im_io = BytesIO()
img.save(im_io, 'PNG', quality=70)
recipe.image = File( recipe.image = File(
im_io, name=f'{uuid.uuid4()}_{recipe.pk}.png' img, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}'
) )
recipe.save() recipe.save()
except UnidentifiedImageError: except UnidentifiedImageError: