Release 1.6.2

This commit is contained in:
2026-01-26 15:34:52 +01:00
parent c5aa8a5156
commit 0c2720618a
5 changed files with 71 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
from datetime import datetime, timedelta
import hashlib
import json
import os
import time
import re
from urllib.parse import urlparse
@@ -20,7 +21,18 @@ MAX_ACTIVE_SESSIONS_PER_DISPLAY = 3
SESSION_TTL_SECONDS = 90
# RSS ticker cache (in-memory; OK for this small app; avoids hammering feeds)
TICKER_CACHE_TTL_SECONDS = 120
#
# Default is intentionally long because displays can refresh headlines on a long interval
# (e.g., twice per day) and we don't want many displays to re-fetch the same feed.
# Override via env var `TICKER_CACHE_TTL_SECONDS`.
def _env_int(name: str, default: int) -> int:
try:
return int(os.environ.get(name, "") or default)
except (TypeError, ValueError):
return default
TICKER_CACHE_TTL_SECONDS = max(10, _env_int("TICKER_CACHE_TTL_SECONDS", 6 * 60 * 60))
_TICKER_CACHE: dict[str, dict] = {}

View File

@@ -280,6 +280,13 @@ h1, h2, h3, .display-1, .display-2, .display-3 {
background: #000;
}
/* Mobile: dashboard display previews are heavy (iframes). Hide them on small screens. */
@media (max-width: 768px) {
.display-gallery-card .display-preview {
display: none;
}
}
@media (max-width: 768px) {
.display-gallery-grid {
grid-template-columns: 1fr;

View File

@@ -76,7 +76,8 @@
<iframe
title="Preview — {{ d.name }}"
data-display-id="{{ d.id }}"
src="{{ url_for('display.display_player', token=d.token) }}?preview=1"
class="js-display-preview"
data-preview-src="{{ url_for('display.display_player', token=d.token) }}?preview=1"
loading="lazy"
referrerpolicy="no-referrer"
style="width: 100%; height: 100%; border: 0;"
@@ -241,17 +242,23 @@
function refreshPreviewIframe(displayId) {
const iframe = document.querySelector(`iframe[data-display-id="${displayId}"]`);
if (!iframe || !iframe.src) return;
// Previews are disabled on mobile.
if (window.matchMedia && window.matchMedia('(max-width: 768px)').matches) return;
if (!iframe) return;
try {
const u = new URL(iframe.src, window.location.origin);
const baseSrc = iframe.dataset.previewSrc || iframe.src;
if (!baseSrc) return;
const u = new URL(baseSrc, window.location.origin);
// Ensure preview flag is present (and bust cache).
u.searchParams.set('preview', '1');
u.searchParams.set('_ts', String(Date.now()));
iframe.src = u.toString();
} catch (e) {
// Fallback: naive cache buster
const sep = iframe.src.includes('?') ? '&' : '?';
iframe.src = `${iframe.src}${sep}_ts=${Date.now()}`;
const baseSrc = iframe.dataset.previewSrc || iframe.src;
if (!baseSrc) return;
const sep = baseSrc.includes('?') ? '&' : '?';
iframe.src = `${baseSrc}${sep}_ts=${Date.now()}`;
}
}
@@ -467,6 +474,31 @@
if (plSaveBtn) {
plSaveBtn.addEventListener('click', savePlaylists);
}
// Disable dashboard previews on small screens (mobile): don't even set iframe src.
function loadDashboardPreviewsIfDesktop() {
const isMobile = window.matchMedia && window.matchMedia('(max-width: 768px)').matches;
if (isMobile) return;
document.querySelectorAll('iframe.js-display-preview[data-preview-src]').forEach((iframe) => {
if (!iframe.src || iframe.src === 'about:blank') {
iframe.src = iframe.dataset.previewSrc;
}
});
}
loadDashboardPreviewsIfDesktop();
// If user rotates/resizes from mobile -> desktop, load previews then.
if (window.matchMedia) {
const mql = window.matchMedia('(max-width: 768px)');
const onChange = () => {
if (!mql.matches) loadDashboardPreviewsIfDesktop();
};
if (typeof mql.addEventListener === 'function') {
mql.addEventListener('change', onChange);
} else if (typeof mql.addListener === 'function') {
mql.addListener(onChange);
}
}
})();
</script>
{% endblock %}

View File

@@ -203,6 +203,14 @@
let tickerInterval = null;
let tickerLastHeadlines = [];
function getTickerPollSeconds() {
// Refresh headlines on a long interval.
// Default: 12 hours (twice per day).
// Override via ?ticker_poll=seconds.
const tp = parseInt(new URLSearchParams(window.location.search).get('ticker_poll') || '', 10);
return Number.isFinite(tp) && tp > 0 ? tp : (12 * 60 * 60);
}
const ANIM_MS = 420;
function getTransitionMode(pl) {
@@ -391,8 +399,7 @@
clearInterval(tickerInterval);
tickerInterval = null;
}
// Refresh every 2 minutes; server caches too.
tickerInterval = setInterval(refreshTickerOnce, 120 * 1000);
tickerInterval = setInterval(refreshTickerOnce, getTickerPollSeconds() * 1000);
}
function clearStage() {