diff options
Diffstat (limited to 'server')
| -rw-r--r-- | server/requirements.txt | 2 | ||||
| -rw-r--r-- | server/szurubooru/func/image_hash.py | 4 | ||||
| -rw-r--r-- | server/szurubooru/func/mime.py | 10 | ||||
| -rw-r--r-- | server/szurubooru/func/posts.py | 3 | ||||
| -rw-r--r-- | server/szurubooru/tests/assets/avif-avis.avif | bin | 0 -> 2698 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/avif-similar.avif | bin | 0 -> 2281 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/avif.avif | bin | 0 -> 2698 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/heic-mif1.heic | bin | 0 -> 2806 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/heic.heic | bin | 0 -> 2806 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/heif-similar.heif | bin | 0 -> 3480 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/assets/heif.heif | bin | 0 -> 3489 bytes | |||
| -rw-r--r-- | server/szurubooru/tests/func/test_image_hash.py | 50 | ||||
| -rw-r--r-- | server/szurubooru/tests/func/test_mime.py | 11 |
13 files changed, 79 insertions, 1 deletions
diff --git a/server/requirements.txt b/server/requirements.txt index 043c4f1..2a09b24 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -9,4 +9,6 @@ pillow>=4.3.0 pynacl>=1.2.1 pytz>=2018.3 pyRFC3339>=1.0 +pillow-avif-plugin>=1.1.0 +pyheif-pillow-opener>=0.1.0 youtube_dl diff --git a/server/szurubooru/func/image_hash.py b/server/szurubooru/func/image_hash.py index fc7d141..8d1cd2a 100644 --- a/server/szurubooru/func/image_hash.py +++ b/server/szurubooru/func/image_hash.py @@ -6,6 +6,10 @@ from typing import Any, Callable, List, Optional, Set, Tuple import numpy as np from PIL import Image +import pillow_avif +import pyheif +from pyheif_pillow_opener import register_heif_opener +register_heif_opener() from szurubooru import config, errors diff --git a/server/szurubooru/func/mime.py b/server/szurubooru/func/mime.py index bd553fc..f1d9fbb 100644 --- a/server/szurubooru/func/mime.py +++ b/server/szurubooru/func/mime.py @@ -21,6 +21,12 @@ def get_mime_type(content: bytes) -> str: if content[8:12] == b"WEBP": return "image/webp" + if content[4:12] in (b"ftypavif", b"ftypavis"): + return "image/avif" + + if content[4:12] in (b"ftypheic", b"ftypmif1"): + return "image/heic" + if content[0:4] == b"\x1A\x45\xDF\xA3": return "video/webm" @@ -37,6 +43,8 @@ def get_extension(mime_type: str) -> Optional[str]: "image/jpeg": "jpg", "image/png": "png", "image/webp": "webp", + "image/avif": "avif", + "image/heic": "heic", "video/mp4": "mp4", "video/webm": "webm", "application/octet-stream": "dat", @@ -58,6 +66,8 @@ def is_image(mime_type: str) -> bool: "image/png", "image/gif", "image/webp", + "image/avif", + "image/heic", ) diff --git a/server/szurubooru/func/posts.py b/server/szurubooru/func/posts.py index 0493681..be2259c 100644 --- a/server/szurubooru/func/posts.py +++ b/server/szurubooru/func/posts.py @@ -650,7 +650,8 @@ def update_post_content(post: model.Post, content: Optional[bytes]) -> None: image = images.Image(content) post.canvas_width = image.width post.canvas_height = image.height - except errors.ProcessingError: + except errors.ProcessingError as ex: + logger.exception(ex) if not config.config["allow_broken_uploads"]: raise InvalidPostContentError("Unable to process image metadata") else: diff --git a/server/szurubooru/tests/assets/avif-avis.avif b/server/szurubooru/tests/assets/avif-avis.avif Binary files differnew file mode 100644 index 0000000..3b27f29 --- /dev/null +++ b/server/szurubooru/tests/assets/avif-avis.avif diff --git a/server/szurubooru/tests/assets/avif-similar.avif b/server/szurubooru/tests/assets/avif-similar.avif Binary files differnew file mode 100644 index 0000000..476f3f5 --- /dev/null +++ b/server/szurubooru/tests/assets/avif-similar.avif diff --git a/server/szurubooru/tests/assets/avif.avif b/server/szurubooru/tests/assets/avif.avif Binary files differnew file mode 100644 index 0000000..edf4960 --- /dev/null +++ b/server/szurubooru/tests/assets/avif.avif diff --git a/server/szurubooru/tests/assets/heic-mif1.heic b/server/szurubooru/tests/assets/heic-mif1.heic Binary files differnew file mode 100644 index 0000000..b370c6b --- /dev/null +++ b/server/szurubooru/tests/assets/heic-mif1.heic diff --git a/server/szurubooru/tests/assets/heic.heic b/server/szurubooru/tests/assets/heic.heic Binary files differnew file mode 100644 index 0000000..f1d2ed0 --- /dev/null +++ b/server/szurubooru/tests/assets/heic.heic diff --git a/server/szurubooru/tests/assets/heif-similar.heif b/server/szurubooru/tests/assets/heif-similar.heif Binary files differnew file mode 100644 index 0000000..26acdc9 --- /dev/null +++ b/server/szurubooru/tests/assets/heif-similar.heif diff --git a/server/szurubooru/tests/assets/heif.heif b/server/szurubooru/tests/assets/heif.heif Binary files differnew file mode 100644 index 0000000..ad1fd77 --- /dev/null +++ b/server/szurubooru/tests/assets/heif.heif diff --git a/server/szurubooru/tests/func/test_image_hash.py b/server/szurubooru/tests/func/test_image_hash.py index e7028b6..5a5dc71 100644 --- a/server/szurubooru/tests/func/test_image_hash.py +++ b/server/szurubooru/tests/func/test_image_hash.py @@ -27,3 +27,53 @@ def test_signature_functions(read_asset, config_injector): words2 = image_hash.generate_words(sig2) words_match = sum(word1 == word2 for word1, word2 in zip(words1, words2)) assert words_match == 18 + + +def test_signature_heif(read_asset, config_injector): + sig1 = image_hash.generate_signature(read_asset("heif.heif")) + sig2 = image_hash.generate_signature(read_asset("heif-similar.heif")) + + sig1_repacked = image_hash.unpack_signature( + image_hash.pack_signature(sig1) + ) + sig2_repacked = image_hash.unpack_signature( + image_hash.pack_signature(sig2) + ) + assert array_equal(sig1, sig1_repacked) + assert array_equal(sig2, sig2_repacked) + + dist1 = image_hash.normalized_distance([sig1], sig2) + assert abs(dist1[0] - 0.136777724290135) < 1e-8 + + dist2 = image_hash.normalized_distance([sig2], sig2) + assert abs(dist2[0]) < 1e-8 + + words1 = image_hash.generate_words(sig1) + words2 = image_hash.generate_words(sig2) + words_match = sum(word1 == word2 for word1, word2 in zip(words1, words2)) + assert words_match == 43 + + +def test_signature_avif(read_asset, config_injector): + sig1 = image_hash.generate_signature(read_asset("avif.avif")) + sig2 = image_hash.generate_signature(read_asset("avif-similar.avif")) + + sig1_repacked = image_hash.unpack_signature( + image_hash.pack_signature(sig1) + ) + sig2_repacked = image_hash.unpack_signature( + image_hash.pack_signature(sig2) + ) + assert array_equal(sig1, sig1_repacked) + assert array_equal(sig2, sig2_repacked) + + dist1 = image_hash.normalized_distance([sig1], sig2) + assert abs(dist1[0] - 0.22628712858355998) < 1e-8 + + dist2 = image_hash.normalized_distance([sig2], sig2) + assert abs(dist2[0]) < 1e-8 + + words1 = image_hash.generate_words(sig1) + words2 = image_hash.generate_words(sig2) + words_match = sum(word1 == word2 for word1, word2 in zip(words1, words2)) + assert words_match == 12 diff --git a/server/szurubooru/tests/func/test_mime.py b/server/szurubooru/tests/func/test_mime.py index 0d8f645..d5238a1 100644 --- a/server/szurubooru/tests/func/test_mime.py +++ b/server/szurubooru/tests/func/test_mime.py @@ -13,6 +13,11 @@ from szurubooru.func import mime ("jpeg.jpg", "image/jpeg"), ("gif.gif", "image/gif"), ("webp.webp", "image/webp"), + ("avif.avif", "image/avif"), + ("avif-avis.avif", "image/avif"), + ("heic.heic", "image/heic"), + ("heic-mif1.heic", "image/heic"), + ("heif.heif", "image/heic"), ("text.txt", "application/octet-stream"), ], ) @@ -34,6 +39,8 @@ def test_get_mime_type_for_empty_file(): ("image/jpeg", "jpg"), ("image/gif", "gif"), ("image/webp", "webp"), + ("image/avif", "avif"), + ("image/heic", "heic"), ("application/octet-stream", "dat"), ], ) @@ -75,9 +82,13 @@ def test_is_video(input_mime_type, expected_state): ("image/gif", True), ("image/png", True), ("image/jpeg", True), + ("image/avif", True), + ("image/heic", True), ("IMAGE/GIF", True), ("IMAGE/PNG", True), ("IMAGE/JPEG", True), + ("IMAGE/AVIF", True), + ("IMAGE/HEIC", True), ("image/anything_else", False), ("not an image", False), ], |