remote control via unix sockets
waybar-mpris --send will send commands to a currently running waybar-mpris instance. Players can be switched between and controlled individually. Also added gifs, more stuff to README.
This commit is contained in:
parent
b49765a848
commit
74015e43a3
52
README.md
52
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.*
|
||||
|
||||
|
BIN
images/bar.gif
Normal file
BIN
images/bar.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
BIN
images/bar.mkv
Normal file
BIN
images/bar.mkv
Normal file
Binary file not shown.
BIN
images/cropped.gif
Normal file
BIN
images/cropped.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
BIN
images/cropped.mkv
Normal file
BIN
images/cropped.mkv
Normal file
Binary file not shown.
BIN
images/cropped.png
Normal file
BIN
images/cropped.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 947 B |
BIN
images/palette.png
Normal file
BIN
images/palette.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 991 B |
111
main.go
111
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,6 +32,7 @@ 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 (
|
||||
@ -37,6 +40,8 @@ var (
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -174,6 +179,7 @@ func (p *Player) JSON() string {
|
||||
|
||||
type PlayerList struct {
|
||||
list List
|
||||
current uint
|
||||
conn *dbus.Conn
|
||||
}
|
||||
|
||||
@ -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,18 +271,44 @@ 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()
|
||||
|
||||
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)
|
||||
@ -272,6 +322,53 @@ func main() {
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}()
|
||||
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)
|
||||
@ -295,12 +392,14 @@ func main() {
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
waybar-mpris
BIN
waybar-mpris
Binary file not shown.
Loading…
Reference in New Issue
Block a user