Upgrading from PlexWatch 1.x¶
PlexWatch is now called MediaWatch. Follow this page to move a PlexWatch 1.x install to MediaWatch 2.0.
Three things are not automatic and have to be done by hand:
- change the container image path (Docker/Unraid only)
- set
DISCORD_GUILD_ID - check the result of the automatic
config.jsonconversion
Everything else happens on the first start: config.json is converted to config.yaml,
a flat user_mapping.json is converted to the platform-keyed format, and the slash
commands that 1.x registered globally are unregistered.
What a 1.x deployment looks like¶
data/config.jsonas the configuration filedata/user_mapping.jsonin the flat formatdata/dashboard_message_id.json- slash commands registered globally
- image
ghcr.io/nichtlegacy/plexwatch
Before you start¶
Back up the whole data/ directory. The conversion never deletes or rewrites
config.json, but a backup is the only way back if you decide to roll back
(see Rolling back to 1.x).
Docker / Unraid¶
-
Stop the container.
-
Change the image path:
These are two separate GHCR packages, not a rename. plexwatch keeps its existing
tags and receives no further updates, so plexwatch:latest will never become 2.0.
Staying on the old path means staying on 1.x.
- Add the
DISCORD_GUILD_IDenvironment variable. It is required in 2.0. Without it the bot stops at startup with an explicit message, because slash commands are published guild-scoped and there would be no guild to publish them into.
To find the ID: Discord -> User Settings -> Advanced -> enable Developer Mode, then right-click your server name -> Copy Server ID.
- Leave the
/app/datavolume mount as it is. The conversion reads and writes inside it, so the container needs write access to that directory.
2.0 no longer runs the bot as root: the container starts as root, takes ownership of
data/ and logs/, and then drops to PUID/PGID (default 1000:1000). On every
start the entrypoint re-owns the whole data/ directory to that account, your 1.x
files included. Set PUID/PGID to your host account before the first start, so you
can edit the converted config.yaml from the host. If you forget, set them and
recreate the container with docker compose up -d: a plain docker restart keeps the
old environment. On Unraid, set PUID=99 and PGID=100; applying the template
recreates the container.
-
Optional, only if you use them:
TAUTULLI_URL/TAUTULLI_API_KEY(Plex only).JELLYFIN_URL/JELLYFIN_API_KEYare only needed if you switch to Jellyfin. -
Start the container and follow the log (
docker logs -f <container>).
On Unraid, points 2 to 4 are template fields: Repository, a new variable
DISCORD_GUILD_ID, and the two variables PUID=99 / PGID=100.
There is no MediaWatch Unraid template yet. The existing PlexWatch template in Community
Applications is maintained by a third party, not by this project, and keeps pointing at
the frozen plexwatch package. Until a MediaWatch template exists, edit the two fields on
your existing container by hand. Do not expect the old template to follow this release.
Local installation¶
-
Stop the bot.
-
Update the checkout. 2.0 needs Python 3.12; if your virtualenv is older, recreate it as described in Running locally.
-
Add
DISCORD_GUILD_IDto your.env. Every variable is listed in the environment reference. -
Start the bot:
The log file is now logs/mediawatch_debug.log, not logs/plexwatch_debug.log.
What the first start does¶
With data/config.json present and no data/config.yaml, the log shows:
======================================================================
MediaWatch 2.0: converting legacy config.json to config.yaml
======================================================================
Converted presence.sections to presence.libraries (Movies, Shows). The display name and emoji now come from the library block.
Source: .../data/config.json (left untouched)
Target: .../data/config.yaml (created)
Migrated sections: media_server, dashboard, plex, presence, cache, sabnzbd
Renamed section 'plex_sections' to its 2.0 name 'plex'
Set media_server.type to 'plex' (1.x was Plex-only)
config.json is NOT read anymore - edit config.yaml from now on.
Review data/config.yaml.example for the options added in 2.0.
======================================================================
The presence.sections line only appears if your config.json had that list.
What the conversion does:
plex_sectionsbecomesplex(same content, 2.0 section name)media_server.typeis set toplex, because 1.x was Plex-onlypresence.sectionsbecomespresence.libraries(library names only). The presence now takes display name and emoji from theplexblock, so a presence label that differed from the dashboard changes- every other key (
dashboard,cache,sabnzbd, anything custom) is carried over unchanged - options that only exist in 2.0 are not written into the file; they fall back to their defaults
config.jsonstays on disk, untouched, and is ignored from then on
data/user_mapping.json is rewritten in place from the flat 1.x format:
to the platform-keyed format:
The global slash commands from 1.x are unregistered once, right after the guild sync:
Cleared globally registered slash commands (1.x leftovers); commands are published to the configured guild from now on.
This runs once per installation. The outcome is recorded in data/runtime_state.json,
so it does not cost an API call on every start.
Verify after the first start¶
data/config.yamlexists. It opens with a generated header comment explaining where it came from, followed bymedia_server:/type: plex- your library sections, dashboard name, presence texts and SABnzbd keywords are in
config.yamland match whatconfig.jsonhad data/config.jsonis still there and unchanged- in Discord, every command appears exactly once, and
/mappingand/reload_configare present. Discord clients cache the command list, so if you still see duplicates, wait a minute or restart the client (Ctrl+R) /mapping listshows your mappings under theplexkey- the dashboard updates in the configured channel
The converted file only holds what 1.x had. Everything added in 2.0 runs on its default:
- new blocks:
server,display,global_stats,stream_controls,stream_details,user_stats - new keys in existing blocks:
presence.enabled,presence.activity_type,presence.status,presence.offline_status,presence.auth_failed_text,cache.user_stats_ttl,sabnzbd.show_when_empty,sabnzbd.show_status_icons,sabnzbd.diskspace_free_key,sabnzbd.diskspace_total_key
data/config.yaml.example documents all of them; copy the blocks you want to change into
your config.yaml. The configuration reference lists
every default.
Once the deployment is confirmed healthy, you can delete data/config.json. Until then
the bot logs on every start that it is being ignored.
If the bot stops with "Config migration failed"¶
The conversion only aborts when it cannot produce a correct config.yaml: a config.json
it cannot read or parse, a payload that is not a JSON object, or a data/ directory it
cannot write to. config.json is never modified in that case. The log names the reason.
To continue manually:
Then transfer your settings from config.json, put the plex_sections block under the
plex key, and set media_server.type to plex.
Rolling back to 1.x¶
Before you stop 2.0, run !sync clear in the server. 1.x never touches the guild scope,
so the 2.0 guild commands would otherwise stay registered and do nothing under 1.x.
Then point the image back at ghcr.io/nichtlegacy/plexwatch:latest (locally: check out
the v1.1.3 tag). config.json was never touched, so 1.x reads its configuration as
before. data/config.yaml and data/runtime_state.json can stay; 1.x ignores them.
One file does need attention: data/user_mapping.json is rewritten by 2.0 into the
platform-keyed format, which 1.x cannot read. Restore it from your backup:
1.x re-registers its slash commands globally on the next start. If you later upgrade to
2.0 again, run !sync clearglobal once after the first start: the automatic cleanup is
already recorded in data/runtime_state.json and does not run a second time.