Merge pull request #22 from AynaLivePlayer/dev

1.1.0 pre-release.
This commit is contained in:
Aynakeya
2024-10-24 00:55:44 -07:00
committed by GitHub
12 changed files with 175 additions and 51 deletions

2
go.mod
View File

@@ -36,7 +36,7 @@ require (
require (
fyne.io/systray v1.11.0 // indirect
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240427041017-949a66917a81 // indirect
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240922031304-c497ed5617c4 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/PuerkitoBio/goquery v1.7.1 // indirect
github.com/XiaoMengXinX/Music163Api-Go v0.1.30 // indirect

18
go.sum
View File

@@ -37,14 +37,12 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne/v2 v2.5.0 h1:lEjEIso0Vi4sJXYngIMoXOM6aUjqnPjK7pBpxRxG9aI=
fyne.io/fyne/v2 v2.5.0/go.mod h1:9D4oT3NWeG+MLi/lP7ItZZyujHC/qqMJpoGTAYX5Uqc=
fyne.io/fyne/v2 v2.5.1 h1:jd2mhQz0ViosZjhgR5l2bdCbc5HFqkYnTzEXX8UOC7I=
fyne.io/fyne/v2 v2.5.1/go.mod h1:NdxEG8L7EVWo06/cYbXW11uA0X7UG8Q8j5CLebvTZi8=
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
fyne.io/x/fyne v0.0.0-20240326131024-3ba9170cc3be h1:HBAwKsmfTO4r1Ndlksy5oXIxgWiazj0gu2qmhOmkRFU=
fyne.io/x/fyne v0.0.0-20240326131024-3ba9170cc3be/go.mod h1:1pa3ZVIopRWNvfSG4ZrSkcZ3mJ8qoHPZv4PT8/zpn1o=
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240427041017-949a66917a81 h1:AAPbv78hgdPQ96mSfPoUARxT1ZNJ0XzezZxaVmeRCVc=
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240427041017-949a66917a81/go.mod h1:wg2vP2yIJNPJKnmFWLi8WwrRL5w3/xl8gHdzYPdesMg=
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240922031304-c497ed5617c4 h1:ERr/3E+lEvJyN+pu9UIm+kMrh4mYVXeBMCuaghSQKZc=
github.com/AynaLivePlayer/blivedm-go v0.0.0-20240922031304-c497ed5617c4/go.mod h1:wg2vP2yIJNPJKnmFWLi8WwrRL5w3/xl8gHdzYPdesMg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -126,8 +124,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-text/render v0.1.0 h1:osrmVDZNHuP1RSu3pNG7Z77Sd2xSbcb/xWytAj9kyVs=
github.com/go-text/render v0.1.0/go.mod h1:jqEuNMenrmj6QRnkdpeaP0oKGFLDNhDkVKwGjsWWYU4=
github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b h1:daoFn+Aw8EIQZO9kYWwHL01FqwwpCl2nTeVEYbsgRHk=
github.com/go-text/render v0.1.1-0.20240418202334-dd62631dae9b/go.mod h1:jqEuNMenrmj6QRnkdpeaP0oKGFLDNhDkVKwGjsWWYU4=
github.com/go-text/typesetting v0.1.0 h1:vioSaLPYcHwPEPLT7gsjCGDCoYSbljxoHJzMnKwVvHw=
github.com/go-text/typesetting v0.1.0/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI=
github.com/go-text/typesetting-utils v0.0.0-20240329101916-eee87fb235a3 h1:levTnuLLUmpavLGbJYLJA7fQnKeS7P1eCdAlM+vReXk=
@@ -292,8 +290,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/rymdport/portal v0.2.2 h1:P2Q/4k673zxdFAsbD8EESZ7psfuO6/4jNu6EDrDICkM=
github.com/rymdport/portal v0.2.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg=
github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=

View File

@@ -7,7 +7,9 @@ import (
"AynaLivePlayer/internal/playlist"
"AynaLivePlayer/internal/plugins"
"AynaLivePlayer/internal/source"
"AynaLivePlayer/internal/sysmediacontrol"
"AynaLivePlayer/internal/updater"
"AynaLivePlayer/pkg/config"
"AynaLivePlayer/plugin/diange"
"AynaLivePlayer/plugin/durationmgmt"
"AynaLivePlayer/plugin/qiege"
@@ -30,11 +32,15 @@ func Initialize() {
wshub.NewWsHub(),
)
updater.Initialize()
//sysmediacontrol.InitSystemMediaControl()
if config.General.EnableSMC {
sysmediacontrol.InitSystemMediaControl()
}
}
func Stop() {
//sysmediacontrol.Destroy()
if config.General.EnableSMC {
sysmediacontrol.Destroy()
}
liveroom.StopAndSave()
playlist.Close()
plugins.ClosePlugins()

View File

@@ -7,14 +7,34 @@ import (
"AynaLivePlayer/pkg/event"
"math/rand"
"sync"
"time"
)
var rng *rand.Rand
func init() {
rng = rand.New(rand.NewSource(time.Now().Unix()))
}
type playlist struct {
Index int
playlistId model.PlaylistID
mode model.PlaylistMode
Medias []model.Media
Lock sync.RWMutex
Index int
playlistId model.PlaylistID
mode model.PlaylistMode
Medias []model.Media
randomIndex []int
Lock sync.RWMutex
}
// resetRandomIndex reset the random index
// this function is not locked, should be called in a locked context
func (p *playlist) resetRandomIndex() {
p.randomIndex = make([]int, p.Size())
for i := 0; i < p.Size(); i++ {
p.randomIndex[i] = i
}
rand.Shuffle(p.Size(), func(i, j int) {
p.randomIndex[i], p.randomIndex[j] = p.randomIndex[j], p.randomIndex[i]
})
}
func newPlaylist(id model.PlaylistID) *playlist {
@@ -41,10 +61,11 @@ func newPlaylist(id model.PlaylistID) *playlist {
pl.Next(event.Data.(events.PlaylistNextCmdEvent).Remove)
})
global.EventManager.RegisterA(events.PlaylistModeChangeCmd(id), "internal.playlist.mode", func(event *event.Event) {
if pl.mode == model.PlaylistModeRandom {
pl.Index = 0
}
pl.Lock.Lock()
pl.mode = event.Data.(events.PlaylistModeChangeCmdEvent).Mode
pl.Index = 0
pl.resetRandomIndex()
pl.Lock.Unlock()
log.Infof("Playlist %s mode changed to %d", id, pl.mode)
global.EventManager.CallA(events.PlaylistModeChangeUpdate(id), events.PlaylistModeChangeUpdateEvent{
Mode: pl.mode,
@@ -57,6 +78,7 @@ func newPlaylist(id model.PlaylistID) *playlist {
}
pl.Index = index
})
pl.resetRandomIndex()
return pl
}
@@ -74,6 +96,7 @@ func (p *playlist) Replace(medias []model.Media) {
p.Lock.Lock()
p.Medias = medias
p.Index = 0
p.resetRandomIndex()
p.Lock.Unlock()
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
Medias: p.CopyMedia(),
@@ -93,6 +116,7 @@ func (p *playlist) Insert(index int, media model.Media) {
p.Medias[i] = p.Medias[i-1]
}
p.Medias[index] = media
p.resetRandomIndex()
p.Lock.Unlock()
global.EventManager.CallA(events.PlaylistInsertUpdate(p.playlistId), events.PlaylistInsertUpdateEvent{
Position: index,
@@ -111,6 +135,10 @@ func (p *playlist) Delete(index int) {
}
// todo: @5 delete optimization
p.Medias = append(p.Medias[:index], p.Medias[index+1:]...)
if p.Index >= p.Size() {
p.Index = 0
}
p.resetRandomIndex()
p.Lock.Unlock()
global.EventManager.CallA(events.PlaylistDetailUpdate(p.playlistId), events.PlaylistDetailUpdateEvent{
Medias: p.CopyMedia(),
@@ -157,38 +185,46 @@ func (p *playlist) Next(delete bool) {
}
var index int
index = p.Index
// add guard
// add guard, i don't know why this is needed
// but it seems to fix a bug
if index >= p.Size() {
index = 0
}
if (index == 0) && p.mode == model.PlaylistModeRandom {
p.resetRandomIndex()
}
var m model.Media
if p.mode == model.PlaylistModeRandom {
p.Index = rand.Intn(p.Size())
} else if p.mode == model.PlaylistModeNormal {
p.Index = (p.Index + 1) % p.Size()
m = p.Medias[p.randomIndex[index]]
} else {
p.Index = index
}
m := p.Medias[index]
// fix race condition
currentSize := p.Size() - 1
if delete {
if p.mode == model.PlaylistModeRandom {
if currentSize == 0 {
p.Index = 0
} else {
p.Index = rand.Intn(currentSize)
}
} else if p.mode == model.PlaylistModeNormal {
p.Index = index
} else {
p.Index = index
}
m = p.Medias[index]
}
//// fix race condition
//currentSize := p.Size() - 1
//if delete {
// if p.mode == model.PlaylistModeRandom {
// if currentSize == 0 {
// p.Index = 0
// } else {
// p.Index = rand.Intn(currentSize)
// }
// } else if p.mode == model.PlaylistModeNormal {
// p.Index = index
// } else {
// p.Index = index
// }
//}
p.Lock.Unlock()
global.EventManager.CallA(events.PlaylistNextUpdate(p.playlistId), events.PlaylistNextUpdateEvent{
Media: m,
})
if delete {
p.Delete(index)
if p.mode == model.PlaylistModeRandom {
p.Delete(p.randomIndex[index])
} else {
p.Delete(index)
}
} else {
p.Index = (p.Index + 1) % p.Size()
}
}

View File

@@ -7,11 +7,13 @@ import (
"AynaLivePlayer/global"
"AynaLivePlayer/pkg/config"
"AynaLivePlayer/pkg/event"
"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"
)
@@ -35,6 +37,8 @@ var (
media.SignatureSystemMediaTransportControls,
media.SignatureSystemMediaTransportControlsButtonPressedEventArgs,
)
timelineProps *media.SystemMediaTransportControlsTimelineProperties
log logger.ILogger
)
func must[T any](t T, err error) T {
@@ -60,6 +64,7 @@ func withMusicProperties(f func(updater *media.SystemMediaTransportControlsDispl
func InitSystemMediaControl() {
_ = ole.RoInitialize(1)
log = global.Logger.WithPrefix("SMTC")
sptr, _ := syscall.UTF16PtrFromString("Aynakeya." + config.ProgramName)
syscall.SyscallN(SetCurrentProcessExplicitAppUserModelID, uintptr(unsafe.Pointer(sptr)))
@@ -74,6 +79,7 @@ func InitSystemMediaControl() {
_ = smtc.SetIsNextEnabled(true)
_ = smtc.SetIsPreviousEnabled(true)
_ = smtc.SetPlaybackStatus(media.MediaPlaybackStatusPlaying)
withDisplayUpdater(func(updater *media.SystemMediaTransportControlsDisplayUpdater) {
_ = updater.SetType(media.MediaPlaybackTypeMusic)
})
@@ -83,13 +89,87 @@ func InitSystemMediaControl() {
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.EventManager.RegisterA(events.PlayerPropertyPauseUpdate, "sysmediacontrol.update_paused", func(event *event.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.EventManager.CallA(
events.PlayerSetPauseCmd, events.PlayerSetPauseCmdEvent{Pause: false})
case media.SystemMediaTransportControlsButtonPause:
global.EventManager.CallA(
events.PlayerSetPauseCmd, events.PlayerSetPauseCmdEvent{Pause: true})
case media.SystemMediaTransportControlsButtonNext:
global.EventManager.CallA(
events.PlayerPlayNextCmd, events.PlayerPlayNextCmdEvent{})
case media.SystemMediaTransportControlsButtonPrevious:
global.EventManager.CallA(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.EventManager.RegisterA(events.PlayerPropertyDurationUpdate, "sysmediacontrol.properties.duration", func(event *event.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.EventManager.RegisterA(events.PlayerPropertyTimePosUpdate, "sysmediacontrol.properties.time_pos", func(event *event.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()
}

View File

@@ -10,7 +10,7 @@ import (
const (
ProgramName = "卡西米尔唱片机"
Version uint32 = 0x010009
Version uint32 = 0x010100
)
const (

View File

@@ -11,6 +11,7 @@ type _GeneralConfig struct {
PlayNextOnFail bool
UseSystemPlaylist bool
FixedSize bool
EnableSMC bool // enable system media control
}
func (c *_GeneralConfig) Name() string {
@@ -27,4 +28,5 @@ var General = &_GeneralConfig{
PlayNextOnFail: false,
UseSystemPlaylist: true,
FixedSize: true,
EnableSMC: true,
}

View File

@@ -3,5 +3,5 @@ Artist: {{ .Current.Artist }}
Album: {{ .Current.Album}}
Username: {{ .Current.Username }}
Progress(in seconds): {{.CurrentTime.TotalSeconds}} / {{.TotalTime.TotalSeconds}}
Progress(in minutes:seconds): {{ .CurrentTime.Minutes}}:{{ .CurrentTime.Seconds}} / {{ .TotalTime.Minutes}}:{{ .TotalTime.Seconds}}
Progress(in minutes:seconds): {{printf "%02d" .CurrentTime.Minutes}}:{{printf "%02d" .CurrentTime.Seconds}} / {{printf "%02d" .TotalTime.Minutes}}:{{printf "%02d" .TotalTime.Seconds}}
Lyric: {{ .Lyric}}

View File

@@ -1,5 +1,5 @@
{{range .Playlist}}
Index: # {{ .Index}}
Index: #{{ .Index}}
Title: {{ .Title }}
Artist: {{ .Artist }}
Album: {{ .Album}}

View File

@@ -8,16 +8,18 @@
- 适配歌词服务器
- 媒体源 - 歌单信息获取
- mpris, SMTC
- web弹幕协议的断线handler (web 重连)
- 歌词event发送全部歌词前端处理不同版本
- 网页输出重写,使用网页版本,不绑定在点歌机内(点歌机不需要启动网页服务)
- optimize local music
- 从搜索里添加的歌不能被切
- 随机歌单
----
Finished
- 2024.08.25 : 添加是否播放闲置歌单选项,修复退出时panic的导致配置无法正确保存的问题
- 2024.10.24 : 修复随机播放是真随机的问题现在每首歌在都会被播放到添加windows系统级控制
- 2024.10.05 : 添加windows system media transport control
- 2024.09.25 : 修复web弹幕粉丝牌子等级获取不正确的问题
- 2024.08.25@1.0.9 : 添加是否播放闲置歌单选项,修复退出时panic的导致配置无法正确保存的问题
- 2024.08.14 : 网页输出模版beta上线
- 2024.08.06 : 修复使用身份码连接的时候房管无法切歌的问题
- 2024.07.25 : 或许修复配置无法正确保存的问题