Merge pull request 'Custom Activity Name Templates' (#23) from atridadl/main into main
Test / Test (push) Has been cancelled
Test / Test (push) Has been cancelled
Reviewed-on: #23
This commit was merged in pull request #23.
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
||||
flake-profile-1-link
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
/nix/store/vvmz1kzp0iwsxsmlh0ss6snzn6mx9g4j-nix-shell-env
|
||||
@@ -1,4 +1,6 @@
|
||||
*.wasm
|
||||
*.ndp
|
||||
tmp
|
||||
|
||||
discordrome
|
||||
.DS_Store
|
||||
|
||||
Generated
+61
@@ -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
|
||||
}
|
||||
@@ -25,12 +25,13 @@ import (
|
||||
|
||||
// Configuration keys
|
||||
const (
|
||||
clientIDKey = "clientid"
|
||||
usersKey = "users"
|
||||
activityNameKey = "activityname"
|
||||
spotifyLinksKey = "spotifylinks"
|
||||
caaEnabledKey = "caaenabled"
|
||||
uguuEnabledKey = "uguuenabled"
|
||||
clientIDKey = "clientid"
|
||||
usersKey = "users"
|
||||
activityNameKey = "activityname"
|
||||
activityNameTemplateKey = "activitynametemplate"
|
||||
spotifyLinksKey = "spotifylinks"
|
||||
caaEnabledKey = "caaenabled"
|
||||
uguuEnabledKey = "uguuenabled"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,6 +48,7 @@ const (
|
||||
activityNameTrack = "Track"
|
||||
activityNameArtist = "Artist"
|
||||
activityNameAlbum = "Album"
|
||||
activityNameCustom = "Custom"
|
||||
)
|
||||
|
||||
// userToken represents a user-token mapping from the config
|
||||
@@ -170,6 +172,16 @@ func (p *discordPlugin) NowPlaying(input scrobbler.NowPlayingRequest) error {
|
||||
case activityNameArtist:
|
||||
activityName = input.Track.Artist
|
||||
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
|
||||
|
||||
@@ -225,6 +225,62 @@ var _ = Describe("discordPlugin", func() {
|
||||
Entry("uses track album when configured", "Album", true, "Test Album", 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() {
|
||||
|
||||
+20
-1
@@ -51,9 +51,15 @@
|
||||
"type": "string",
|
||||
"title": "Activity Name Display",
|
||||
"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"
|
||||
},
|
||||
"activitynametemplate": {
|
||||
"type": "string",
|
||||
"title": "Custom Activity Name Template",
|
||||
"description": "Template for the activity name. Available placeholders: {track}, {artist}, {album}",
|
||||
"default": "{artist} - {track}"
|
||||
},
|
||||
"caaenabled": {
|
||||
"type": "boolean",
|
||||
"title": "Use artwork from Cover Art Archive (for MusicBrainz-tagged music)",
|
||||
@@ -112,6 +118,19 @@
|
||||
"format": "radio"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Control",
|
||||
"scope": "#/properties/activitynametemplate",
|
||||
"rule": {
|
||||
"effect": "SHOW",
|
||||
"condition": {
|
||||
"scope": "#/properties/activityname",
|
||||
"schema": {
|
||||
"const": "Custom"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Control",
|
||||
"scope": "#/properties/caaenabled"
|
||||
|
||||
Reference in New Issue
Block a user