-
Notifications
You must be signed in to change notification settings - Fork 0
SyncPlay Intern DE
🌐 English · Deutsch
SyncPlay ist ein natives, server-autoritatives Watch-Together-Feature, komplett in der App gebaut — es spricht nicht das externe Syncplay-Protokoll und braucht keinen Drittanbieter-Server. Alle Clients sind Browser auf derselben Instanz.
| Datei | Rolle |
|---|---|
web/syncplay_rooms.py |
In-Memory-Raum-/Mitglieder-Dienst (das Herzstück). Reine Logik, unit-testbar. |
web/transcoder.py |
HLS-Transcoder + geteilte/refcountete Sessions pro Raum. |
web/app.py |
Alle /api/syncplay/*-Routen, Gast-Zugang, Raum-Persistenz. |
web/static/syncplay_page.js |
Seiten-Controller: SSE-Client, Player-Einbettung, gesamte UI. |
web/templates/syncplay.html |
Die /syncplay-Seite (Lobby, Raum, Picker, Modals). |
web/static/syncplay.css |
Seitenspezifisches, responsives Styling. |
web/static/player.js |
Wiederverwendeter Player; leitet den Raum-Token an die Stream-API. |
-
Runter: Server-Sent Events —
GET /api/syncplay/stream?token=…streamt JSON-Events. -
Hoch: einfaches
POST /api/syncplay/*für Aktionen. - Der Token ist ein Raum-Session-Token pro Mitglied (
secrets.token_urlsafe). Er identifiziert das Mitglied und dient zugleich als Gast-Auth-Token.
- Registry nach Raumname; Mitglieder nach Token (
_token_indexmappt Token → Raum). - Host = das Mitglied, das den Raum erstellt hat; verlässt der Host, geht die Rolle an das am längsten anwesende verbleibende Mitglied.
- Der Server hält den autoritativen Playstate:
position+updated_at+paused.effective_position()läuft beim Abspielen weiter, sodass ein Mid-Stream-Joiner exakt die aktuelle Position erhält. - Ein Seek wird nicht an den Auslöser zurückgespiegelt (
broadcast(..., exclude=token)) — der hat lokal schon gesprungen, ein Echo würde nur seinen Transcode neu starten.
| Konstante | Wert | Bedeutung |
|---|---|---|
MEMBER_TIMEOUT |
30 s | Mitglied ohne Heartbeat/Poll entfernen. |
ROOM_GRACE |
60 s | (legacy) Leer-Raum-Gnadenfrist; Räume bleiben jetzt bestehen und werden nur beim expliziten Schließen entfernt. |
CHAT_HISTORY |
100 | Begrenzter Chat-Verlauf für Late Joiner. |
EVENT_BACKLOG |
500 | SSE-Queue-Limit pro Mitglied → sendet resync statt zu lecken. |
SHARE_EPSILON |
3 s | Positionsfenster, in dem Zuschauer eine Transcode-Session teilen. |
MAX_TRANSCODE_SESSIONS |
8 | Globales Limit gleichzeitiger ffmpeg-Sessions. |
Alle unter /api/syncplay/. Gast-erreichbare Endpoints sind aus login_required ausgenommen und stattdessen über gültigen Raum-Token + syncplay_enabled abgesichert. Host-Aktionen prüfen token == room.host_token.
| Methode | Pfad | Zweck |
|---|---|---|
| GET | config |
enabled, eingeloggter username, can_manage. |
| POST | join |
Erstellen/Beitreten; liefert token + Raum-snapshot. |
| GET | stream?token= |
SSE-Event-Stream. |
| POST | control |
play / pause / seek. |
| POST | report |
Positions-Heartbeat (+ buffering, aktuelle file). |
| POST | ready |
Ready-Umschalter. |
| POST | chat |
Chat-Nachricht senden. |
| POST | episode |
Host wählt Medium; mit countdown → synchrones Auto-Next. |
| GET | snapshot?token= |
Zustand neu holen, um nach Reload wieder beizutreten. |
| POST | leave |
Raum verlassen. |
| GET | rooms |
Raum-Verzeichnis (Name, Anzahl, läuft, Host, Passwort/Lock). |
| POST | close-room |
Raum per Name schließen (nur eingeloggter Owner). |
| POST |
kick / ban / transfer-host / close
|
Host-Moderation. |
| POST |
host-lock / max / password
|
Raum-Einstellungen (nur Host). |
| POST |
away / typing / reaction / track
|
Präsenz & Social. |
members, chat, left, play, pause, seek, sync, waiting, buffering, all_ready, media, history, countdown, host, host_lock, denied, typing, reaction, track, kicked, closed, resync.
- Gäste (nicht eingeloggt) erreichen Seite und API, weil diese Endpoints ausgenommen sind; Sicherheit kommt über Raum-Token +
syncplay_enabled. - Fürs Video sind die nötigen
/api/stream/*-Endpoints (check,start,index.m3u8, Segmente,status,stop,active) ausgenommen und perbefore_requestabgesichert: eingeloggt ODER gültigersp_guest-Session-Token (beim Join gesetzt).
transcoder.start_or_join_session(file, start_pos, share_key):
-
share_key = "sp:" + raumname(serverseitig inapi_stream_startaus dem vom Client gesendetensyncplay_tokenabgeleitet). - Nutzt die aktuelle Raum-Session wieder, wenn dieselbe Datei innerhalb von
SHARE_EPSILONzurstart_posangefragt wird; sonst frische Session,_shared[share_key]zeigt darauf. - Sessions sind refcountet:
stop_sessionzählt runter und killt ffmpeg erst bei 0.player.jsmacht beim Seek Stop-dann-Start, so bleiben die Refs balanciert; synchrone Seeks lassen alle Mitglieder auf eine Session an der neuen Position konvergieren. - Ohne
share_key(normale Bibliotheks-Wiedergabe) bleibt das Verhalten unverändert.
- Offene Raum-Namen werden in der Einstellung
syncplay_roomsgespeichert und beim Start viaensure_room()wiederhergestellt, sodass Räume einen Neustart überstehen. - Der Client legt sein
{token, room}imlocalStorageab; beim Laden ruft ersnapshot, um still wieder beizutreten (innerhalb vonMEMBER_TIMEOUT). Das Leave-beim-Reload-Beacon wurde entfernt, damit ein Neuladen die Mitgliedschaft nicht beendet.
syncplay_enabled ("0"/"1"), syncplay_rooms (JSON-Liste persistierter Raumnamen). Ein syncplay_enabled-Flag wird zudem in den Template-Context gegeben, damit der Sidebar-Eintrag bedingt rendert.
🇬🇧 English
Users
- Installation
- Getting Started
- Migration from AniWorld
- Configuration
- Web UI
- Download System
- Download History
- AutoSync
- Calendar
- Library
- Authentication
- Notifications
- Integrations
- SyncPlay
- Anime4K Upscaling
- Encoding
- Docker
- Supported Sites
Developers