WatchState Documentation

repository·master·Indexed 23 days ago

https://github.com/arabcoders/watchstate

A tool for synchronizing user play states across multiple media servers including Jellyfin, Plex, and Emby without third-party dependencies. WatchState provides centralized management of watch progress, media health auditing, and play state data backups. It features multi-user support via identities, one-way and many-to-many sync modes, and a comprehensive HTTP API for managing backends, webhooks, and metadata search.

Tokens
39.9K
Snippets
37
Records
241
Agent score
80%

What's inside WatchState

  1. WatchState Core Features

    master

    WatchState is a tool designed to sync user play states across different media backends (supporting Jellyfin, Plex, and Emby) without relying on third-party services.

    Key capabilities include:

    • Multi-user support: Managed via identities.
    • Sync modes: Supports many-to-many or one-way backend play state synchronization.
    • Data Portability: Backup backend play states into a portable format.
    • Event Handling: Receive webhook events from media backends.
    • Media Auditing: Use Media Health to find record issues like GUID conflicts, duplicate file references, or structural metadata disagreements.
    • Metadata Search: Search through your backend metadata.
    • Progress Sync: Sync watch progress/play state via webhooks or scheduled tasks.
  2. Understand Identity Modes: Single Backend vs. Multi-Backend

    master

    WatchState supports two modes for managing multiple users (identities) depending on your backend configuration:

    Multi-Backend Mode (Default)

    Used when you have 2 or more backends configured.

    • Matching: The system attempts to match users across different backends based on usernames.
    • Grouping: Requires user grouping/mapping to associate the same person across multiple services.
    • Syncing: Creates unified configurations that sync play states across all matched backends.

    Single Backend Mode

    Used when you have exactly 1 backend configured. Ideal for dedicated servers or backing up individual user play states.

    • No Matching: Each user from the backend gets their own independent configuration automatically.
    • Simplified Setup: No need to create user groups or mappings.
    • PIN Support: You can still set PINs for protected users via the mapper.

    To enable Single Backend Mode:

    1. Navigate to Configuration > Identities > Match & Provision.
    2. Toggle the "Allow single backend identities" switch (visible only when exactly 1 backend is configured).
  3. How webhooks work in WatchState

    master

    Webhooks provide near-instant synchronization by triggering updates based on user actions (play, pause, stop, etc.) on a media backend. When an action occurs, the backend sends a webhook to the WatchState API, which processes the data and triggers events across other connected backends.

    Important Reliability Note: Webhooks are not 100% reliable and may be missed or delayed. They should not be used as the sole synchronization method. To ensure data integrity, always use webhooks in combination with scheduled tasks (e.g., running Import and Export tasks every 12 or 24 hours).

  4. How Two-Way Sync works in WatchState

    master

    Two-way sync allows WatchState to exchange play progress and watch state between multiple backends (e.g., Plex, Jellyfin, Emby) using the import and export mechanisms.

    • Import Process: WatchState pulls play and progress information from the backend and stores it locally. Older data is kept as metadata and does not overwrite the latest local watch state.
    • Export Process: WatchState compares the backend's last sync date with local changes to build updates.
      • Push mode: Used for small changes.
      • Full export: Used for larger changes, comparing remote and local data.

    Note: You should keep Enable Import active unless you want a backend to remain in metadata-only mode.

  5. Single backend backup vs. Multi-backend sync

    master

    It is important to choose the correct mode based on your goal:

    • Single Backend Backup Mode: Use this if you have one media server and want to preserve/restore user playstate in case of server failure or data loss. It treats users as individual identities for local storage.
    • Multi-Backend Sync (Two-Way Sync): Use this if you have multiple media servers and want to synchronize playstate between them so that progress on one server is reflected on the others.
  6. Global HTTP API Request and Response Standards

    master

    When interacting with the WatchState API, follow these global conventions:

    Request Headers

    • JSON Bodies: Always send Content-Type: application/json for request bodies. Auto-parsing is only supported for application/json and application/*+json.

    Identity Context

    Many endpoints operate on a per-identity basis. You can specify a specific identity context using:

    • Header: X-User: <name>
    • Query String: ?user=<name>
    • If omitted, the main identity context is used.

    Response Formats

    • Success: Returns a JSON object or array. Informational success messages use the format: {"info": {"code": 200, "message": "..."}}.
    • Errors: Error responses use the format: {"error": {"code": 400, "message": "..."}}.

    Pagination and Data

    • Pagination: Most paginated endpoints support page and perpage parameters. Metadata is included in responses for History, Media Health, and events.
    • Raw Backend Data: Append raw=true to several backend endpoints to receive unnormalized, backend-specific upstream payloads.
    • Real-time: Real-time endpoints (like logs and commands) utilize Server-Sent Events (SSE).
  7. How player streaming works with HLS

    master

    WatchState implements a player streaming system using HLS (HTTP Live Streaming). Access to these routes is gated by a short-lived playback token, which must be generated via POST /v1/api/system/sign/{id}.

    1. Master Playlist

    GET /v1/api/player/playlist/{token}[/{fake...}] Builds the top-level HLS master playlist containing video and subtitle tracks.

    • Query: sd (segment duration, default 6.000), debug (verbose debugging).

    2. Segment Playlist

    GET /v1/api/player/m3u8/{token}[/{fake...}] Builds the VOD segment playlist which references individual .ts segments.

    3. Media Segments

    GET /v1/api/player/segments/{token}/{segment}[.{type}] Returns a single MPEG-TS segment. Segments can be direct-played or transcoded using ffmpeg.

    • Query: sd (override final segment duration).
    • Headers: X-Transcode-Time, X-Ffmpeg, and X-Transcode-Config (if debug mode is enabled).

    4. Subtitles

    WatchState supports both internal and external subtitles.

    • Subtitle Playlist: GET /v1/api/player/subtitle/{token}/{type}.{source}{index}.m3u8 (Builds a one-track HLS playlist).
    • Subtitle Stream: GET /v1/api/player/subtitle/{token}/{source}{index}.{ext} (Converts tracks to WebVTT and streams them).

    Subtitle Source Types:

    • x: External subtitle files (supports vtt, webvtt, srt, ass).
    • i: Internal subtitle streams.

    Notes:

    • Segment generation is serialized per playback token using a lock file.
    • External subtitles can be burned into the video stream.
  8. How Path Matching suffix rules work

    master

    Path matching works by normalizing backend-reported paths (replacing \ with /, collapsing duplicate separators, and lowercasing segments) and then hashing specific trailing suffixes. It ignores leading path segments, which allows different roots to match.

    Suffix Rules by Type

    TypeExample Backend PathSuffix Used for MatchingRule
    Movies/foo/bar/movies/movie.mkv/movies/movie.mkvFinal 2 path segments
    Episodes/foo/bar/tv/show/episode.mkv/tv/show/episode.mkv/1/1Final 3 path segments plus logical season/episode
    Episodes (Show)/foo/bar/tv/show/episode.mkv/tv/showThe 2 directory segments immediately before the file

    Limitations

    • Insufficient Segments: If a path has too few segments to build the required suffix (e.g., a movie with only one segment), path GUID generation is skipped.
    • Root Stripping: WatchState does not support manual root stripping or base-path remapping. It relies entirely on the stability of the trailing suffixes described above.
  9. Understand Import vs Export terminology in WatchState

    master

    WatchState synchronizes data between configured backends using two primary modes:

    • Import: Allows WatchState to read data from a backend. This backend acts as a source.
    • Export: Allows WatchState to write data to a backend. This backend acts as a target.

    Usage Scenarios:

    • To sync Plex data to Jellyfin: Enable Import on Plex and Export on Jellyfin.
    • To allow a backend to both provide and receive updates: Enable both Import and Export on that backend.
  10. Run a Media Health audit

    master

    Media Health is an audit for the main WatchState database that identifies issues like GUID conflicts, duplicate references, and metadata disagreements. To run an audit via the WebUI:

    1. Navigate to Diagnostics > Media Health.
    2. Click Queue Audit.
    3. Wait for the background task to complete.
    4. Click Reload to view the results.

    Note: The WebUI displays the latest completed report. If the data has changed since the report was generated, a stale warning will appear, and you should queue a fresh audit. You can use the Export menu to download the full report for external processing.

  11. Fix and refresh workflow for Media Health issues

    master

    When resolving issues found in a Media Health audit, follow this workflow:

    1. Prioritize high-priority records like guid_conflict and duplicate_guid.
    2. Fix the metadata in the backend that owns the problematic item.
    3. Refresh the data in WatchState:
      • For a single backend: Go to Configuration > Backends, open the backend's actions menu, and run 9. Force metadata-only import from this backend.
      • For multiple backends: Run a global metadata-only import via CLI:
    state:import -f -v --metadata-only
    1. Queue a new Media Health audit to verify the fixes.
  12. Automate One-Way Sync with Scheduled Tasks

    master

    To automate the synchronization process, you can enable scheduled tasks and configure their frequency using CRON expressions.

    1. Enable Tasks: Go to the Tasks page and toggle the switches for both Import and Export.
    2. Configure Schedule: To change how often tasks run, go to Configuration > Environment and add the following environment variables:
      • WS_CRON_EXPORT_AT: Accepts a CRON expression for the export task.
      • WS_CRON_IMPORT_AT: Accepts a CRON expression for the import task.

    Example: To run the export task every 6 hours, set WS_CRON_EXPORT_AT to 0 */6 * * *.