forked from Elara6331/itd
		
	Merge pull request 'Move mpris implementation from infinitime library to itd, where it really belongs' (#41) from FloralExMachina/itd:master into master
Reviewed-on: https://gitea.arsenm.dev/Arsen6331/itd/pulls/41
This commit is contained in:
		
							
								
								
									
										6
									
								
								calls.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								calls.go
									
									
									
									
									
								
							| @@ -7,11 +7,13 @@ import ( | |||||||
| 	"github.com/godbus/dbus/v5" | 	"github.com/godbus/dbus/v5" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"go.arsenm.dev/infinitime" | 	"go.arsenm.dev/infinitime" | ||||||
|  |         "go.arsenm.dev/itd/internal/utils" | ||||||
|  |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func initCallNotifs(ctx context.Context, dev *infinitime.Device) error { | func initCallNotifs(ctx context.Context, dev *infinitime.Device) error { | ||||||
| 	// Connect to system bus. This connection is for method calls. | 	// Connect to system bus. This connection is for method calls. | ||||||
| 	conn, err := newSystemBusConn(ctx) | 	conn, err := utils.NewSystemBusConn(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -29,7 +31,7 @@ func initCallNotifs(ctx context.Context, dev *infinitime.Device) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Connect to system bus. This connection is for monitoring. | 	// Connect to system bus. This connection is for monitoring. | ||||||
| 	monitorConn, err := newSystemBusConn(ctx) | 	monitorConn, err := utils.NewSystemBusConn(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package main | package utils | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -6,7 +6,7 @@ import ( | |||||||
| 	"github.com/godbus/dbus/v5" | 	"github.com/godbus/dbus/v5" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func newSystemBusConn(ctx context.Context) (*dbus.Conn, error) { | func NewSystemBusConn(ctx context.Context) (*dbus.Conn, error) { | ||||||
| 	// Connect to dbus session bus | 	// Connect to dbus session bus | ||||||
| 	conn, err := dbus.SystemBusPrivate(dbus.WithContext(ctx)) | 	conn, err := dbus.SystemBusPrivate(dbus.WithContext(ctx)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -23,7 +23,7 @@ func newSystemBusConn(ctx context.Context) (*dbus.Conn, error) { | |||||||
| 	return conn, nil | 	return conn, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newSessionBusConn(ctx context.Context) (*dbus.Conn, error) { | func NewSessionBusConn(ctx context.Context) (*dbus.Conn, error) { | ||||||
| 	// Connect to dbus session bus | 	// Connect to dbus session bus | ||||||
| 	conn, err := dbus.SessionBusPrivate(dbus.WithContext(ctx)) | 	conn, err := dbus.SessionBusPrivate(dbus.WithContext(ctx)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								main.go
									
									
									
									
									
								
							| @@ -146,7 +146,7 @@ func main() { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Initialize music controls | 	// Initialize music controls | ||||||
| 	err = initMusicCtrl(dev) | 	err = initMusicCtrl(ctx, dev) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error().Err(err).Msg("Error initializing music control") | 		log.Error().Err(err).Msg("Error initializing music control") | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								maps.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								maps.go
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ import ( | |||||||
| 	"github.com/godbus/dbus/v5" | 	"github.com/godbus/dbus/v5" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"go.arsenm.dev/infinitime" | 	"go.arsenm.dev/infinitime" | ||||||
|  | 	"go.arsenm.dev/itd/internal/utils" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -19,7 +20,7 @@ const ( | |||||||
|  |  | ||||||
| func initPureMaps(ctx context.Context, dev *infinitime.Device) error { | func initPureMaps(ctx context.Context, dev *infinitime.Device) error { | ||||||
| 	// Connect to session bus. This connection is for method calls. | 	// Connect to session bus. This connection is for method calls. | ||||||
| 	conn, err := newSessionBusConn(ctx) | 	conn, err := utils.NewSessionBusConn(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -30,7 +31,7 @@ func initPureMaps(ctx context.Context, dev *infinitime.Device) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Connect to session bus. This connection is for method calls. | 	// Connect to session bus. This connection is for method calls. | ||||||
| 	monitorConn, err := newSessionBusConn(ctx) | 	monitorConn, err := utils.NewSessionBusConn(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								music.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								music.go
									
									
									
									
									
								
							| @@ -19,29 +19,31 @@ | |||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  |         "context" | ||||||
|  |  | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"go.arsenm.dev/infinitime" | 	"go.arsenm.dev/infinitime" | ||||||
| 	"go.arsenm.dev/infinitime/pkg/player" |  | ||||||
| 	"go.arsenm.dev/itd/translit" | 	"go.arsenm.dev/itd/translit" | ||||||
|  |         "go.arsenm.dev/itd/pkg/mpris" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func initMusicCtrl(dev *infinitime.Device) error { | func initMusicCtrl(ctx context.Context, dev *infinitime.Device) error { | ||||||
| 	player.Init() |         mpris.Init(ctx) | ||||||
|  |  | ||||||
| 	maps := k.Strings("notifs.translit.use") | 	maps := k.Strings("notifs.translit.use") | ||||||
| 	translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom")) | 	translit.Transliterators["custom"] = translit.Map(k.Strings("notifs.translit.custom")) | ||||||
|  |  | ||||||
| 	player.OnChange(func(ct player.ChangeType, val string) { |         mpris.OnChange(func(ct mpris.ChangeType, val string) { | ||||||
| 		newVal := translit.Transliterate(val, maps...) | 		newVal := translit.Transliterate(val, maps...) | ||||||
| 		if !firmwareUpdating { | 		if !firmwareUpdating { | ||||||
| 			switch ct { | 			switch ct { | ||||||
| 			case player.ChangeTypeStatus: | 			case mpris.ChangeTypeStatus: | ||||||
| 			        dev.Music.SetStatus(val == "Playing") | 			        dev.Music.SetStatus(val == "Playing") | ||||||
| 			case player.ChangeTypeTitle: | 			case mpris.ChangeTypeTitle: | ||||||
| 			        dev.Music.SetTrack(newVal) | 			        dev.Music.SetTrack(newVal) | ||||||
| 			case player.ChangeTypeAlbum: | 			case mpris.ChangeTypeAlbum: | ||||||
| 			        dev.Music.SetAlbum(newVal) | 			        dev.Music.SetAlbum(newVal) | ||||||
| 			case player.ChangeTypeArtist: | 			case mpris.ChangeTypeArtist: | ||||||
| 				dev.Music.SetArtist(newVal) | 				dev.Music.SetArtist(newVal) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -58,17 +60,17 @@ func initMusicCtrl(dev *infinitime.Device) error { | |||||||
| 			// Perform appropriate action based on event | 			// Perform appropriate action based on event | ||||||
| 			switch musicEvt { | 			switch musicEvt { | ||||||
| 			case infinitime.MusicEventPlay: | 			case infinitime.MusicEventPlay: | ||||||
| 				player.Play() | 				mpris.Play() | ||||||
| 			case infinitime.MusicEventPause: | 			case infinitime.MusicEventPause: | ||||||
| 				player.Pause() | 				mpris.Pause() | ||||||
| 			case infinitime.MusicEventNext: | 			case infinitime.MusicEventNext: | ||||||
| 				player.Next() | 				mpris.Next() | ||||||
| 			case infinitime.MusicEventPrev: | 			case infinitime.MusicEventPrev: | ||||||
| 				player.Prev() | 				mpris.Prev() | ||||||
| 			case infinitime.MusicEventVolUp: | 			case infinitime.MusicEventVolUp: | ||||||
| 				player.VolUp(uint(k.Int("music.vol.interval"))) | 				mpris.VolUp(uint(k.Int("music.vol.interval"))) | ||||||
| 			case infinitime.MusicEventVolDown: | 			case infinitime.MusicEventVolDown: | ||||||
| 				player.VolDown(uint(k.Int("music.vol.interval"))) | 				mpris.VolDown(uint(k.Int("music.vol.interval"))) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
|   | |||||||
| @@ -26,11 +26,12 @@ import ( | |||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"go.arsenm.dev/infinitime" | 	"go.arsenm.dev/infinitime" | ||||||
| 	"go.arsenm.dev/itd/translit" | 	"go.arsenm.dev/itd/translit" | ||||||
|  | 	"go.arsenm.dev/itd/internal/utils" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func initNotifRelay(ctx context.Context, dev *infinitime.Device) error { | func initNotifRelay(ctx context.Context, dev *infinitime.Device) error { | ||||||
| 	// Connect to dbus session bus | 	// Connect to dbus session bus | ||||||
| 	bus, err := newSessionBusConn(ctx) | 	bus, err := utils.NewSessionBusConn(ctx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										272
									
								
								pkg/mpris/mpris.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								pkg/mpris/mpris.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | |||||||
|  | package mpris | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/godbus/dbus/v5" | ||||||
|  | 	"go.arsenm.dev/itd/internal/utils" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	method, monitor *dbus.Conn | ||||||
|  | 	monitorCh       chan *dbus.Message | ||||||
|  | 	onChangeOnce    sync.Once | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Init makes required connections to DBus and | ||||||
|  | // initializes change monitoring channel | ||||||
|  | func Init(ctx context.Context) error { | ||||||
|  | 	// Connect to session bus for monitoring | ||||||
|  | 	monitorConn, err := utils.NewSessionBusConn(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// Add match rule for PropertiesChanged on media player | ||||||
|  | 	monitorConn.AddMatchSignal( | ||||||
|  | 		dbus.WithMatchObjectPath("/org/mpris/MediaPlayer2"), | ||||||
|  | 		dbus.WithMatchInterface("org.freedesktop.DBus.Properties"), | ||||||
|  | 		dbus.WithMatchMember("PropertiesChanged"), | ||||||
|  | 	) | ||||||
|  | 	monitorCh = make(chan *dbus.Message, 10) | ||||||
|  | 	monitorConn.Eavesdrop(monitorCh) | ||||||
|  |  | ||||||
|  | 	// Connect to session bus for method calls | ||||||
|  | 	methodConn, err := utils.NewSessionBusConn(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	method, monitor = methodConn, monitorConn | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Exit closes all connections and channels | ||||||
|  | func Exit() { | ||||||
|  | 	close(monitorCh) | ||||||
|  | 	method.Close() | ||||||
|  | 	monitor.Close() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Play uses MPRIS to play media | ||||||
|  | func Play() error { | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  | 		call := player.Call("org.mpris.MediaPlayer2.Player.Play", 0) | ||||||
|  | 		if call.Err != nil { | ||||||
|  | 			return call.Err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Pause uses MPRIS to pause media | ||||||
|  | func Pause() error { | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  | 		call := player.Call("org.mpris.MediaPlayer2.Player.Pause", 0) | ||||||
|  | 		if call.Err != nil { | ||||||
|  | 			return call.Err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Next uses MPRIS to skip to next media | ||||||
|  | func Next() error { | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  | 		call := player.Call("org.mpris.MediaPlayer2.Player.Next", 0) | ||||||
|  | 		if call.Err != nil { | ||||||
|  | 			return call.Err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Prev uses MPRIS to skip to previous media | ||||||
|  | func Prev() error { | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  | 		call := player.Call("org.mpris.MediaPlayer2.Player.Previous", 0) | ||||||
|  | 		if call.Err != nil { | ||||||
|  | 			return call.Err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func VolUp(percent uint) error { | ||||||
|  |  | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  |                 currentVal, err := player.GetProperty("org.mpris.MediaPlayer2.Player.Volume") | ||||||
|  |                 if err != nil { | ||||||
|  |                     return err | ||||||
|  |                 } | ||||||
|  |                 newVal := currentVal.Value().(float64) + (float64(percent) / 100) | ||||||
|  |                 err = player.SetProperty("org.mpris.MediaPlayer2.Player.Volume", newVal) | ||||||
|  |                 if err != nil { | ||||||
|  |                     return err | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func VolDown(percent uint) error { | ||||||
|  |  | ||||||
|  | 	player, err := getPlayerObj() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if player != nil { | ||||||
|  |                 currentVal, err := player.GetProperty("org.mpris.MediaPlayer2.Player.Volume") | ||||||
|  |                 if err != nil { | ||||||
|  |                     return err | ||||||
|  |                 } | ||||||
|  |                 newVal := currentVal.Value().(float64) - (float64(percent) / 100) | ||||||
|  |                 err = player.SetProperty("org.mpris.MediaPlayer2.Player.Volume", newVal) | ||||||
|  |                 if err != nil { | ||||||
|  |                     return err | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ChangeType int | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	ChangeTypeTitle ChangeType = iota | ||||||
|  | 	ChangeTypeArtist | ||||||
|  | 	ChangeTypeAlbum | ||||||
|  | 	ChangeTypeStatus | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func (ct ChangeType) String() string { | ||||||
|  | 	switch ct { | ||||||
|  | 	case ChangeTypeTitle: | ||||||
|  | 		return "Title" | ||||||
|  | 	case ChangeTypeAlbum: | ||||||
|  | 		return "Album" | ||||||
|  | 	case ChangeTypeArtist: | ||||||
|  | 		return "Artist" | ||||||
|  | 	case ChangeTypeStatus: | ||||||
|  | 		return "Status" | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OnChange runs cb when a value changes | ||||||
|  | func OnChange(cb func(ChangeType, string)) { | ||||||
|  | 	go onChangeOnce.Do(func() { | ||||||
|  | 		// For every message on channel | ||||||
|  | 		for msg := range monitorCh { | ||||||
|  | 			// Parse PropertiesChanged | ||||||
|  | 			iface, changed, ok := parsePropertiesChanged(msg) | ||||||
|  | 			if !ok || iface != "org.mpris.MediaPlayer2.Player" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// For every property changed | ||||||
|  | 			for name, val := range changed { | ||||||
|  | 				// If metadata changed | ||||||
|  | 				if name == "Metadata" { | ||||||
|  | 					// Get fields | ||||||
|  | 					fields := val.Value().(map[string]dbus.Variant) | ||||||
|  | 					// For every field | ||||||
|  | 					for name, val := range fields { | ||||||
|  | 						// Handle each field appropriately | ||||||
|  | 						if strings.HasSuffix(name, "title") { | ||||||
|  | 							title := val.Value().(string) | ||||||
|  | 							if title == "" { | ||||||
|  | 								title = "Unknown " + ChangeTypeTitle.String() | ||||||
|  | 							} | ||||||
|  | 							cb(ChangeTypeTitle, title) | ||||||
|  | 						} else if strings.HasSuffix(name, "album") { | ||||||
|  | 							album := val.Value().(string) | ||||||
|  | 							if album == "" { | ||||||
|  | 								album = "Unknown " + ChangeTypeAlbum.String() | ||||||
|  | 							} | ||||||
|  | 							cb(ChangeTypeAlbum, album) | ||||||
|  | 						} else if strings.HasSuffix(name, "artist") { | ||||||
|  | 							var artists string | ||||||
|  | 							switch artistVal := val.Value().(type) { | ||||||
|  | 							case string: | ||||||
|  | 								artists = artistVal | ||||||
|  | 							case []string: | ||||||
|  | 								artists = strings.Join(artistVal, ", ") | ||||||
|  | 							} | ||||||
|  | 							if artists == "" { | ||||||
|  | 								artists = "Unknown " + ChangeTypeArtist.String() | ||||||
|  | 							} | ||||||
|  | 							cb(ChangeTypeArtist, artists) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} else if name == "PlaybackStatus" { | ||||||
|  | 					// Handle status change | ||||||
|  | 					cb(ChangeTypeStatus, val.Value().(string)) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getPlayerNames gets all DBus MPRIS player bus names | ||||||
|  | func getPlayerNames(conn *dbus.Conn) ([]string, error) { | ||||||
|  | 	var names []string | ||||||
|  | 	err := conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&names) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	var players []string | ||||||
|  | 	for _, name := range names { | ||||||
|  | 		if strings.HasPrefix(name, "org.mpris.MediaPlayer2") { | ||||||
|  | 			players = append(players, name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return players, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetPlayerObj gets the object corresponding to the first | ||||||
|  | // bus name found in DBus | ||||||
|  | func getPlayerObj() (dbus.BusObject, error) { | ||||||
|  | 	players, err := getPlayerNames(method) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(players) == 0 { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	return method.Object(players[0], "/org/mpris/MediaPlayer2"), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // parsePropertiesChanged parses a DBus PropertiesChanged signal | ||||||
|  | func parsePropertiesChanged(msg *dbus.Message) (iface string, changed map[string]dbus.Variant, ok bool) { | ||||||
|  | 	if len(msg.Body) != 3 { | ||||||
|  | 		return "", nil, false | ||||||
|  | 	} | ||||||
|  | 	iface, ok = msg.Body[0].(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	changed, ok = msg.Body[1].(map[string]dbus.Variant) | ||||||
|  | 	if !ok { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user