Small header thumbnail showing exactly what the frame is currently
displaying -- same widget compositor /frame/image uses, handed back as
a plain PNG (image_pipeline.render_panel/render_placeholder gain an
as_png option) instead of packed native-panel bytes. New session-authed
GET /api/frames/{id}/preview exposes it; click-to-refresh plus a slow
60s poll on the frame header so it doesn't hammer Immich/calendar
sources just for a header thumbnail.
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
"""GET /api/frames/{id}/preview -- the dashboard header's live "how it's
|
|
displaying" thumbnail (see routers/device.py's render_frame_preview_png).
|
|
Same compositor as /frame/image, just PNG instead of packed bytes, and
|
|
gated by require_frame_view instead of device auth -- so the interesting
|
|
things to check are that it's a real PNG of the right logical size and
|
|
that view-only auth (not device auth) actually applies."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
|
|
from PIL import Image
|
|
|
|
from app.image_pipeline import logical_render_size
|
|
from app.models import Frame
|
|
|
|
from .conftest import link_user, login, make_user
|
|
|
|
|
|
def test_preview_returns_a_png_at_logical_size(client, db_session):
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
frame = db_session.get(Frame, 1)
|
|
|
|
resp = client.get("/api/frames/1/preview")
|
|
assert resp.status_code == 200
|
|
assert resp.headers["content-type"] == "image/png"
|
|
|
|
img = Image.open(io.BytesIO(resp.content))
|
|
assert img.size == logical_render_size(frame.orientation)
|
|
|
|
|
|
def test_preview_reflects_orientation(client, db_session):
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
frame = db_session.get(Frame, 1)
|
|
frame.orientation = "portrait"
|
|
db_session.commit()
|
|
|
|
resp = client.get("/api/frames/1/preview")
|
|
assert resp.status_code == 200
|
|
img = Image.open(io.BytesIO(resp.content))
|
|
assert img.size == logical_render_size("portrait")
|
|
assert img.width < img.height
|
|
|
|
|
|
def test_preview_visible_to_linked_user(client, db_session):
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
bob = make_user(db_session, "bob")
|
|
frame = db_session.get(Frame, 1)
|
|
link_user(db_session, bob, frame)
|
|
|
|
client.cookies.clear()
|
|
login(client, "bob")
|
|
resp = client.get("/api/frames/1/preview")
|
|
assert resp.status_code == 200
|
|
|
|
|
|
def test_preview_hidden_from_unrelated_user(client, db_session):
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
make_user(db_session, "mallory")
|
|
|
|
client.cookies.clear()
|
|
login(client, "mallory")
|
|
resp = client.get("/api/frames/1/preview")
|
|
assert resp.status_code == 404
|
|
|
|
|
|
def test_preview_requires_login(client, db_session):
|
|
client.post("/setup", data={"username": "alice", "password": "hunter22"})
|
|
client.cookies.clear()
|
|
resp = client.get("/api/frames/1/preview")
|
|
assert resp.status_code in (401, 403)
|