Skip to content

Commit 8fd9d5f

Browse files
luisegardunoJanDeDobbeleer
authored andcommitted
feat(spotify): add linux support
1 parent 891e03c commit 8fd9d5f

File tree

4 files changed

+137
-51
lines changed

4 files changed

+137
-51
lines changed

src/segments/spotify_linux.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//go:build linux && !darwin && !windows
2+
3+
package segments
4+
5+
import (
6+
"strings"
7+
8+
"github.com/jandedobbeleer/oh-my-posh/src/shell"
9+
)
10+
11+
func (s *Spotify) Enabled() bool {
12+
// Check if we're in WSL and handle that separately
13+
if s.env.IsWsl() {
14+
return s.enabledWsl()
15+
}
16+
17+
// Standard Linux implementation
18+
running := s.runLinuxScriptCommand(" string:PlaybackStatus | awk -F '\"' '/string/ {print tolower($2)}'")
19+
20+
if strings.HasPrefix(running, "Error") {
21+
return false
22+
}
23+
24+
if strings.Contains(running, "Error.ServiceUnknown") || strings.HasSuffix(running, "-") {
25+
s.Status = stopped
26+
return false
27+
}
28+
29+
if running == stopped {
30+
s.Status = stopped
31+
return false
32+
}
33+
34+
s.Status = running
35+
s.Artist = s.runLinuxScriptCommand(" string:Metadata | awk -F '\"' 'BEGIN {RS=\"entry\"}; /'xesam:artist'/ {a=$4} END {print a}'")
36+
s.Track = s.runLinuxScriptCommand(" string:Metadata | awk -F '\"' 'BEGIN {RS=\"entry\"}; /'xesam:title'/ {t=$4} END {print t}'")
37+
s.resolveIcon()
38+
39+
return true
40+
}
41+
42+
func (s *Spotify) runLinuxScriptCommand(command string) string {
43+
dbusCMD := "dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player"
44+
val := s.env.RunShellCommand(shell.BASH, dbusCMD+command)
45+
return val
46+
}
47+
48+
// Implementation for WSL - moved from spotify_wsl.go
49+
func (s *Spotify) enabledWsl() bool {
50+
tlist, err := s.env.RunCommand("tasklist.exe", "/V", "/FI", "Imagename eq Spotify.exe", "/FO", "CSV", "/NH")
51+
if err != nil || strings.HasPrefix(tlist, "INFO") {
52+
return false
53+
}
54+
55+
records := strings.Split(tlist, "\n")
56+
if len(records) == 0 {
57+
return false
58+
}
59+
60+
for _, record := range records {
61+
record = strings.TrimSpace(record)
62+
fields := strings.Split(record, ",")
63+
if len(fields) == 0 {
64+
continue
65+
}
66+
67+
// last elemant is the title
68+
title := fields[len(fields)-1]
69+
// trim leading and trailing quotes from the field
70+
title = strings.TrimPrefix(title, `"`)
71+
title = strings.TrimSuffix(title, `"`)
72+
if !strings.Contains(title, " - ") {
73+
continue
74+
}
75+
76+
infos := strings.Split(title, " - ")
77+
s.Artist = infos[0]
78+
s.Track = strings.Join(infos[1:], " - ")
79+
s.Status = playing
80+
s.resolveIcon()
81+
82+
return true
83+
}
84+
85+
return false
86+
}

src/segments/spotify_linux_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//go:build linux && !darwin && !windows
2+
3+
package segments
4+
5+
import (
6+
"errors"
7+
"testing"
8+
9+
"github.com/jandedobbeleer/oh-my-posh/src/properties"
10+
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
11+
"github.com/jandedobbeleer/oh-my-posh/src/shell"
12+
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestSpotifyLinux(t *testing.T) {
17+
cases := []struct {
18+
Error error
19+
Expected string
20+
Status string
21+
Artist string
22+
Track string
23+
Running bool
24+
}{
25+
{Running: false, Expected: ""},
26+
{Running: false, Expected: "", Error: errors.New("oops")},
27+
{Running: true, Expected: "\uF8E3 Candlemass - Spellbreaker", Status: "paused", Artist: "Candlemass", Track: "Spellbreaker"},
28+
{Running: true, Expected: "\uE602 Candlemass - Spellbreaker", Status: "playing", Artist: "Candlemass", Track: "Spellbreaker"},
29+
}
30+
for _, tc := range cases {
31+
env := new(mock.Environment)
32+
env.On("IsWsl").Return(false)
33+
34+
dbusCMD := "dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:org.mpris.MediaPlayer2.Player"
35+
env.On("RunShellCommand", shell.BASH, dbusCMD+" string:PlaybackStatus | awk -F '\"' '/string/ {print tolower($2)}'").Return(tc.Status, nil)
36+
env.On("RunShellCommand", shell.BASH, dbusCMD+" string:Metadata | awk -F '\"' 'BEGIN {RS=\"entry\"}; /'xesam:artist'/ {a=$4} END {print a}'").Return(tc.Artist, nil)
37+
env.On("RunShellCommand", shell.BASH, dbusCMD+" string:Metadata | awk -F '\"' 'BEGIN {RS=\"entry\"}; /'xesam:title'/ {t=$4} END {print t}'").Return(tc.Track, nil)
38+
39+
s := &Spotify{}
40+
s.Init(properties.Map{}, env)
41+
42+
enable := s.Enabled()
43+
assert.True(t, enable)
44+
if tc.Running {
45+
assert.True(t, s.Enabled())
46+
assert.Equal(t, tc.Expected, renderTemplate(env, s.Template(), s))
47+
}
48+
}
49+
}

src/segments/spotify_wsl.go

Lines changed: 0 additions & 49 deletions
This file was deleted.

website/docs/segments/music/spotify.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ sidebar_label: Spotify
66

77
## What
88

9-
Show the currently playing song in the [Spotify][spotify] macOS/Windows clients.
9+
Show the currently playing song in the [Spotify][spotify] client.
1010

1111
:::caution
1212
Be aware this can make the prompt a tad bit slower as it needs to get a response from the Spotify player.
1313

14-
On _macOS_, all states are supported (playing/paused/stopped).
14+
On _macOS & Linux_, all states are supported (playing/paused/stopped).
1515

1616
On _Windows/WSL_, **only the playing state is supported** (no information when paused/stopped). It supports
1717
fetching information from the native Spotify application and Edge PWA.

0 commit comments

Comments
 (0)