# WatchState ![Build Status](https://github.com/arabcoders/WatchState/actions/workflows/build.yml/badge.svg) ![MIT License](https://img.shields.io/github/license/arabcoders/WatchState.svg) ![Docker pull](https://img.shields.io/docker/pulls/arabcoders/watchstate.svg) ![ghcr pull](https://ghcr-badge.elias.eu.org/shield/arabcoders/watchstate/watchstate) This tool primary goal is to sync your backends **users** play state without relying on third party services, out of the box, this tool supports `Jellyfin`, `Plex` and `Emby` media servers. # Updates ### 2026-10-21 **Updating to WatchState v1.10.9 or later is recommended** following the PHP2Shell disclosure ([CVE-2026-109618](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w) and [CVE-2026-109619](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)). These vulnerabilities affect the PHP runtime and can allow unauthenticated remote code execution; they are not bugs in WatchState itself. Pull the updated container image and recreate the container, as restarting alone does not update the bundled runtime. ### 2026-09-15 Path matching is now enabled by default. WatchState will generate `guid_path` values for new and refreshed movie and episode records unless `WS_GUID_PATH_ENABLED` is explicitly disabled. Existing records still need a full import before they can match by path. See the [path matching guide](/guides/path-match.md) for details. Please refer to [NEWS](/NEWS.md) for the latest updates and changes. ------ # Features * **Multi-users** support via `identities`. * Sync backends play state (`many-to-many` or `one-way`). * Backup your backends play state into `portable` format. * Receive [webhook](guides/webhooks.md) events from media backends. * Find record issues with [Media Health](guides/media-health.md). * View local user statistics and export them as Markdown or JSON. * Search your backend metadata. * Sync your watch [progress/play](FAQ.md#sync-watch-progress) state via webhooks or scheduled tasks. ### If you like my work, you might also like my other projects: * [YTPTube](https://github.com/arabcoders/ytptube): Media download manager, automation interface, and media library preparation layer for yt-dlp. * [Millwright](https://github.com/arabcoders/millwright): is a self-hosted automation interface for FFmpeg. # Install If you prefer video format [AlienTech42 YouTube Channel](https://www.youtube.com/@AlienTech42) had a video about installing WatchState using unraid [at this link](https://www.youtube.com/watch?v=XoztOwGHGxk). 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. ```yaml 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 ```bash mkdir -p ./data && docker compose up -d ``` ### Via docker command. ```bash 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] > Match `user:` and `--user` to the owner of the `data` directory. The container runs rootless and exits if it cannot > write to that directory. > > Running the container as root is not recommended. If the container fails to start, try `user: "0:0"` or > `--user '0:0'`. If that works, the problem is a permissions issue. See the [FAQ](FAQ.md) for troubleshooting steps. ### Unraid users For `Unraid` users, install the `Community Applications` plugin and search for **watchstate**. It is preconfigured. To install it manually, add `--user 99:100` to the `Extra Parameters` section in the advanced tab. Set this before starting the container. If the container already created files with another user ID, run `chown -R 99:100 /mnt/user/appdata/watchstate` from a terminal. ### Podman instead of docker To use this container with `podman`, set `user` to `0:0` in `compose.yaml`. The container appears to run as root, but Podman maps it to the user who ran the command. # Management After starting the container, you can access the WebUI by visiting `http://localhost:8080` in your browser. > [!NOTE] > On first access, you will be prompted to create a system user. This is a one-time operation. WatchState matches items using local media paths by default. See the [path matching guide](guides/path-match.md) for details. To add your backends, click the help button in the top-right corner and choose [one-way](guides/one-way-sync.md) or [two-way](guides/two-way-sync.md) sync. Follow the instructions in the selected guide. Once you have added your backends and imported your data you should see something like ![WebUI](/screenshots/index.jpg) ### Supported import methods Currently, the tool supports three methods to import data from backends. - **Scheduled Tasks**. - `A scheduled job that pulls data from backends.` - **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] > Keep the import task enabled even when all your backends support webhooks. It can pick up missed events. See the > [webhook guide](/guides/webhooks.md) for backend limitations. # FAQ Take look at this [frequently asked questions](FAQ.md) page, or the [guides](/guides/) for more in-depth guides on how to configure things. # Social channels If you have questions or want to chat with other users, join the [Discord server](https://discord.gg/haUXHJyj6Y). This is a solo project, so replies may take some time. I’m based in the `UTC+3` timezone. # Donate I don’t accept donations. If you want to support the project, consider donating to a children’s charity such as [Make-A-Wish](https://worldwish.org). # Disclaimer AI-based tools may have been used to assist with parts of this project. Regardless of how a change is produced, every change is reviewed and approved by the human maintainer before it is included.