344 lines
12 KiB
Markdown
344 lines
12 KiB
Markdown
# WatchState
|
|
|
|
WatchState is a CLI based tool to sync your watch state between different media servers, without relying on 3rd parties
|
|
services, like trakt.tv, This tool support `Plex Media Server`, `Emby` and `Jellyfin` out of the box currently, with
|
|
plans for future expansion for other media servers.
|
|
|
|
# Breaking Change
|
|
|
|
If you are using old version of the tool i.e. before (2022-05-08) you need to run manual import to populate the new
|
|
database. We had massive code/db changes. The new database name should be `watchstate_v0.db`, if you don't have a
|
|
database named `watchstate.db` then there is nothing to do.
|
|
|
|
to manually import the run this command with --force-full flag to get all previous records.
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console state:import --force-full -vvrm
|
|
```
|
|
|
|
# Install
|
|
|
|
create your `docker-compose.yaml` file:
|
|
|
|
```yaml
|
|
version: '3.3'
|
|
services:
|
|
watchstate:
|
|
image: arabcoders/watchstate:latest
|
|
container_name: watchstate
|
|
restart: unless-stopped
|
|
environment:
|
|
# For more environment variables please read at the bottom of this page.
|
|
WS_UID: ${UID:-1000} # Set container operation user id.
|
|
WS_GID: ${GID:-1000} # Set container operation group id.
|
|
ports:
|
|
- "8081:80" # webhook listener port
|
|
volumes:
|
|
- ${PWD}/:/config:rw # mount current directory to container /config directory.
|
|
```
|
|
|
|
After creating your docker-compose file, start the container.
|
|
|
|
```bash
|
|
$ docker-compose up -d
|
|
```
|
|
|
|
# First time
|
|
|
|
Run the following command to see all available commands you can also run help on each command to get more info.
|
|
|
|
```bash
|
|
# Show all commands.
|
|
$ docker exec -ti watchstate console list
|
|
|
|
# Show help document for each command.
|
|
$ docker exec -ti watchstate console help state:import
|
|
```
|
|
|
|
---
|
|
|
|
After starting the container, you have to add your media servers, to do so run the following command
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console servers:manage --add -- [SERVER_NAME]
|
|
```
|
|
|
|
This command will ask you for some questions to add your servers, you can run the command as many times as you want, if
|
|
you want to edit the config again or if you made mistake just run the same command without `--add` flag.
|
|
|
|
After adding your servers, You should import your current watch state by running the following command.
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console state:import -vvrm
|
|
```
|
|
|
|
---
|
|
|
|
# Pulling watch state.
|
|
|
|
now that you have imported your watch state, you can stop manually running the command again. and rely on the webhooks
|
|
to update the watch state. To start receiving webhook events from servers you need to do few more steps.
|
|
|
|
### Enable webhooks events for specific server.
|
|
|
|
To see the server specific api key run the following command
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console servers:view --servers-filter [SERVER_NAME] -- webhook.token
|
|
```
|
|
|
|
If you see 'Not configured, or invalid key.' or empty value. run the following command
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console servers:edit --regenerate-api-key -- [SERVER_NAME]
|
|
```
|
|
|
|
---
|
|
|
|
#### TIP:
|
|
|
|
If you have multiple plex servers and use the same plex account for all of them, you have to unify the API key, by
|
|
running the following command
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console servers:unify plex
|
|
Plex global webhook API key is: [random_string]
|
|
```
|
|
|
|
The reason is due to the way plex handle webhooks, And to know which webhook request belong to which server we have to
|
|
identify the servers, The unify command will do the necessary adjustments to handle multi plex server setup. for more
|
|
information run.
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console help servers:unify
|
|
```
|
|
|
|
This command is not limited to plex, you can unify API key for all supported backend servers.
|
|
|
|
---
|
|
|
|
If you don't want to use webhooks and want to rely only on scheduled task for importing, then set the value
|
|
of `WS_CRON_IMPORT` to `1`. By default, we run the import command every hour. However, you can change the scheduled task
|
|
timer by adding another variable `WS_CRON_IMPORT_AT` and set its value to valid cron expression. for
|
|
example, `0 */2 * * *` it will run every two hours instead of 1 hour. beware, this operation is somewhat costly as it's
|
|
pulls the entire server library.
|
|
|
|
---
|
|
|
|
#### TIP
|
|
|
|
You should still have `WS_CRON_IMPORT` enabled as sometimes plex does not really report new items, or report them in a
|
|
way that is not compatible with the way we handle webhooks events. running the import command regularly helps keep
|
|
healthy `GUIDS <> serverInternalID mapping` relations.
|
|
|
|
---
|
|
|
|
# Export watch state
|
|
|
|
To manually export your watch state back to servers you can run the following command
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console state:export -vvr
|
|
```
|
|
|
|
to sync specific server/s, use the `--servers-filter` which accept comma seperated list of server names.
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console state:export -vvr --servers-filter 'server1,server2'
|
|
```
|
|
|
|
To enable the export scheduled task set the value of `WS_CRON_EXPORT` to `1`. By default, we run export every 90
|
|
minutes. However, you can change the schedule by adding another variable called `WS_CRON_EXPORT_AT` and set its value to
|
|
valid cron expression. for example, `0 */3 * * *` it will run every three hours instead of 90 minutes.
|
|
|
|
# Start receiving webhook events.
|
|
|
|
By default, the official container includes a small http server exposed at port `80`, we officially don't support HTTPS
|
|
inside the container for the HTTP server. However, for the adventurous people we expose port 443 as well, as such you
|
|
can customize the `docker/files/nginx.conf` to support SSL. and do the necessary adjustments. However, do not expect us
|
|
to help with it.
|
|
|
|
#### Example nginx reverse proxy.
|
|
|
|
```nginx
|
|
server {
|
|
server_name watchstate.domain.example;
|
|
|
|
location / {
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header X-Forwarded-Protocol $scheme;
|
|
proxy_set_header X-Forwarded-Host $http_host;
|
|
proxy_pass http://localhost:8081/;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Adding webhook to server
|
|
|
|
to your server the url will be dependent on how you expose the server, but typically it will be like this:
|
|
|
|
#### Webhook URL
|
|
|
|
Via reverse proxy : `https://watchstate.domain.example/?apikey=[WEBHOOK_TOKEN]`.
|
|
|
|
Directly to container: `https://localhost:8081/?apikey=[WEBHOOK_TOKEN]`
|
|
|
|
If your server support sending headers then omit the query parameter '?apikey=[WEBHOOK_TOKEN]', and add new this header
|
|
|
|
```http request
|
|
X-apikey: [WEBHOOK_TOKEN]
|
|
```
|
|
|
|
it's more secure that way.
|
|
|
|
#### [WEBHOOK_TOKEN]
|
|
|
|
Should match the server specific ``webhook.token`` value. Refer to the steps described
|
|
at **[Steps to enable webhook servers](#enable-webhooks-events-for-specific-server)**.
|
|
|
|
# Configuring Media servers to send webhook events.
|
|
|
|
#### Jellyfin (Free)
|
|
|
|
go to your jellyfin dashboard > plugins > Catalog > install: Notifications > Webhook, restart your jellyfin. After that
|
|
go back again to dashboard > plugins > webhook. Add A `Add Generic Destination`,
|
|
|
|
##### Webhook Name:
|
|
|
|
Choose whatever name you want.
|
|
|
|
##### Webhook Url:
|
|
|
|
`http://localhost:8081`
|
|
|
|
##### Notification Type:
|
|
|
|
Select the following events
|
|
|
|
* Item Added
|
|
* User Data Saved
|
|
* Playback Start
|
|
* Playback Stop
|
|
|
|
##### Item Type:
|
|
|
|
* Movies
|
|
* Episodes
|
|
|
|
### Send All Properties (ignores template)
|
|
|
|
Toggle this checkbox.
|
|
|
|
### Add Request Header
|
|
|
|
Key: `X-apikey`
|
|
|
|
Value: `[YOUR_API_KEY]`
|
|
|
|
Click `save`
|
|
|
|
#### Emby (you need emby premiere to use webhooks)
|
|
|
|
Go to your Manage Emby Server > Server > Webhooks > (Click Add Webhook)
|
|
|
|
##### Webhook Url:
|
|
|
|
`http://localhost:8081/?apikey=[YOUR_API_KEY]`
|
|
|
|
##### Webhook Events
|
|
|
|
Select the following events
|
|
|
|
* Playback events
|
|
* User events
|
|
|
|
Click `Add Webhook`
|
|
|
|
#### Plex (you need PlexPass to use webhooks)
|
|
|
|
Go to your plex WebUI > Settings > Your Account > Webhooks > (Click ADD WEBHOOK)
|
|
|
|
##### URL:
|
|
|
|
`http://localhost:8081/?apikey=[YOUR_API_KEY]`
|
|
|
|
Click `Save Changes`
|
|
|
|
# Webhook limitations
|
|
|
|
# Plex
|
|
|
|
Does not send webhooks events for "marked as watched/unwatched", or you added more than 1 item at time i.e. folder
|
|
import.
|
|
|
|
If you have multi-user setup, Plex will still report the admin account user id as 1 even though when you get the list
|
|
of users ids it shows completely different user ID, so when you initially set up your server for multi-user, select your
|
|
admin account and after finishing you have to set the value manually to `1`. to do so please do the following
|
|
|
|
```bash
|
|
$ docker exec -ti watchstate console servers:edit --key user --set 1 -- [SERVER_NAME]
|
|
```
|
|
|
|
This only for the main admin account, other managed/home/external users, you should leave the user as it is reported by
|
|
plex.
|
|
|
|
# Emby
|
|
|
|
Emby does not send webhooks events for newly added items.
|
|
|
|
# Jellyfin
|
|
|
|
If you don't select a user id there, sometimes the plugin will send itemAdd event without user info, and thus will fail
|
|
the check if you happen to enable `match user id`.
|
|
|
|
# Tool specific environment variables.
|
|
|
|
- (string) `WS_DATA_PATH` Where key data stored (config|db).
|
|
- (string) `WS_TMP_DIR` Where temp data stored. (logs|cache). Defaults to `WS_DATA_PATH` if not set.
|
|
- (string) `WS_TZ` Set timezone for example, `UTC`
|
|
- (bool) `WS_WEBHOOK_DEBUG` enable debug mode for webhook events.
|
|
- (bool) `WS_REQUEST_DEBUG` enable debug mode for pre webhook request.
|
|
- (integer) `WS_WEBHOOK_TOKEN_LENGTH` how many bits for the webhook api key generator.
|
|
- (bool) `WS_LOGGER_STDERR_ENABLED` enable stderr output logging.
|
|
- (string) `WS_LOGGER_STDERR_LEVEL` level to log (DEBUG|INFO|NOTICE|WARNING|ERROR|CRITICAL|ALERT|EMERGENCY).
|
|
- (bool) `WS_LOGGER_FILE_ENABLE` enable file logging.
|
|
- (string) `WS_LOGGER_FILE_LEVEL` level to log (DEBUG|INFO|NOTICE|WARNING|ERROR|CRITICAL|ALERT|EMERGENCY).
|
|
- (string) `WS_LOGGER_FILE` full path for log file. By default, it's stored at `$(WS_TMP_DIR)/logs/app.log`
|
|
- (bool) `WS_LOGGER_SYSLOG_ENABLED` enable syslog logger.
|
|
- (int) `WS_LOGGER_SYSLOG_FACILITY` syslog logging facility
|
|
- (string) `WS_LOGGER_SYSLOG_LEVEL` level to log (DEBUG|INFO|NOTICE|WARNING|ERROR|CRITICAL|ALERT|EMERGENCY).
|
|
- (string) `WS_LOGGER_SYSLOG_NAME` What name should logs be under.
|
|
- (int) `WS_CRON_IMPORT` enable import scheduled task.
|
|
- (string) `WS_CRON_IMPORT_AT` cron expression timer.
|
|
- (string) `WS_CRON_IMPORT_DEBUG_LEVEL` set debug level, to see unmatched items set to `-vv`. Defaults to `-v`
|
|
- (int) `WS_CRON_EXPORT` enable export scheduled task.
|
|
- (string) `WS_CRON_EXPORT_AT` cron expression timer.
|
|
- (string) `WS_CRON_EXPORT_DEBUG_LEVEL` set debug level. Defaults to `-v`
|
|
- (int) `WS_CRON_PUSH` enable push scheduled task.
|
|
- (string) `WS_CRON_PUSH_AT` cron expression timer.
|
|
- (string) `WS_CRON_PUSH_DEBUG_LEVEL` set debug level. Defaults to `-v`
|
|
- (int) `WS_CRON_CACHE` enable caching of GUIDs relations.
|
|
- (string) `WS_CRON_CACHE_AT` cron expression timer.
|
|
- (string) `WS_CRON_CACHE_DEBUG_LEVEL` set debug level. Defaults to `-v`
|
|
- (string) `WS_LOGS_PRUNE_AFTER` Delete logs older than specified time, set to `disable` to disable logs pruning. it
|
|
follows php [strtotime](https://www.php.net/strtotime) function rules.
|
|
- (bool) `WS_DEBUG_IMPORT` Whether to log invalid GUID items from server in `${WS_TMP_DIR}/debug`.
|
|
|
|
# Container specific environment variables.
|
|
|
|
- (int) `WS_NO_CHOWN` do not change ownership needed paths inside container.
|
|
- (int) `WS_DISABLE_HTTP` disable included HTTP Server.
|
|
- (int) `WS_DISABLE_CRON` disable included Task Scheduler.
|
|
- (int) `WS_UID` Container app user ID.
|
|
- (int) `WS_GID` Container app group ID.
|
|
|
|
---
|
|
|
|
# FAQ
|
|
|
|
For some common questions, Take look at this [frequently asked questions](FAQ.md) page.
|