mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2026-04-15 21:53:27 +08:00
重写controller部分,修改search界面,添加歌词滚动效果,部分资源添加到bundle,修复拖动进度条时产生的噪音
This commit is contained in:
29
Makefile
29
Makefile
@@ -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}
|
||||
@@ -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
BIN
assets/msyhbd.ttc
Normal file
Binary file not shown.
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"AynaLivePlayer/controller"
|
||||
"AynaLivePlayer/model"
|
||||
"AynaLivePlayer/player"
|
||||
"AynaLivePlayer/provider"
|
||||
"AynaLivePlayer/repo/provider"
|
||||
)
|
||||
|
||||
type PlayController struct {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
2
go.mod
@@ -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
19
go.sum
@@ -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
43
gui/component/button.go
Normal 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
32
gui/component/entry.go
Normal 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
38
gui/component/slider.go
Normal 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
156
gui/component/split.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
41
gui/gui.go
41
gui/gui.go
@@ -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
56
gui/gutil/resize.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
15
gui/interface.go
Normal 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...)
|
||||
}
|
||||
@@ -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()
|
||||
})
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
|
||||
@@ -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(
|
||||
@@ -1,8 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/resource"
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
var ResEmptyImage = fyne.NewStaticResource("empty", resource.EmptyImage)
|
||||
@@ -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
43
gui/theme.go
Normal 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)
|
||||
}
|
||||
@@ -34,7 +34,7 @@ type PlayEvent struct {
|
||||
type LyricUpdateEvent struct {
|
||||
Lyrics *Lyric
|
||||
Time float64
|
||||
Lyric *LyricLine
|
||||
Lyric *LyricContext
|
||||
}
|
||||
|
||||
type LyricReloadEvent struct {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
@@ -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 {
|
||||
@@ -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]+$"),
|
||||
@@ -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
27
resource/bundle.go
Normal file
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user