summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordelthas <delthas@dille.cc>2022-11-23 20:06:42 +0100
committerdelthas <delthas@dille.cc>2022-11-23 20:11:41 +0100
commit5ff0f2c63b96623c81657a8f0e2dbdc537edf103 (patch)
tree8a9980f2236491cb73debe40aea98b37bead045e
parentAdd documentation for /MOTD (diff)
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.
-rw-r--r--commands.go94
-rw-r--r--doc/senpai.1.scd4
-rw-r--r--go.mod1
-rw-r--r--go.sum2
4 files changed, 101 insertions, 0 deletions
diff --git a/commands.go b/commands.go
index 55582a0..5a2c71b 100644
--- a/commands.go
+++ b/commands.go
@@ -3,6 +3,7 @@ package senpai
import (
"errors"
"fmt"
+ "net/url"
"sort"
"strconv"
"strings"
@@ -11,6 +12,8 @@ import (
"git.sr.ht/~taiite/senpai/irc"
"git.sr.ht/~taiite/senpai/ui"
"github.com/gdamore/tcell/v2"
+ "github.com/godbus/dbus/v5"
+ "golang.org/x/net/context"
)
var (
@@ -57,6 +60,11 @@ func init() {
Desc: "send an action (reply to last query if sent from home)",
Handle: commandDoMe,
},
+ "NP": {
+ AllowHome: true,
+ Desc: "send the current song that is being played on the system",
+ Handle: commandDoNP,
+ },
"MSG": {
AllowHome: true,
MinArgs: 2,
@@ -343,6 +351,14 @@ func commandDoMe(app *App, args []string) (err error) {
return nil
}
+func commandDoNP(app *App, args []string) (err error) {
+ song := getSong()
+ if song == "" {
+ return fmt.Errorf("no song was detected")
+ }
+ return commandDoMe(app, []string{fmt.Sprintf("np: %s", song)})
+}
+
func commandDoMsg(app *App, args []string) (err error) {
target := args[0]
content := args[1]
@@ -775,3 +791,81 @@ func (app *App) handleInput(buffer, content string) error {
return cmd.Handle(app, args)
}
+
+func getSong() string {
+ ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+ defer cancel()
+
+ bus, err := dbus.ConnectSessionBus(dbus.WithContext(ctx))
+ if err != nil {
+ return ""
+ }
+ defer bus.Close()
+
+ var names []string
+ if err := bus.BusObject().CallWithContext(ctx, "org.freedesktop.DBus.ListNames", 0).Store(&names); err != nil {
+ return ""
+ }
+ song := ""
+ for _, name := range names {
+ if !strings.HasPrefix(name, "org.mpris.MediaPlayer2.") {
+ continue
+ }
+ var playing bool
+ o := bus.Object(name, "/org/mpris/MediaPlayer2")
+ var status string
+ if err := o.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.mpris.MediaPlayer2.Player", "PlaybackStatus").Store(&status); err != nil {
+ continue
+ }
+ switch status {
+ case "Playing":
+ playing = true
+ case "Paused":
+ playing = false
+ default:
+ continue
+ }
+ var metadata map[string]dbus.Variant
+ if err := o.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.mpris.MediaPlayer2.Player", "Metadata").Store(&metadata); err != nil {
+ continue
+ }
+ var trackURL string
+ var title string
+ var album string
+ var artist string
+ if e, ok := metadata["xesam:title"].Value().(string); ok {
+ title = e
+ }
+ if e, ok := metadata["xesam:album"].Value().(string); ok {
+ album = e
+ }
+ if e, ok := metadata["xesam:url"].Value().(string); ok {
+ trackURL = e
+ }
+ if e, ok := metadata["xesam:artist"].Value().([]string); ok && len(e) > 0 {
+ artist = e[0]
+ }
+ if title == "" {
+ continue
+ }
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "\x02%s\x02", title)
+ if artist != "" {
+ fmt.Fprintf(&sb, " by \x02%s\x02", artist)
+ }
+ if album != "" {
+ fmt.Fprintf(&sb, " from \x02%s\x02", album)
+ }
+ if u, err := url.Parse(trackURL); err == nil {
+ switch u.Scheme {
+ case "http", "https":
+ fmt.Fprintf(&sb, " — %s", trackURL)
+ }
+ }
+ song = sb.String()
+ if playing {
+ return song
+ }
+ }
+ return song
+}
diff --git a/doc/senpai.1.scd b/doc/senpai.1.scd
index d58e55a..20befac 100644
--- a/doc/senpai.1.scd
+++ b/doc/senpai.1.scd
@@ -165,6 +165,10 @@ _name_ is matched case-insensitively. It can be one of the following:
Send a message prefixed with your nick (a user action). If sent from home,
reply to the last person who sent a private message.
+*NP*
+ Send the current song that is being played on the system. Uses DBus/MPRIS
+ internally.
+
*QUOTE* <raw message>
Send _raw message_ verbatim.
diff --git a/go.mod b/go.mod
index 2c304cd..8cfff1b 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
git.sr.ht/~emersion/go-scfg v0.0.0-20201019143924-142a8aa629fc
github.com/delthas/go-localeinfo v0.0.0-20221115102303-5a7785a1acc1
github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680
+ github.com/godbus/dbus/v5 v5.1.0
github.com/mattn/go-runewidth v0.0.14
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
diff --git a/go.sum b/go.sum
index afa9274..b421a96 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680 h1:bCjGvZsZNvhzJZ+gDmXpKdDPyf358nSPqepaTI1AsMQ=
github.com/gdamore/tcell/v2 v2.5.4-0.20221017224006-ede1dd5ee680/go.mod h1:XmCynGHvvGG7UFI6az9zzoEHBvZB1PGf5APwOJMFUyE=
+github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
+github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=