Initial commit, copy from examples folder
This commit is contained in:
129
README.md
Normal file
129
README.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Discord Rich Presence Plugin
|
||||
|
||||
This plugin integrates Navidrome with Discord Rich Presence, displaying your currently playing track in your Discord status. It demonstrates how a Navidrome plugin can maintain real-time connections to external services while remaining completely stateless. Based on the [Navicord](https://github.com/logixism/navicord) project.
|
||||
|
||||
**⚠️ WARNING: This plugin requires storing Discord user tokens, which may violate Discord's Terms of Service. Use at your own risk.**
|
||||
|
||||
## Features
|
||||
|
||||
- Shows currently playing track with title, artist, and album art
|
||||
- Displays playback progress with start/end timestamps
|
||||
- Automatic presence clearing when track finishes
|
||||
- Multi-user support with individual Discord tokens
|
||||
|
||||
## How It Works
|
||||
|
||||
### Plugin Capabilities
|
||||
|
||||
The plugin implements three Navidrome capabilities:
|
||||
|
||||
| Capability | Purpose |
|
||||
|-----------------------|------------------------------------------------------------------------------|
|
||||
| **Scrobbler** | Receives `NowPlaying` events when users start playing tracks |
|
||||
| **WebSocketCallback** | Handles incoming Discord gateway messages (heartbeat ACKs, sequence numbers) |
|
||||
| **SchedulerCallback** | Processes scheduled events for heartbeats and presence clearing |
|
||||
|
||||
### Files
|
||||
|
||||
| File | Description |
|
||||
|--------------------------------|------------------------------------------------------------------------|
|
||||
| [main.go](main.go) | Plugin entry point, scrobbler and scheduler implementations |
|
||||
| [rpc.go](rpc.go) | Discord gateway communication, WebSocket handling, activity management |
|
||||
| [manifest.json](manifest.json) | Plugin metadata and permission declarations |
|
||||
| [Makefile](Makefile) | Build automation |
|
||||
|
||||
### Host Services
|
||||
|
||||
| Service | Usage |
|
||||
|---------------|---------------------------------------------------------------------|
|
||||
| **HTTP** | Discord API calls (gateway discovery, external assets registration) |
|
||||
| **WebSocket** | Persistent connection to Discord gateway |
|
||||
| **Cache** | Sequence numbers, processed image URLs |
|
||||
| **Scheduler** | Recurring heartbeats, one-time presence clearing |
|
||||
| **Artwork** | Track artwork URL resolution |
|
||||
|
||||
### Flow
|
||||
|
||||
1. **Track starts playing** - Navidrome calls `NowPlaying`
|
||||
2. **Plugin connects** - If not already connected, establishes WebSocket to Discord gateway
|
||||
3. **Authentication** - Sends identify payload with user's Discord token
|
||||
4. **Presence update** - Sends activity with track info and processed artwork URL
|
||||
5. **Heartbeat loop** - Recurring scheduler sends heartbeats every 41 seconds to keep connection alive
|
||||
6. **Track ends** - One-time scheduler callback clears presence and disconnects
|
||||
|
||||
### Stateless Design
|
||||
|
||||
Navidrome plugins are stateless - each call creates a fresh instance. This plugin handles that by:
|
||||
|
||||
- **WebSocket connections**: Managed by host, keyed by username
|
||||
- **Sequence numbers**: Stored in cache for heartbeat messages
|
||||
- **Configuration**: Reloaded on every method call
|
||||
- **Artwork URLs**: Cached after processing through Discord's external assets API
|
||||
|
||||
### Image Processing
|
||||
|
||||
Discord requires images to be registered via their external assets API. The plugin:
|
||||
1. Fetches track artwork URL from Navidrome
|
||||
2. Registers it with Discord's API to get an `mp:` prefixed URL
|
||||
3. Caches the result (4 hours for track art, 48 hours for default image)
|
||||
4. Falls back to a default image if artwork is unavailable
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure via the Navidrome UI under **Settings > Plugins > Discord Rich Presence**:
|
||||
|
||||
| Field | Description |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| **Client ID** | Your Discord Application ID (create at [Discord Developer Portal](https://discord.com/developers/applications)) |
|
||||
| **Users** | Array of username/token pairs mapping Navidrome users to Discord tokens |
|
||||
|
||||
Example JSON configuration:
|
||||
```json
|
||||
{
|
||||
"clientid": "123456789012345678",
|
||||
"users": [
|
||||
{"username": "alice", "token": "discord-token-here"},
|
||||
{"username": "bob", "token": "another-discord-token"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Although the plugin can be compiled to WebAssembly with standard Go, it is recommended to use
|
||||
[TinyGo](https://tinygo.org/getting-started/install/) for smaller binary size.
|
||||
|
||||
|
||||
```sh
|
||||
# Run tests
|
||||
make test
|
||||
|
||||
# Build plugin.wasm
|
||||
make build
|
||||
|
||||
# Create distributable package
|
||||
make package
|
||||
```
|
||||
|
||||
The `make package` command creates `discord-rich-presence.ndp` containing the compiled WebAssembly module and manifest.
|
||||
|
||||
Manual build:
|
||||
```sh
|
||||
tinygo build -target wasip1 -buildmode=c-shared -o plugin.wasm -scheduler=none .
|
||||
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
||||
```
|
||||
|
||||
Using standard Go:
|
||||
```sh
|
||||
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o plugin.wasm .
|
||||
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy the `discord-rich-presence.ndp` file to your Navidrome plugins folder (default is `plugins/` under the Navidrome data directory).
|
||||
2. Configure the plugin in **Settings > Plugins > Discord Rich Presence**
|
||||
3. Enable the plugin
|
||||
|
||||
There is no need to restart Navidrome; Check the logs for any errors during initialization.
|
||||
|
||||
Reference in New Issue
Block a user