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 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)
icc_profile = img.info.get('icc_profile') # remember color profile to not mess up colors
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)
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 zipfile import ZipFile
from cookbook.helper.image_processing import get_filetype
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
@ -71,7 +72,7 @@ class Chowdown(Integration):
import_zip = ZipFile(f['file'])
for z in import_zip.filelist:
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

View File

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

View File

@ -45,7 +45,7 @@ class Domestica(Integration):
recipe.steps.add(step)
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

View File

@ -1,5 +1,6 @@
import datetime
import json
import os
import re
import uuid
from io import BytesIO, StringIO
@ -12,6 +13,7 @@ from django.utils.translation import gettext as _
from django_scopes import scope
from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype
from cookbook.models import Keyword, Recipe
@ -59,7 +61,7 @@ class Integration:
recipe_zip_obj.writestr(filename, recipe_stream.getvalue())
recipe_stream.close()
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:
pass
@ -185,13 +187,14 @@ class Integration:
self.ignored_recipes.append(recipe.name)
@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
:param recipe: Recipe object
: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()
def get_recipe_from_file(self, file):

View File

@ -3,6 +3,7 @@ import re
from io import BytesIO
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.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient
@ -49,7 +50,7 @@ class Mealie(Integration):
import_zip = ZipFile(f['file'])
for z in import_zip.filelist:
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

View File

@ -3,6 +3,7 @@ import re
from io import BytesIO
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.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient
@ -51,7 +52,7 @@ class NextcloudCookbook(Integration):
import_zip = ZipFile(f['file'])
for z in import_zip.filelist:
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

View File

@ -81,6 +81,6 @@ class Paprika(Integration):
recipe.steps.add(step)
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

View File

@ -7,6 +7,7 @@ from zipfile import ZipFile
import imghdr
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.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
@ -25,7 +26,7 @@ class RecetteTek(Integration):
recipe_list = [r for r in recipe_json]
return recipe_list
def get_recipe_from_file(self, file):
# Create initial recipe with just a title and a decription
@ -53,7 +54,7 @@ class RecetteTek(Integration):
step.save()
except Exception as e:
print(recipe.name, ': failed to import source url ', str(e))
try:
# Process the ingredients. Assumes 1 ingredient per line.
for ingredient in file['ingredients'].split('\n'):
@ -96,7 +97,7 @@ class RecetteTek(Integration):
recipe.waiting_time = int(file['cookingTime'])
except Exception as e:
print(recipe.name, ': failed to parse cooking time ', str(e))
recipe.save()
# Import the recipe keywords
@ -110,20 +111,20 @@ class RecetteTek(Integration):
pass
# TODO: Parse Nutritional Information
# Import the original image from the zip file, if we cannot do that, attempt to download it again.
try:
if file['pictures'][0] !='':
if file['pictures'][0] != '':
image_file_name = file['pictures'][0].split('/')[-1]
for f in self.files:
if '.rtk' in f['name']:
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:
if file['originalPicture'] != '':
response=requests.get(file['originalPicture'])
response = requests.get(file['originalPicture'])
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:
raise Exception("Original image failed to download.")
except Exception as e:

View File

@ -70,7 +70,7 @@ class RecipeKeeper(Integration):
for f in self.files:
if '.zip' in f['name']:
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:
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.viewsets import ViewSetMixin
from cookbook.helper.image_processing import handle_image
from cookbook.helper.ingredient_parser import parse
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
CustomIsOwner, CustomIsShare,
@ -394,16 +395,9 @@ class RecipeViewSet(viewsets.ModelViewSet):
if serializer.is_valid():
serializer.save()
img = Image.open(obj.image)
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 = io.BytesIO()
img.save(im_io, 'PNG', quality=70)
obj.image = File(im_io, name=f'{uuid.uuid4()}_{obj.pk}.png')
img, filetype = handle_image(request, obj.image)
obj.image = File(img, name=f'{uuid.uuid4()}_{obj.pk}{filetype}')
obj.save()
return Response(serializer.data)
@ -495,7 +489,6 @@ class BookmarkletImportViewSet(viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space).all()
class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = UserFile.objects
serializer_class = UserFileSerializer

View File

@ -17,6 +17,7 @@ from PIL import Image, UnidentifiedImageError
from requests.exceptions import MissingSchema
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.recipe_url_import import parse_cooktime
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:
try:
response = requests.get(data['image'])
img = Image.open(BytesIO(response.content))
# todo move image processing to dedicated function
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)
img, filetype = handle_image(request, response.content)
recipe.image = File(
im_io, name=f'{uuid.uuid4()}_{recipe.pk}.png'
img, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}'
)
recipe.save()
except UnidentifiedImageError: