diff --git a/go.mod b/go.mod index 88a35ef..11fa2ba 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,9 @@ module git.hrfee.pw/hrfee/waybar-mpris go 1.15 require ( - github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/fsnotify/fsnotify v1.4.9 github.com/godbus/dbus/v5 v5.0.3 - github.com/hpcloud/tail v1.0.0 github.com/hrfee/mpris2client v0.0.5 github.com/spf13/pflag v1.0.5 golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect - gopkg.in/fsnotify.v1 v1.4.7 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) diff --git a/go.sum b/go.sum index 3634c75..147a035 100644 --- a/go.sum +++ b/go.sum @@ -2,28 +2,10 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hrfee/mpris2client v0.0.0-20210108004725-d2a36745cc4a h1:3H1awMdF3eJBCg0QPpDtNW+ujQPvUCuLsqlMRoODMA4= -github.com/hrfee/mpris2client v0.0.0-20210108004725-d2a36745cc4a/go.mod h1:tVpzzAlsljmQevNA4mJwUy1onUUiaQcRAa4gdl37okY= -github.com/hrfee/mpris2client v0.0.0-20210115213010-244c4cd6a569 h1:aCHzdXajnRUeaaW9FHzunrjGcRqmyFedwO0ODJRAdBk= -github.com/hrfee/mpris2client v0.0.0-20210115213010-244c4cd6a569/go.mod h1:tVpzzAlsljmQevNA4mJwUy1onUUiaQcRAa4gdl37okY= -github.com/hrfee/mpris2client v0.0.2 h1:AqsDoJCKyKjSGwVk3sJ5fjx+7M0tsSueZbVUIDxS1xI= -github.com/hrfee/mpris2client v0.0.2/go.mod h1:tVpzzAlsljmQevNA4mJwUy1onUUiaQcRAa4gdl37okY= -github.com/hrfee/mpris2client v0.0.4 h1:2WnCkTWGBr1eg3qHFjxDo7hIeCeQWh3jJ0hFJSts9Ks= -github.com/hrfee/mpris2client v0.0.4/go.mod h1:tVpzzAlsljmQevNA4mJwUy1onUUiaQcRAa4gdl37okY= github.com/hrfee/mpris2client v0.0.5 h1:h3gcb8Q68i6SfcEgOxL9cXcGee9ekvxXltrUxGwRLDU= github.com/hrfee/mpris2client v0.0.5/go.mod h1:tVpzzAlsljmQevNA4mJwUy1onUUiaQcRAa4gdl37okY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200928205150-006507a75852 h1:sXxgOAXy8JwHhZnPuItAlUtwIlxrlEqi28mKhUR+zZY= -golang.org/x/sys v0.0.0-20200928205150-006507a75852/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48 h1:AYCWBZhgIw6XobZ5CibNJr0Rc4ZofGGKvWa1vcx2IGk= -golang.org/x/sys v0.0.0-20201116194326-cc9327a14d48/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/main.go b/main.go index 4135e78..f4bf549 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,8 @@ import ( "strings" "time" + "github.com/fsnotify/fsnotify" "github.com/godbus/dbus/v5" - "github.com/hpcloud/tail" mpris2 "github.com/hrfee/mpris2client" flag "github.com/spf13/pflag" ) @@ -37,6 +37,7 @@ var ( SHOW_POS = false INTERPOLATE = false REPLACE = false + isSharing = false WRITER io.Writer = os.Stdout ) @@ -129,6 +130,182 @@ func (pl *players) Prev() { pl.mpris2.List[pl.mpris2.Current].Previous() } func (pl *players) Toggle() { pl.mpris2.List[pl.mpris2.Current].Toggle() } +func execCommand(cmd string) { + conn, err := net.Dial("unix", SOCK) + if err != nil { + log.Fatalln("Couldn't dial:", err) + } + _, err = conn.Write([]byte(cmd)) + if err != nil { + log.Fatalln("Couldn't send command") + } + fmt.Println("Sent.") + if cmd == "list" { + buf := make([]byte, 512) + nr, err := conn.Read(buf) + if err != nil { + log.Fatalln("Couldn't read response.") + } + response := string(buf[0:nr]) + fmt.Println("Response:") + fmt.Printf(response) + } + os.Exit(0) +} + +func duplicateOutput(conn net.Conn) { + // Print to stderr to avoid errors from waybar + 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")) + if err != nil { + log.Fatalf("Couldn't send command: %v", err) + } + buf := make([]byte, 512) + nr, err := conn.Read(buf) + if err != nil { + log.Fatalf("Couldn't read response: %v", err) + } + if resp := string(buf[0:nr]); resp == "success" { + // t, err := tail.TailFile(OUTFILE, tail.Config{ + // Follow: true, + // MustExist: true, + // Logger: tail.DiscardingLogger, + // }) + // if err == nil { + // for line := range t.Lines { + // fmt.Println(line.Text) + // } + // } + f, err := os.Open(OUTFILE) + if err != nil { + log.Fatalf("Failed to open \"%s\": %v", OUTFILE, err) + } + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatalf("Failed to start watcher: %v", err) + } + defer watcher.Close() + err = watcher.Add(OUTFILE) + if err != nil { + log.Fatalf("Failed to watch file: %v", err) + } + for { + select { + case event, ok := <-watcher.Events: + if !ok { + log.Printf("Watcher failed: %v", err) + return + } + if event.Op&fsnotify.Write == fsnotify.Write { + l, err := io.ReadAll(f) + if err != nil { + log.Printf("Failed to read file: %v", err) + return + } + str := string(l) + if str[len(str)-2:] == "\n\n" { + fmt.Print(str[:len(str)-1]) + } else { + fmt.Print(string(l)) + } + f.Seek(0, 0) + } + } + } + } + +} + +func listenForCommands(players *players) { + listener, err := net.Listen("unix", SOCK) + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + <-c + os.Remove(OUTFILE) + os.Remove(SOCK) + os.Exit(1) + }() + if err != nil { + log.Fatalf("Couldn't establish socket connection at %s (error %s)\n", SOCK, err) + } + defer func() { + listener.Close() + os.Remove(SOCK) + }() + 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]) + switch command { + case "player-next": + length := len(players.mpris2.List) + if length != 1 { + if players.mpris2.Current < uint(length-1) { + players.mpris2.Current++ + } else { + players.mpris2.Current = 0 + } + players.mpris2.Refresh() + } + case "player-prev": + length := len(players.mpris2.List) + if length != 1 { + if players.mpris2.Current != 0 { + players.mpris2.Current-- + } else { + players.mpris2.Current = uint(length - 1) + } + players.mpris2.Refresh() + } + case "next": + players.Next() + case "prev": + players.Prev() + case "toggle": + players.Toggle() + case "list": + con.Write([]byte(players.mpris2.String())) + case "share": + if !isSharing { + f, err := os.OpenFile(OUTFILE, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + defer f.Close() + if err != nil { + fmt.Fprintf(con, "Failed: %v", err) + } + var out io.Writer = emptyEveryWrite{file: f} + WRITER = io.MultiWriter(os.Stdout, out) + isSharing = true + } + fmt.Fprint(con, "success") + default: + fmt.Println("Invalid command") + } + } +} + +type emptyEveryWrite struct { + file *os.File +} + +func (w emptyEveryWrite) Write(p []byte) (n int, err error) { + offset, err := w.file.Seek(0, 0) + if err != nil { + return 0, err + } + return w.file.WriteAt(p, offset) +} + func main() { logfile, err := os.OpenFile(LOGFILE, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) if err != nil { @@ -151,26 +328,7 @@ func main() { os.Stderr = logfile 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.") - if command == "list" { - buf := make([]byte, 512) - nr, err := conn.Read(buf) - if err != nil { - log.Fatalln("Couldn't read response.") - } - response := string(buf[0:nr]) - fmt.Println("Response:") - fmt.Printf(response) - } - os.Exit(0) + execCommand(command) } // fmt.Println("New array", players) // Start command listener @@ -194,33 +352,11 @@ func main() { } } 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. - // Print to stderr to avoid errors from waybar - os.Stderr.WriteString("waybar-mpris is already running. This instance will clone its output.") - if err != nil { - log.Fatalln("Couldn't dial:", err) - } - _, err = conn.Write([]byte("share")) - if err != nil { - log.Fatalln("Couldn't send command") - } - buf := make([]byte, 512) - nr, err := conn.Read(buf) - if err != nil { - log.Fatalln("Couldn't read response.") - } - if string(buf[0:nr]) == "success" { - t, err := tail.TailFile(OUTFILE, tail.Config{ - Follow: true, - MustExist: true, - Logger: tail.DiscardingLogger, - }) - if err == nil { - for line := range t.Lines { - fmt.Println(line.Text) - } - } - } + duplicateOutput(conn) } else { + if err != nil { + os.Stdout.WriteString("Couldn't dial socket, deleting instead: " + err.Error()) + } os.Remove(SOCK) os.Remove(OUTFILE) } @@ -235,77 +371,7 @@ func main() { players.mpris2.Reload() players.mpris2.Sort() lastLine := "" - go func() { - listener, err := net.Listen("unix", SOCK) - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - go func() { - <-c - os.Remove(OUTFILE) - os.Remove(SOCK) - os.Exit(1) - }() - if err != nil { - log.Fatalf("Couldn't establish socket connection at %s (error %s)\n", SOCK, err) - } - defer func() { - listener.Close() - os.Remove(SOCK) - }() - 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]) - switch command { - case "player-next": - length := len(players.mpris2.List) - if length != 1 { - if players.mpris2.Current < uint(length-1) { - players.mpris2.Current++ - } else { - players.mpris2.Current = 0 - } - players.mpris2.Refresh() - } - case "player-prev": - length := len(players.mpris2.List) - if length != 1 { - if players.mpris2.Current != 0 { - players.mpris2.Current-- - } else { - players.mpris2.Current = uint(length - 1) - } - players.mpris2.Refresh() - } - case "next": - players.Next() - case "prev": - players.Prev() - case "toggle": - players.Toggle() - case "list": - con.Write([]byte(players.mpris2.String())) - case "share": - out, err := os.Create(OUTFILE) - if err != nil { - con.Write([]byte(fmt.Sprintf("Failed: %s", err))) - } - WRITER = io.MultiWriter(os.Stdout, out) - con.Write([]byte("success")) - default: - fmt.Println("Invalid command") - } - } - }() + go listenForCommands(players) go players.mpris2.Listen() if SHOW_POS { go func() { diff --git a/waybar-mpris b/waybar-mpris index 8f71aec..a940dcf 100755 Binary files a/waybar-mpris and b/waybar-mpris differ