only share output when args are identical; use fixed length socket

commands

fixed length commands were originally a guess for an issue I was having,
but it's cleaner this way anyway.
This commit is contained in:
Harvey Tindall 2021-05-17 13:43:56 +01:00
parent 51c7a983a0
commit ea4f47a1b1
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
2 changed files with 127 additions and 58 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
./main ./main
./waybar-mpris waybar-mpris

113
main.go
View File

@ -40,6 +40,41 @@ var (
WRITER io.Writer = os.Stdout WRITER io.Writer = os.Stdout
) )
const (
cPlayerNext = "pn"
cPlayerPrev = "pp"
cNext = "mn"
cPrev = "mp"
cToggle = "mt"
cList = "ls"
cShare = "sh"
cPreShare = "ps"
rSuccess = "sc"
rInvalidCommand = "iv"
)
func stringToCmd(str string) string {
switch str {
case "player-next":
return cPlayerNext
case "player-prev":
return cPlayerPrev
case "next":
return cNext
case "prev":
return cPrev
case "toggle":
return cToggle
case "list":
return cList
case "share":
return cShare
case "pre-share":
return cPreShare
}
return ""
}
// JSON returns json for waybar to consume. // JSON returns json for waybar to consume.
func playerJSON(p *mpris2.Player) string { func playerJSON(p *mpris2.Player) string {
symbol := PLAY symbol := PLAY
@ -140,7 +175,8 @@ func execCommand(cmd string) {
if err != nil { if err != nil {
log.Fatalln("Couldn't dial:", err) log.Fatalln("Couldn't dial:", err)
} }
_, err = conn.Write([]byte(cmd)) shortCmd := stringToCmd(cmd)
_, err = conn.Write([]byte(shortCmd))
if err != nil { if err != nil {
log.Fatalln("Couldn't send command") log.Fatalln("Couldn't send command")
} }
@ -158,20 +194,46 @@ func execCommand(cmd string) {
os.Exit(0) os.Exit(0)
} }
func duplicateOutput(conn net.Conn) { func duplicateOutput() error {
// Print to stderr to avoid errors from waybar // Print to stderr to avoid errors from waybar
os.Stderr.WriteString("waybar-mpris is already running. This instance will clone its output.") os.Stderr.WriteString("waybar-mpris is already running. This instance will clone its output.")
// Tell other instance to share output in OUTFILE
_, err := conn.Write([]byte("share")) conn, err := net.Dial("unix", SOCK)
if err != nil {
return err
}
_, err = conn.Write([]byte(cPreShare))
if err != nil { if err != nil {
log.Fatalf("Couldn't send command: %v", err) log.Fatalf("Couldn't send command: %v", err)
return err
} }
buf := make([]byte, 512) buf := make([]byte, 512)
nr, err := conn.Read(buf) nr, err := conn.Read(buf)
if err != nil { if err != nil {
log.Fatalf("Couldn't read response: %v", err) log.Fatalf("Couldn't read response: %v", err)
return err
} }
if resp := string(buf[0:nr]); resp == "success" { argString := ""
for _, arg := range os.Args {
argString += arg + "|"
}
if string(buf[0:nr]) == argString {
conn.Close()
conn, err = net.Dial("unix", SOCK)
if err != nil {
return err
}
// Tell other instance to share output in OUTFILE
_, err := conn.Write([]byte(cShare))
if err != nil {
log.Fatalf("Couldn't send command: %v", err)
}
buf := make([]byte, 2)
nr, err := conn.Read(buf)
if err != nil {
log.Fatalf("Couldn't read response: %v", err)
}
if resp := string(buf[0:nr]); resp == rSuccess {
// t, err := tail.TailFile(OUTFILE, tail.Config{ // t, err := tail.TailFile(OUTFILE, tail.Config{
// Follow: true, // Follow: true,
// MustExist: true, // MustExist: true,
@ -200,13 +262,13 @@ func duplicateOutput(conn net.Conn) {
case event, ok := <-watcher.Events: case event, ok := <-watcher.Events:
if !ok { if !ok {
log.Printf("Watcher failed: %v", err) log.Printf("Watcher failed: %v", err)
return return err
} }
if event.Op&fsnotify.Write == fsnotify.Write { if event.Op&fsnotify.Write == fsnotify.Write {
l, err := io.ReadAll(f) l, err := io.ReadAll(f)
if err != nil { if err != nil {
log.Printf("Failed to read file: %v", err) log.Printf("Failed to read file: %v", err)
return return err
} }
str := string(l) str := string(l)
// Trim extra newline is necessary // Trim extra newline is necessary
@ -220,7 +282,8 @@ func duplicateOutput(conn net.Conn) {
} }
} }
} }
}
return nil
} }
func listenForCommands(players *players) { func listenForCommands(players *players) {
@ -246,7 +309,7 @@ func listenForCommands(players *players) {
log.Println("Couldn't accept:", err) log.Println("Couldn't accept:", err)
continue continue
} }
buf := make([]byte, 512) buf := make([]byte, 2)
nr, err := con.Read(buf) nr, err := con.Read(buf)
if err != nil { if err != nil {
log.Println("Couldn't read:", err) log.Println("Couldn't read:", err)
@ -254,7 +317,7 @@ func listenForCommands(players *players) {
} }
command := string(buf[0:nr]) command := string(buf[0:nr])
switch command { switch command {
case "player-next": case cPlayerNext:
length := len(players.mpris2.List) length := len(players.mpris2.List)
if length != 1 { if length != 1 {
if players.mpris2.Current < uint(length-1) { if players.mpris2.Current < uint(length-1) {
@ -264,7 +327,7 @@ func listenForCommands(players *players) {
} }
players.mpris2.Refresh() players.mpris2.Refresh()
} }
case "player-prev": case cPlayerPrev:
length := len(players.mpris2.List) length := len(players.mpris2.List)
if length != 1 { if length != 1 {
if players.mpris2.Current != 0 { if players.mpris2.Current != 0 {
@ -274,15 +337,15 @@ func listenForCommands(players *players) {
} }
players.mpris2.Refresh() players.mpris2.Refresh()
} }
case "next": case cNext:
players.Next() players.Next()
case "prev": case cPrev:
players.Prev() players.Prev()
case "toggle": case cToggle:
players.Toggle() players.Toggle()
case "list": case cList:
con.Write([]byte(players.mpris2.String())) con.Write([]byte(players.mpris2.String()))
case "share": case cShare:
if !isSharing { if !isSharing {
f, err := os.OpenFile(OUTFILE, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) f, err := os.OpenFile(OUTFILE, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
defer f.Close() defer f.Close()
@ -293,10 +356,20 @@ func listenForCommands(players *players) {
WRITER = io.MultiWriter(os.Stdout, out) WRITER = io.MultiWriter(os.Stdout, out)
isSharing = true isSharing = true
} }
fmt.Fprint(con, "success") fmt.Fprint(con, rSuccess)
/* Prior to sharing, the first instance sends its os.Args.
If the second instances args are different, the first sends the raw data (artist, album, etc.)
If they are the same, the first instance just sends its output and the second prints it. */
case cPreShare:
out := ""
for _, arg := range os.Args {
out += arg + "|"
}
con.Write([]byte(out))
default: default:
fmt.Println("Invalid command") fmt.Println("Invalid command")
} }
con.Close()
} }
} }
@ -356,13 +429,9 @@ func main() {
ignoreChoice = true ignoreChoice = true
// os.Remove(SOCK) // os.Remove(SOCK)
} }
} else if conn, err := net.Dial("unix", SOCK); err == nil {
// When waybar-mpris is already running, we attach to its output instead of launching a whole new instance. // When waybar-mpris is already running, we attach to its output instead of launching a whole new instance.
duplicateOutput(conn) } else if err := duplicateOutput(); err != nil {
} else {
if err != nil {
os.Stdout.WriteString("Couldn't dial socket, deleting instead: " + err.Error()) os.Stdout.WriteString("Couldn't dial socket, deleting instead: " + err.Error())
}
os.Remove(SOCK) os.Remove(SOCK)
os.Remove(OUTFILE) os.Remove(OUTFILE)
} }