fix halt when player is removed
rewritten without goroutines or checking if processes exist and it seems to work much better now.
This commit is contained in:
parent
3cf3f910ca
commit
5020e1c929
4
go.mod
4
go.mod
@ -2,6 +2,4 @@ module github.com/hrfee/waybar-mpris
|
|||||||
|
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require github.com/godbus/dbus/v5 v5.0.3
|
||||||
github.com/godbus/dbus/v5 v5.0.3
|
|
||||||
)
|
|
||||||
|
1
go.sum
1
go.sum
@ -1,4 +1,3 @@
|
|||||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
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 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
275
main.go
275
main.go
@ -3,104 +3,221 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
// "time"
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var knownPlayers = map[string]string{
|
||||||
|
"plasma-browser-integration": "Browser",
|
||||||
|
"noson": "Noson",
|
||||||
|
}
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
player dbus.BusObject
|
player dbus.BusObject
|
||||||
|
fullName, name, title, artist, album string
|
||||||
playing, stopped bool
|
playing, stopped bool
|
||||||
playerName, title, artist, album string
|
|
||||||
metadata map[string]dbus.Variant
|
metadata map[string]dbus.Variant
|
||||||
conn *dbus.Conn
|
conn *dbus.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlayer(conn *dbus.Conn, name string) *Player {
|
const (
|
||||||
p := &Player{
|
INTERFACE = "org.mpris.MediaPlayer2"
|
||||||
player: conn.Object(name, "/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,
|
conn: conn,
|
||||||
playerName: strings.ReplaceAll(name, "org.mpris.MediaPlayer2.", ""),
|
name: playerName,
|
||||||
|
fullName: name,
|
||||||
}
|
}
|
||||||
p.Refresh()
|
p.Refresh()
|
||||||
return p
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Player) Refresh() {
|
// Refresh grabs playback info.
|
||||||
val, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.PlaybackStatus")
|
func (p *Player) Refresh() (err error) {
|
||||||
|
val, err := p.player.GetProperty(INTERFACE + ".Player.PlaybackStatus")
|
||||||
if err != nil {
|
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.playing = true
|
||||||
p.stopped = false
|
p.stopped = false
|
||||||
} else if strings.Contains(val.String(), "Paused") {
|
} else if strings.Contains(strVal, "Paused") {
|
||||||
p.playing = false
|
p.playing = false
|
||||||
p.stopped = false
|
p.stopped = false
|
||||||
} else {
|
} else {
|
||||||
p.playing = false
|
p.playing = false
|
||||||
p.stopped = true
|
p.stopped = true
|
||||||
}
|
}
|
||||||
md, err := p.player.GetProperty("org.mpris.MediaPlayer2.Player.Metadata")
|
metadata, err := p.player.GetProperty(INTERFACE + ".Player.Metadata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
p.metadata = map[string]dbus.Variant{}
|
||||||
|
p.title = ""
|
||||||
|
p.artist = ""
|
||||||
|
p.album = ""
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.metadata = md.Value().(map[string]dbus.Variant)
|
p.metadata = metadata.Value().(map[string]dbus.Variant)
|
||||||
p.artist = strings.Join(p.metadata["xesam:artist"].Value().([]string), ", ")
|
switch artist := p.metadata["xesam:artist"].Value().(type) {
|
||||||
p.title = p.metadata["xesam:title"].Value().(string)
|
case []string:
|
||||||
p.album = p.metadata["xesam:album"].Value().(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 {
|
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 := map[string]string{}
|
||||||
data["tooltip"] = fmt.Sprintf(
|
data["tooltip"] = fmt.Sprintf(
|
||||||
"%s\nby %s\nfrom %s\n(%s)",
|
"%s\nby %s\n",
|
||||||
p.title,
|
p.title,
|
||||||
p.artist,
|
p.artist)
|
||||||
p.album,
|
if p.album != "" {
|
||||||
p.playerName)
|
data["tooltip"] += "from " + p.album + "\n"
|
||||||
var symbol string
|
}
|
||||||
if p.playing {
|
data["tooltip"] += "(" + p.name + ")"
|
||||||
data["class"] = "playing"
|
symbol := PLAY
|
||||||
symbol = ""
|
data["class"] = "paused"
|
||||||
} else {
|
if p.playing {
|
||||||
data["class"] = "paused"
|
symbol = PAUSE
|
||||||
symbol = "▶"
|
data["class"] = "playing"
|
||||||
|
}
|
||||||
|
data["text"] = symbol + " " + strings.Join(items, " - ")
|
||||||
|
text, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return "{}"
|
||||||
}
|
}
|
||||||
data["text"] = fmt.Sprintf(
|
|
||||||
"%s %s - %s - %s",
|
|
||||||
symbol,
|
|
||||||
p.artist,
|
|
||||||
p.album,
|
|
||||||
p.title)
|
|
||||||
|
|
||||||
text, _ := json.Marshal(data)
|
|
||||||
return string(text)
|
return string(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Players []*Player
|
type PlayerList struct {
|
||||||
|
list List
|
||||||
func (s Players) Len() int {
|
conn *dbus.Conn
|
||||||
return len(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Players) Less(i, j int) bool {
|
type List []*Player
|
||||||
s[i].Refresh()
|
|
||||||
s[j].Refresh()
|
func (ls List) Len() int {
|
||||||
var states [2]int
|
return len(ls)
|
||||||
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]
|
return states[0] > states[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Players) Swap(i, j int) {
|
func (ls List) Swap(i, j int) {
|
||||||
s[i], s[j] = s[j], s[i]
|
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() {
|
func main() {
|
||||||
@ -108,44 +225,38 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var players Players
|
players := &PlayerList{
|
||||||
getPlayers := func() {
|
conn: conn,
|
||||||
var fd []string
|
|
||||||
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&fd)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
for _, name := range fd {
|
players.Reload()
|
||||||
if strings.HasPrefix(name, "org.mpris.MediaPlayer2") {
|
players.Sort()
|
||||||
players = append(players, NewPlayer(conn, name))
|
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)
|
||||||
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)
|
c := make(chan *dbus.Signal, 10)
|
||||||
conn.Signal(c)
|
conn.Signal(c)
|
||||||
for v := range 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) {
|
switch name := v.Body[0].(type) {
|
||||||
case string:
|
case string:
|
||||||
if strings.Contains(name, "org.mpris.MediaPlayer2") && name != "org.mpris.MediaPlayer2.Player" {
|
var pid uint32
|
||||||
players = append(players, NewPlayer(conn, name))
|
conn.BusObject().Call("org.freedesktop.DBus.GetConnectionUnixProcessID", 0, name).Store(&pid)
|
||||||
sort.Sort(players)
|
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") {
|
||||||
conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
players.Refresh()
|
||||||
"type='signal',path='/org/mpris/MediaPlayer2',interface='org.freedesktop.DBus.Properties'")
|
players.Sort()
|
||||||
c := make(chan *dbus.Signal, 10)
|
fmt.Println(players.JSON())
|
||||||
conn.Signal(c)
|
}
|
||||||
fmt.Println(players[0].JSON())
|
fmt.Println("New array", players)
|
||||||
for range c {
|
|
||||||
sort.Sort(players)
|
|
||||||
players[0].Refresh()
|
|
||||||
fmt.Println(players[0].JSON())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user