重写controller部分,修改search界面,添加歌词滚动效果,部分资源添加到bundle,修复拖动进度条时产生的噪音

This commit is contained in:
Aynakeya
2022-12-24 03:51:21 -08:00
parent c47d338a9e
commit 9ec4057412
52 changed files with 777 additions and 376 deletions

View File

@@ -1,5 +1,13 @@
NAME = AynaLivePlayer
ifeq ($(OS),Windows_NT)
RM = del /Q /F
RRM = rmdir /Q /S
else
RM = rm -f
RRM = rm -rf
endif
ifeq ($(OS), Windows_NT)
EXECUTABLE=$(NAME).exe
else
@@ -13,14 +21,15 @@ run:
go run ./app/gui/main.go
clear:
ifeq ($(OS), Windows_NT)
-DEL $(EXECUTABLE) config.ini log.txt playlists.json liverooms.json /s /q
else
rm config.ini log.txt playlists.txt liverooms.json
endif
$(RM) config.ini log.txt playlists.txt liverooms.json
bundle:
fyne bundle --name resImageEmpty --package resource ./assets/empty.png > ./resource/bundle.go
fyne bundle --append --name resImageIcon --package resource ./assets/icon.jpg >> ./resource/bundle.go
fyne bundle --append --name resFontMSYaHei --package resource ./assets/msyh.ttc >> ./resource/bundle.go
fyne bundle --append --name resFontMSYaHeiBold --package resource ./assets/msyhbd.ttc >> ./resource/bundle.go
release: ${EXECUTABLE}
release: ${EXECUTABLE} bundle
-mkdir release
ifeq ($(OS), Windows_NT)
COPY .\$(EXECUTABLE) .\release\$(EXECUTABLE)
@@ -39,11 +48,7 @@ else
endif
clean:
ifeq ($(OS), Windows_NT)
-DEL $(EXECUTABLE) /s /q
-rmdir .\release /s /q
else
rm -r $(EXECUTABLE) ./release
endif
$(RM) $(EXECUTABLE) config.ini log.txt playlists.txt liverooms.json
$(RRM) release
.PHONY: ${EXECUTABLE}

View File

