Self-hosted service to sync your plex, jellyfin and emby play state. without relying on 3rd-party external services.
Find a file
Abdulmohsen 9063155a26
Merge pull request #758 from arabcoders/dev
Doc: update README/FAQ
2025-11-28 18:38:47 +03:00
.github Remove the majority of console commands as they no longer works with the multi-user setup we have. 2025-09-20 17:39:51 +03:00
bin A code clean up commit. 2024-12-27 14:28:32 +03:00
config Feat: Ability to disable or change the level of webhook logs. Ref #755 2025-11-18 17:35:51 +03:00
container/files prevent silent errors in container start up 2025-09-09 16:55:31 +03:00
frontend Feat: Ability to disable or change the level of webhook logs. Ref #755 2025-11-18 17:35:51 +03:00
guides [FIX] SSL disable via WebUI 2025-11-02 17:36:23 +03:00
migrations Added new event viewer to the WebUI. Migrated requests to use the new event system. 2024-08-18 18:35:19 +03:00
public Added back X_REQUEST_ID, frankenphp doesn't include it by default. 2025-05-18 00:10:11 +03:00
screenshots finalizing the move to user/pass auth. 2025-05-15 00:10:58 +03:00
src Fix: remove duplicate code due to local merge issue 2025-11-18 18:03:51 +03:00
tests Feat: Ability to disable or change the level of webhook logs. Ref #755 2025-11-18 17:35:51 +03:00
var Updated linefeed to be Unix style instead of CRLF. 2022-02-14 18:36:08 +03:00
.dockerignore Include screenshots inside the final docker container image. 2024-05-19 16:07:47 +03:00
.editorconfig Initial commit. 2022-02-10 16:41:48 +03:00
.gitignore update gitignore 2025-04-10 16:17:04 +03:00
.phpstorm.meta.php Added more checks to prevent needless progress updates. 2023-11-14 14:29:56 +03:00
CHANGELOG.md Add CHANGELOG to container. 2025-02-03 18:39:39 +03:00
composer.json Feat: Ability to disable or change the level of webhook logs. Ref #755 2025-11-18 17:35:51 +03:00
composer.lock Doc: update README/FAQ 2025-11-28 18:31:50 +03:00
Dockerfile Rebase on debian, ref #721 2025-09-14 16:59:02 +03:00
FAQ.md Doc: update README/FAQ 2025-11-28 18:31:50 +03:00
LICENSE Updated license file. 2024-01-15 19:02:45 +03:00
NEWS.md finalize v1.0 2025-10-29 16:27:18 +03:00
phpstan.neon Improve DFR page rendering to show more information 2025-09-19 18:40:42 +03:00
phpunit.xml.dist updated dependencies 2024-12-26 18:21:30 +03:00
pre_init.php Fixed performance regression in HTTP API 2025-01-31 18:24:47 +03:00
psalm.xml.dist general cleanups 2024-12-28 21:53:22 +03:00
README.md Doc: update README/FAQ 2025-11-28 18:31:50 +03:00

WatchState

Build Status MIT License Docker pull ghcr pull

This tool primary goal is to sync your backends users play state without relying on third party services, out of the box, this tool support Jellyfin, Plex and Emby media servers.

Updates

2025-10-29

After more than 3.5 years, 2.2k+ commits, 900+ stars, and 1 million+ downloads, were happy to announce the first stable release of WatchState v1.0.0.

This milestone marks the projects maturity and reliability for production use. We extend our thanks to everyone who provided feedback, reported bugs, and helped refine the tool your input has been invaluable.

The current feature set and stability meet our goals, so future work will focus on maintenance and bug fixes. Feedback and suggestions remain welcome, but major new features may be limited as we prioritize stability and long-term reliability.

Please refer to NEWS for the latest updates and changes.


