package main import ( "encoding/json" "fmt" "github.com/godbus/dbus/v5" "sort" "strings" // "time" ) type Player struct { player dbus.BusObject playing, stopped bool playerName, title, artist, album string 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.", ""), } p.Refresh() return p } func (p *Player) Refresh() { val, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.PlaybackStatus") if err != nil { panic(err) } if strings.Contains(val.String(), "Playing") { p.playing = true p.stopped = false } else if strings.Contains(val.String(), "Paused") { p.playing = false p.stopped = false } else { p.playing = false p.stopped = true } md, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.Metadata") if err != nil { 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) } func (p *Player) JSON() string { data := map[string]string{} data["tooltip"] = fmt.Sprintf( "%s\nby %s\nfrom %s\n(%s)", p.title, p.artist, p.album, p.playerName) var symbol string if p.playing { 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) return string(text) } type Players []*Player func (s Players) Len() int { return len(s) } 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 } // reverse return states[0] > states[1] } func (s Players) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func main() { conn, err := dbus.SessionBus() 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 { 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) } } } }() 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()) } }