11 Commits

Author SHA1 Message Date
atridad b5e9822626 Oopsie! 2026-04-02 16:35:18 -06:00
atridad 2b096b23ff Updated to 1.1.0 2026-04-02 16:00:14 -06:00
atridad dc64de825c O_O 2026-04-02 15:59:19 -06:00
atridad 6c051734cc Remove tests until I can figure out why it hangs 2026-04-02 15:57:50 -06:00
atridad 7de0568bc7 Update .gitignore
Test / Test (push) Has been cancelled
2026-04-02 15:56:37 -06:00
atridad 383789a6a5 Remove .devenv 2026-04-02 15:56:17 -06:00
atridad a903d6cdea Merge pull request 'Custom Activity Name Templates' (#23) from atridadl/main into main
Test / Test (push) Has been cancelled
Reviewed-on: #23
2026-04-02 15:48:21 -06:00
atridad fa381fbc83 Merge
Test / Test (pull_request) Has been cancelled
2026-04-02 15:46:58 -06:00
atridad 41cf2971c1 Merge branch 'main' into main 2026-04-02 15:34:34 +00:00
atridad 414021f471 Addressing gemini's suggestion 2026-03-09 23:28:41 -06:00
atridad 24fb4cf752 added custom activity name template 2026-03-09 18:01:58 -06:00
6 changed files with 160 additions and 40 deletions
-31
View File
@@ -1,31 +0,0 @@
name: Test
on:
pull_request:
branches:
- main
push:
branches:
- main
jobs:
test:
name: Test
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
+3
View File
@@ -1,4 +1,7 @@
*.wasm *.wasm
*.ndp *.ndp
tmp tmp
discordrome discordrome
.DS_Store
.direnv/
Generated
+61
View File
@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1775036866,
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+12
View File
@@ -28,6 +28,7 @@ const (
clientIDKey = "clientid" clientIDKey = "clientid"
usersKey = "users" usersKey = "users"
activityNameKey = "activityname" activityNameKey = "activityname"
activityNameTemplateKey = "activitynametemplate"
spotifyLinksKey = "spotifylinks" spotifyLinksKey = "spotifylinks"
caaEnabledKey = "caaenabled" caaEnabledKey = "caaenabled"
uguuEnabledKey = "uguuenabled" uguuEnabledKey = "uguuenabled"
@@ -47,6 +48,7 @@ const (
activityNameTrack = "Track" activityNameTrack = "Track"
activityNameArtist = "Artist" activityNameArtist = "Artist"
activityNameAlbum = "Album" activityNameAlbum = "Album"
activityNameCustom = "Custom"
) )
// userToken represents a user-token mapping from the config // userToken represents a user-token mapping from the config
@@ -170,6 +172,16 @@ func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingRequest) error {
case activityNameArtist: case activityNameArtist:
activityName = input.Track.Artist activityName = input.Track.Artist
statusDisplayType = statusDisplayName statusDisplayType = statusDisplayName
case activityNameCustom:
template, _ := pdk.GetConfig(activityNameTemplateKey)
if template != "" {
r := strings.NewReplacer(
"{track}", input.Track.Title,
"{artist}", input.Track.Artist,
"{album}", input.Track.Album,
)
activityName = r.Replace(template)
}
} }
// Resolve Spotify URLs if enabled // Resolve Spotify URLs if enabled
+56
View File
@@ -225,6 +225,62 @@ var _ = Describe("discordPlugin", func() {
Entry("uses track album when configured", "Album", true, "Test Album", 0), Entry("uses track album when configured", "Album", true, "Test Album", 0),
Entry("uses track artist when configured", "Artist", true, "Test Artist", 0), Entry("uses track artist when configured", "Artist", true, "Test Artist", 0),
) )
DescribeTable("custom activity name template",
func(template string, templateExists 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("Custom", true)
pdk.PDKMock.On("GetConfig", activityNameTemplateKey).Return(template, templateExists)
pdk.PDKMock.On("GetConfig", spotifyLinksKey).Return("", false)
// Connect mocks
host.CacheMock.On("GetInt", "discord.seq.testuser").Return(int64(0), false, errors.New("not found"))
gatewayResp := []byte(`{"url":"wss://gateway.discord.gg"}`)
host.HTTPMock.On("Send", mock.MatchedBy(func(req host.HTTPRequest) bool {
return req.Method == "GET" && req.URL == "https://discord.com/api/gateway"
})).Return(&host.HTTPResponse{StatusCode: 200, Body: gatewayResp}, nil)
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", discordImageKey).Return("", false, nil)
host.CacheMock.On("SetString", discordImageKey, mock.Anything, mock.Anything).Return(nil)
host.ArtworkMock.On("GetTrackUrl", "track1", int32(300)).Return("https://example.com/art.jpg", nil)
host.HTTPMock.On("Send", externalAssetsReq).Return(&host.HTTPResponse{StatusCode: 200, Body: []byte(`{}`)}, nil)
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("uses custom template with all placeholders", "{artist} - {track} ({album})", true, "Test Artist - Test Song (Test Album)"),
Entry("uses custom template with only track", "{track}", true, "Test Song"),
Entry("uses custom template with only artist", "{artist}", true, "Test Artist"),
Entry("uses custom template with only album", "{album}", true, "Test Album"),
Entry("uses custom template with plain text", "Now Playing", true, "Now Playing"),
Entry("falls back to Navidrome when template is empty", "", false, "Navidrome"),
)
}) })
Describe("Scrobble", func() { Describe("Scrobble", func() {
+22 -3
View File
@@ -2,9 +2,9 @@
"$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": "Discodrome", "name": "Discodrome",
"author": "Atridad Lahiji", "author": "Atridad Lahiji",
"version": "1.0.0", "version": "1.1.0",
"description": "Discord Rich Presence integration for Navidrome", "description": "Discord Rich Presence integration for Navidrome",
"website": "https://git.atri.dad.com/atridad/discodrome", "website": "https://git.atri.dad/atridad/discodrome",
"permissions": { "permissions": {
"users": { "users": {
"reason": "To process scrobbles on behalf of users" "reason": "To process scrobbles on behalf of users"
@@ -51,9 +51,15 @@
"type": "string", "type": "string",
"title": "Activity Name Display", "title": "Activity Name Display",
"description": "Choose what to display as the activity name in Discord Rich Presence", "description": "Choose what to display as the activity name in Discord Rich Presence",
"enum": ["Default", "Track", "Album", "Artist"], "enum": ["Default", "Track", "Album", "Artist", "Custom"],
"default": "Default" "default": "Default"
}, },
"activitynametemplate": {
"type": "string",
"title": "Custom Activity Name Template",
"description": "Template for the activity name. Available placeholders: {track}, {artist}, {album}",
"default": "{artist} - {track}"
},
"caaenabled": { "caaenabled": {
"type": "boolean", "type": "boolean",
"title": "Use artwork from Cover Art Archive (for MusicBrainz-tagged music)", "title": "Use artwork from Cover Art Archive (for MusicBrainz-tagged music)",
@@ -112,6 +118,19 @@
"format": "radio" "format": "radio"
} }
}, },
{
"type": "Control",
"scope": "#/properties/activitynametemplate",
"rule": {
"effect": "SHOW",
"condition": {
"scope": "#/properties/activityname",
"schema": {
"const": "Custom"
}
}
}
},
{ {
"type": "Control", "type": "Control",
"scope": "#/properties/caaenabled" "scope": "#/properties/caaenabled"