-
Notifications
You must be signed in to change notification settings - Fork 0
/
mpris.go
124 lines (102 loc) · 3.18 KB
/
mpris.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main
import (
"errors"
"fmt"
"log"
"reflect"
"regexp"
"strings"
"github.com/godbus/dbus/v5"
)
// https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html#Enum:Playback_Status
const (
PlaybackPlaying = "Playing"
PlaybackPaused = "Paused"
PlaybackStopped = "Stopped"
)
type NowPlaying struct {
Artists []string
Track string
Album string
Duration int64
PlaybackStatus string
Position int64
}
func NowPlayingEquals(left NowPlaying, right NowPlaying) bool {
return reflect.DeepEqual(left.Artists, right.Artists) &&
left.Track == right.Track &&
left.Album == right.Album
}
func GetNowPlaying(conn *dbus.Conn, blacklist []*regexp.Regexp) (map[string]NowPlaying, error) {
var dbusNames []string
err := conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus").
Call("org.freedesktop.DBus.ListNames", 0).Store(&dbusNames)
if err != nil {
return nil, err
}
var playerNames []string
for _, name := range dbusNames {
if strings.HasPrefix(name, "org.mpris.MediaPlayer2.") {
playerNames = append(playerNames, name)
}
}
info := map[string]NowPlaying{}
for _, player := range playerNames {
if isBlacklisted(blacklist, player) {
continue
}
playerObj := conn.Object(player, "/org/mpris/MediaPlayer2")
metadata, err1 := getProperty[map[string]dbus.Variant](playerObj, "org.mpris.MediaPlayer2.Player.Metadata")
playbackStatus, err2 := getProperty[string](playerObj, "org.mpris.MediaPlayer2.Player.PlaybackStatus")
position, err3 := getProperty[int64](playerObj, "org.mpris.MediaPlayer2.Player.Position")
if err := errors.Join(err1, err2, err3); err != nil {
log.Printf("error reading DBus properties for player %s", player)
continue
}
track, err1 := getMapEntry[string](*metadata, "xesam:title")
artists, err2 := getMapEntry[[]string](*metadata, "xesam:artist")
album, err3 := getMapEntry[string](*metadata, "xesam:album")
duration, err4 := getMapEntry[int64](*metadata, "mpris:length")
if err := errors.Join(err1, err2, err3, err4); err != nil {
log.Printf("error parsing metadata for player %s", player)
continue
}
info[player] = NowPlaying{
Artists: *artists,
Track: *track,
Album: *album,
Duration: *duration / 1_000_000,
PlaybackStatus: *playbackStatus,
Position: *position / 1_000_000,
}
}
return info, nil
}
func getProperty[E any](obj dbus.BusObject, property string) (*E, error) {
value, err := obj.GetProperty(property)
if err != nil {
return nil, fmt.Errorf("failed to get property: %v", err)
}
if parsedValue, ok := value.Value().(E); ok {
return &parsedValue, nil
}
return nil, errors.New("failed to read property from DBus object")
}
func getMapEntry[E any](metadata map[string]dbus.Variant, key string) (*E, error) {
value, ok := metadata[key]
if !ok {
return nil, fmt.Errorf("map entry with key %s not found", key)
}
if parsedValue, ok := value.Value().(E); ok {
return &parsedValue, nil
}
return nil, fmt.Errorf("invalid data type for map entry %s", key)
}
func isBlacklisted(blacklist []*regexp.Regexp, player string) bool {
for _, re := range blacklist {
if re.MatchString(player) {
return true
}
}
return false
}