commit 5ff0f2c

delthas  ·  2022-11-23 19:06:42 +0000 UTC
parent 9a0cc2c
Implement /NP

This queries the system for the current song being played. This uses
DBus & MPRIS/xesam.

The priority is any player in playing state > any player in paused
state.

Only players in the playing or paused state, with a valid song title are
considered.
4 files changed,  +101, -0
M go.mod
M go.sum
+94, -0
  1@@ -3,6 +3,7 @@ package senpai
  2 import (
  3 	"errors"
  4 	"fmt"
  5+	"net/url"
  6 	"sort"
  7 	"strconv"
  8 	"strings"
  9@@ -11,6 +12,8 @@ import (
 10 	"git.sr.ht/~taiite/senpai/irc"
 11 	"git.sr.ht/~taiite/senpai/ui"
 12 	"github.com/gdamore/tcell/v2"
 13+	"github.com/godbus/dbus/v5"
 14+	"golang.org/x/net/context"
 15 )
 16 
 17 var (
 18@@ -57,6 +60,11 @@ func init() {
 19 			Desc:      "send an action (reply to last query if sent from home)",
 20 			Handle:    commandDoMe,
 21 		},
 22+		"NP": {
 23+			AllowHome: true,
 24+			Desc:      "send the current song that is being played on the system",
 25+			Handle:    commandDoNP,
 26+		},
 27 		"MSG": {
 28 			AllowHome: true,
 29 			MinArgs:   2,
 30@@ -343,6 +351,14 @@ func commandDoMe(app *App, args []string) (err error) {
 31 	return nil
 32 }
 33 
 34+func commandDoNP(app *App, args []string) (err error) {
 35+	song := getSong()
 36+	if song == "" {
 37+		return fmt.Errorf("no song was detected")
 38+	}
 39+	return commandDoMe(app, []string{fmt.Sprintf("np: %s", song)})
 40+}
 41+
 42 func commandDoMsg(app *App, args []string) (err error) {
 43 	target := args[0]
 44 	content := args[1]
 45@@ -775,3 +791,81 @@ func (app *App) handleInput(buffer, content string) error {
 46 
 47 	return cmd.Handle(app, args)
 48 }
 49+
 50+func getSong() string {
 51+	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
 52+	defer cancel()
 53+
 54+	bus, err := dbus.ConnectSessionBus(dbus.WithContext(ctx))
 55+	if err != nil {
 56+		return ""
 57+	}
 58+	defer bus.Close()
 59+
 60+	var names []string
 61+	if err := bus.BusObject().CallWithContext(ctx, "org.freedesktop.DBus.ListNames", 0).Store(&names); err != nil {
 62+		return ""
 63+	}
 64+	song := ""
 65+	for _, name := range names {
 66+		if !strings.HasPrefix(name, "org.mpris.MediaPlayer2.") {
 67+			continue
 68+		}
 69+		var playing bool
 70+		o := bus.Object(name, "/org/mpris/MediaPlayer2")
 71+		var status string
 72+		if err := o.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.mpris.MediaPlayer2.Player", "PlaybackStatus").Store(&status); err != nil {
 73+			continue
 74+		}
 75+		switch status {
 76+		case "Playing":
 77+			playing = true
 78+		case "Paused":
 79+			playing = false
 80+		default:
 81+			continue
 82+		}
 83+		var metadata map[string]dbus.Variant
 84+		if err := o.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.mpris.MediaPlayer2.Player", "Metadata").Store(&metadata); err != nil {
 85+			continue
 86+		}
 87+		var trackURL string
 88+		var title string
 89+		var album string
 90+		var artist string
 91+		if e, ok := metadata["xesam:title"].Value().(string); ok {
 92+			title = e
 93+		}
 94+		if e, ok := metadata["xesam:album"].Value().(string); ok {
 95+			album = e
 96+		}
 97+		if e, ok := metadata["xesam:url"].Value().(string); ok {
 98+			trackURL = e
 99+		}
100+		if e, ok := metadata["xesam:artist"].Value().([]string); ok && len(e) > 0 {
101+			artist = e[0]
102+		}
103+		if title == "" {
104+			continue
105+		}
106+		var sb strings.Builder
107+		fmt.Fprintf(&sb, "\x02%s\x02", title)
108+		if artist != "" {
109+			fmt.Fprintf(&sb, " by \x02%s\x02", artist)
110+		}
111+		if album != "" {
112+			fmt.Fprintf(&sb, " from \x02%s\x02", album)
113+		}
114+		if u, err := url.Parse(trackURL); err == nil {
115+			switch u.Scheme {
116+			case "http", "https":
117+				fmt.Fprintf(&sb, " — %s", trackURL)
118+			}
119+		}
120+		song = sb.String()
121+		if playing {
122+			return song
123+		}
124+	}
125+	return song
126+}
+4, -0
 1@@ -165,6 +165,10 @@ _name_ is matched case-insensitively.  It can be one of the following:
 2 	Send a message prefixed with your nick (a user action). If sent from home,
 3 	reply to the last person who sent a private message.
 4 
 5+*NP*
 6+	Send the current song that is being played on the system. Uses DBus/MPRIS
 7+	internally.
 8+
 9 *QUOTE* <raw message>
10 	Send _raw message_ verbatim.
11 
M go.mod
+1, -0
1@@ -6,6 +6,7 @@ require (
2 	git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
3 	github.com/delthas/go-localeinfo v0.0.0-20221115102303-5a7785a1acc1
4 	github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680
5+	github.com/godbus/dbus/v5 v5.1.0
6 	github.com/mattn/go-runewidth v0.0.14
7 	golang.org/x/net v0.0.0-20220722155237-a158d28d115b
8 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
M go.sum
+2, -0
1@@ -8,6 +8,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
2 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
3 github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680 h1:bCjGvZsZNvhzJZ+gDmXpKdDPyf358nSPqepaTI1AsMQ=
4 github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680/go.mod h1:XmCynGHvvGG7UFI6az9zzoEHBvZB1PGf5APwOJMFUyE=
5+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
6+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
7 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
8 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
9 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=