Release 1.6.1
This commit is contained in:
@@ -303,10 +303,11 @@ def display_playlist(token: str):
|
||||
if not display:
|
||||
abort(404)
|
||||
|
||||
company = Company.query.filter_by(id=display.company_id).first()
|
||||
|
||||
# Optional overlay URL (per-company) when enabled on this display.
|
||||
overlay_src = None
|
||||
if display.show_overlay:
|
||||
company = Company.query.filter_by(id=display.company_id).first()
|
||||
if company and company.overlay_file_path and is_valid_upload_relpath(company.overlay_file_path):
|
||||
overlay_src = url_for("static", filename=company.overlay_file_path)
|
||||
|
||||
@@ -317,15 +318,16 @@ def display_playlist(token: str):
|
||||
if not ok:
|
||||
return resp
|
||||
|
||||
# Ticker settings are configured per-company; displays can enable/disable individually.
|
||||
ticker_cfg = {
|
||||
"enabled": bool(display.ticker_enabled),
|
||||
"rss_url": display.ticker_rss_url,
|
||||
"color": display.ticker_color,
|
||||
"bg_color": display.ticker_bg_color,
|
||||
"bg_opacity": display.ticker_bg_opacity,
|
||||
"font_family": display.ticker_font_family,
|
||||
"font_size_px": display.ticker_font_size_px,
|
||||
"speed": display.ticker_speed,
|
||||
"rss_url": (company.ticker_rss_url if company else None),
|
||||
"color": (company.ticker_color if company else None),
|
||||
"bg_color": (company.ticker_bg_color if company else None),
|
||||
"bg_opacity": (company.ticker_bg_opacity if company else None),
|
||||
"font_family": (company.ticker_font_family if company else None),
|
||||
"font_size_px": (company.ticker_font_size_px if company else None),
|
||||
"speed": (company.ticker_speed if company else None),
|
||||
}
|
||||
|
||||
# Determine active playlists. If display_playlist has any rows, use those.
|
||||
@@ -432,6 +434,8 @@ def display_ticker(token: str):
|
||||
if not display:
|
||||
abort(404)
|
||||
|
||||
company = Company.query.filter_by(id=display.company_id).first()
|
||||
|
||||
# Enforce concurrent session limit the same way as /playlist.
|
||||
sid = request.args.get("sid")
|
||||
ok, resp = _enforce_and_touch_display_session(display, sid)
|
||||
@@ -441,7 +445,7 @@ def display_ticker(token: str):
|
||||
if not display.ticker_enabled:
|
||||
return jsonify({"enabled": False, "headlines": []})
|
||||
|
||||
rss_url = (display.ticker_rss_url or "").strip()
|
||||
rss_url = ((company.ticker_rss_url if company else None) or "").strip()
|
||||
if not rss_url:
|
||||
return jsonify({"enabled": True, "headlines": []})
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user