Add a live preview thumbnail next to the frame name
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.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user