Release 1.6.1

This commit is contained in:
2026-01-25 18:35:28 +01:00
parent 9fd3f03b87
commit c5aa8a5156
8 changed files with 291 additions and 207 deletions

View File

@@ -315,6 +315,44 @@ def _storage_limit_error_message(*, storage_max_human: str | None) -> str:
return "Storage limit reached. Please delete items to free space."
def _normalize_css_color(val: str | None) -> str | None:
"""Accept a limited set of CSS color inputs (primarily hex + a few keywords)."""
v = (val or "").strip()
if not v:
return None
low = v.lower()
if low in {"white", "black", "red", "green", "blue", "yellow", "orange", "purple", "gray", "grey"}:
return low
if low.startswith("#"):
h = low[1:]
if len(h) in {3, 6, 8} and all(c in "0123456789abcdef" for c in h):
return "#" + h
return None
def _normalize_font_family(val: str | None) -> str | None:
v = (val or "").strip()
if not v:
return None
v = v.replace("\n", " ").replace("\r", " ").replace('"', "").replace("'", "")
v = " ".join(v.split())
return v[:120] if v else None
def _normalize_int(val, *, min_val: int, max_val: int) -> int | None:
if val in (None, ""):
return None
try:
n = int(val)
except (TypeError, ValueError):
return None
return min(max_val, max(min_val, n))
@bp.get("/my-company")
@login_required
def my_company():
@@ -376,6 +414,37 @@ def my_company():
)
@bp.post("/my-company/ticker")
@login_required
def update_company_ticker_settings():
"""Update per-company ticker tape settings.
Note: per-display enable/disable remains on the Display model.
"""
company_user_required()
company = db.session.get(Company, current_user.company_id)
if not company:
abort(404)
rss_url = (request.form.get("ticker_rss_url") or "").strip() or None
if rss_url is not None:
rss_url = rss_url[:1000]
company.ticker_rss_url = rss_url
company.ticker_color = _normalize_css_color(request.form.get("ticker_color"))
company.ticker_bg_color = _normalize_css_color(request.form.get("ticker_bg_color"))
company.ticker_bg_opacity = _normalize_int(request.form.get("ticker_bg_opacity"), min_val=0, max_val=100)
company.ticker_font_family = _normalize_font_family(request.form.get("ticker_font_family"))
company.ticker_font_size_px = _normalize_int(request.form.get("ticker_font_size_px"), min_val=10, max_val=200)
company.ticker_speed = _normalize_int(request.form.get("ticker_speed"), min_val=1, max_val=100)
db.session.commit()
flash("Ticker settings updated.", "success")
return redirect(url_for("company.my_company"))
@bp.post("/my-company/overlay")
@login_required
def upload_company_overlay():
@@ -1268,7 +1337,8 @@ def update_display(display_id: int):
if raw is not None:
display.show_overlay = (raw or "").strip().lower() in {"1", "true", "yes", "on"}
# Ticker tape settings
# Ticker tape: per-display enable/disable only.
# RSS + styling is configured per-company (see /my-company).
if request.is_json:
if payload is None:
return _json_error("Invalid JSON")
@@ -1282,61 +1352,11 @@ def update_display(display_id: int):
else:
s = ("" if raw is None else str(raw)).strip().lower()
display.ticker_enabled = s in {"1", "true", "yes", "on"}
if "ticker_rss_url" in payload:
u = (payload.get("ticker_rss_url") or "").strip() or None
# Keep within column limit and avoid whitespace-only.
if u is not None:
u = u[:1000]
display.ticker_rss_url = u
if "ticker_color" in payload:
display.ticker_color = _normalize_css_color(payload.get("ticker_color"))
if "ticker_bg_color" in payload:
display.ticker_bg_color = _normalize_css_color(payload.get("ticker_bg_color"))
if "ticker_bg_opacity" in payload:
display.ticker_bg_opacity = _normalize_percent(payload.get("ticker_bg_opacity"))
if "ticker_font_family" in payload:
display.ticker_font_family = _normalize_font_family(payload.get("ticker_font_family"))
if "ticker_font_size_px" in payload:
display.ticker_font_size_px = _normalize_font_size_px(payload.get("ticker_font_size_px"))
if "ticker_speed" in payload:
display.ticker_speed = _normalize_speed(payload.get("ticker_speed"))
else:
# Form POST implies full update
raw = request.form.get("ticker_enabled")
if raw is not None:
display.ticker_enabled = (raw or "").strip().lower() in {"1", "true", "yes", "on"}
if "ticker_rss_url" in request.form:
u = (request.form.get("ticker_rss_url") or "").strip() or None
if u is not None:
u = u[:1000]
display.ticker_rss_url = u
if "ticker_color" in request.form:
display.ticker_color = _normalize_css_color(request.form.get("ticker_color"))
if "ticker_bg_color" in request.form:
display.ticker_bg_color = _normalize_css_color(request.form.get("ticker_bg_color"))
if "ticker_bg_opacity" in request.form:
display.ticker_bg_opacity = _normalize_percent(request.form.get("ticker_bg_opacity"))
if "ticker_font_family" in request.form:
display.ticker_font_family = _normalize_font_family(request.form.get("ticker_font_family"))
if "ticker_font_size_px" in request.form:
display.ticker_font_size_px = _normalize_font_size_px(request.form.get("ticker_font_size_px"))
if "ticker_speed" in request.form:
display.ticker_speed = _normalize_speed(request.form.get("ticker_speed"))
# Playlist assignment
if request.is_json:
if "playlist_id" in payload:
@@ -1379,13 +1399,13 @@ def update_display(display_id: int):
"transition": display.transition,
"show_overlay": bool(display.show_overlay),
"ticker_enabled": bool(display.ticker_enabled),
"ticker_rss_url": display.ticker_rss_url,
"ticker_color": display.ticker_color,
"ticker_bg_color": display.ticker_bg_color,
"ticker_bg_opacity": display.ticker_bg_opacity,
"ticker_font_family": display.ticker_font_family,
"ticker_font_size_px": display.ticker_font_size_px,
"ticker_speed": display.ticker_speed,
"ticker_rss_url": None,
"ticker_color": None,
"ticker_bg_color": None,
"ticker_bg_opacity": None,
"ticker_font_family": None,
"ticker_font_size_px": None,
"ticker_speed": None,
"assigned_playlist_id": display.assigned_playlist_id,
},
}