Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4700e15a3c | |||
| 6b5ca1a54f | |||
| 24c4c36651 | |||
| 7e94c83a12 | |||
| b916c4c8fd | |||
| 35fbcbb46e | |||
| b68502fc05 | |||
| a39bcec7b2 | |||
| 982d3aae29 |
@@ -0,0 +1,80 @@
|
|||||||
|
name: Create Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Release version (e.g., 1.2.3, without the 'v' prefix)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
create-release:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Validate version format
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$ ]]; then
|
||||||
|
echo "::error::Invalid version format '$VERSION'. Use X.X.X (e.g., 1.2.3)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Check tag does not already exist
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
if git ls-remote --tags origin "refs/tags/v${VERSION}" | grep -q .; then
|
||||||
|
echo "::error::Tag v${VERSION} already exists"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: go test -race ./...
|
||||||
|
|
||||||
|
- name: Update manifest.json version
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
jq --arg v "$VERSION" '.version = $v' manifest.json > manifest.tmp && mv manifest.tmp manifest.json
|
||||||
|
|
||||||
|
- name: Commit, tag, and push
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version }}
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add manifest.json
|
||||||
|
git commit --allow-empty -m "Release v${VERSION}"
|
||||||
|
git tag "v${VERSION}"
|
||||||
|
git push origin main "v${VERSION}"
|
||||||
|
|
||||||
|
- name: Install TinyGo
|
||||||
|
run: |
|
||||||
|
wget https://github.com/tinygo-org/tinygo/releases/download/v0.40.1/tinygo_0.40.1_amd64.deb
|
||||||
|
sudo dpkg -i tinygo_0.40.1_amd64.deb
|
||||||
|
sudo apt install -y binaryen
|
||||||
|
|
||||||
|
- name: Build and package plugin
|
||||||
|
run: make package
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: v${{ inputs.version }}
|
||||||
|
draft: true
|
||||||
|
files: discord-rich-presence.ndp
|
||||||
|
generate_release_notes: true
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
|
|
||||||
- name: Install TinyGo
|
|
||||||
run: |
|
|
||||||
wget https://github.com/tinygo-org/tinygo/releases/download/v0.40.1/tinygo_0.40.1_amd64.deb
|
|
||||||
sudo dpkg -i tinygo_0.40.1_amd64.deb
|
|
||||||
sudo apt install -y binaryen
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: make test
|
|
||||||
|
|
||||||
- name: Build and package plugin
|
|
||||||
run: make package
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
draft: true
|
|
||||||
files: discord-rich-presence.ndp
|
|
||||||
generate_release_notes: true
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
SHELL := /usr/bin/env bash
|
||||||
.PHONY: test build package clean
|
.PHONY: test build package clean
|
||||||
|
|
||||||
PLUGIN_NAME := discord-rich-presence
|
PLUGIN_NAME := discord-rich-presence
|
||||||
@@ -15,10 +16,8 @@ package: build
|
|||||||
clean:
|
clean:
|
||||||
rm -f $(WASM_FILE) $(PLUGIN_NAME).ndp
|
rm -f $(WASM_FILE) $(PLUGIN_NAME).ndp
|
||||||
|
|
||||||
release: test
|
release:
|
||||||
@if [[ ! "${V}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$$ ]]; then echo "Usage: make release V=X.X.X"; exit 1; fi
|
@if [[ ! "${V}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$$ ]]; then echo "Usage: make release V=X.X.X"; exit 1; fi
|
||||||
go mod tidy
|
gh workflow run create-release.yml -f version=${V}
|
||||||
@if [ -n "`git status -s`" ]; then echo "\n\nThere are pending changes. Please commit or stash first"; exit 1; fi
|
@echo "Release v${V} workflow triggered. Check progress: gh run list --workflow=create-release.yml"
|
||||||
git tag v${V}
|
|
||||||
git push origin v${V} --no-verify
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
|
|||||||
@@ -14,27 +14,80 @@ Based on the [Navicord](https://github.com/logixism/navicord) project.
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Shows currently playing track with title, artist, and album art
|
- Shows currently playing track with title, artist, and album art
|
||||||
|
- Customizable activity name: "Navidrome" is default, but can be configured to display track title, artist, or album
|
||||||
- Displays playback progress with start/end timestamps
|
- Displays playback progress with start/end timestamps
|
||||||
- Automatic presence clearing when track finishes
|
- Automatic presence clearing when track finishes
|
||||||
- Multi-user support with individual Discord tokens
|
- Multi-user support with individual Discord tokens
|
||||||
- Optional image hosting via [uguu.se](https://uguu.se) for non-public Navidrome instances
|
- Optional image hosting via [uguu.se](https://uguu.se) for non-public Navidrome instances
|
||||||
|
|
||||||
<img height="550" src="https://raw.githubusercontent.com/navidrome/discord-rich-presence-plugin/master/.github/screenshot.png">
|
<img height="550" alt="Discord Rich Presence showing currently playing track with album art, artist, and playback progress" src="https://raw.githubusercontent.com/navidrome/discord-rich-presence-plugin/master/.github/screenshot.png">
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Copy the `discord-rich-presence.ndp` file from the [releases page](https://github.com/navidrome/discord-rich-presence-plugin/releases) to your Navidrome plugins folder (default is `plugins/` under the Navidrome data directory).
|
### Step 1: Download and Install the Plugin
|
||||||
2. Configure the plugin in **Settings > Plugins > Discord Rich Presence**
|
1. Download the `discord-rich-presence.ndp` file from the [releases page](https://github.com/navidrome/discord-rich-presence-plugin/releases)
|
||||||
3. Enable the plugin
|
2. Copy it to your Navidrome plugins folder. Default location: `<navidrome-data-directory>/plugins/`
|
||||||
|
|
||||||
Important: Remember to configure your account in Discord to share activity status with others:
|
### Step 2: Create a Discord Application
|
||||||
- Go to **User Settings > Activity Privacy**
|
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
|
||||||
- Enable **Share my activity**
|
2. Click "New Application" and give it a name (e.g., "My Navidrome")
|
||||||
|
3. Note down the **Application ID** (Client ID) - you'll need this for configuration
|
||||||
|
|
||||||
There is no need to restart Navidrome; Check the logs for any errors during initialization.
|
### Step 3: Get Your Discord User Token
|
||||||
|
⚠️ **WARNING**: This step involves using your Discord user token, which may violate Discord's Terms of Service. Proceed at your own risk.
|
||||||
|
|
||||||
Note: Currently album art can only be displayed if your Navidrome instance is public. Additionally you must set the ND_BASEURL config to your public facing URL. Once this is complete you will need to restart Navidrome for the change to take effect.
|
We don't provide instructions for obtaining the token as it may violate Discord's policies. You can find guides online by searching for "how to get Discord user token".
|
||||||
|
|
||||||
|
### Step 4: Configure the Plugin
|
||||||
|
1. Open Navidrome and go to **Settings > Plugins > Discord Rich Presence**
|
||||||
|
2. Fill in the configuration:
|
||||||
|
- **Client ID**: Your Discord Application ID from Step 2
|
||||||
|
- **Upload to uguu.se**: Enable this if your Navidrome isn't publicly accessible (see Album Art section below)
|
||||||
|
- **Users**: Add your Navidrome username and Discord token from Step 3
|
||||||
|
|
||||||
|
### Step 5: Enable Discord Activity Sharing
|
||||||
|
In Discord, ensure your activity is visible to others:
|
||||||
|
1. Go to **User Settings** (gear icon)
|
||||||
|
2. Navigate to **Activity Privacy**
|
||||||
|
3. Enable **"Display current activity as a status message"**
|
||||||
|
|
||||||
|
### Step 6: Enable the Plugin
|
||||||
|
1. In Navidrome's plugin settings, toggle the plugin to **Enabled**
|
||||||
|
2. No restart required - check Navidrome logs for any initialization errors
|
||||||
|
|
||||||
|
## Album Art Display
|
||||||
|
|
||||||
|
For album artwork to display in Discord, Discord needs to be able to access the image. Choose one of these options:
|
||||||
|
|
||||||
|
### Option 1: Public Navidrome Instance
|
||||||
|
**Use this if**: Your Navidrome server can be reached from the internet
|
||||||
|
|
||||||
|
**Setup**:
|
||||||
|
1. Set the `ND_BASEURL` environment variable to your public URL:
|
||||||
|
```bash
|
||||||
|
# Example for Docker or Docker Compose
|
||||||
|
ND_BASEURL=https://music.yourdomain.com
|
||||||
|
|
||||||
|
# Example for navidrome.toml
|
||||||
|
BaseURL = "https://music.yourdomain.com"
|
||||||
|
```
|
||||||
|
2. **Restart Navidrome** (required for ND_BASEURL changes)
|
||||||
|
3. In plugin settings: **Disable** "Upload to uguu.se"
|
||||||
|
|
||||||
|
### Option 2: Private Instance with uguu.se Upload
|
||||||
|
**Use this if**: Your Navidrome is only accessible locally (home network, behind VPN, etc.)
|
||||||
|
|
||||||
|
**Setup**:
|
||||||
|
1. In plugin settings: **Enable** "Upload to uguu.se"
|
||||||
|
2. No other configuration needed
|
||||||
|
|
||||||
|
**How it works**: Album art is automatically uploaded to uguu.se (temporary, anonymous hosting service) so Discord can access it. Files are deleted after 3 hours.
|
||||||
|
|
||||||
|
### Troubleshooting Album Art
|
||||||
|
- **No album art showing**: Check Navidrome logs for errors
|
||||||
|
- **Using public instance**: Verify ND_BASEURL is correct and Navidrome was restarted
|
||||||
|
- **Using uguu.se**: Check that the option is enabled and your server has internet access
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
@@ -99,21 +152,64 @@ Discord requires images to be registered via their external assets API. The plug
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Configure via the Navidrome UI under **Settings > Plugins > Discord Rich Presence**:
|
Access the plugin configuration in Navidrome: **Settings > Plugins > Discord Rich Presence**
|
||||||
|
|
||||||
| Field | Description |
|
### Configuration Fields
|
||||||
|-----------------------|-----------------------------------------------------------------------------------------------------------------|
|
|
||||||
| **Client ID** | Your Discord Application ID (create at [Discord Developer Portal](https://discord.com/developers/applications)) |
|
#### Client ID
|
||||||
| **Upload to uguu.se** | Enable if your Navidrome instance isn't publicly accessible (uploads artwork to temporary file host) |
|
- **What it is**: Your Discord Application ID
|
||||||
| **Users** | Array of username/token pairs mapping Navidrome users to Discord tokens |
|
- **How to get it**:
|
||||||
|
1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
|
||||||
|
2. Create a new application or select an existing one
|
||||||
|
3. Copy the "Application ID" from the General Information page
|
||||||
|
- **Example**: `1234567890123456789`
|
||||||
|
|
||||||
|
#### Activity Name Display
|
||||||
|
- **What it is**: Choose what information to display as the activity name in Discord Rich Presence
|
||||||
|
- **Options**:
|
||||||
|
- **Default**: Shows "Navidrome" (static app name)
|
||||||
|
- **Track**: Shows the currently playing track title
|
||||||
|
- **Album**: Shows the currently playing track's album name
|
||||||
|
- **Artist**: Shows the currently playing track's artist name
|
||||||
|
- **Default**: "Default"
|
||||||
|
- **Use case**: Choose "Track" or "Artist" for more dynamic, music-focused presence that changes with each song
|
||||||
|
|
||||||
|
#### Upload to uguu.se
|
||||||
|
- **When to enable**: Your Navidrome instance is NOT publicly accessible from the internet
|
||||||
|
- **What it does**: Automatically uploads album artwork to uguu.se (temporary hosting) so Discord can display it
|
||||||
|
- **When to disable**: Your Navidrome is publicly accessible and you've set `ND_BASEURL`
|
||||||
|
|
||||||
|
#### Users
|
||||||
|
Add each Navidrome user who wants Discord Rich Presence:
|
||||||
|
|
||||||
|
**Format**: Array of user objects with `username` and `token` fields
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"username": "john",
|
||||||
|
"token": "your-discord-user-token-here"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "jane",
|
||||||
|
"token": "another-discord-user-token"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**:
|
||||||
|
- `username`: Your Navidrome login username (case-sensitive)
|
||||||
|
- `token`: Your Discord user token (see installation instructions for how to obtain this)
|
||||||
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Although the plugin can be compiled to WebAssembly with standard Go, it is recommended to use
|
### Prerequisites
|
||||||
[TinyGo](https://tinygo.org/getting-started/install/) for smaller binary size.
|
- **Recommended**: [TinyGo](https://tinygo.org/getting-started/install/) (produces smaller binary size)
|
||||||
|
- **Alternative**: Standard Go 1.19+ (larger binary but easier setup)
|
||||||
|
|
||||||
|
### Quick Build (Using Makefile)
|
||||||
```sh
|
```sh
|
||||||
# Run tests
|
# Run tests
|
||||||
make test
|
make test
|
||||||
@@ -127,14 +223,22 @@ make package
|
|||||||
|
|
||||||
The `make package` command creates `discord-rich-presence.ndp` containing the compiled WebAssembly module and manifest.
|
The `make package` command creates `discord-rich-presence.ndp` containing the compiled WebAssembly module and manifest.
|
||||||
|
|
||||||
### Manual build:
|
### Manual Build Options
|
||||||
|
|
||||||
|
#### Using TinyGo (Recommended)
|
||||||
```sh
|
```sh
|
||||||
|
# Install TinyGo first: https://tinygo.org/getting-started/install/
|
||||||
tinygo build -target wasip1 -buildmode=c-shared -o plugin.wasm -scheduler=none .
|
tinygo build -target wasip1 -buildmode=c-shared -o plugin.wasm -scheduler=none .
|
||||||
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using standard Go:
|
#### Using Standard Go
|
||||||
```sh
|
```sh
|
||||||
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o plugin.wasm .
|
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o plugin.wasm .
|
||||||
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
zip discord-rich-presence.ndp plugin.wasm manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Output
|
||||||
|
- `plugin.wasm`: The compiled WebAssembly module
|
||||||
|
- `discord-rich-presence.ndp`: The complete plugin package ready for installation
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,17 @@ import (
|
|||||||
|
|
||||||
// Configuration keys
|
// Configuration keys
|
||||||
const (
|
const (
|
||||||
clientIDKey = "clientid"
|
clientIDKey = "clientid"
|
||||||
usersKey = "users"
|
usersKey = "users"
|
||||||
|
activityNameKey = "activityname"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Activity name display options
|
||||||
|
const (
|
||||||
|
activityNameDefault = "Default"
|
||||||
|
activityNameTrack = "Track"
|
||||||
|
activityNameArtist = "Artist"
|
||||||
|
activityNameAlbum = "Album"
|
||||||
)
|
)
|
||||||
|
|
||||||
// userToken represents a user-token mapping from the config
|
// userToken represents a user-token mapping from the config
|
||||||
@@ -136,10 +145,22 @@ func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingRequest) error {
|
|||||||
startTime := (now - int64(input.Position)) * 1000
|
startTime := (now - int64(input.Position)) * 1000
|
||||||
endTime := startTime + int64(input.Track.Duration)*1000
|
endTime := startTime + int64(input.Track.Duration)*1000
|
||||||
|
|
||||||
|
// Resolve the activity name based on configuration
|
||||||
|
activityName := "Navidrome"
|
||||||
|
activityNameOption, _ := pdk.GetConfig(activityNameKey)
|
||||||
|
switch activityNameOption {
|
||||||
|
case activityNameTrack:
|
||||||
|
activityName = input.Track.Title
|
||||||
|
case activityNameAlbum:
|
||||||
|
activityName = input.Track.Album
|
||||||
|
case activityNameArtist:
|
||||||
|
activityName = input.Track.Artist
|
||||||
|
}
|
||||||
|
|
||||||
// Send activity update
|
// Send activity update
|
||||||
if err := rpc.sendActivity(clientID, input.Username, userToken, activity{
|
if err := rpc.sendActivity(clientID, input.Username, userToken, activity{
|
||||||
Application: clientID,
|
Application: clientID,
|
||||||
Name: "Navidrome",
|
Name: activityName,
|
||||||
Type: 2, // Listening
|
Type: 2, // Listening
|
||||||
Details: input.Track.Title,
|
Details: input.Track.Title,
|
||||||
State: input.Track.Artist,
|
State: input.Track.Artist,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/navidrome/navidrome/plugins/pdk/go/host"
|
"github.com/navidrome/navidrome/plugins/pdk/go/host"
|
||||||
@@ -119,6 +120,7 @@ var _ = Describe("discordPlugin", func() {
|
|||||||
pdk.PDKMock.On("GetConfig", clientIDKey).Return("test-client-id", true)
|
pdk.PDKMock.On("GetConfig", clientIDKey).Return("test-client-id", true)
|
||||||
pdk.PDKMock.On("GetConfig", usersKey).Return(`[{"username":"testuser","token":"test-token"}]`, true)
|
pdk.PDKMock.On("GetConfig", usersKey).Return(`[{"username":"testuser","token":"test-token"}]`, true)
|
||||||
pdk.PDKMock.On("GetConfig", uguuEnabledKey).Return("", false)
|
pdk.PDKMock.On("GetConfig", uguuEnabledKey).Return("", false)
|
||||||
|
pdk.PDKMock.On("GetConfig", activityNameKey).Return("", false)
|
||||||
|
|
||||||
// Connect mocks (isConnected check via heartbeat)
|
// Connect mocks (isConnected check via heartbeat)
|
||||||
host.CacheMock.On("GetInt", "discord.seq.testuser").Return(int64(0), false, errors.New("not found"))
|
host.CacheMock.On("GetInt", "discord.seq.testuser").Return(int64(0), false, errors.New("not found"))
|
||||||
@@ -169,6 +171,65 @@ var _ = Describe("discordPlugin", func() {
|
|||||||
})
|
})
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DescribeTable("activity name configuration",
|
||||||
|
func(configValue string, configExists bool, expectedName string) {
|
||||||
|
pdk.PDKMock.On("GetConfig", clientIDKey).Return("test-client-id", true)
|
||||||
|
pdk.PDKMock.On("GetConfig", usersKey).Return(`[{"username":"testuser","token":"test-token"}]`, true)
|
||||||
|
pdk.PDKMock.On("GetConfig", uguuEnabledKey).Return("", false)
|
||||||
|
pdk.PDKMock.On("GetConfig", activityNameKey).Return(configValue, configExists)
|
||||||
|
|
||||||
|
// Connect mocks
|
||||||
|
host.CacheMock.On("GetInt", "discord.seq.testuser").Return(int64(0), false, errors.New("not found"))
|
||||||
|
gatewayResp := []byte(`{"url":"wss://gateway.discord.gg"}`)
|
||||||
|
gatewayReq := &pdk.HTTPRequest{}
|
||||||
|
pdk.PDKMock.On("NewHTTPRequest", pdk.MethodGet, "https://discord.com/api/gateway").Return(gatewayReq).Once()
|
||||||
|
pdk.PDKMock.On("Send", gatewayReq).Return(pdk.NewStubHTTPResponse(200, nil, gatewayResp)).Once()
|
||||||
|
host.WebSocketMock.On("Connect", mock.MatchedBy(func(url string) bool {
|
||||||
|
return strings.Contains(url, "gateway.discord.gg")
|
||||||
|
}), mock.Anything, "testuser").Return("testuser", nil)
|
||||||
|
|
||||||
|
// Capture the activity payload sent to Discord
|
||||||
|
var sentPayload string
|
||||||
|
host.WebSocketMock.On("SendText", "testuser", mock.Anything).Run(func(args mock.Arguments) {
|
||||||
|
sentPayload = args.Get(1).(string)
|
||||||
|
}).Return(nil)
|
||||||
|
host.SchedulerMock.On("ScheduleRecurring", mock.Anything, payloadHeartbeat, "testuser").Return("testuser", nil)
|
||||||
|
host.SchedulerMock.On("CancelSchedule", "testuser-clear").Return(nil)
|
||||||
|
|
||||||
|
// Image mocks
|
||||||
|
host.CacheMock.On("GetString", mock.MatchedBy(func(key string) bool {
|
||||||
|
return strings.HasPrefix(key, "discord.image.")
|
||||||
|
})).Return("", false, nil)
|
||||||
|
host.CacheMock.On("SetString", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||||
|
host.ArtworkMock.On("GetTrackUrl", "track1", int32(300)).Return("https://example.com/art.jpg", nil)
|
||||||
|
assetsReq := &pdk.HTTPRequest{}
|
||||||
|
pdk.PDKMock.On("NewHTTPRequest", pdk.MethodPost, mock.MatchedBy(func(url string) bool {
|
||||||
|
return strings.Contains(url, "external-assets")
|
||||||
|
})).Return(assetsReq)
|
||||||
|
pdk.PDKMock.On("Send", assetsReq).Return(pdk.NewStubHTTPResponse(200, nil, []byte(`{"key":"test-key"}`)))
|
||||||
|
host.SchedulerMock.On("ScheduleOneTime", mock.Anything, payloadClearActivity, "testuser-clear").Return("testuser-clear", nil)
|
||||||
|
|
||||||
|
err := plugin.NowPlaying(scrobbler.NowPlayingRequest{
|
||||||
|
Username: "testuser",
|
||||||
|
Position: 10,
|
||||||
|
Track: scrobbler.TrackInfo{
|
||||||
|
ID: "track1",
|
||||||
|
Title: "Test Song",
|
||||||
|
Artist: "Test Artist",
|
||||||
|
Album: "Test Album",
|
||||||
|
Duration: 180,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(sentPayload).To(ContainSubstring(fmt.Sprintf(`"name":"%s"`, expectedName)))
|
||||||
|
},
|
||||||
|
Entry("defaults to Navidrome when not configured", "", false, "Navidrome"),
|
||||||
|
Entry("defaults to Navidrome with explicit default value", "Default", true, "Navidrome"),
|
||||||
|
Entry("uses track title when configured", "Track", true, "Test Song"),
|
||||||
|
Entry("uses track album when configured", "Album", true, "Test Album"),
|
||||||
|
Entry("uses track artist when configured", "Artist", true, "Test Artist"),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("Scrobble", func() {
|
Describe("Scrobble", func() {
|
||||||
|
|||||||
+35
-5
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://raw.githubusercontent.com/navidrome/navidrome/refs/heads/master/plugins/manifest-schema.json",
|
"$schema": "https://raw.githubusercontent.com/navidrome/navidrome/refs/heads/master/plugins/manifest-schema.json",
|
||||||
"name": "Discord Rich Presence",
|
"name": "Discord Rich Presence",
|
||||||
"author": "Navidrome Team",
|
"author": "Navidrome Team",
|
||||||
"version": "0.1.0",
|
"version": "0.3.0",
|
||||||
"description": "Discord Rich Presence integration for Navidrome",
|
"description": "Discord Rich Presence integration for Navidrome",
|
||||||
"website": "https://github.com/navidrome/discord-rich-presence-plugin",
|
"website": "https://github.com/navidrome/discord-rich-presence-plugin",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
@@ -11,11 +11,16 @@
|
|||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"reason": "To communicate with Discord API for gateway discovery and image uploads",
|
"reason": "To communicate with Discord API for gateway discovery and image uploads",
|
||||||
"requiredHosts": ["discord.com", "uguu.se"]
|
"requiredHosts": [
|
||||||
|
"discord.com",
|
||||||
|
"uguu.se"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"reason": "To maintain real-time connection with Discord gateway",
|
"reason": "To maintain real-time connection with Discord gateway",
|
||||||
"requiredHosts": ["gateway.discord.gg"]
|
"requiredHosts": [
|
||||||
|
"gateway.discord.gg"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"cache": {
|
"cache": {
|
||||||
"reason": "To store connection state and sequence numbers"
|
"reason": "To store connection state and sequence numbers"
|
||||||
@@ -42,6 +47,18 @@
|
|||||||
"maxLength": 20,
|
"maxLength": 20,
|
||||||
"pattern": "^[0-9]+$"
|
"pattern": "^[0-9]+$"
|
||||||
},
|
},
|
||||||
|
"activityname": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Activity Name Display",
|
||||||
|
"description": "Choose what to display as the activity name in Discord Rich Presence",
|
||||||
|
"enum": [
|
||||||
|
"Default",
|
||||||
|
"Track",
|
||||||
|
"Album",
|
||||||
|
"Artist"
|
||||||
|
],
|
||||||
|
"default": "Default"
|
||||||
|
},
|
||||||
"uguuenabled": {
|
"uguuenabled": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "Upload artwork to uguu.se (enable if Navidrome is not publicly accessible)",
|
"title": "Upload artwork to uguu.se (enable if Navidrome is not publicly accessible)",
|
||||||
@@ -68,11 +85,17 @@
|
|||||||
"minLength": 1
|
"minLength": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["username", "token"]
|
"required": [
|
||||||
|
"username",
|
||||||
|
"token"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["clientid", "users"]
|
"required": [
|
||||||
|
"clientid",
|
||||||
|
"users"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"uiSchema": {
|
"uiSchema": {
|
||||||
"type": "VerticalLayout",
|
"type": "VerticalLayout",
|
||||||
@@ -81,6 +104,13 @@
|
|||||||
"type": "Control",
|
"type": "Control",
|
||||||
"scope": "#/properties/clientid"
|
"scope": "#/properties/clientid"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "Control",
|
||||||
|
"scope": "#/properties/activityname",
|
||||||
|
"options": {
|
||||||
|
"format": "radio"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "Control",
|
"type": "Control",
|
||||||
"scope": "#/properties/uguuenabled"
|
"scope": "#/properties/uguuenabled"
|
||||||
|
|||||||
Reference in New Issue
Block a user