Skip to content

Commit

Permalink
Merge pull request #854 from sickelap/delegate_thumbnail_generation_t…
Browse files Browse the repository at this point in the history
…o_external_service

delegate thumbnails for raw images to external service
  • Loading branch information
derneuere committed May 14, 2023
2 parents 43ac1ad + b1cd148 commit 175a421
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 10 deletions.
17 changes: 8 additions & 9 deletions api/thumbnails.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import subprocess

import pyvips
from wand.image import Image
import requests

import api.util as util
import ownphotos.settings
Expand All @@ -16,14 +16,13 @@ def createThumbnail(inputPath, outputHeight, outputPath, hash, fileType):
completePath = os.path.join(
ownphotos.settings.MEDIA_ROOT, outputPath, hash + fileType
).strip()
with Image(filename=inputPath) as img:
with img.clone() as thumbnail:
thumbnail.format = "webp"
thumbnail.transform(resize="x" + str(outputHeight))
thumbnail.compression_quality = 95
thumbnail.auto_orient()
thumbnail.save(filename=completePath)
return completePath
json = {
"source": inputPath,
"destination": completePath,
"height": outputHeight,
}
response = requests.post("http://localhost:8003/", json=json).json()
return response["thumbnail"]
else:
bigThumbnailPath = os.path.join(
ownphotos.settings.MEDIA_ROOT, "thumbnails_big", hash + fileType
Expand Down
3 changes: 2 additions & 1 deletion requirements.dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Faker==17.6.0
isort==5.12.0
setuptools==67.6.1
black==23.3.0
pyfakefs==5.2.0
pyfakefs==5.2.0
pytest==7.3.1
Empty file added service/__init__.py
Empty file.
Empty file added service/thumbnail/__init__.py
Empty file.
38 changes: 38 additions & 0 deletions service/thumbnail/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import gevent
from flask import Flask, request
from gevent.pywsgi import WSGIServer
from wand.image import Image

app = Flask(__name__)


def log(message):
print("thumbnail: {}".format(message))


@app.route("/", methods=["POST"])
def create_thumbnail():
try:
data = request.get_json()
source = data["source"]
destination = data["destination"]
height = data["height"]
except Exception:
return "", 400
log(f"creating for source={source} height={height}")
with Image(filename=source) as img:
with img.clone() as thumbnail:
thumbnail.format = "webp"
thumbnail.transform(resize=f"x{height}")
thumbnail.compression_quality = 95
thumbnail.auto_orient()
thumbnail.save(filename=destination)
log(f"created at location={destination}")
return {"thumbnail": destination}, 201


if __name__ == "__main__":
log("service starting")
server = WSGIServer(("0.0.0.0", 8003), app)
server_thread = gevent.spawn(server.serve_forever)
gevent.joinall([server_thread])
3 changes: 3 additions & 0 deletions service/thumbnail/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
samples/*
!samples/.gitkeep
!samples/README.md
Empty file.
Empty file.
1 change: 1 addition & 0 deletions service/thumbnail/test/samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
place any *image* files in this directory that you want to use as test data
47 changes: 47 additions & 0 deletions service/thumbnail/test/test_thumbnail_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import os

from pytest import fixture

from service.thumbnail.main import app


@fixture()
def client():
return app.test_client()


def test_must_fail_when_passing_empty_string(client):
response = client.post("/", data="")
assert response.status_code == 400


def test_must_fail_when_passing_invalid_json(client):
response = client.post("/", data="invalid json")
assert response.status_code == 400


def test_must_fail_when_passing_incomplete_json(client):
invalid_payloads = [
{"source": "foo"},
{"destination": "/tmp/result.webp"},
{"height": 100},
{"source": "foo", "destination": "/tmp/result.webp"},
{"destination": "/tmp/result.webp", "height": 100},
{"height": 100, "source": "foo"},
]
for payload in invalid_payloads:
response = client.post("/", json=payload)
assert response.status_code == 400


def test_should_create_thumbnail(client):
samples_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "samples")
samples = [f for f in os.listdir(samples_dir) if f not in [".gitkeep", "README.md"]]
thumbnail_path = "/tmp/result.webp"
for sample in samples:
if os.path.exists(thumbnail_path):
os.remove(thumbnail_path)
source = os.path.join(samples_dir, sample)
json = {"source": source, "destination": thumbnail_path, "height": 100}
response = client.post("/", json=json)
assert response.status_code == 201

0 comments on commit 175a421

Please sign in to comment.