@@ -240,6 +240,7 @@ This styled row should also wrap as expected, but only *when required*.
grid := makeTextGrid()
return container.NewBorder(fixed, grid, nil, nil,
container.NewGridWithRows(2, rich, entryLoremIpsum))
}
func makeInputTab(_ fyne.Window) fyne.CanvasObject {

BIN
assets/msyhbd.ttc Normal file

Binary file not shown.

View File

@@ -21,7 +21,7 @@ type Handler struct {
}
type Manager struct {
handlers map[string]*Handler
handlers map[EventId]map[string]*Handler
queue chan func()
stopSig chan int
queueSize int
@@ -31,7 +31,7 @@ type Manager struct {
func NewManger(queueSize int, workerSize int) *Manager {
manager := &Manager{
handlers: make(map[string]*Handler),
handlers: make(map[EventId]map[string]*Handler),
queue: make(chan func(), queueSize),
stopSig: make(chan int, workerSize),
queueSize: queueSize,
@@ -54,7 +54,7 @@ func NewManger(queueSize int, workerSize int) *Manager {
func (h *Manager) NewChildManager() *Manager {
return &Manager{
handlers: make(map[string]*Handler),
handlers: make(map[EventId]map[string]*Handler),
queue: h.queue,
stopSig: h.stopSig,
queueSize: h.queueSize,
@@ -71,7 +71,12 @@ func (h *Manager) Stop() {
func (h *Manager) Register(handler *Handler) {
h.lock.Lock()
defer h.lock.Unlock()
h.handlers[handler.Name] = handler
m, ok := h.handlers[handler.EventId]
if !ok {
m = make(map[string]*Handler)
h.handlers[handler.EventId] = m
}
m[handler.Name] = handler
}
func (h *Manager) RegisterA(id EventId, name string, handler HandlerFunc) {
@@ -85,24 +90,30 @@ func (h *Manager) RegisterA(id EventId, name string, handler HandlerFunc) {
func (h *Manager) UnregisterAll() {
h.lock.Lock()
defer h.lock.Unlock()
h.handlers = make(map[string]*Handler)
h.handlers = make(map[EventId]map[string]*Handler)
}
func (h *Manager) Unregister(name string) {
h.lock.Lock()
defer h.lock.Unlock()
delete(h.handlers, name)
for _, m := range h.handlers {
if _, ok := m[name]; ok {
delete(m, name)
}
}
}
func (h *Manager) Call(event *Event) {
h.lock.RLock()
defer h.lock.RUnlock()
for _, eh := range h.handlers {
if eh.EventId == event.Id {
handler := eh.Handler
h.queue <- func() {
handler(event)
}
handlers, ok := h.handlers[event.Id]
h.lock.RUnlock()
if !ok {
return
}
for _, eh := range handlers {
handler := eh.Handler
h.queue <- func() {
handler(event)
}
}
}

View File

@@ -37,14 +37,14 @@ func (l *LyricLoader) Reload(lyric string) {
}
func (l *LyricLoader) Update(time float64) {
lrc := l.Lyric.Find(time)
lrc := l.Lyric.FindContext(time, 1, 3)
if lrc == nil {
return
}
if l.prev == lrc.Time {
if l.prev == lrc.Now.Time {
return
}
l.prev = lrc.Time
l.prev = lrc.Now.Time
l.Handler.CallA(
model.EventLyricUpdate,
model.LyricUpdateEvent{

View File

@@ -6,7 +6,7 @@ import (
"AynaLivePlayer/controller"
"AynaLivePlayer/model"
"AynaLivePlayer/player"
"AynaLivePlayer/provider"
"AynaLivePlayer/repo/provider"
)
type PlayController struct {

View File

@@ -5,7 +5,7 @@ import (
"AynaLivePlayer/config"
"AynaLivePlayer/controller"
"AynaLivePlayer/model"
"AynaLivePlayer/provider"
"AynaLivePlayer/repo/provider"
"errors"
"fmt"
"math/rand"
@@ -27,7 +27,7 @@ type PlaylistController struct {
func NewPlaylistController(
provider controller.IProviderController) controller.IPlaylistController {
pc := &PlaylistController{
PlaylistPath: "playlist.json",
PlaylistPath: "playlists.json",
provider: provider,
History: NewPlaylist("history"),
Default: NewPlaylist("default"),

View File

@@ -4,7 +4,7 @@ import (
"AynaLivePlayer/config"
"AynaLivePlayer/controller"
"AynaLivePlayer/model"
"AynaLivePlayer/provider"
provider2 "AynaLivePlayer/repo/provider"
)
type ProviderController struct {
@@ -23,7 +23,7 @@ func NewProviderController() controller.IProviderController {
LocalDir: "./music",
}
config.LoadConfig(p)
provider.NewLocal(p.LocalDir)
provider2.NewLocal(p.LocalDir)
return p
}
@@ -35,21 +35,21 @@ func (pc *ProviderController) PrepareMedia(media *model.Media) error {
var err error
if media.Title == "" || !media.Cover.Exists() {
lg.Trace("fetching media info")
if err = provider.UpdateMedia(media); err != nil {
if err = provider2.UpdateMedia(media); err != nil {
lg.Warn("fail to prepare media when fetch info", err)
return err
}
}
if media.Url == "" {
lg.Trace("fetching media url")
if err = provider.UpdateMediaUrl(media); err != nil {
if err = provider2.UpdateMediaUrl(media); err != nil {
lg.Warn("fail to prepare media when url", err)
return err
}
}
if media.Lyric == "" {
lg.Trace("fetching media lyric")
if err = provider.UpdateMediaLyric(media); err != nil {
if err = provider2.UpdateMediaLyric(media); err != nil {
lg.Warn("fail to prepare media when lyric", err)
}
}
@@ -59,12 +59,12 @@ func (pc *ProviderController) PrepareMedia(media *model.Media) error {
func (pc *ProviderController) MediaMatch(keyword string) *model.Media {
lg.Infof("Match media for %s", keyword)
for _, p := range pc.Priority {
if pr, ok := provider.Providers[p]; ok {
if pr, ok := provider2.Providers[p]; ok {
m := pr.MatchMedia(keyword)
if m == nil {
continue
}
if err := provider.UpdateMedia(m); err == nil {
if err := provider2.UpdateMedia(m); err == nil {
return m
}
} else {
@@ -77,7 +77,7 @@ func (pc *ProviderController) MediaMatch(keyword string) *model.Media {
func (pc *ProviderController) Search(keyword string) ([]*model.Media, error) {
lg.Infof("Search for %s", keyword)
for _, p := range pc.Priority {
if pr, ok := provider.Providers[p]; ok {
if pr, ok := provider2.Providers[p]; ok {
r, err := pr.Search(keyword)
if err != nil {
lg.Warn("Provider %s return err", err)
@@ -88,22 +88,22 @@ func (pc *ProviderController) Search(keyword string) ([]*model.Media, error) {
lg.Warnf("Provider %s not exist", p)
}
}
return nil, provider.ErrorNoSuchProvider
return nil, provider2.ErrorNoSuchProvider
}
func (pc *ProviderController) SearchWithProvider(keyword string, p string) ([]*model.Media, error) {
lg.Infof("Search for %s using %s", keyword, p)
if pr, ok := provider.Providers[p]; ok {
if pr, ok := provider2.Providers[p]; ok {
r, err := pr.Search(keyword)
return r, err
}
lg.Warnf("Provider %s not exist", p)
return nil, provider.ErrorNoSuchProvider
return nil, provider2.ErrorNoSuchProvider
}
func (pc *ProviderController) PreparePlaylist(playlist controller.IPlaylist) error {
lg.Debug("Prepare playlist ", playlist.Name())
medias, err := provider.GetPlaylist(&playlist.Model().Meta)
medias, err := provider2.GetPlaylist(&playlist.Model().Meta)
if err != nil {
lg.Warn("prepare playlist failed ", err)
return err

2
go.mod
View File

@@ -12,6 +12,7 @@ require (
github.com/go-resty/resty/v2 v2.7.0
github.com/gorilla/websocket v1.5.0
github.com/jinzhu/copier v0.3.5
github.com/magiconair/properties v1.8.5
github.com/sirupsen/logrus v1.8.1
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cast v1.5.0
@@ -35,6 +36,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect

19
go.sum
View File

@@ -37,8 +37,6 @@ 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.2.3 h1:Umi3vVVW8XnWWPJmMkhIWQOMU/jxB1OqpWVUmjhODD0=
fyne.io/fyne/v2 v2.2.3/go.mod h1:MBoGuHzLLSXdQOWFAwWhIhYTEMp33zqtGCReSWhaQTA=
fyne.io/fyne/v2 v2.2.4 h1:izyiDUjJYAB7B/MST7M9GDs+mQ0CwDgRZTiVJZQoEe4=
fyne.io/fyne/v2 v2.2.4/go.mod h1:MBoGuHzLLSXdQOWFAwWhIhYTEMp33zqtGCReSWhaQTA=
fyne.io/systray v1.10.1-0.20220621085403-9a2652634e93 h1:V2IC9t0Zj9Ur6qDbfhUuzVmIvXKFyxZXRJyigUvovs4=
@@ -55,8 +53,6 @@ github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBa
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aynakeya/blivedm v0.1.5 h1:O+7qEI9Q75eUyOW7NOAtj9v1Zpk001tb0h+YXEKHntQ=
github.com/aynakeya/blivedm v0.1.5/go.mod h1:g7cA6/BfDcrsD4v9w+P6B9Z+gANi4jPlGkZ1Oyuu/i4=
github.com/aynakeya/blivedm v0.1.6 h1:fnjyyHYnXAArcLqMRNLQCAdvRFiEVGA/a/54UatZF0k=
github.com/aynakeya/blivedm v0.1.6/go.mod h1:g7cA6/BfDcrsD4v9w+P6B9Z+gANi4jPlGkZ1Oyuu/i4=
github.com/aynakeya/go-mpv v0.0.6 h1:WCBwHrzl700C1J3f+aXR+URw/OKYPjwUjDW9diOsXYY=
@@ -88,6 +84,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -160,6 +157,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -228,10 +226,12 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -250,6 +250,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -263,6 +264,7 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -278,7 +280,6 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDq
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
@@ -304,15 +305,12 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -424,7 +422,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
@@ -498,7 +495,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -572,6 +568,7 @@ golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=

43
gui/component/button.go Normal file
View File

@@ -0,0 +1,43 @@
package component
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type AsyncButton struct {
widget.Button
}
func NewAsyncButton(label string, tapped func()) *AsyncButton {
b := &AsyncButton{
Button: widget.Button{
Text: label,
},
}
b.ExtendBaseWidget(b)
b.SetOnTapped(tapped)
return b
}
func NewAsyncButtonWithIcon(label string, icon fyne.Resource, tapped func()) *AsyncButton {
b := &AsyncButton{
Button: widget.Button{
Text: label,
Icon: icon,
},
}
b.ExtendBaseWidget(b)
b.SetOnTapped(tapped)
return b
}
func (b *AsyncButton) SetOnTapped(f func()) {
b.Button.OnTapped = func() {
b.Disable()
go func() {
f()
b.Enable()
}()
}
}

32
gui/component/entry.go Normal file
View File

@@ -0,0 +1,32 @@
package component
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type Entry struct {
widget.Entry
OnKeyUp func(key *fyne.KeyEvent)
OnKeyDown func(key *fyne.KeyEvent)
}
func NewEntry() *Entry {
e := &Entry{}
e.ExtendBaseWidget(e)
return e
}
func (m *Entry) KeyUp(key *fyne.KeyEvent) {
m.Entry.KeyUp(key)
if m.OnKeyUp != nil {
m.OnKeyUp(key)
}
}
func (m *Entry) KeyDown(key *fyne.KeyEvent) {
m.Entry.KeyDown(key)
if m.OnKeyDown != nil {
m.OnKeyDown(key)
}
}

38
gui/component/slider.go Normal file
View File

@@ -0,0 +1,38 @@
package component
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
type SliderPlus struct {
widget.Slider
OnDragEnd func(value float64)
Dragging bool // during dragging
}
func NewSliderPlus(min, max float64) *SliderPlus {
slider := &SliderPlus{
Slider: widget.Slider{
Value: 0,
Min: min,
Max: max,
Step: 1,
Orientation: widget.Horizontal,
},
}
slider.ExtendBaseWidget(slider)
return slider
}
func (s *SliderPlus) DragEnd() {
if s.OnDragEnd != nil {
s.OnDragEnd(s.Value)
}
s.Dragging = false
}
func (s *SliderPlus) Dragged(e *fyne.DragEvent) {
s.Dragging = true
s.Slider.Dragged(e)
}

156
gui/component/split.go Normal file
View File

@@ -0,0 +1,156 @@
package component
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type FixedSplit struct {
widget.BaseWidget
Offset float64
Horizontal bool
Leading fyne.CanvasObject
Trailing fyne.CanvasObject
}
func NewFixedHSplitContainer(leading, trailing fyne.CanvasObject, offset float64) *FixedSplit {
return NewFixedSplitContainer(leading, trailing, true, offset)
}
func NewFixedVSplitContainer(top, bottom fyne.CanvasObject, offset float64) *FixedSplit {
return NewFixedSplitContainer(top, bottom, false, offset)
}
func NewFixedSplitContainer(leading, trailing fyne.CanvasObject, horizontal bool, offset float64) *FixedSplit {
s := &FixedSplit{
Offset: offset, // Sensible default, can be overridden with SetOffset
Horizontal: horizontal,
Leading: leading,
Trailing: trailing,
}
s.BaseWidget.ExtendBaseWidget(s)
return s
}
func (s *FixedSplit) CreateRenderer() fyne.WidgetRenderer {
s.BaseWidget.ExtendBaseWidget(s)
d := widget.NewSeparator()
return &fixedSplitContainerRenderer{
split: s,
divider: d,
objects: []fyne.CanvasObject{s.Leading, d, s.Trailing},
}
}
func (s *FixedSplit) SetOffset(offset float64) {
if s.Offset == offset {
return
}
s.Offset = offset
s.Refresh()
}
type fixedSplitContainerRenderer struct {
split *FixedSplit
divider *widget.Separator
objects []fyne.CanvasObject
}
func (r *fixedSplitContainerRenderer) Destroy() {
}
func (r *fixedSplitContainerRenderer) Layout(size fyne.Size) {
var dividerPos, leadingPos, trailingPos fyne.Position
var dividerSize, leadingSize, trailingSize fyne.Size
if r.split.Horizontal {
lw, tw := r.computeSplitLengths(size.Width, r.split.Leading.MinSize().Width, r.split.Trailing.MinSize().Width)
leadingPos.X = 0
leadingSize.Width = lw
leadingSize.Height = size.Height
dividerPos.X = lw
dividerSize.Width = theme.SeparatorThicknessSize()
dividerSize.Height = size.Height
trailingPos.X = lw + dividerSize.Width
trailingSize.Width = tw
trailingSize.Height = size.Height
} else {
lh, th := r.computeSplitLengths(size.Height, r.split.Leading.MinSize().Height, r.split.Trailing.MinSize().Height)
leadingPos.Y = 0
leadingSize.Width = size.Width
leadingSize.Height = lh
dividerPos.Y = lh
dividerSize.Width = size.Width
dividerSize.Height = theme.SeparatorThicknessSize()
trailingPos.Y = lh + dividerSize.Height
trailingSize.Width = size.Width
trailingSize.Height = th
}
r.divider.Move(dividerPos)
r.divider.Resize(dividerSize)
r.split.Leading.Move(leadingPos)
r.split.Leading.Resize(leadingSize)
r.split.Trailing.Move(trailingPos)
r.split.Trailing.Resize(trailingSize)
canvas.Refresh(r.divider)
canvas.Refresh(r.split.Leading)
canvas.Refresh(r.split.Trailing)
}
func (r *fixedSplitContainerRenderer) MinSize() fyne.Size {
s := fyne.NewSize(0, 0)
for _, o := range r.objects {
min := o.MinSize()
if r.split.Horizontal {
s.Width += min.Width
s.Height = fyne.Max(s.Height, min.Height)
} else {
s.Width = fyne.Max(s.Width, min.Width)
s.Height += min.Height
}
}
return s
}
func (r *fixedSplitContainerRenderer) Objects() []fyne.CanvasObject {
return r.objects
}
func (r *fixedSplitContainerRenderer) Refresh() {
r.objects[0] = r.split.Leading
// [1] is divider which doesn't change
r.objects[2] = r.split.Trailing
r.Layout(r.split.Size())
canvas.Refresh(r.split)
}
func (r *fixedSplitContainerRenderer) computeSplitLengths(total, lMin, tMin float32) (float32, float32) {
available := float64(total - theme.SeparatorThicknessSize())
if available <= 0 {
return 0, 0
}
ld := float64(lMin)
tr := float64(tMin)
offset := r.split.Offset
min := ld / available
max := 1 - tr/available
if min <= max {
if offset < min {
offset = min
}
if offset > max {
offset = max
}
} else {
offset = ld / (ld + tr)
}
ld = offset * available
tr = available - ld
return float32(ld), float32(tr)
}

View File

@@ -1,26 +1,12 @@
package gui
import (
"AynaLivePlayer/gui/component"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
type TestConfig struct {
}
func (t *TestConfig) Title() string {
return "Test Title"
}
func (T *TestConfig) Description() string {
return "Test Description"
}
func (t *TestConfig) CreatePanel() fyne.CanvasObject {
return widget.NewLabel("asdf")
}
func createConfigLayout() fyne.CanvasObject {
// initialize config panels
for _, c := range ConfigList {
@@ -32,7 +18,7 @@ func createConfigLayout() fyne.CanvasObject {
return len(ConfigList)
},
func() fyne.CanvasObject {
return widget.NewLabel("AAAAAAAAAAAAAAAA")
return widget.NewLabel("")
},
func(id widget.ListItemID, object fyne.CanvasObject) {
object.(*widget.Label).SetText(ConfigList[id].Title())
@@ -44,16 +30,11 @@ func createConfigLayout() fyne.CanvasObject {
seg.Style.Alignment = fyne.TextAlignCenter
}
}
a := container.NewVScroll(ConfigList[id].CreatePanel())
content.Objects = []fyne.CanvasObject{
container.NewBorder(container.NewVBox(desc, widget.NewSeparator()), nil, nil, nil,
a),
container.NewVScroll(container.NewVBox(desc, widget.NewSeparator(), ConfigList[id].CreatePanel())),
}
content.Refresh()
}
return container.NewBorder(
nil, nil,
container.NewHBox(entryList, widget.NewSeparator()), nil,
content)
return component.NewFixedSplitContainer(entryList, content, true, 0.23)
}

View File

@@ -4,66 +4,71 @@ import (
"AynaLivePlayer/common/i18n"
"AynaLivePlayer/common/logger"
"AynaLivePlayer/config"
"AynaLivePlayer/controller"
"AynaLivePlayer/resource"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"os"
)
const MODULE_GUI = "GUI"
type ConfigLayout interface {
Title() string
Description() string
CreatePanel() fyne.CanvasObject
}
var App fyne.App
var MainWindow fyne.Window
var ConfigList = []ConfigLayout{&bascicConfig{}}
func l() *logrus.Entry {
return logger.Logger.WithField("Module", MODULE_GUI)
}
func black_magic() {
widget.RichTextStyleStrong.TextStyle.Bold = false
}
func Initialize() {
//black_magic()
l().Info("Initializing GUI")
os.Setenv("FYNE_FONT", config.GetAssetPath("msyh.ttc"))
//os.Setenv("FYNE_FONT", config.GetAssetPath("msyh.ttc"))
App = app.New()
App.Settings().SetTheme(&myTheme{})
MainWindow = App.NewWindow(fmt.Sprintf("%s Ver.%s", config.ProgramName, config.Version))
tabs := container.NewAppTabs(
container.NewTabItem(i18n.T("gui.tab.player"),
newPaddedBoarder(nil, createPlayControllerV2(), nil, nil, createPlaylist()),
container.NewBorder(nil, createPlayControllerV2(), nil, nil, createPlaylist()),
),
container.NewTabItem(i18n.T("gui.tab.search"),
newPaddedBoarder(createSearchBar(), nil, nil, nil, createSearchList()),
container.NewBorder(createSearchBar(), nil, nil, nil, createSearchList()),
),
container.NewTabItem(i18n.T("gui.tab.room"),
newPaddedBoarder(nil, nil, createRoomSelector(), nil, createRoomController()),
container.NewBorder(nil, nil, createRoomSelector(), nil, createRoomController()),
),
container.NewTabItem(i18n.T("gui.tab.playlist"),
newPaddedBoarder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
container.NewBorder(nil, nil, createPlaylists(), nil, createPlaylistMedias()),
),
container.NewTabItem(i18n.T("gui.tab.history"),
newPaddedBoarder(nil, nil, nil, nil, createHistoryList()),
container.NewBorder(nil, nil, nil, nil, createHistoryList()),
),
container.NewTabItem(i18n.T("gui.tab.config"),
newPaddedBoarder(nil, nil, nil, nil, createConfigLayout()),
createConfigLayout(),
),
)
tabs.SetTabLocation(container.TabLocationTop)
MainWindow.SetIcon(fyne.NewStaticResource("icon", resource.ProgramIcon))
MainWindow.SetIcon(resource.ImageIcon)
MainWindow.SetContent(tabs)
//MainWindow.Resize(fyne.NewSize(1280, 720))
MainWindow.Resize(fyne.NewSize(960, 480))
//MainWindow.SetFixedSize(true)
}
func AddConfigLayout(cfgs ...ConfigLayout) {
ConfigList = append(ConfigList, cfgs...)
func addShortCut() {
key := &desktop.CustomShortcut{KeyName: fyne.KeyRight, Modifier: fyne.KeyModifierControl | fyne.KeyModifierShift}
MainWindow.Canvas().AddShortcut(key, func(shortcut fyne.Shortcut) {
l().Info("Shortcut pressed: Ctrl+Shift+Right")
controller.Instance.PlayControl().PlayNext()
})
}

56
gui/gutil/resize.go Normal file
View File

@@ -0,0 +1,56 @@
package gutil
import (
"AynaLivePlayer/model"
"bytes"
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/storage"
"github.com/nfnt/resize"
"image"
"image/png"
)
func ResizeImage(resource fyne.Resource, width int, height int) fyne.Resource {
data := resource.Content()
img, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
return resource
}
img = resize.Thumbnail(uint(width), uint(height), img, resize.Lanczos3)
buf := bytes.NewBuffer([]byte{})
err = png.Encode(buf, img)
if err != nil {
return resource
}
return fyne.NewStaticResource(resource.Name(), buf.Bytes())
}
func NewImageFromPlayerPicture(picture model.Picture) (*canvas.Image, error) {
var img *canvas.Image
if picture.Data != nil {
img = canvas.NewImageFromReader(bytes.NewReader(picture.Data), "cover")
// return an error when img is nil
if img == nil {
return nil, errors.New("fail to read image")
}
} else {
uri, err := storage.ParseURI(picture.Url)
if err != nil {
return nil, err
}
if uri == nil {
return nil, errors.New("fail to fail url")
}
img = canvas.NewImageFromURI(uri)
if img == nil {
// bug fix, return a new error to indicate fail to read an image
return nil, errors.New("fail to read image")
}
}
// compress image, so it won't be too large
img.Resource = ResizeImage(img.Resource, 128, 128)
return img, nil
}

View File

@@ -1,14 +1,9 @@
package gui
import (
"AynaLivePlayer/model"
"bytes"
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
)
@@ -23,21 +18,6 @@ func newLabelWithWrapping(text string, wrapping fyne.TextWrap) *widget.Label {
return w
}
func createAsyncOnTapped(btn *widget.Button, f func()) func() {
return func() {
btn.Disable()
go func() {
f()
btn.Enable()
}()
}
}
func createAsyncButton(btn *widget.Button, tapped func()) *widget.Button {
btn.OnTapped = createAsyncOnTapped(btn, tapped)
return btn
}
type ContextMenuButton struct {
widget.Button
menu *fyne.Menu
@@ -55,57 +35,6 @@ func newContextMenuButton(label string, menu *fyne.Menu) *ContextMenuButton {
return b
}
type FixedSplitContainer struct {
*container.Split
}
func (f *FixedSplitContainer) Dragged(event *fyne.DragEvent) {
// do nothing
}
func (f *FixedSplitContainer) DragEnd() {
// do nothing
}
func newFixedSplitContainer(horizontal bool, leading, trailing fyne.CanvasObject) *FixedSplitContainer {
s := &container.Split{
Offset: 0.5, // Sensible default, can be overridden with SetOffset
Horizontal: horizontal,
Leading: leading,
Trailing: trailing,
}
fs := &FixedSplitContainer{
s,
}
fs.Split.BaseWidget.ExtendBaseWidget(s)
return fs
}
func newImageFromPlayerPicture(picture model.Picture) (*canvas.Image, error) {
if picture.Data != nil {
img := canvas.NewImageFromReader(bytes.NewReader(picture.Data), "cover")
// return an error when img is nil
if img == nil {
return nil, errors.New("fail to read image")
}
return img, nil
} else {
uri, err := storage.ParseURI(picture.Url)
if err != nil {
return nil, err
}
if uri == nil {
return nil, errors.New("fail to fail url")
}
img := canvas.NewImageFromURI(uri)
if img == nil {
// bug fix, return a new error to indicate fail to read an image
return nil, errors.New("fail to read image")
}
return img, nil
}
}
func showDialogIfError(err error) {
if err != nil {
dialog.ShowError(err, MainWindow)

View File

@@ -1,52 +0,0 @@
package gui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
)
// AsyncButton is a Button that handle OnTapped handler asynchronously.
type AsyncButton struct {
widget.Button
anim *fyne.Animation
}
func NewAsyncButton(label string, tapped func()) *AsyncButton {
button := &AsyncButton{
Button: widget.Button{
Text: label,
OnTapped: tapped,
},
}
button.ExtendBaseWidget(button)
return button
}
func NewAsyncButtonWithIcon(label string, icon fyne.Resource, tapped func()) *AsyncButton {
button := &AsyncButton{
Button: widget.Button{
Text: label,
Icon: icon,
OnTapped: tapped,
},
}
button.ExtendBaseWidget(button)
return button
}
func (b *AsyncButton) Tapped(e *fyne.PointEvent) {
if b.Disabled() {
return
}
// missing animation
b.Refresh()
if b.OnTapped != nil {
b.Disable()
go func() {
b.OnTapped()
b.Enable()
}()
}
}

15
gui/interface.go Normal file
View File

@@ -0,0 +1,15 @@
package gui
import "fyne.io/fyne/v2"
var ConfigList = []ConfigLayout{&bascicConfig{}}
type ConfigLayout interface {
Title() string
Description() string
CreatePanel() fyne.CanvasObject
}
func AddConfigLayout(cfgs ...ConfigLayout) {
ConfigList = append(ConfigList, cfgs...)
}

View File

@@ -30,7 +30,7 @@ func createRoomSelector() fyne.CanvasObject {
return controller.Instance.LiveRooms().Size()
},
func() fyne.CanvasObject {
return widget.NewLabel("AAAAAAAAAAAAAAAA")
return widget.NewLabel("")
},
func(id widget.ListItemID, object fyne.CanvasObject) {
object.(*widget.Label).SetText(
@@ -56,10 +56,7 @@ func createRoomSelector() fyne.CanvasObject {
func(b bool) {
if b && len(clientNameEntry.Selected) > 0 && len(idEntry.Text) > 0 {
_, err := controller.Instance.LiveRooms().AddRoom(clientNameEntry.Selected, idEntry.Text)
if err != nil {
dialog.ShowError(err, MainWindow)
return
}
showDialogIfError(err)
RoomTab.Rooms.Refresh()
}
},
@@ -69,10 +66,7 @@ func createRoomSelector() fyne.CanvasObject {
dia.Show()
})
RoomTab.RemoveBtn = widget.NewButton(i18n.T("gui.room.button.remove"), func() {
if err := controller.Instance.LiveRooms().DeleteRoom(PlaylistManager.Index); err != nil {
dialog.ShowError(err, MainWindow)
return
}
showDialogIfError(controller.Instance.LiveRooms().DeleteRoom(PlaylistManager.Index))
RoomTab.Rooms.Select(0)
RoomTab.Rooms.Refresh()
})

View File

@@ -4,9 +4,11 @@ import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/common/i18n"
"AynaLivePlayer/common/util"
"AynaLivePlayer/config"
"AynaLivePlayer/controller"
"AynaLivePlayer/gui/component"
"AynaLivePlayer/gui/gutil"
"AynaLivePlayer/model"
"AynaLivePlayer/resource"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
@@ -22,7 +24,7 @@ type PlayControllerContainer struct {
ButtonPrev *widget.Button
ButtonSwitch *widget.Button
ButtonNext *widget.Button
Progress *widget.Slider
Progress *component.SliderPlus
Volume *widget.Slider
ButtonLrc *widget.Button
LrcWindowOpen bool
@@ -31,48 +33,12 @@ type PlayControllerContainer struct {
}
func (p *PlayControllerContainer) SetDefaultCover() {
p.Cover.Resource = ResEmptyImage
p.Cover.Resource = resource.ImageEmpty
p.Cover.Refresh()
}
var PlayController = &PlayControllerContainer{}
func createPlayController() fyne.CanvasObject {
PlayController.Cover = canvas.NewImageFromFile(config.GetAssetPath("empty.png"))
PlayController.Cover.SetMinSize(fyne.NewSize(128, 128))
PlayController.Cover.FillMode = canvas.ImageFillContain
PlayController.ButtonPrev = widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func() {})
PlayController.ButtonSwitch = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() {})
PlayController.ButtonNext = widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func() {})
buttonsBox := container.NewCenter(
container.NewHBox(PlayController.ButtonPrev, PlayController.ButtonSwitch, PlayController.ButtonNext))
PlayController.Progress = widget.NewSlider(0, 1000)
PlayController.CurrentTime = widget.NewLabel("0:00")
PlayController.TotalTime = widget.NewLabel("0:00")
progressItem := container.NewBorder(nil, nil, PlayController.CurrentTime, PlayController.TotalTime, PlayController.Progress)
PlayController.Title = widget.NewLabel("Title")
PlayController.Artist = widget.NewLabel("Artist")
PlayController.Username = widget.NewLabel("Username")
playInfo := container.NewVBox(PlayController.Title, PlayController.Artist, PlayController.Username)
PlayController.Volume = widget.NewSlider(0, 100)
volumeIcon := widget.NewIcon(theme.VolumeMuteIcon())
PlayController.ButtonLrc = widget.NewButton(i18n.T("gui.player.button.lrc"), func() {})
volumeControl := container.NewBorder(nil, nil, container.NewHBox(widget.NewLabel(" "), volumeIcon), nil,
container.NewGridWithColumns(3, container.NewMax(PlayController.Volume), PlayController.ButtonLrc))
registerPlayControllerHandler()
return container.NewBorder(nil, nil, container.NewHBox(PlayController.Cover, playInfo, widget.NewSeparator()), nil,
container.NewVBox(buttonsBox, progressItem, volumeControl))
}
func registerPlayControllerHandler() {
PlayController.ButtonPrev.OnTapped = func() {
controller.Instance.PlayControl().Seek(0, true)
@@ -109,6 +75,9 @@ func registerPlayControllerHandler() {
if controller.Instance.PlayControl().GetPlayer().ObserveProperty(
model.PlayerPropPercentPos, "gui.play_controller.percent_pos", func(ev *event.Event) {
if PlayController.Progress.Dragging {
return
}
data := ev.Data.(model.PlayerPropertyUpdateEvent).Value
if data == nil {
PlayController.Progress.Value = 0
@@ -140,7 +109,7 @@ func registerPlayControllerHandler() {
}
PlayController.Progress.Max = 0
PlayController.Progress.OnChanged = func(f float64) {
PlayController.Progress.OnDragEnd = func(f float64) {
controller.Instance.PlayControl().Seek(f/10, false)
}
@@ -202,7 +171,7 @@ func registerPlayControllerHandler() {
PlayController.SetDefaultCover()
} else {
go func() {
picture, err := newImageFromPlayerPicture(media.Cover)
picture, err := gutil.NewImageFromPlayerPicture(media.Cover)
if err != nil {
l().Warn("fail to load parse cover url", media.Cover)
PlayController.SetDefaultCover()
@@ -217,7 +186,7 @@ func registerPlayControllerHandler() {
}
func createPlayControllerV2() fyne.CanvasObject {
PlayController.Cover = canvas.NewImageFromResource(ResEmptyImage)
PlayController.Cover = canvas.NewImageFromResource(resource.ImageEmpty)
PlayController.Cover.SetMinSize(fyne.NewSize(128, 128))
PlayController.Cover.FillMode = canvas.ImageFillContain
@@ -239,7 +208,7 @@ func createPlayControllerV2() fyne.CanvasObject {
PlayController.Volume)),
))
PlayController.Progress = widget.NewSlider(0, 1000)
PlayController.Progress = component.NewSliderPlus(0, 1000)
PlayController.CurrentTime = widget.NewLabel("0:00")
PlayController.TotalTime = widget.NewLabel("0:00")
progressItem := container.NewBorder(nil, nil,

View File

@@ -7,26 +7,33 @@ import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"strings"
)
func createLyricObj(lyric *model.Lyric) []fyne.CanvasObject {
lrcs := make([]fyne.CanvasObject, len(lyric.Lyrics))
for i := 0; i < len(lrcs); i++ {
l := widget.NewLabelWithStyle(
lyric.Lyrics[i].Lyric,
fyne.TextAlignCenter, fyne.TextStyle{Italic: true})
l.Wrapping = fyne.TextWrapWord
lrcs[i] = l
}
return lrcs
}
func createLyricWindow() fyne.Window {
// create widgets
w := App.NewWindow("Lyric")
currentLrc := newLabelWithWrapping("", fyne.TextWrapBreak)
currentLrc.Alignment = fyne.TextAlignCenter
lrcs := make([]string, len(controller.Instance.PlayControl().GetLyric().Get().Lyrics))
for i := 0; i < len(lrcs); i++ {
lrcs[i] = controller.Instance.PlayControl().GetLyric().Get().Lyrics[i].Lyric
}
fullLrc := widget.NewRichTextWithText(strings.Join(lrcs, "\n\n"))
fullLrc.Scroll = container.ScrollVerticalOnly
fullLrc.Wrapping = fyne.TextWrapWord
fullLrc := container.NewVBox(createLyricObj(controller.Instance.PlayControl().GetLyric().Get())...)
lrcWindow := container.NewVScroll(fullLrc)
prevIndex := 0
w.SetContent(container.NewBorder(nil,
container.NewVBox(widget.NewSeparator(), currentLrc),
nil, nil,
fullLrc))
lrcWindow))
w.Resize(fyne.NewSize(360, 540))
w.CenterOnScreen()
@@ -38,7 +45,19 @@ func createLyricWindow() fyne.Window {
currentLrc.SetText("")
return
}
currentLrc.SetText(e.Lyric.Lyric)
fullLrc.Objects[prevIndex].(*widget.Label).TextStyle.Bold = false
fullLrc.Objects[prevIndex].Refresh()
fullLrc.Objects[e.Lyric.Index].(*widget.Label).TextStyle.Bold = true
fullLrc.Objects[e.Lyric.Index].Refresh()
prevIndex = e.Lyric.Index
currentLrc.SetText(e.Lyric.Now.Lyric)
lrcWindow.Scrolled(&fyne.ScrollEvent{
Scrolled: fyne.Delta{
DX: 0,
DY: lrcWindow.Offset.Y - float32(e.Lyric.Index-2)/float32(e.Lyric.Total)*lrcWindow.Content.Size().Height,
},
})
fullLrc.Refresh()
})
controller.Instance.PlayControl().GetLyric().EventManager().RegisterA(
model.EventLyricReload, "player.lyric.new_media", func(event *event.Event) {
@@ -47,11 +66,13 @@ func createLyricWindow() fyne.Window {
for i := 0; i < len(lrcs); i++ {
lrcs[i] = e.Lyrics.Lyrics[i].Lyric
}
fullLrc.Segments[0] = &widget.TextSegment{
Style: widget.RichTextStyleInline,
Text: strings.Join(lrcs, "\n\n"),
}
fullLrc.Refresh()
fullLrc.Objects = createLyricObj(e.Lyrics)
//fullLrc.SetText(strings.Join(lrcs, "\n"))
//fullLrc.Segments[0] = &widget.TextSegment{
// Style: widget.RichTextStyleInline,
// Text: strings.Join(lrcs, "\n\n"),
//}
lrcWindow.Refresh()
})
w.SetOnClosed(func() {

View File

@@ -49,7 +49,6 @@ func createPlaylist() fyne.CanvasObject {
UserPlaylist.Playlist = controller.Instance.Playlists().GetCurrent().Model().Copy()
UserPlaylist.List = widget.NewList(
func() int {
//debug.PrintStack()
//todo: @4
return UserPlaylist.Playlist.Size()
},

View File

@@ -3,6 +3,7 @@ package gui
import (
"AynaLivePlayer/common/i18n"
"AynaLivePlayer/controller"
"AynaLivePlayer/gui/component"
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
@@ -12,22 +13,22 @@ import (
"fyne.io/fyne/v2/widget"
)
type PlaylistManagerContainer struct {
type PlaylistsTab struct {
Playlists *widget.List
PlaylistMedia *widget.List
Index int
AddBtn *widget.Button
RemoveBtn *widget.Button
SetAsSystemBtn *widget.Button
RefreshBtn *widget.Button
SetAsSystemBtn *component.AsyncButton
RefreshBtn *component.AsyncButton
CurrentSystemPlaylist *widget.Label
}
func (p *PlaylistManagerContainer) UpdateCurrentSystemPlaylist() {
func (p *PlaylistsTab) UpdateCurrentSystemPlaylist() {
p.CurrentSystemPlaylist.SetText(i18n.T("gui.playlist.current") + controller.Instance.Playlists().GetDefault().Name())
}
var PlaylistManager = &PlaylistManagerContainer{}
var PlaylistManager = &PlaylistsTab{}
func createPlaylists() fyne.CanvasObject {
PlaylistManager.Playlists = widget.NewList(
@@ -92,19 +93,20 @@ func createPlaylists() fyne.CanvasObject {
}
func createPlaylistMedias() fyne.CanvasObject {
PlaylistManager.RefreshBtn = createAsyncButton(
widget.NewButtonWithIcon(i18n.T("gui.playlist.button.refresh"), theme.ViewRefreshIcon(), nil),
PlaylistManager.RefreshBtn = component.NewAsyncButtonWithIcon(
i18n.T("gui.playlist.button.refresh"), theme.ViewRefreshIcon(),
func() {
showDialogIfError(controller.Instance.Playlists().PreparePlaylistByIndex(PlaylistManager.Index))
PlaylistManager.PlaylistMedia.Refresh()
})
PlaylistManager.SetAsSystemBtn = createAsyncButton(
widget.NewButton(i18n.T("gui.playlist.button.set_as_system"), nil),
PlaylistManager.SetAsSystemBtn = component.NewAsyncButton(
i18n.T("gui.playlist.button.set_as_system"),
func() {
showDialogIfError(controller.Instance.Playlists().SetDefault(PlaylistManager.Index))
PlaylistManager.PlaylistMedia.Refresh()
PlaylistManager.UpdateCurrentSystemPlaylist()
})
PlaylistManager.CurrentSystemPlaylist = widget.NewLabel("Current: ")
PlaylistManager.UpdateCurrentSystemPlaylist()
PlaylistManager.PlaylistMedia = widget.NewList(

View File

@@ -1,8 +0,0 @@
package gui
import (
"AynaLivePlayer/resource"
"fyne.io/fyne/v2"
)
var ResEmptyImage = fyne.NewStaticResource("empty", resource.EmptyImage)

View File

@@ -3,33 +3,34 @@ package gui
import (
"AynaLivePlayer/common/i18n"
"AynaLivePlayer/controller"
"AynaLivePlayer/model"
"AynaLivePlayer/gui/component"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
)
var SearchBar = &struct {
Input *widget.Entry
Button *widget.Button
UseSource *widget.CheckGroup
Items []*model.Media
Input *component.Entry
Button *component.AsyncButton
UseSource *widget.Select
}{}
func createSearchBar() fyne.CanvasObject {
SearchBar.Input = widget.NewEntry()
SearchBar.Input = component.NewEntry()
SearchBar.Input.SetPlaceHolder(i18n.T("gui.search.placeholder"))
SearchBar.Button = widget.NewButton(i18n.T("gui.search.search"), nil)
SearchBar.Button.OnTapped = createAsyncOnTapped(SearchBar.Button, func() {
SearchBar.Input.OnKeyUp = func(key *fyne.KeyEvent) {
if key.Name == fyne.KeyReturn {
SearchBar.Button.OnTapped()
}
}
SearchBar.Button = component.NewAsyncButton(i18n.T("gui.search.search"), func() {
keyword := SearchBar.Input.Text
s := make([]string, len(SearchBar.UseSource.Selected))
copy(s, SearchBar.UseSource.Selected)
items := make([]*model.Media, 0)
for _, p := range s {
if r, err := controller.Instance.Provider().SearchWithProvider(keyword, p); err == nil {
items = append(items, r...)
}
pr := SearchBar.UseSource.Selected
l().Debugf("Search keyword: %s, provider: %s", keyword, pr)
items, err := controller.Instance.Provider().SearchWithProvider(keyword, pr)
if err != nil {
dialog.ShowError(err, MainWindow)
}
controller.ApplyUser(items, controller.SystemUser)
SearchResult.Items = items
@@ -38,14 +39,14 @@ func createSearchBar() fyne.CanvasObject {
s := make([]string, len(controller.Instance.Provider().GetPriority()))
copy(s, controller.Instance.Provider().GetPriority())
SearchBar.UseSource = widget.NewCheckGroup(s, nil)
SearchBar.UseSource.Horizontal = true
SearchBar.UseSource.SetSelected(s)
SearchBar.UseSource = widget.NewSelect(s, func(s string) {})
if len(s) > 0 {
SearchBar.UseSource.SetSelected(s[0])
}
searchInput := container.NewBorder(
nil, nil, widget.NewLabel(i18n.T("gui.search.search")), SearchBar.Button,
SearchBar.Input)
container.NewBorder(nil, nil, SearchBar.UseSource, nil, SearchBar.Input))
return container.NewVBox(
searchInput,
container.NewHBox(widget.NewLabel(i18n.T("gui.search.filter")), SearchBar.UseSource),
widget.NewSeparator())
}

43
gui/theme.go Normal file
View File

@@ -0,0 +1,43 @@
package gui
import (
"AynaLivePlayer/resource"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"image/color"
)
type myTheme struct{}
var _ fyne.Theme = (*myTheme)(nil)
// return bundled font resource
func (*myTheme) Font(s fyne.TextStyle) fyne.Resource {
l().Debugf("12313123")
if s.Monospace {
return resource.FontMSYaHei
}
if s.Bold {
if s.Italic {
//return theme.DefaultTheme().Font(s)
return resource.FontMSYaHeiBold
}
return resource.FontMSYaHei
}
if s.Italic {
return resource.FontMSYaHei
}
return resource.FontMSYaHei
}
func (*myTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
return theme.DefaultTheme().Color(n, v)
}
func (*myTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
return theme.DefaultTheme().Icon(n)
}
func (*myTheme) Size(n fyne.ThemeSizeName) float32 {
return theme.DefaultTheme().Size(n)
}

View File

@@ -34,7 +34,7 @@ type PlayEvent struct {
type LyricUpdateEvent struct {
Lyrics *Lyric
Time float64
Lyric *LyricLine
Lyric *LyricContext
}
type LyricReloadEvent struct {

View File

@@ -16,9 +16,11 @@ type LyricLine struct {
}
type LyricContext struct {
Current *LyricLine
Prev []*LyricLine
Next []*LyricLine
Now *LyricLine
Index int
Total int
Prev []*LyricLine
Next []*LyricLine
}
type Lyric struct {
@@ -55,30 +57,58 @@ func LoadLyric(lyric string) *Lyric {
return &Lyric{Lyrics: lrcs}
}
func (l *Lyric) Find(time float64) *LyricLine {
func (l *Lyric) findIndexV1(time float64) int {
for i := 0; i < len(l.Lyrics)-1; i++ {
if l.Lyrics[i].Time <= time && time < l.Lyrics[i+1].Time {
return l.Lyrics[i]
return i
}
}
return nil
return -1
}
func (l *Lyric) findIndex(time float64) int {
start := 0
end := len(l.Lyrics) - 1
mid := (start + end) / 2
for start < end {
if l.Lyrics[mid].Time <= time && time < l.Lyrics[mid+1].Time {
return mid
}
if l.Lyrics[mid].Time > time {
end = mid
} else {
start = mid
}
mid = (start + end) / 2
}
return -1
}
func (l *Lyric) Find(time float64) *LyricLine {
idx := l.findIndex(time)
if idx == -1 {
return nil
}
return l.Lyrics[idx]
}
func (l *Lyric) FindContext(time float64, prev int, next int) *LyricContext {
for i := 0; i < len(l.Lyrics)-1; i++ {
if l.Lyrics[i].Time <= time && time < l.Lyrics[i+1].Time {
if (i + prev) < 0 {
prev = -i
}
if (i + 1 + next) > len(l.Lyrics) {
next = len(l.Lyrics) - i - 1
}
return &LyricContext{
Current: l.Lyrics[i],
Prev: l.Lyrics[i+prev : i],
Next: l.Lyrics[i+1 : i+1+next],
}
}
prev = -prev
idx := l.findIndex(time)
if idx == -1 {
return nil
}
if (idx + prev) < 0 {
prev = -idx
}
if (idx + 1 + next) > len(l.Lyrics) {
next = len(l.Lyrics) - idx - 1
}
return &LyricContext{
Now: l.Lyrics[idx],
Index: idx,
Total: len(l.Lyrics),
Prev: l.Lyrics[idx+prev : idx],
Next: l.Lyrics[idx+1 : idx+1+next],
}
return nil
}

View File

@@ -2,6 +2,7 @@ package model
import (
"fmt"
"github.com/magiconair/properties/assert"
"testing"
)
@@ -21,3 +22,10 @@ func TestLyricFind(t *testing.T) {
fmt.Println(l)
}
}
func TestLyricFindV2(t *testing.T) {
lryic := LoadLyric(testLyric)
for i := 0.0; i < 170; i += 0.01 {
assert.Equal(t, lryic.FindV1(i), lryic.Find(i))
}
}

View File

@@ -260,7 +260,7 @@ func (t *TextInfo) registerHandlers() {
t.ctr.PlayControl().GetLyric().EventManager().RegisterA(
model.EventLyricUpdate, "plugin.textinfo.lyric", func(event *event.Event) {
lrcLine := event.Data.(model.LyricUpdateEvent).Lyric
t.info.Lyric = lrcLine.Lyric
t.info.Lyric = lrcLine.Now.Lyric
t.RenderTemplates()
})

View File

@@ -8,6 +8,7 @@ import (
"AynaLivePlayer/config"
"AynaLivePlayer/controller"
"AynaLivePlayer/gui"
"AynaLivePlayer/gui/component"
"AynaLivePlayer/model"
"fmt"
"fyne.io/fyne/v2"
@@ -142,7 +143,7 @@ func (t *WebInfo) registerHandlers() {
t.ctr.PlayControl().GetLyric().EventManager().RegisterA(
model.EventLyricUpdate, "plugin.webinfo.lyric", func(event *event.Event) {
lrcLine := event.Data.(model.LyricUpdateEvent).Lyric
t.server.Info.Lyric = lrcLine.Lyric
t.server.Info.Lyric = lrcLine.Now.Lyric
t.server.SendInfo(
OutInfoL,
OutInfo{Lyric: t.server.Info.Lyric},
@@ -185,7 +186,7 @@ func (w *WebInfo) CreatePanel() fyne.CanvasObject {
widget.NewLabel(i18n.T("plugin.webinfo.server_preview")),
serverUrl,
)
stopBtn := gui.NewAsyncButtonWithIcon(
stopBtn := component.NewAsyncButtonWithIcon(
i18n.T("plugin.webinfo.server_control.stop"),
theme.MediaStopIcon(),
func() {
@@ -201,7 +202,7 @@ func (w *WebInfo) CreatePanel() fyne.CanvasObject {
statusText.SetText(w.getServerStatusText())
},
)
startBtn := gui.NewAsyncButtonWithIcon(
startBtn := component.NewAsyncButtonWithIcon(
i18n.T("plugin.webinfo.server_control.start"),
theme.MediaPlayIcon(),
func() {
@@ -216,7 +217,7 @@ func (w *WebInfo) CreatePanel() fyne.CanvasObject {
_ = serverUrl.SetURLFromString(w.getServerUrl())
},
)
restartBtn := gui.NewAsyncButtonWithIcon(
restartBtn := component.NewAsyncButtonWithIcon(
i18n.T("plugin.webinfo.server_control.restart"),
theme.MediaReplayIcon(),
func() {

View File

@@ -5,7 +5,9 @@ import (
"AynaLivePlayer/common/logger"
"AynaLivePlayer/config"
"AynaLivePlayer/gui"
"AynaLivePlayer/provider"
"AynaLivePlayer/gui/component"
"AynaLivePlayer/repo/provider"
"AynaLivePlayer/resource"
"bytes"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
@@ -90,7 +92,7 @@ func (w *WYLogin) CreatePanel() fyne.CanvasObject {
widget.NewLabel(i18n.T("plugin.neteaselogin.current_user")),
currentUser)
refreshBtn := gui.NewAsyncButton(
refreshBtn := component.NewAsyncButton(
i18n.T("plugin.neteaselogin.refresh"),
func() {
provider.NeteaseAPI.UpdateStatus()
@@ -102,7 +104,7 @@ func (w *WYLogin) CreatePanel() fyne.CanvasObject {
},
)
logoutBtn := gui.NewAsyncButton(
logoutBtn := component.NewAsyncButton(
i18n.T("plugin.neteaselogin.logout"),
func() {
provider.NeteaseAPI.Logout()
@@ -110,13 +112,13 @@ func (w *WYLogin) CreatePanel() fyne.CanvasObject {
},
)
controlBtns := container.NewHBox(refreshBtn, logoutBtn)
qrcodeImg := canvas.NewImageFromResource(gui.ResEmptyImage)
qrcodeImg := canvas.NewImageFromResource(resource.ImageEmpty)
qrcodeImg.SetMinSize(fyne.NewSize(200, 200))
qrcodeImg.FillMode = canvas.ImageFillContain
var key string
qrStatus := widget.NewLabel("AAAAAAAA")
qrStatus.SetText("")
newQrBtn := gui.NewAsyncButton(
newQrBtn := component.NewAsyncButton(
i18n.T("plugin.neteaselogin.qr.new"),
func() {
qrStatus.SetText("")
@@ -138,7 +140,7 @@ func (w *WYLogin) CreatePanel() fyne.CanvasObject {
qrcodeImg.Refresh()
},
)
finishQrBtn := gui.NewAsyncButton(
finishQrBtn := component.NewAsyncButton(
i18n.T("plugin.neteaselogin.qr.finish"),
func() {
if key == "" {
@@ -149,7 +151,7 @@ func (w *WYLogin) CreatePanel() fyne.CanvasObject {
qrStatus.SetText(msg)
if ok {
key = ""
qrcodeImg.Resource = gui.ResEmptyImage
qrcodeImg.Resource = resource.ImageEmpty
qrcodeImg.Refresh()
}
},

View File

@@ -16,6 +16,16 @@ type Bilibili struct {
IdRegex1 *regexp.Regexp
}
func NewBilibili(config MediaProviderConfig) MediaProvider {
return &Bilibili{
InfoApi: "https://www.bilibili.com/audio/music-service-c/web/song/info?sid=%s",
FileApi: "https://api.bilibili.com/audio/music-service-c/url?device=phone&mid=8047632&mobi_app=iphone&platform=ios&privilege=2&songid=%s&quality=2",
SearchApi: "https://api.bilibili.com/audio/music-service-c/s?search_type=music&keyword=%s&page=1&pagesize=100",
IdRegex0: regexp.MustCompile("^[0-9]+"),
IdRegex1: regexp.MustCompile("^au[0-9]+"),
}
}
func _newBilibili() *Bilibili {
return &Bilibili{
InfoApi: "https://www.bilibili.com/audio/music-service-c/web/song/info?sid=%s",

View File

@@ -20,6 +20,22 @@ type BilibiliVideo struct {
header map[string]string
}
func NewBilibiliVideo(config MediaProviderConfig) MediaProvider {
return &BilibiliVideo{
InfoApi: "https://api.bilibili.com/x/web-interface/view/detail?bvid=%s&aid=&jsonp=jsonp",
FileApi: "https://api.bilibili.com/x/player/playurl?type=&otype=json&fourk=1&qn=32&avid=&bvid=%s&cid=%s",
SearchApi: "https://api.bilibili.com/x/web-interface/search/type?search_type=video&page=1&keyword=%s",
BVRegex: regexp.MustCompile("^BV[0-9A-Za-z]+"),
IdRegex: regexp.MustCompile("^BV[0-9A-Za-z]+(\\?p=[0-9]+)?"),
PageRegex: regexp.MustCompile("p=[0-9]+"),
header: map[string]string{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0",
"Referer": "https://www.bilibili.com/",
"Origin": "https://www.bilibili.com",
},
}
}
func _newBilibiliVideo() *BilibiliVideo {
return &BilibiliVideo{
InfoApi: "https://api.bilibili.com/x/web-interface/view/detail?bvid=%s&aid=&jsonp=jsonp",

View File

@@ -23,6 +23,22 @@ type Kuwo struct {
IdRegex1 *regexp.Regexp
}
func NewKuwo(config MediaProviderConfig) MediaProvider {
return &Kuwo{
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",
//FileApi: "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1",
FileApi: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_%s",
SearchCookie: "http://kuwo.cn/search/list?key=%s",
SearchApi: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=%s&pn=%d&rn=%d",
LyricApi: "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=%s",
PlaylistApi: "http://www.kuwo.cn/api/www/playlist/playListInfo?pid=%s&pn=%d&rn=%d&httpsStatus=1",
PlaylistRegex0: regexp.MustCompile("[0-9]+"),
PlaylistRegex1: regexp.MustCompile("playlist/[0-9]+"),
IdRegex0: regexp.MustCompile("^[0-9]+"),
IdRegex1: regexp.MustCompile("^kw[0-9]+"),
}
}
func _newKuwo() *Kuwo {
return &Kuwo{
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",

View File

@@ -17,6 +17,26 @@ type Local struct {
Playlists []*_LocalPlaylist
}
func NewLocalCtor(config MediaProviderConfig) MediaProvider {
localDir, ok := config["local_dir"]
if !ok {
localDir = "./local"
}
l := &Local{Playlists: make([]*_LocalPlaylist, 0), localDir: localDir}
if err := os.MkdirAll(localDir, 0755); err != nil {
return l
}
for _, n := range getPlaylistNames(localDir) {
l.Playlists = append(l.Playlists, &_LocalPlaylist{Name: n})
}
for i, _ := range l.Playlists {
_ = readLocalPlaylist(localDir, l.Playlists[i])
}
LocalAPI = l
Providers[LocalAPI.GetName()] = LocalAPI
return l
}
var LocalAPI *Local
func NewLocal(localdir string) *Local {

View File

@@ -20,6 +20,24 @@ type Netease struct {
loginStatus neteaseTypes.LoginStatusData
}
func NewNetease(config MediaProviderConfig) MediaProvider {
return &Netease{
PlaylistRegex0: regexp.MustCompile("^[0-9]+$"),
// https://music.163.com/playlist?id=2382819181&userid=95906480
PlaylistRegex1: regexp.MustCompile("playlist\\?id=[0-9]+"),
ReqData: neteaseUtil.RequestData{
Headers: neteaseUtil.Headers{
{
"X-Real-IP",
"118.88.88.88",
},
},
},
IdRegex0: regexp.MustCompile("^[0-9]+"),
IdRegex1: regexp.MustCompile("^wy[0-9]+"),
}
}
func _newNetease() *Netease {
return &Netease{
PlaylistRegex0: regexp.MustCompile("^[0-9]+$"),

View File

@@ -12,6 +12,9 @@ func l() *logrus.Entry {
return logger.Logger.WithField("Module", MODULE_CONTROLLER)
}
type MediaProviderConfig map[string]string
type MediaProviderCtor func(config MediaProviderConfig) MediaProvider
type MediaProvider interface {
GetName() string
MatchMedia(keyword string) *model.Media
@@ -23,6 +26,14 @@ type MediaProvider interface {
UpdateMediaLyric(media *model.Media) error
}
var RegisteredProviders = map[string]MediaProviderCtor{
"netease": NewNetease,
"local": NewLocalCtor,
"kuwo": NewKuwo,
"bilibili": NewBilibili,
"bilibili-video": NewBilibiliVideo,
}
var Providers map[string]MediaProvider = make(map[string]MediaProvider)
func GetPlaylist(meta *model.Meta) ([]*model.Media, error) {

27
resource/bundle.go Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,20 +1,20 @@
package resource
import (
"AynaLivePlayer/config"
"io/ioutil"
)
//var ProgramIcon = []byte{}
//var EmptyImage = []byte{}
//
//func init() {
// loadResource(config.GetAssetPath("icon.jpg"), &ProgramIcon)
// loadResource(config.GetAssetPath("empty.png"), &EmptyImage)
//}
//
//func loadResource(path string, res *[]byte) {
// if file, err := ioutil.ReadFile(path); err == nil {
// *res = file
// }
//}
var ProgramIcon = []byte{}
var EmptyImage = []byte{}
func init() {
loadResource(config.GetAssetPath("icon.jpg"), &ProgramIcon)
loadResource(config.GetAssetPath("empty.png"), &EmptyImage)
}
func loadResource(path string, res *[]byte) {
if file, err := ioutil.ReadFile(path); err == nil {
*res = file
}
}
var ImageEmpty = resImageEmpty
var ImageIcon = resImageIcon
var FontMSYaHei = resFontMSYaHei
var FontMSYaHeiBold = resFontMSYaHeiBold

View File

@@ -13,6 +13,8 @@ beta
----
Finished
- 2022.12.24 : 修复拖动进度条时产生的噪音
- 2022.12.23 : 重写controller部分修改search界面添加歌词滚动效果部分资源添加到bundle
- 2022.8.24 : 本地搜索优化/牌子点歌
- 2022.8.19@0.9.2: 修复歌词不更新/修复web自定义页面空白
- 2022.7.21 : 修复本地歌单的bug/fix webinfo can't apply media-cover css/修复切换歌单时不自动刷新