2022-02-19 20:56:51 +03:00
2022-02-19 16:38:24 +03:00
2022-02-18 17:09:27 +03:00
2022-02-10 16:41:48 +03:00
2022-02-10 16:41:48 +03:00
2022-02-19 16:38:24 +03:00
2022-02-18 17:09:27 +03:00
2022-02-10 16:41:48 +03:00
2022-02-19 16:38:24 +03:00

Warning

This is an early release version, expect bugs and edge cases that we haven't encountered. Please keep that in mind before running this tool. while its works for me, it might not work for your setup.

Watch State Sync (Early Preview)

A commandline based tool to sync watch state between different media servers.

Introduction

Ever wanted to sync your watch state without having to rely on 3rd party service like trakt.tv? then this tool is for you. I had multiple problems with Plex trakt.tv plugin which led to my account being banned at trakt.tv, and on top of that the plugin no longer supported. And I like to keep my own data locally if possible.

Supported Media servers.

  • Plex
  • Emby
  • Jellyfin

Install

create your docker-compose.yaml file

version: '3.3'
services:
    watchstate:
        image: arabcoders/watchstate:latest
        container_name: watchstate
        restart: unless-stopped
        environment:
            WS_UID: ${UID:-1000} # Set container operation user id.
            WS_GID: ${GID:-1000} # Set container operation group id.
            WS_WEBHOOK_ENABLE: 0 # Enable webhook listening server. Disabled by default.
            WS_CRON_IMPORT: 0 # Enable manual pulling of watch state from servers. Disabled by default.
            WS_CRON_EXPORT: 1 # Enable manual push of watch state back to servers. Disabled by default.
        ports:
            - "8081:80" # webhook listener port
        volumes:
            - ./:/config:rw # mount current directory to container /config directory.

After creating your docker-compose file, start the container.

docker-compose up -d

First time

You have to set up your servers in config/servers.yaml you will have examples inside the file, after editing the file remove the unused servers examples.

after configuring your servers at config/servers.yaml you should import your current watch state by running the following command.

docker exec -ti watchstate console state:import -vvrm --mapper-preload

TIP

To see the whole sync operation information you could run the command with -vvvr it will show all debug information, be careful it might crash your terminal depending on how many servers and media you have. the output is excessive.


Pull 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 enable webhook listening server, Edit the WS_WEBHOOK_ENABLE variable by setting its value to 1. If the variable does not exist or is set to value other than 1 it will not start the included caddy server.

If you don't want to use webhook events to update the watch state, and want to rely on manual polling the servers for state change, then set the value of WS_CRON_IMPORT to 1. By default, we run import every hour. However, you can change the schedule by adding another variable WS_CRON_IMPORT_AT and set it value to valid cron timing. for example, 0 */2 * * * it will run every two hours instead of 1 hour.

Push watch state

To manually push your watch state back to servers you can run the following command

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.

docker exec -ti watchstate console state:export -vvr --servers-filter 'server1,server2' 

If you want to automate the pushing of your watch state back to servers, 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 it value to valid cron timing. for example, 0 */3 * * * it will run every three hours instead of 90 minutes.

Memory usage (Import)

The default mapper we use is called MemoryMapper this mapper store the entire state in memory during import operation, by doing so we gain massive speed up. However, this approach has drawbacks mainly large memory usage. Depending on your media count, it might use 1GB+ or more of memory. Tests done on 2k movies and 70k episodes and 4 servers. Ofc the memory will be freed as as as the comparison is done.

However, If you are on a memory constraint system, there is an alternative mapper implementation called DirectMapper , this implementation works directly via db the only thing stored in memory is the api call body. which get removed as soon as it's parsed. The drawback for this mapper is it's like really slow for large libraries. It has to do 3x call to db for each time.

To see memory usage during the operation run the import command with following flags. -vvvrm these flags will redirect logger output, log memory usage and print them to the screen.

bash docker exec -ti watchstate console state:import -vvvrm

How to change Import mapper.

Set the environment variable WS_IMPORT_MAPPER to DirectMapper or edit the config/config.yaml and add the following lines

mapper:
    import:
        type: DirectMapper

Servers.yaml

Example of working server with all options. You can have as many servers as you want.

my_home_server:
    type: jellyfin|emby|plex # Choose one
    url: 'https://mymedia.example.com' # The URL for the media server api
    # User API Token.
    # Jellyfin: Create API token via (Dashboard > Advanced > API keys > +)
    # Emby: Create API token via (Manage Emby server > Advanced > API keys > + New Api Key)
    # Plex: see on how to get your plex-token https://support.plex.tv/articles/204059436
    token: user-api-token
    # Get your user ID. For Jellyfin/emby only.
    # Jellyfin : Dashboard > Server > Users > click your user > copy the userId= value
    # Emby: Manage Emby server > Server > Users > click your user > copy the userId= value
    # Plex: for plex managed users the X-Plex-Token acts as userId.
    user: user-id
    export:
        enabled: true # Enable export.
    import:
        enabled: true # Enable import.
    options:
        http2: false # Enable HTTP/2 support for faster http requests (server must support http 2.0).
        importUnwatched: false # By default, We do not import unwatched state to enable it set to true. 
        exportIgnoreDate: false # By default, we respect the server watch date. To override the check, set this to true.

Start Webhook Server.

The default container includes webserver to listen for webhook events, to enable the server. edit your docker-compose.yaml file, and set the environment variable WS_WEBHOOK_ENABLE to 1.

View the contents of your config/config.yaml and take note of the webhook.apikey. if the apikey does not exist run the following command.

docker exec -ti watchstate console config:generate 

it should populate your config.yaml with randomly generated key, and it will be printed to your screen. Adding webhook to your server the url will be dependent on how you expose the server, but typically it will be like this:

http://localhost:8081/?type=[SERVER_TYPE]&apikey=[YOUR_API_KEY]

[SERVER_TYPE]

Change the parameter to one of supported backends. e.g. emby, plex or jellyfin. it should match the server type that you are adding the link to, Each server type has different webhook payload.

[YOUR_API_KEY]

Change this parameter to your api key you can find it by viewing config/config.yaml under the key of webhook.apikey.

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/?type=jellyfin&apikey=[YOUR_API_KEY]

Notification Type:

Select the following events

  • Item Added
  • User Data Saved

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/?type=emby&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/?type=plex&apikey=[YOUR_API_KEY]

Click Save Changes

Finally

after making sure everything is running smoothly, edit your docker-compose.yaml file and enable the exporting of your watch state back to servers. by setting the value of WS_CRON_EXPORT to 1.

restart your docker container for changes to take effect.

FAQ


Q1: How to update new server watched state without overwriting the existing watch state?

Add the server, disable the import operation, and enable export. Then run the following commands.

docker exec -ti watchstate console state:export -vvrm --ignore-date --force-full --servers-filter [SERVER_NAME]

[SERVER_NAME]

Replace [SERVER_NAME] with what you have chosen to name your server in config e.g. my_home_server

this command will force export your current database state back to the selected server. If the operation is successful you can then enable the import feature if you want.

Q2: Is there support for Multi-user setup?

No, Not at this time. The database design centered on single user. However, It's possible to run container for each user.

Note: for Plex managed users you can log in via managed user and then extract the user x-plex-token (this token acts as userId for plex)

For jellyfin/emby, you can use same api-token and just replace the userId.

Description
Self-hosted service to sync your plex, jellyfin and emby play state. without relying on 3rd-party external services.
Readme MIT 11 MiB
Languages
PHP 76.7%
Vue 16.5%
CSS 5.7%
JavaScript 0.8%
Shell 0.1%
Other 0.1%