mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-06 18:32:50 +08:00
176 lines
6.7 KiB
Go
176 lines
6.7 KiB
Go
//go:build windows
|
|
|
|
package sysmediacontrol
|
|
|
|
import (
|
|
"AynaLivePlayer/core/events"
|
|
"AynaLivePlayer/global"
|
|
"AynaLivePlayer/pkg/config"
|
|
"AynaLivePlayer/pkg/eventbus"
|
|
"AynaLivePlayer/pkg/logger"
|
|
"github.com/go-ole/go-ole"
|
|
"github.com/saltosystems/winrt-go"
|
|
"github.com/saltosystems/winrt-go/windows/foundation"
|
|
"github.com/saltosystems/winrt-go/windows/media"
|
|
"github.com/saltosystems/winrt-go/windows/media/playback"
|
|
"github.com/saltosystems/winrt-go/windows/storage/streams"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
TicksPerMicrosecond int64 = 10
|
|
TicksPerMillisecond = TicksPerMicrosecond * 1000
|
|
TicksPerSecond = TicksPerMillisecond * 1000
|
|
)
|
|
|
|
var (
|
|
shell32, _ = syscall.LoadLibrary("shell32.dll")
|
|
SetCurrentProcessExplicitAppUserModelID, _ = syscall.GetProcAddress(shell32, "SetCurrentProcessExplicitAppUserModelID")
|
|
)
|
|
|
|
var (
|
|
smtc *media.SystemMediaTransportControls
|
|
_player *playback.MediaPlayer // Note: Do not use it!!! useless player, just for get smtc
|
|
buttonPressedEventGUID = winrt.ParameterizedInstanceGUID(
|
|
foundation.GUIDTypedEventHandler,
|
|
media.SignatureSystemMediaTransportControls,
|
|
media.SignatureSystemMediaTransportControlsButtonPressedEventArgs,
|
|
)
|
|
timelineProps *media.SystemMediaTransportControlsTimelineProperties
|
|
log logger.ILogger
|
|
)
|
|
|
|
func must[T any](t T, err error) T {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func withDisplayUpdater(f func(updater *media.SystemMediaTransportControlsDisplayUpdater)) {
|
|
updater := must(smtc.GetDisplayUpdater())
|
|
f(updater)
|
|
updater.Release()
|
|
}
|
|
|
|
func withMusicProperties(f func(updater *media.SystemMediaTransportControlsDisplayUpdater, properties *media.MusicDisplayProperties)) {
|
|
updater := must(smtc.GetDisplayUpdater())
|
|
properties := must(updater.GetMusicProperties())
|
|
f(updater, properties)
|
|
properties.Release()
|
|
updater.Release()
|
|
}
|
|
|
|
func InitSystemMediaControl() {
|
|
_ = ole.RoInitialize(1)
|
|
log = global.Logger.WithPrefix("SMTC")
|
|
|
|
sptr, _ := syscall.UTF16PtrFromString("Aynakeya." + config.ProgramName)
|
|
syscall.SyscallN(SetCurrentProcessExplicitAppUserModelID, uintptr(unsafe.Pointer(sptr)))
|
|
_player = must(playback.NewMediaPlayer())
|
|
smtc = must(_player.GetSystemMediaTransportControls())
|
|
cmdManager := must(_player.GetCommandManager())
|
|
_ = cmdManager.SetIsEnabled(false)
|
|
cmdManager.Release()
|
|
_ = smtc.SetIsEnabled(true)
|
|
_ = smtc.SetIsPauseEnabled(true)
|
|
_ = smtc.SetIsPlayEnabled(true)
|
|
_ = smtc.SetIsNextEnabled(true)
|
|
_ = smtc.SetIsPreviousEnabled(true)
|
|
_ = smtc.SetPlaybackStatus(media.MediaPlaybackStatusPlaying)
|
|
|
|
withDisplayUpdater(func(updater *media.SystemMediaTransportControlsDisplayUpdater) {
|
|
_ = updater.SetType(media.MediaPlaybackTypeMusic)
|
|
})
|
|
|
|
global.EventBus.Subscribe("", events.PlayerPlayingUpdate, "sysmediacontrol.update_playing", func(event *eventbus.Event) {
|
|
data := event.Data.(events.PlayerPlayingUpdateEvent)
|
|
withMusicProperties(func(updater *media.SystemMediaTransportControlsDisplayUpdater, properties *media.MusicDisplayProperties) {
|
|
properties.SetArtist(data.Media.Info.Artist)
|
|
properties.SetTitle(data.Media.Info.Title)
|
|
properties.SetAlbumTitle(data.Media.Info.Album)
|
|
if data.Media.Info.Cover.Url != "" {
|
|
imgUri, _ := foundation.UriCreateUri(data.Media.Info.Cover.Url)
|
|
defer imgUri.Release()
|
|
stream, _ := streams.RandomAccessStreamReferenceCreateFromUri(imgUri)
|
|
defer stream.Release()
|
|
_ = updater.SetThumbnail(stream)
|
|
} else {
|
|
// todo: using cover data
|
|
}
|
|
_ = updater.Update()
|
|
})
|
|
if data.Removed {
|
|
smtc.SetPlaybackStatus(media.MediaPlaybackStatusChanging)
|
|
}
|
|
})
|
|
|
|
global.EventBus.Subscribe("", events.PlayerPropertyPauseUpdate, "sysmediacontrol.update_paused", func(event *eventbus.Event) {
|
|
if event.Data.(events.PlayerPropertyPauseUpdateEvent).Paused {
|
|
smtc.SetPlaybackStatus(media.MediaPlaybackStatusPaused)
|
|
} else {
|
|
smtc.SetPlaybackStatus(media.MediaPlaybackStatusPlaying)
|
|
}
|
|
})
|
|
|
|
pressedHandler := foundation.NewTypedEventHandler(
|
|
ole.NewGUID(buttonPressedEventGUID),
|
|
func(_ *foundation.TypedEventHandler, _ unsafe.Pointer, args unsafe.Pointer) {
|
|
eventArgs := (*media.SystemMediaTransportControlsButtonPressedEventArgs)(args)
|
|
defer eventArgs.Release()
|
|
switch val, _ := eventArgs.GetButton(); val {
|
|
case media.SystemMediaTransportControlsButtonPlay:
|
|
_ = global.EventBus.Publish(
|
|
events.PlayerSetPauseCmd, events.PlayerSetPauseCmdEvent{Pause: false})
|
|
case media.SystemMediaTransportControlsButtonPause:
|
|
_ = global.EventBus.Publish(
|
|
events.PlayerSetPauseCmd, events.PlayerSetPauseCmdEvent{Pause: true})
|
|
case media.SystemMediaTransportControlsButtonNext:
|
|
_ = global.EventBus.Publish(
|
|
events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{})
|
|
case media.SystemMediaTransportControlsButtonPrevious:
|
|
_ = global.EventBus.Publish(events.PlayerSeekCmd, events.PlayerSeekCmdEvent{
|
|
Position: 0,
|
|
Absolute: true,
|
|
})
|
|
}
|
|
},
|
|
)
|
|
_, _ = smtc.AddButtonPressed(pressedHandler)
|
|
pressedHandler.Release()
|
|
|
|
// todo: finish timeline properties
|
|
// cuz win 11 are not display timeline properties now
|
|
// i just ignore it
|
|
lastDuration := int64(0)
|
|
lastTimePos := int64(0)
|
|
timelineProps, _ = media.NewSystemMediaTransportControlsTimelineProperties()
|
|
global.EventBus.Subscribe("", events.PlayerPropertyDurationUpdate, "sysmediacontrol.properties.duration", func(event *eventbus.Event) {
|
|
data := event.Data.(events.PlayerPropertyDurationUpdateEvent)
|
|
lastDuration = int64(data.Duration * 1000)
|
|
_ = timelineProps.SetStartTime(foundation.TimeSpan{Duration: 0})
|
|
_ = timelineProps.SetMinSeekTime(foundation.TimeSpan{Duration: 0})
|
|
_ = timelineProps.SetEndTime(foundation.TimeSpan{Duration: lastDuration * TicksPerMillisecond})
|
|
_ = timelineProps.SetMaxSeekTime(foundation.TimeSpan{Duration: lastDuration * TicksPerMillisecond})
|
|
_ = timelineProps.SetPosition(foundation.TimeSpan{Duration: lastTimePos * TicksPerMillisecond})
|
|
_ = smtc.UpdateTimelineProperties(timelineProps)
|
|
})
|
|
global.EventBus.Subscribe("", events.PlayerPropertyTimePosUpdate, "sysmediacontrol.properties.time_pos", func(event *eventbus.Event) {
|
|
data := event.Data.(events.PlayerPropertyTimePosUpdateEvent)
|
|
lastTimePos = int64(data.TimePos * 1000)
|
|
_ = timelineProps.SetStartTime(foundation.TimeSpan{Duration: 0})
|
|
_ = timelineProps.SetMinSeekTime(foundation.TimeSpan{Duration: 0})
|
|
_ = timelineProps.SetEndTime(foundation.TimeSpan{Duration: lastDuration * TicksPerMillisecond})
|
|
_ = timelineProps.SetMaxSeekTime(foundation.TimeSpan{Duration: lastDuration * TicksPerMillisecond})
|
|
_ = timelineProps.SetPosition(foundation.TimeSpan{Duration: lastTimePos * TicksPerMillisecond})
|
|
_ = smtc.UpdateTimelineProperties(timelineProps)
|
|
})
|
|
}
|
|
|
|
func Destroy() {
|
|
timelineProps.Release()
|
|
smtc.Release()
|
|
_player.Release()
|
|
}
|