diff --git a/api/thumbnails.py b/api/thumbnails.py index 08edb7083f..cd2d6d788f 100644 --- a/api/thumbnails.py +++ b/api/thumbnails.py @@ -2,7 +2,7 @@ import subprocess import pyvips -from wand.image import Image +import requests import api.util as util import ownphotos.settings @@ -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 diff --git a/requirements.dev.txt b/requirements.dev.txt index 6d740a239a..82acc7d3b1 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -13,4 +13,5 @@ Faker==17.6.0 isort==5.12.0 setuptools==67.6.1 black==23.3.0 -pyfakefs==5.2.0 \ No newline at end of file +pyfakefs==5.2.0 +pytest==7.3.1 diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/service/thumbnail/__init__.py b/service/thumbnail/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/service/thumbnail/main.py b/service/thumbnail/main.py new file mode 100644 index 0000000000..dfcd463bb6 --- /dev/null +++ b/service/thumbnail/main.py @@ -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]) diff --git a/service/thumbnail/test/.gitignore b/service/thumbnail/test/.gitignore new file mode 100644 index 0000000000..044ffe51eb --- /dev/null +++ b/service/thumbnail/test/.gitignore @@ -0,0 +1,3 @@ +samples/* +!samples/.gitkeep +!samples/README.md diff --git a/service/thumbnail/test/__init__.py b/service/thumbnail/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/service/thumbnail/test/samples/.gitkeep b/service/thumbnail/test/samples/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/service/thumbnail/test/samples/README.md b/service/thumbnail/test/samples/README.md new file mode 100644 index 0000000000..635003a6b8 --- /dev/null +++ b/service/thumbnail/test/samples/README.md @@ -0,0 +1 @@ +place any *image* files in this directory that you want to use as test data diff --git a/service/thumbnail/test/test_thumbnail_worker.py b/service/thumbnail/test/test_thumbnail_worker.py new file mode 100644 index 0000000000..20989c7abb --- /dev/null +++ b/service/thumbnail/test/test_thumbnail_worker.py @@ -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