Filter NAS photos by EXIF-aware orientation with persistent dimension cache
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from pigway_frame_flow.providers.photo_sources import PhotoSourceInput
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def oriented(service, settings):
|
||||
settings.photo_root = settings.data_dir.parent / "photos"
|
||||
root = settings.photo_root / "20240101 Day"
|
||||
root.mkdir(parents=True)
|
||||
for name, size, rotation in [
|
||||
("wide.jpg", (200, 100), 1),
|
||||
("tall.jpg", (100, 200), 1),
|
||||
("rotated.jpg", (200, 100), 6),
|
||||
("square.jpg", (100, 100), 1),
|
||||
]:
|
||||
exif = Image.Exif()
|
||||
exif[274] = rotation
|
||||
Image.new("RGB", size).save(root / name, exif=exif)
|
||||
return service.photo_sources
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"direction,names",
|
||||
[
|
||||
("landscape", {"wide.jpg"}),
|
||||
("portrait", {"tall.jpg", "rotated.jpg"}),
|
||||
("auto", {"tall.jpg", "rotated.jpg"}),
|
||||
(None, {"wide.jpg", "tall.jpg", "rotated.jpg", "square.jpg"}),
|
||||
],
|
||||
)
|
||||
def test_orientation_catalog_and_exif(oriented, direction, names):
|
||||
oriented.save(
|
||||
"family",
|
||||
PhotoSourceInput(
|
||||
name="Family",
|
||||
kind="local",
|
||||
location=".",
|
||||
orientation=direction,
|
||||
organized_only=True,
|
||||
),
|
||||
)
|
||||
rows = oriented.catalog("family")
|
||||
assert {r["name"] for r in rows} == names
|
||||
if direction:
|
||||
assert oriented.db.one("SELECT COUNT(*) AS n FROM photo_dimensions")["n"] == 4
|
||||
if direction != "landscape":
|
||||
rotated = next(r for r in rows if r["name"] == "rotated.jpg")
|
||||
assert (rotated["width"], rotated["height"]) == (100, 200)
|
||||
|
||||
|
||||
def test_cached_dimensions_invalidation_and_no_fallback(
|
||||
oriented, settings, monkeypatch
|
||||
):
|
||||
from pigway_frame_flow.errors import FlowError
|
||||
|
||||
oriented.save(
|
||||
"family",
|
||||
PhotoSourceInput(
|
||||
name="Family", kind="local", location=".", orientation="landscape"
|
||||
),
|
||||
)
|
||||
assert len(oriented.catalog("family")) == 1
|
||||
monkeypatch.setattr(
|
||||
"pigway_frame_flow.providers.photo_sources.dimensions",
|
||||
lambda *a: pytest.fail("unchanged dimensions should be cached"),
|
||||
)
|
||||
assert len(oriented.catalog("family", refresh=True)) == 1
|
||||
monkeypatch.undo()
|
||||
Image.new("RGB", (50, 100)).save(settings.photo_root / "20240101 Day" / "wide.jpg")
|
||||
assert oriented.catalog("family", refresh=True) == []
|
||||
with pytest.raises(FlowError):
|
||||
oriented.read("family")
|
||||
|
||||
|
||||
def test_orientation_validation_and_api(client, oriented):
|
||||
r = client.put(
|
||||
"/api/v1/photo-sources/family",
|
||||
json={
|
||||
"name": "Family",
|
||||
"kind": "local",
|
||||
"location": ".",
|
||||
"orientation": "landscape",
|
||||
},
|
||||
)
|
||||
assert r.status_code == 200 and r.json()["orientation"] == "landscape"
|
||||
assert client.get("/api/v1/photo-sources/family/images").json()["total"] == 1
|
||||
assert (
|
||||
client.put(
|
||||
"/api/v1/photo-sources/family",
|
||||
json={
|
||||
"name": "Family",
|
||||
"kind": "local",
|
||||
"location": ".",
|
||||
"orientation": "sideways",
|
||||
},
|
||||
).status_code
|
||||
== 422
|
||||
)
|
||||
assert (
|
||||
client.put(
|
||||
"/api/v1/photo-sources/dav",
|
||||
json={
|
||||
"name": "Remote",
|
||||
"kind": "webdav",
|
||||
"location": "https://example.com/",
|
||||
"orientation": "portrait",
|
||||
},
|
||||
).json()["error"]["code"]
|
||||
== "UNSUPPORTED_CAPABILITY"
|
||||
)
|
||||
Reference in New Issue
Block a user