Add a whiteboard file picker and force-refresh
Build and push server image / build-and-push (push) Successful in 1m57s
Build and push server image / build-and-push (push) Successful in 1m57s
File picker: an optional WebDAV browse root in Settings (User.webdav_base_url) plus a plain-PROPFIND directory listing (webdav_client.list_directory) power a "Browse..." panel on a frame's Whiteboard tab, so a file can be clicked into rather than typing its exact WebDAV URL. Manual URL entry still works unchanged either way. Force-refresh: get_or_refresh_whiteboard takes a force flag that skips the fetch throttle entirely; the Whiteboard tab's refresh button now passes it, so clicking it always re-fetches and re-renders instead of possibly just re-showing the same cached image from within the last ~20 minutes.
This commit is contained in:
@@ -25,7 +25,7 @@ from pydantic import BaseModel
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .. import calendar_render, gitea_releases, photo_queue, quiet_hours, weather
|
||||
from .. import calendar_render, gitea_releases, photo_queue, quiet_hours, weather, webdav_client
|
||||
from ..auth import require_frame_control, require_frame_view, require_user_api
|
||||
from ..db import frame_locked, get_db
|
||||
from ..image_pipeline import (
|
||||
@@ -52,6 +52,7 @@ from .common import (
|
||||
list_assets,
|
||||
require_configured,
|
||||
valid_http_url,
|
||||
webdav_creds_for,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -650,15 +651,51 @@ def api_whiteboard_source(
|
||||
return {"status": "saved", "url": body.url}
|
||||
|
||||
|
||||
@router.get("/api/frames/{frame_id}/whiteboard-browse")
|
||||
def api_whiteboard_browse(
|
||||
request: Request,
|
||||
url: str | None = None,
|
||||
frame: Frame = Depends(require_frame_view),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""One level of a WebDAV directory listing, using the calling user's
|
||||
own credentials (never the frame's saved whiteboard_user_id -- this
|
||||
is "help me find a file in MY account", same person as whoever would
|
||||
go on to Save it, before that's even happened) -- powers the file
|
||||
picker on the Whiteboard tab as an alternative to pasting a URL.
|
||||
`url` omitted/None starts from the user's webdav_base_url (see
|
||||
models.py's User docstring); passing back a previous response's
|
||||
`entries[].url` (for a folder) descends into it."""
|
||||
user = require_user_api(request, db)
|
||||
creds = webdav_creds_for(user)
|
||||
if creds is None:
|
||||
raise HTTPException(400, "Set up WebDAV credentials in Settings first")
|
||||
target = url or user.webdav_base_url
|
||||
if not target:
|
||||
raise HTTPException(400, "Set a WebDAV browse root in Settings first, or paste the file URL directly")
|
||||
if not valid_http_url(target):
|
||||
raise HTTPException(400, "Browse URL must be a plain http:// or https:// URL")
|
||||
try:
|
||||
entries = webdav_client.list_directory(target, creds[0], creds[1])
|
||||
except webdav_client.WebDavError as e:
|
||||
raise HTTPException(502, f"Could not browse: {e}")
|
||||
base = user.webdav_base_url or target
|
||||
parent_url = webdav_client.parent_directory_url(base, target)
|
||||
return {"current_url": target, "parent_url": parent_url, "entries": entries}
|
||||
|
||||
|
||||
@router.get("/api/frames/{frame_id}/preview/whiteboard")
|
||||
def api_preview_whiteboard(frame: Frame = Depends(require_frame_view), db: Session = Depends(get_db)):
|
||||
def api_preview_whiteboard(
|
||||
force: bool = False, frame: Frame = Depends(require_frame_view), db: Session = Depends(get_db)
|
||||
):
|
||||
"""The same throttled fetch/render cache a live device request would
|
||||
use, run through the same panel composition/quantization pipeline
|
||||
(see routers/device.py's _render_whiteboard_mode) -- "how it will
|
||||
look on the frame" (dithered, letterboxed), not just the raw
|
||||
Excalidraw export, same convention as preview/rendered and
|
||||
preview/calendar."""
|
||||
png_bytes = get_or_refresh_whiteboard(db, frame)
|
||||
preview/calendar. force=True (the "Refresh now" button, as opposed
|
||||
to just reopening this tab) bypasses the fetch throttle."""
|
||||
png_bytes = get_or_refresh_whiteboard(db, frame, force=force)
|
||||
if png_bytes is None:
|
||||
if not frame.whiteboard_url:
|
||||
raise HTTPException(400, "No whiteboard configured on this frame yet")
|
||||
|
||||
Reference in New Issue
Block a user