Features

  • Sub-users support. Multi-users.
  • Sync backends play state (many-to-many or one-way).
  • Backup your backends play state into portable format.
  • Receive webhook events from media backends.
  • Find un-matched or mis-matched items.
  • Search your backend metadata.
  • Check if your media servers reporting same data via the parity checks.
  • Sync your watch progress/play state via webhooks or scheduled tasks.
  • Check if your media backends have stale references to old files.

If you like my work, you might also like my other project YTPTube, which is simple and to the point yt-dlp frontend to help download content from all supported sites by yt-dlp.

Install

If you prefer video format AlienTech42 YouTube Channel had a video about installing WatchState using unraid at this link. Much appreciated.

PS: I don't know the channel owner, but I appreciate the effort. There is small mistake in the video regarding the webhook URL, please copy the URL directly from the backends page. And this tool does support multi-users.


First, start by creating a directory to store the data, to follow along with this setup, create directory called data at your working directory. Then proceed to use your preferred method to install the tool.

Via compose file.

create your compose.yaml next to the data directory, and add the following content to it.

services:
    watchstate:
        image: ghcr.io/arabcoders/watchstate:latest
        # To change the user/group id associated with the tool change the following line.
        user: "${UID:-1000}:${UID:-1000}"
        container_name: watchstate
        restart: unless-stopped
        ports:
            - "8080:8080" # The port which the watchstate will listen on.
        volumes:
            - ./data:/config:rw # mount ./data in current directory to container /config directory.

Next, to run the container, use the following command

mkdir -p ./data && docker compose up -d

Via docker command.

mkdir -p ./data && docker run -itd --name watchstate \
          --user "${UID:-1000}:${GID:-${UID:-1000}}"  \
          --restart unless-stopped -p 8080:8080 \
          -v ./data:/config:rw \
          ghcr.io/arabcoders/watchstate:latest

Important

It's really important to match the user:, --user to the owner of the data directory, the container is rootless, as such it will crash if it's unable to write to the data directory.

It's really not recommended to run containers as root, but if you fail to run the container you can try setting the user: "0:0" or --user '0:0' if that works it means you have permissions issues. refer to FAQ to troubleshoot the problem.

Unraid users

For Unraid users You can install the Community Applications plugin, and search for watchstate it comes preconfigured. Otherwise, to manually install it, you need to add value to the Extra Parameters section in advanced tab/view. add the following value --user 99:100.

This has to happen before you start the container, otherwise it will have the old user id, and you then have to run the following command from terminal chown -R 99:100 /mnt/user/appdata/watchstate.

Podman instead of docker

To use this container with podman set compose.yaml user to 0:0. it will appear to be working as root inside the container, but it will be mapped to the user in which the command was run under.

Management

After starting the container, you can access the WebUI by visiting http://localhost:8080 in your browser.

Note

Note, For the first time, you will be prompted to create a new system user, this is a one time operation.

To add your backends, please click on the help button in the top right corner, and choose which method you want one-way or two-way sync. and follow the instructions.

Once you have added your backends and imported your data you should see something like

WebUI

Supported import methods

Currently, the tool supports three methods to import data from backends.

  • Scheduled Tasks.
    • A scheduled job that pull data from backends on a schedule.
  • On demand.
    • Pull data from backends on demand. By running the import task manually.
  • Webhooks.
    • Receive events from backends and update the database accordingly.

Note

Even if all your backends support webhooks, you should keep import task enabled. This help keep healthy relationship and pick up any missed events. For more information please check the webhook guide to understand webhooks limitations.

FAQ

Take look at this frequently asked questions page, or the guides for more in-depth guides on how to configure things.

Social channels

If you have quick questions or would like to chat with other users, you can join the Discord server. Please note that this is a solo project, so replies may take some time. Im based in the UTC+3 timezone.

Donate

If youd like to show appreciation for my work, please note that I dont accept donations. Instead, I encourage you to donate to a childrens charity of your choice. For example, The International Make-A-Wish foundation.