From 3a0bb1cd3701ae4c2c3b6dd20c0bc455fe2f7354 Mon Sep 17 00:00:00 2001 From: bramval Date: Thu, 12 Feb 2026 10:50:49 +0100 Subject: [PATCH] Initial commit --- .gitignore | 21 ++ README.md | 169 +++++++++ app/__init__.py | 66 ++++ config.py | 31 ++ event_manager.py | 63 ++++ media/idle_image.png | Bin 0 -> 49910 bytes models.py | 87 +++++ package-lock.json | 141 ++++++++ package.json | 16 + requirements.txt | 12 + routes.py | 498 +++++++++++++++++++++++++++ run.py | 68 ++++ sockets.py | 205 +++++++++++ static/admin.js | 33 ++ static/display.js | 536 +++++++++++++++++++++++++++++ static/vendor/socket.io.min.js | 7 + sync.py | 85 +++++ templates/admin/base.html | 37 ++ templates/admin/dashboard.html | 74 ++++ templates/admin/display_form.html | 17 + templates/admin/displays.html | 28 ++ templates/admin/event_form.html | 53 +++ templates/admin/event_logs.html | 17 + templates/admin/events.html | 45 +++ templates/admin/idle_image.html | 42 +++ templates/admin/system_logs.html | 9 + templates/admin/videos.html | 26 ++ templates/admin/videos_upload.html | 12 + templates/display.html | 38 ++ udp_listener.py | 135 ++++++++ 30 files changed, 2571 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 config.py create mode 100644 event_manager.py create mode 100644 media/idle_image.png create mode 100644 models.py create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 requirements.txt create mode 100644 routes.py create mode 100644 run.py create mode 100644 sockets.py create mode 100644 static/admin.js create mode 100644 static/display.js create mode 100644 static/vendor/socket.io.min.js create mode 100644 sync.py create mode 100644 templates/admin/base.html create mode 100644 templates/admin/dashboard.html create mode 100644 templates/admin/display_form.html create mode 100644 templates/admin/displays.html create mode 100644 templates/admin/event_form.html create mode 100644 templates/admin/event_logs.html create mode 100644 templates/admin/events.html create mode 100644 templates/admin/idle_image.html create mode 100644 templates/admin/system_logs.html create mode 100644 templates/admin/videos.html create mode 100644 templates/admin/videos_upload.html create mode 100644 templates/display.html create mode 100644 udp_listener.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b52adc3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.venv/ +node_modules/ +__pycache__/ +*.pyc + +# Logs +logs/ +*.log + +# Local database +syncplayer.db +*.db + +# Media (keep only the default idle image) +media/* +!media/idle_image.png + +# OS / IDE +.DS_Store +Thumbs.db +.vscode/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a4830c --- /dev/null +++ b/README.md @@ -0,0 +1,169 @@ +# SyncPlayer (LAN Synchronized Video Playback) + +Flask + Flask-SocketIO system for near frame-level synchronized playback across many browser-capable SoC displays on a local LAN. + +## Key synchronization mechanism + +### 1) Custom NTP-style time sync (client does **not** use its wall clock) + +Client keeps a high-resolution monotonic clock using: + +```js +client_ms = performance.timeOrigin + performance.now() +``` + +Every 10s the client sends `t1_ms` and the server replies with `{t1_ms, t2_ms}` where `t2_ms` is server time in ms. + +Client receives the reply at `t3_ms` and calculates: + +```text +rtt = t3 - t1 +offset = t2 - (t1 + t3)/2 +server_time ~= client_time + offset +``` + +Offset is smoothed to reduce jitter. + +### 2) Scheduled absolute start + +When an event triggers, the server schedules a future start time: + +```text +start_time_ms = server_time_ms() + EVENT_LEAD_TIME_MS (default 5000) +``` + +Server sends each participating display: + +- `video_url` +- `start_time_ms` +- `event_id` + +Client preloads, pauses at 0, then uses a `setTimeout` + `requestAnimationFrame` loop to call `video.play()` *as close as possible* to `start_time_ms` using the corrected server clock. + +### 3) Drift correction during playback + +Every 2s client computes: + +```text +expected = (serverNowMs - start_time_ms)/1000 +drift = video.currentTime - expected +``` + +- if `|drift| > 15ms`: adjust `playbackRate` slightly (0.99 / 1.01) +- if `|drift| > 100ms`: hard seek to expected + +This keeps the group synchronized on low-latency LANs. + +## Project structure + +``` +/app +/templates +/static +/media +models.py +routes.py +sockets.py +udp_listener.py +sync.py +config.py +run.py +requirements.txt +``` + +## Setup + +### 1) Create venv + install deps + +Windows (cmd): + +```bat +python -m venv .venv +.venv\Scripts\activate +pip install -r requirements.txt +``` + +### 2) Initialize database + +```bat +python run.py init-db +``` + +### 3) Run server + +```bat +python run.py --host 0.0.0.0 --port 5000 +``` + +Notes: +- On Windows / Python 3.14+, this runs in `threading` async mode by default (good enough for LAN testing). For highest scale/throughput, run on Linux and install `eventlet` or `gevent`. +- You can force an async mode with: `set ASYNC_MODE=threading` (Windows) or `export ASYNC_MODE=eventlet|gevent|threading` (Linux). + +Open admin UI: + +- `http://:5000/admin/` + +Open display pages: + +- `http://:5000/display/` + +## UDP trigger + +Configure UDP Port + UDP Payload on an Event in the admin UI. The UDP listener binds ports automatically (refreshes config every 5s). + +Send a UDP packet (example PowerShell): + +```powershell +$udp = new-Object System.Net.Sockets.UdpClient +$bytes = [Text.Encoding]::UTF8.GetBytes("SHOW_START") +$udp.Send($bytes, $bytes.Length, "192.168.1.10", 7777) | Out-Null +$udp.Close() +``` + +### Firewall notes + +- Allow TCP port (default 5000) for HTTP/WebSocket. +- Allow the configured UDP ports for triggers. + +## HTTP URL trigger (LAN) + +You can also trigger an event by opening a URL (useful for simple controllers without UDP): + +- By id: `http://:5000/trigger/` +- By name: `http://:5000/trigger_by_name/` + +Cooldown: +- Cooldown is enforced by default. +- To bypass cooldown (LAN only), add `?force=1`. + +Example: + +``` +http://192.168.1.10:5000/trigger/1?force=1 +``` + +## Idle image (no active event) + +You can upload an **Idle Image** in the admin UI: + +- `http://:5000/admin/idle-image` + +When no triggered event is active, displays show this image fullscreen. + +## Debug overlay + +On display page: + +- Press **F2** or **Ctrl+Shift+D**, or +- **Double click** anywhere + +to toggle a small overlay showing Socket.IO connection state, offset/RTT, event id, drift and playbackRate. + +## Display video cache warming + +When a display page loads, it will fetch `/api/videos` and perform **background cache warming**: + +- Default strategy: request the first **1MB** of each video via HTTP `Range` (`bytes=0-1048575`). +- Purpose: get the container + initial moov atoms into cache to reduce startup stutter. +- It is **best-effort**: browsers may evict cache under pressure, and some platforms ignore caching for large media. +- Cache warming automatically pauses once an event starts to avoid competing bandwidth during synchronized start. diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e267923 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +import logging +import os +from logging.handlers import RotatingFileHandler + +from flask import Flask +from flask_compress import Compress +from flask_cors import CORS + +from config import Config +from models import db +from routes import admin_bp, bp +from sockets import socketio + + +def create_app() -> Flask: + app = Flask(__name__, template_folder=os.path.join(os.path.dirname(__file__), "..", "templates"), static_folder=os.path.join(os.path.dirname(__file__), "..", "static")) + app.config.from_object(Config) + + os.makedirs(app.config["MEDIA_DIR"], exist_ok=True) + + # Extensions + db.init_app(app) + CORS(app, resources={r"/*": {"origins": app.config.get("CORS_ORIGINS", "*")}}) + Compress(app) + + # SocketIO tuning from config + socketio.init_app( + app, + cors_allowed_origins=app.config.get("CORS_ORIGINS", "*"), + ping_interval=app.config["SOCKETIO_PING_INTERVAL"], + ping_timeout=app.config["SOCKETIO_PING_TIMEOUT"], + ) + + # Blueprints + app.register_blueprint(bp) + app.register_blueprint(admin_bp) + + # Logging + _configure_logging(app) + + @app.after_request + def _lan_headers(resp): + # Keep headers permissive for embedded Chromium/Tizen/WebOS. + resp.headers.pop("Content-Security-Policy", None) + resp.headers.pop("Cross-Origin-Opener-Policy", None) + resp.headers.pop("Cross-Origin-Embedder-Policy", None) + # Disable caching for HTML/JS (but media route sets its own cache-control) + if resp.mimetype in ("text/html", "application/javascript"): + resp.headers["Cache-Control"] = "no-store" + return resp + + return app + + +def _configure_logging(app: Flask) -> None: + os.makedirs(os.path.join(app.root_path, "..", "logs"), exist_ok=True) + log_path = os.path.abspath(os.path.join(app.root_path, "..", "logs", "system.log")) + handler = RotatingFileHandler(log_path, maxBytes=2_000_000, backupCount=3) + fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s") + handler.setFormatter(fmt) + handler.setLevel(logging.INFO) + app.logger.addHandler(handler) + logging.getLogger().addHandler(handler) + logging.getLogger().setLevel(logging.INFO) diff --git a/config.py b/config.py new file mode 100644 index 0000000..2cfb87b --- /dev/null +++ b/config.py @@ -0,0 +1,31 @@ +import os + + +class Config: + # LAN / kiosk deployments typically don't need strong cookie security. + SECRET_KEY = os.environ.get("SECRET_KEY", "dev-secret-key-change-me") + + BASE_DIR = os.path.abspath(os.path.dirname(__file__)) + DB_PATH = os.path.join(BASE_DIR, "syncplayer.db") + SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", f"sqlite:///{DB_PATH}") + SQLALCHEMY_TRACK_MODIFICATIONS = False + + MEDIA_DIR = os.environ.get("MEDIA_DIR", os.path.join(BASE_DIR, "media")) + MAX_CONTENT_LENGTH = int(os.environ.get("MAX_CONTENT_LENGTH", str(2 * 1024 * 1024 * 1024))) # 2GB + + # Socket.IO tuning for LAN + SOCKETIO_PING_INTERVAL = float(os.environ.get("SOCKETIO_PING_INTERVAL", "5")) + SOCKETIO_PING_TIMEOUT = float(os.environ.get("SOCKETIO_PING_TIMEOUT", "10")) + + # Client drift correction thresholds (ms) + DRIFT_SOFT_MS = float(os.environ.get("DRIFT_SOFT_MS", "15")) + DRIFT_HARD_MS = float(os.environ.get("DRIFT_HARD_MS", "100")) + + # When an event triggers we schedule playback this many ms into the future. + EVENT_LEAD_TIME_MS = int(os.environ.get("EVENT_LEAD_TIME_MS", "5000")) + + # If video duration isn't known, keep event state alive for this many seconds. + EVENT_FALLBACK_TTL_SECONDS = int(os.environ.get("EVENT_FALLBACK_TTL_SECONDS", "3600")) + + # CORS: allow everything within LAN. Tighten if needed. + CORS_ORIGINS = os.environ.get("CORS_ORIGINS", "*") diff --git a/event_manager.py b/event_manager.py new file mode 100644 index 0000000..ad38943 --- /dev/null +++ b/event_manager.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import logging +from typing import Dict, Optional + +from flask import url_for + +from models import Display, EventDisplayMap, Video, db +from sync import ActiveEventState, server_time_ms, set_trigger_event + + +logger = logging.getLogger(__name__) + + +def _build_assignments(event_id: int) -> Dict[str, str]: + maps = ( + db.session.query(EventDisplayMap) + .filter_by(event_id=event_id) + .join(Display, EventDisplayMap.display_id == Display.id) + .join(Video, EventDisplayMap.video_id == Video.id) + .all() + ) + assignments: Dict[str, str] = {} + for m in maps: + assignments[m.display.public_id] = url_for("main.media", filename=m.video.filename) + return assignments + + +def broadcast_trigger_state(state: ActiveEventState) -> None: + # Lazy import to avoid circular import (sockets -> event_manager -> sockets) + from sockets import socketio + + for public_id, video_url in state.assignments.items(): + socketio.emit( + "event_start", + { + "event_id": state.event_id, + "event_name": state.name, + "video_url": video_url, + "start_time_ms": state.start_time_ms, + "server_time_ms": server_time_ms(), + }, + room=f"display:{public_id}", + ) + + +def activate_trigger_event(event_id: int, start_ms: float, end_ms: float, name: str) -> Optional[ActiveEventState]: + """Activate a triggered event, store it in-memory, and broadcast it to displays.""" + assignments = _build_assignments(event_id) + if not assignments: + return None + + state = ActiveEventState( + event_id=event_id, + name=name, + start_time_ms=start_ms, + end_time_ms=end_ms, + assignments=assignments, + ) + set_trigger_event(state) + broadcast_trigger_state(state) + logger.info("Activated trigger event %s (%s)", event_id, name) + return state diff --git a/media/idle_image.png b/media/idle_image.png new file mode 100644 index 0000000000000000000000000000000000000000..9b9105c5ea38fb80083b90a7f5c0a1ab9f0a3022 GIT binary patch literal 49910 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 str: + return f"" + + +class Video(db.Model): + __tablename__ = "videos" + + id = db.Column(db.Integer, primary_key=True) + filename = db.Column(db.String(255), nullable=False, unique=True) + duration = db.Column(db.Float, nullable=True) # seconds + uploaded_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) + + def __repr__(self) -> str: + return f"