initial implementation of Windows SMTC integration

This commit is contained in:
Drew Weymouth
2025-01-15 17:42:44 -08:00
parent 6d4d87cb66
commit cb01703776
5 changed files with 282 additions and 15 deletions
+52
View File
@@ -12,6 +12,7 @@ import (
"reflect"
"runtime"
"slices"
"strings"
"time"
"github.com/dweymouth/supersonic/backend/ipc"
@@ -45,6 +46,7 @@ type App struct {
LocalPlayer *mpv.Player
UpdateChecker UpdateChecker
MPRISHandler *MPRISHandler
WinSMTC *SMTC
ipcServer ipc.IPCServer
// UI callbacks to be set in main
@@ -165,11 +167,14 @@ func StartupApp(appName, displayAppName, appVersion, appVersionTag, latestReleas
}
// OS media center integrations
// Linux MPRIS
a.setupMPRIS(displayAppName)
// MacOS MPNowPlayingInfoCenter
InitMPMediaHandler(a.PlaybackManager, func(id string) (string, error) {
a.ImageManager.GetCoverThumbnail(id) // ensure image is cached locally
return a.ImageManager.GetCoverArtUrl(id)
})
// Windows SMTC is initialized from main once we have a window HWND.
a.startConfigWriter(a.bgrndCtx)
@@ -331,6 +336,50 @@ func (a *App) setupMPRIS(mprisAppName string) {
a.MPRISHandler.Start()
}
func (a *App) SetupWindowsSMTC(hwnd uintptr) {
smtc, err := InitSMTCForWindow(hwnd)
if err != nil {
log.Printf("error initializing SMTC: %d", err)
return
}
a.WinSMTC = smtc
smtc.OnButtonPressed(func(btn SMTCButton) {
switch btn {
case SMTCButtonPlay:
a.PlaybackManager.Continue()
case SMTCButtonPause:
a.PlaybackManager.Pause()
case SMTCButtonNext:
a.PlaybackManager.SeekNext()
case SMTCButtonPrevious:
a.PlaybackManager.SeekBackOrPrevious()
case SMTCButtonStop:
a.PlaybackManager.Stop()
}
})
smtc.OnSeek(func(millis int) {
a.PlaybackManager.SeekSeconds(float64(millis) / 1000)
})
a.PlaybackManager.OnSongChange(func(nowPlaying mediaprovider.MediaItem, _ *mediaprovider.Track) {
if nowPlaying == nil {
smtc.UpdateMetadata("", "")
return
}
artist := strings.Join(nowPlaying.Metadata().Artists, ", ")
smtc.UpdateMetadata(nowPlaying.Metadata().Name, artist)
smtc.UpdatePosition(0, nowPlaying.Metadata().Duration*1000)
})
a.PlaybackManager.OnSeek(func() {
dur := a.PlaybackManager.NowPlaying().Metadata().Duration
smtc.UpdatePosition(int(a.PlaybackManager.CurrentPlayer().GetStatus().TimePos*1000), dur*1000)
})
a.PlaybackManager.OnPlaying(func() { smtc.UpdatePlaybackState(SMTCPlaybackStatePlaying) })
a.PlaybackManager.OnPaused(func() { smtc.UpdatePlaybackState(SMTCPlaybackStatePaused) })
a.PlaybackManager.OnStopped(func() { smtc.UpdatePlaybackState(SMTCPlaybackStateStopped) })
}
func (a *App) LoginToDefaultServer(string) error {
serverCfg := a.ServerManager.GetDefaultServer()
if serverCfg == nil {
@@ -370,6 +419,9 @@ func (a *App) Shutdown() {
a.ipcServer.Shutdown(a.bgrndCtx)
}
a.MPRISHandler.Shutdown()
if a.WinSMTC != nil {
a.WinSMTC.Shutdown()
}
a.PlaybackManager.DisableCallbacks()
a.PlaybackManager.Stop() // will trigger scrobble check
a.cancel()