aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md52
-rw-r--r--images/bar.gifbin0 -> 174473 bytes
-rw-r--r--images/bar.mkvbin0 -> 33222 bytes
-rw-r--r--images/cropped.gifbin0 -> 151082 bytes
-rw-r--r--images/cropped.mkvbin0 -> 20109 bytes
-rw-r--r--images/cropped.pngbin0 -> 947 bytes
-rw-r--r--images/palette.pngbin0 -> 991 bytes
-rw-r--r--main.go195
-rwxr-xr-xwaybar-mprisbin4413239 -> 4469393 bytes
9 files changed, 193 insertions, 54 deletions
diff --git a/README.md b/README.md
index e7363e0..2208e3c 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,62 @@
## waybar-mpris
+<p>
+ <img src="images/cropped.gif" style="width: 100%;" alt="bar gif"></img>
+</p>
-a custom waybar component for displaying info from MPRIS2 players. It automatically focuses on currently playing music players, and can easily be customized.
+a waybar component/utility for displaying and controlling MPRIS2 compliant media players individually, inspired by [waybar-media](https://github.com/yurihs/waybar-media).
+
+MPRIS2 is widely supported, so this component should work with:
+* Chrome/Chromium
+* Other browsers (with kde plasma integration installed)
+* VLC
+* Spotify
+* Noson
+* mpd (with [mpDris2](https://github.com/eonpatapon/mpDris2))
+* Most other music/media players
## Install
`go get github.com/hrfee/waybar-mpris`
-or just grab the binary from here.
+or just grab the `waybar-mpris` binary from here and place it in your PATH.
## Usage
-When running, the program will pipe out json in waybar's format. Make a custom component in your configuration and set `return-type` to `json`, and `exec` to the path to the program.
+When running, the program will pipe out json in waybar's format. Add something like this to your waybar `config.json`:
+``` json
+"custom/waybar-mpris": {
+ "return-type": "json",
+ "exec": "waybar-mpris",
+ "on-click": "waybar-mpris --send toggle",
+ // This option will switch between players on right click.
+ "on-click-right": "waybar-mpris --send player-next",
+ // The options below will switch the selected player on scroll
+ // "on-scroll-up": "waybar-mpris --send player-next",
+ // "on-scroll-down": "waybar-mpris --send player-prev",
+ // The options below will go to next/previous track on scroll
+ // "on-scroll-up": "waybar-mpris --send next",
+ // "on-scroll-down": "waybar-mpris --send prev",
+ "escape": true,
+},
```
-Usage of ./waybar-mpris:
+
+
+```
+Usage of waybar-mpris:
+ --autofocus Auto switch to currently playing music players.
--order string Element order. (default "SYMBOL:ARTIST:ALBUM:TITLE")
- --pause string Pause symbol/text to use. (default "")
+ --pause string Pause symbol/text to use. (default "\uf8e3")
--play string Play symbol/text to use. (default "▶")
+ --send string send command to already runnning waybar-mpris instance. (options: player-next/player-prev/next/prev/toggle)
--separator string Separator string to use between artist, album, and title. (default " - ")
```
+
* Modify the order of components with `--order`. `SYMBOL` is the play/paused icon or text, other options are self explanatory.
* `--play/--pause` specify the symbols or text to display when music is paused/playing respectively.
-* --separator specifies a string to separate the artist, album and title text.
+* `--separator` specifies a string to separate the artist, album and title text.
+* `--autofocus` makes waybar-mpris automatically focus on currently playing music players.
+* `--send` sends commands to an already running waybar-mpris instance via a unix socket. Commands:
+ * `player-next`: Switch to displaying and controlling next available player.
+ * `player-prev`: Same as `player-next`, but for the previous player.
+ * `next/prev`: Next/previous track on the selected player.
+ * `toggle`: Play/pause.
+ * *Note: you can also bind these commands to keys in your sway/other wm config.*
diff --git a/images/bar.gif b/images/bar.gif
new file mode 100644
index 0000000..efc093e
--- /dev/null
+++ b/images/bar.gif
Binary files differ
diff --git a/images/bar.mkv b/images/bar.mkv
new file mode 100644
index 0000000..3085bfd
--- /dev/null
+++ b/images/bar.mkv
Binary files differ
diff --git a/images/cropped.gif b/images/cropped.gif
new file mode 100644
index 0000000..03b519c
--- /dev/null
+++ b/images/cropped.gif
Binary files differ
diff --git a/images/cropped.mkv b/images/cropped.mkv
new file mode 100644
index 0000000..6d0c3e9
--- /dev/null
+++ b/images/cropped.mkv
Binary files differ
diff --git a/images/cropped.png b/images/cropped.png
new file mode 100644
index 0000000..e6d7361
--- /dev/null
+++ b/images/cropped.png
Binary files differ
diff --git a/images/palette.png b/images/palette.png
new file mode 100644
index 0000000..4b6927e
--- /dev/null
+++ b/images/palette.png
Binary files differ
diff --git a/main.go b/main.go
index a6f7688..e018f15 100644
--- a/main.go
+++ b/main.go
@@ -3,11 +3,13 @@ package main
import (
"encoding/json"
"fmt"
- "sort"
- "strings"
-
"github.com/godbus/dbus/v5"
flag "github.com/spf13/pflag"
+ "log"
+ "net"
+ "os"
+ "sort"
+ "strings"
)
var knownPlayers = map[string]string{
@@ -30,13 +32,16 @@ const (
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'"
+ SOCK = "/tmp/waybar-mpris.sock"
)
var (
- PLAY = "▶"
- PAUSE = ""
- SEP = " - "
- ORDER = "SYMBOL:ARTIST:ALBUM:TITLE"
+ PLAY = "▶"
+ PAUSE = ""
+ SEP = " - "
+ ORDER = "SYMBOL:ARTIST:ALBUM:TITLE"
+ AUTOFOCUS = false
+ COMMANDS = []string{"player-next", "player-prev", "next", "prev", "toggle"}
)
// NewPlayer returns a new player object.
@@ -135,7 +140,7 @@ func (p *Player) JSON() string {
items = append(items, p.album)
}
} else if v == "TITLE" {
- if p.album != "" {
+ if p.title != "" {
items = append(items, p.title)
}
}
@@ -173,8 +178,9 @@ func (p *Player) JSON() string {
}
type PlayerList struct {
- list List
- conn *dbus.Conn
+ list List
+ current uint
+ conn *dbus.Conn
}
type List []*Player
@@ -200,6 +206,7 @@ func (ls List) Swap(i, j int) {
// Doesn't retain order since sorting if constantly done anyway
func (pl *PlayerList) Remove(fullName string) {
+ currentName := pl.list[pl.current].fullName
var i int
found := false
for ind, p := range pl.list {
@@ -212,6 +219,19 @@ func (pl *PlayerList) Remove(fullName string) {
if found {
pl.list[0], pl.list[i] = pl.list[i], pl.list[0]
pl.list = pl.list[1:]
+ found = false
+ for ind, p := range pl.list {
+ if p.fullName == currentName {
+ pl.current = uint(ind)
+ found = true
+ break
+ }
+ }
+ if !found {
+ pl.current = 0
+ pl.Refresh()
+ fmt.Println(pl.JSON())
+ }
}
// ls[len(ls)-1], ls[i] = ls[i], ls[len(ls)-1]
// ls = ls[:len(ls)-1]
@@ -233,10 +253,14 @@ func (pl *PlayerList) Reload() error {
func (pl *PlayerList) New(name string) {
pl.list = append(pl.list, NewPlayer(pl.conn, name))
+ if AUTOFOCUS {
+ pl.current = uint(len(pl.list) - 1)
+ }
}
func (pl *PlayerList) Sort() {
sort.Sort(pl.list)
+ pl.current = 0
}
func (pl *PlayerList) Refresh() {
@@ -247,60 +271,135 @@ func (pl *PlayerList) Refresh() {
func (pl *PlayerList) JSON() string {
if len(pl.list) != 0 {
- return pl.list[0].JSON()
+ return pl.list[pl.current].JSON()
}
return "{}"
}
+func (pl *PlayerList) Next() {
+ pl.list[pl.current].player.Call(INTERFACE+".Player.Next", 0)
+}
+
+func (pl *PlayerList) Prev() {
+ pl.list[pl.current].player.Call(INTERFACE+".Player.Previous", 0)
+}
+
+func (pl *PlayerList) Toggle() {
+ pl.list[pl.current].player.Call(INTERFACE+".Player.PlayPause", 0)
+}
+
func main() {
flag.StringVar(&PLAY, "play", PLAY, "Play symbol/text to use.")
flag.StringVar(&PAUSE, "pause", PAUSE, "Pause symbol/text to use.")
flag.StringVar(&SEP, "separator", SEP, "Separator string to use between artist, album, and title.")
flag.StringVar(&ORDER, "order", ORDER, "Element order.")
+ flag.BoolVar(&AUTOFOCUS, "autofocus", AUTOFOCUS, "Auto switch to currently playing music players.")
+ var command string
+ flag.StringVar(&command, "send", "", "send command to already runnning waybar-mpris instance. (options: "+strings.Join(COMMANDS, "/")+")")
flag.Parse()
- conn, err := dbus.SessionBus()
- if err != nil {
- panic(err)
- }
- players := &PlayerList{
- conn: conn,
- }
- players.Reload()
- players.Sort()
- players.Refresh()
- fmt.Println(players.JSON())
- lastLine := ""
- // 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:
- 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)
+ if command != "" {
+ conn, err := net.Dial("unix", SOCK)
+ if err != nil {
+ log.Fatalln("Couldn't dial:", err)
+ }
+ _, err = conn.Write([]byte(command))
+ if err != nil {
+ log.Fatalln("Couldn't send command")
+ }
+ fmt.Println("Sent.")
+ } else {
+ conn, err := dbus.SessionBus()
+ if err != nil {
+ panic(err)
+ }
+ players := &PlayerList{
+ conn: conn,
+ }
+ players.Reload()
+ players.Sort()
+ players.Refresh()
+ fmt.Println(players.JSON())
+ lastLine := ""
+ // fmt.Println("New array", players)
+ go func() {
+ os.Remove(SOCK)
+ listener, err := net.Listen("unix", SOCK)
+ if err != nil {
+ log.Fatalln("Couldn't establish socket connection at", SOCK)
+ }
+ defer listener.Close()
+ for {
+ con, err := listener.Accept()
+ if err != nil {
+ log.Println("Couldn't accept:", err)
+ continue
+ }
+ buf := make([]byte, 512)
+ nr, err := con.Read(buf)
+ if err != nil {
+ log.Println("Couldn't read:", err)
+ continue
+ }
+ command := string(buf[0:nr])
+ if command == "player-next" {
+ if players.current < uint(len(players.list)-1) {
+ players.current += 1
+ } else {
+ players.current = 0
+ }
+ players.Refresh()
+ fmt.Println(players.JSON())
+ } else if command == "player-prev" {
+ if players.current != 0 {
+ players.current -= 1
} else {
- // fmt.Println("Adding", name)
- players.New(name)
+ players.current = uint(len(players.list) - 1)
}
+ players.Refresh()
+ fmt.Println(players.JSON())
+ } else if command == "next" {
+ players.Next()
+ } else if command == "prev" {
+ players.Prev()
+ } else if command == "toggle" {
+ players.Toggle()
+ } else {
+ fmt.Println("Invalid command")
}
}
- } else if strings.Contains(v.Name, "PropertiesChanged") && strings.Contains(v.Body[0].(string), INTERFACE+".Player") {
- players.Refresh()
- players.Sort()
- if l := players.JSON(); l != lastLine {
- lastLine = l
- fmt.Println(l)
+ }()
+ 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:
+ 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()
+ if AUTOFOCUS {
+ players.Sort()
+ }
+ if l := players.JSON(); l != lastLine {
+ lastLine = l
+ fmt.Println(l)
+ }
}
}
- // fmt.Println("New array", players)
}
}
diff --git a/waybar-mpris b/waybar-mpris
index 31c553d..1492d17 100755
--- a/waybar-mpris
+++ b/waybar-mpris
Binary files differ