diff options
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | main.go | 67 |
2 files changed, 41 insertions, 29 deletions
@@ -41,7 +41,7 @@ When running, the program will pipe out json in waybar's format. Add something l ``` "custom/waybar-mpris": { "return-type": "json", - "exec": "waybar-mpris --position --autofocus", + "exec": "waybar-mpris --autofocus --text-format=\"%i [%p/%d] %a - %t\"", "on-click": "waybar-mpris --send toggle", // This option will switch between players on right click. "on-click-right": "waybar-mpris --send player-next", @@ -82,7 +82,6 @@ Usage of ./waybar-mpris: apply. * `--play/--pause` specify the symbols or text to display when music is paused/playing respectively. * `--autofocus` makes waybar-mpris automatically focus on currently playing music players. -* `--position` enables the display of the track position. * `--interpolate` increments the track position every second. This is useful for players (e.g mpDris2) that don't regularly update the position. * `--max-title` the maximum number of characters before the title cuts off. * `--replace`: By default, new instances will attach to the existing one so that the output is identical. This lets this instance replace any others running. It isn't recommended. @@ -24,7 +24,7 @@ const ( LOGFILE = "/tmp/waybar-mpris.log" OUTFILE = "/tmp/waybar-mpris.out" // Used for sharing waybar output when args are the same. DATAFILE = "/tmp/waybar-mpris.data.out" // Used for sharing "\n"-separated player data between instances when args are different. - POLL = 1 + POLL = 900 ) // Mostly default values for flag options. @@ -36,12 +36,11 @@ var ( autofocus = false // Available commands that can be sent to running instances. commands = []string{"player-next", "player-prev", "next", "prev", "toggle", "list"} - showPos = false interpolate = false replace = false isSharing = false isDataSharing = false - maxTitleLen = 70 + maxTitleLen = 50 writer io.Writer = os.Stdout shareWriter, dataWriter io.Writer ) @@ -55,7 +54,6 @@ const ( tokPosition = 'p' tokLength = 'd' tokPlayer = 'P' - tokPercent = '%' ) const ( @@ -169,7 +167,9 @@ func secondsToString(seconds int) string { return fmt.Sprintf("%02d:%02d", minutes, seconds) } -func formatOutput(p *player, icon string, fstr string) string { +// Function to format the output as requested by the user preferences of icon, +// format specifier string and maximum title length +func formatOutput(p *player, icon string, fstr string, maxTitleLen int) string { var buf []byte for i := 0; i < len(fstr); i++ { if fstr[i] == '%' { @@ -181,17 +181,31 @@ func formatOutput(p *player, icon string, fstr string) string { case tokIcon: buf = append(buf, []byte(icon)...) case tokArtist: - buf = append(buf, []byte(p.Artist)...) + artist := strings.ReplaceAll(p.Artist, "&", "&") + buf = append(buf, []byte(artist)...) case tokAlbum: - buf = append(buf, []byte(p.Album)...) + album := strings.ReplaceAll(p.Album, "&", "&") + buf = append(buf, []byte(album)...) case tokTitle: title := p.Title - if maxTitleLen > 0 && len(title) > maxTitleLen { - title = title[:maxTitleLen-3] + "..." + if maxTitleLen > 0 && len([]rune(title)) > maxTitleLen { + // Probably not the most effective thing to do, but this way + // we don't slice the string in the middle of a UTF-8 + // character it happens to be more than 1 byte, and the + // maximum length actually correlates to the displayed + // charaacters instead of the bytes. + title = string([]rune(title)[:maxTitleLen-3]) + "..." } + title = strings.ReplaceAll(title, "&", "&") buf = append(buf, []byte(title)...) case tokPosition: - position := secondsToString(int(p.Position / 1000000)) + var position string + if p.Length > 0 { + p.GetPosition() + position = secondsToString(int(p.Position / 1000000)) + } else { + position = secondsToString(0) + } buf = append(buf, []byte(position)...) case tokLength: length := secondsToString(p.Length) @@ -206,10 +220,7 @@ func formatOutput(p *player, icon string, fstr string) string { } } - out := strings.ReplaceAll(string(buf), `"`, `\"`) - out = strings.ReplaceAll(out, "&", "&") - - return out + return string(buf) } // Returns JSON for waybar to consume. @@ -224,8 +235,10 @@ func playerJSON(p *player) string { data.Class = "paused" } - data.Text = formatOutput(p, icon, textFormat) - data.Tooltip = formatOutput(p, icon, tooltipFormat) + data.Text = formatOutput(p, icon, textFormat, maxTitleLen) + // There's no point in limiting the length of the output/title in the + // tooltip, so we set it to 0 + data.Tooltip = formatOutput(p, icon, tooltipFormat, 0) out, err := json.Marshal(data) if err != nil { @@ -277,7 +290,7 @@ func execCommand(cmd string) { func duplicateOutput() error { // 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.\n") conn, err := net.Dial("unix", SOCK) if err != nil { @@ -613,6 +626,8 @@ func main() { os.Remove(SOCK) os.Remove(OUTFILE) } + } else { + fmt.Fprintf(os.Stderr, "error: %v\n", err) } conn, err := dbus.SessionBus() if err != nil { @@ -626,18 +641,16 @@ func main() { lastLine := "" go listenForCommands(players) go players.mpris2.Listen() - if showPos { - go func() { - for { - time.Sleep(POLL * time.Second) - if len(players.mpris2.List) != 0 { - if players.mpris2.List[players.mpris2.Current].Playing { - go fmt.Fprintln(writer, players.JSON()) - } + go func() { + for { + time.Sleep(POLL * time.Millisecond) + if len(players.mpris2.List) != 0 { + if players.mpris2.List[players.mpris2.Current].Playing { + go fmt.Fprintln(writer, players.JSON()) } } - }() - } + } + }() fmt.Fprintln(writer, players.JSON()) for v := range players.mpris2.Messages { if v.Name == "refresh" { |