aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarvey Tindall <hrfee@protonmail.ch>2020-08-22 14:39:23 +0100
committerHarvey Tindall <hrfee@protonmail.ch>2020-08-22 14:39:23 +0100
commit5020e1c9298bfa76e89016be1bd2dd157563ea41 (patch)
tree5ba3340e13e6756b63459cbf17ac72433b21f2d3
parent3cf3f910caea1fe0f3f7da08ca3bc20ba2f44d30 (diff)
downloadwaybar-mpris-5020e1c9298bfa76e89016be1bd2dd157563ea41.tar.gz
waybar-mpris-5020e1c9298bfa76e89016be1bd2dd157563ea41.zip
fix halt when player is removed
rewritten without goroutines or checking if processes exist and it seems to work much better now.
-rw-r--r--go.mod4
-rw-r--r--go.sum1
-rw-r--r--main.go289
3 files changed, 201 insertions, 93 deletions
diff --git a/go.mod b/go.mod
index 1d3171e..04302e1 100644
--- a/go.mod
+++ b/go.mod
@@ -2,6 +2,4 @@ module github.com/hrfee/waybar-mpris
go 1.15
-require (
- github.com/godbus/dbus/v5 v5.0.3
-)
+require github.com/godbus/dbus/v5 v5.0.3
diff --git a/go.sum b/go.sum
index 0f7c8d1..4eefd05 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,3 @@
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
-github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
diff --git a/main.go b/main.go
index 9f2b249..95d04cb 100644
--- a/main.go
+++ b/main.go
@@ -3,104 +3,221 @@ package main
import (
"encoding/json"
"fmt"
- "github.com/godbus/dbus/v5"
"sort"
"strings"
- // "time"
+
+ "github.com/godbus/dbus/v5"
)
+var knownPlayers = map[string]string{
+ "plasma-browser-integration": "Browser",
+ "noson": "Noson",
+}
+
type Player struct {
- player dbus.BusObject
- playing, stopped bool
- playerName, title, artist, album string
- metadata map[string]dbus.Variant
- conn *dbus.Conn
+ player dbus.BusObject
+ fullName, name, title, artist, album string
+ playing, stopped bool
+ metadata map[string]dbus.Variant
+ conn *dbus.Conn
}
-func NewPlayer(conn *dbus.Conn, name string) *Player {
- p := &Player{
- player: conn.Object(name, "/org/mpris/MediaPlayer2"),
- conn: conn,
- playerName: strings.ReplaceAll(name, "org.mpris.MediaPlayer2.", ""),
+const (
+ INTERFACE = "org.mpris.MediaPlayer2"
+ PATH = "/org/mpris/MediaPlayer2"
+ PLAY = "▶"
+ PAUSE = ""
+ // NameOwnerChanged
+ MATCH_NOC = "type='signal',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
+ // PropertiesChanged
+ MATCH_PC = "type='signal',path='/org/mpris/MediaPlayer2',interface='org.freedesktop.DBus.Properties'"
+)
+
+// NewPlayer returns a new player object.
+func NewPlayer(conn *dbus.Conn, name string) (p *Player) {
+ playerName := strings.ReplaceAll(name, INTERFACE+".", "")
+ for key, val := range knownPlayers {
+ if strings.Contains(name, key) {
+ playerName = val
+ break
+ }
+ }
+ p = &Player{
+ player: conn.Object(name, PATH),
+ conn: conn,
+ name: playerName,
+ fullName: name,
}
p.Refresh()
- return p
+ return
}
-func (p *Player) Refresh() {
- val, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.PlaybackStatus")
+// Refresh grabs playback info.
+func (p *Player) Refresh() (err error) {
+ val, err := p.player.GetProperty(INTERFACE + ".Player.PlaybackStatus")
if err != nil {
- panic(err)
+ p.playing = false
+ p.stopped = false
+ p.metadata = map[string]dbus.Variant{}
+ p.title = ""
+ p.artist = ""
+ p.album = ""
+ return
}
- if strings.Contains(val.String(), "Playing") {
+ strVal := val.String()
+ if strings.Contains(strVal, "Playing") {
p.playing = true
p.stopped = false
- } else if strings.Contains(val.String(), "Paused") {
+ } else if strings.Contains(strVal, "Paused") {
p.playing = false
p.stopped = false
} else {
p.playing = false
p.stopped = true
}
- md, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.Metadata")
+ metadata, err := p.player.GetProperty(INTERFACE + ".Player.Metadata")
if err != nil {
+ p.metadata = map[string]dbus.Variant{}
+ p.title = ""
+ p.artist = ""
+ p.album = ""
return
}
- p.metadata = md.Value().(map[string]dbus.Variant)
- p.artist = strings.Join(p.metadata["xesam:artist"].Value().([]string), ", ")
- p.title = p.metadata["xesam:title"].Value().(string)
- p.album = p.metadata["xesam:album"].Value().(string)
+ p.metadata = metadata.Value().(map[string]dbus.Variant)
+ switch artist := p.metadata["xesam:artist"].Value().(type) {
+ case []string:
+ p.artist = strings.Join(artist, ", ")
+ case string:
+ p.artist = artist
+ default:
+ p.artist = ""
+ }
+ switch title := p.metadata["xesam:title"].Value().(type) {
+ case string:
+ p.title = title
+ default:
+ p.title = ""
+ }
+ switch album := p.metadata["xesam:album"].Value().(type) {
+ case string:
+ p.album = album
+ default:
+ p.album = ""
+ }
+ return nil
}
func (p *Player) JSON() string {
+ var items []string
+ for _, v := range []string{p.artist, p.album, p.title} {
+ if v != "" {
+ items = append(items, v)
+ }
+ }
+ if len(items) == 0 {
+ return "{}"
+ }
data := map[string]string{}
data["tooltip"] = fmt.Sprintf(
- "%s\nby %s\nfrom %s\n(%s)",
+ "%s\nby %s\n",
p.title,
- p.artist,
- p.album,
- p.playerName)
- var symbol string
+ p.artist)
+ if p.album != "" {
+ data["tooltip"] += "from " + p.album + "\n"
+ }
+ data["tooltip"] += "(" + p.name + ")"
+ symbol := PLAY
+ data["class"] = "paused"
if p.playing {
+ symbol = PAUSE
data["class"] = "playing"
- symbol = ""
- } else {
- data["class"] = "paused"
- symbol = "▶"
}
- data["text"] = fmt.Sprintf(
- "%s %s - %s - %s",
- symbol,
- p.artist,
- p.album,
- p.title)
-
- text, _ := json.Marshal(data)
+ data["text"] = symbol + " " + strings.Join(items, " - ")
+ text, err := json.Marshal(data)
+ if err != nil {
+ return "{}"
+ }
return string(text)
}
-type Players []*Player
+type PlayerList struct {
+ list List
+ conn *dbus.Conn
+}
+
+type List []*Player
-func (s Players) Len() int {
- return len(s)
+func (ls List) Len() int {
+ return len(ls)
}
-func (s Players) Less(i, j int) bool {
- s[i].Refresh()
- s[j].Refresh()
- var states [2]int
- if s[i].playing {
- states[0] = 1
- }
- if s[j].playing {
- states[1] = 1
+func (ls List) Less(i, j int) bool {
+ var states [2]uint8
+ for i, p := range []bool{ls[i].playing, ls[j].playing} {
+ if p {
+ states[i] = 1
+ }
}
- // reverse
+ // Reverse order
return states[0] > states[1]
}
-func (s Players) Swap(i, j int) {
- s[i], s[j] = s[j], s[i]
+func (ls List) Swap(i, j int) {
+ ls[i], ls[j] = ls[j], ls[i]
+}
+
+// Doesn't retain order since sorting if constantly done anyway
+func (pl *PlayerList) Remove(fullName string) {
+ var i int
+ found := false
+ for ind, p := range pl.list {
+ if p.fullName == fullName {
+ i = ind
+ found = true
+ break
+ }
+ }
+ if found {
+ pl.list[0], pl.list[i] = pl.list[i], pl.list[0]
+ pl.list = pl.list[1:]
+ }
+ // ls[len(ls)-1], ls[i] = ls[i], ls[len(ls)-1]
+ // ls = ls[:len(ls)-1]
+}
+
+func (pl *PlayerList) Reload() error {
+ var buses []string
+ err := pl.conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&buses)
+ if err != nil {
+ return err
+ }
+ for _, name := range buses {
+ if strings.HasPrefix(name, INTERFACE) {
+ pl.New(name)
+ }
+ }
+ return nil
+}
+
+func (pl *PlayerList) New(name string) {
+ pl.list = append(pl.list, NewPlayer(pl.conn, name))
+}
+
+func (pl *PlayerList) Sort() {
+ sort.Sort(pl.list)
+}
+
+func (pl *PlayerList) Refresh() {
+ for i := range pl.list {
+ pl.list[i].Refresh()
+ }
+}
+
+func (pl *PlayerList) JSON() string {
+ if len(pl.list) != 0 {
+ return pl.list[0].JSON()
+ }
+ return "{}"
}
func main() {
@@ -108,44 +225,38 @@ func main() {
if err != nil {
panic(err)
}
- var players Players
- getPlayers := func() {
- var fd []string
- err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&fd)
- if err != nil {
- panic(err)
- }
- for _, name := range fd {
- if strings.HasPrefix(name, "org.mpris.MediaPlayer2") {
- players = append(players, NewPlayer(conn, name))
- }
- }
- sort.Sort(players)
- }
- getPlayers()
- go func() {
- conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
- "type='signal',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'")
- c := make(chan *dbus.Signal, 10)
- conn.Signal(c)
- for v := range c {
+ players := &PlayerList{
+ conn: conn,
+ }
+ players.Reload()
+ players.Sort()
+ fmt.Println("New array", players)
+ conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, MATCH_NOC)
+ conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, MATCH_PC)
+ c := make(chan *dbus.Signal, 10)
+ conn.Signal(c)
+ for v := range c {
+ // fmt.Printf("SIGNAL: Sender %s, Path %s, Name %s, Body %s\n", v.Sender, v.Path, v.Name, v.Body)
+ if strings.Contains(v.Name, "NameOwnerChanged") {
switch name := v.Body[0].(type) {
case string:
- if strings.Contains(name, "org.mpris.MediaPlayer2") && name != "org.mpris.MediaPlayer2.Player" {
- players = append(players, NewPlayer(conn, name))
- sort.Sort(players)
+ var pid uint32
+ conn.BusObject().Call("org.freedesktop.DBus.GetConnectionUnixProcessID", 0, name).Store(&pid)
+ if strings.Contains(name, INTERFACE) {
+ if pid == 0 {
+ fmt.Println("Removing", name)
+ players.Remove(name)
+ } else {
+ fmt.Println("Adding", name)
+ players.New(name)
+ }
}
}
+ } else if strings.Contains(v.Name, "PropertiesChanged") && strings.Contains(v.Body[0].(string), INTERFACE+".Player") {
+ players.Refresh()
+ players.Sort()
+ fmt.Println(players.JSON())
}
- }()
- conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
- "type='signal',path='/org/mpris/MediaPlayer2',interface='org.freedesktop.DBus.Properties'")
- c := make(chan *dbus.Signal, 10)
- conn.Signal(c)
- fmt.Println(players[0].JSON())
- for range c {
- sort.Sort(players)
- players[0].Refresh()
- fmt.Println(players[0].JSON())
+ fmt.Println("New array", players)
}
}