Merge 1.0.x branch (#8)

* rewrite

* update submodule

* make width height configurable

* update dependency

* update

* update file

* update dep

* fix basic config layout

* update plugin management

* more stuff

* add blacklist

* fix todo

* fix windows gethandle

* update windows update guide

* update windows build guide

* include go mod tidy in script

* update todo

* fix source session

* fix text output

* add plugin play duration control

* fix id diange not working

* update todo

* update version number
This commit is contained in:
Aynakeya
2024-04-22 21:21:02 -07:00
committed by GitHub
parent 8d73a3c284
commit 5cc5948a85
184 changed files with 6772 additions and 7420 deletions

View File

@@ -1,9 +0,0 @@
package adapter
import "AynaLivePlayer/core/model"
type IApplication interface {
Version() model.VersionInfo
LatestVersion() model.VersionInfo
CheckUpdate() error
}

View File

@@ -1,31 +0,0 @@
package adapter
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/model"
)
type LiveClientCtor func(id string, ev *event.Manager, log ILogger) (LiveClient, error)
type LiveClient interface {
ClientName() string
RoomName() string
Connect() bool
Disconnect() bool
Status() bool
EventManager() *event.Manager
}
type ILiveRoom interface {
Client() LiveClient
Model() *model.LiveRoom // should return mutable model (not a copy)
Identifier() string
DisplayName() string
Status() bool
EventManager() *event.Manager
}
type LiveRoomExecutor interface {
Match(command string) bool
Execute(command string, args []string, danmu *model.DanmuMessage)
}

View File

@@ -1,23 +0,0 @@
package adapter
type LogLevel uint32
const (
LogLevelError LogLevel = iota
LogLevelWarn
LogLevelInfo
LogLevelDebug
)
type ILogger interface {
Debug(args ...interface{})
Debugf(format string, args ...interface{})
Info(args ...interface{})
Infof(format string, args ...interface{})
Warn(args ...interface{})
Warnf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
WithModule(prefix string) ILogger
SetLogLevel(level LogLevel)
}

View File

@@ -1,40 +0,0 @@
package adapter
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/model"
)
type PlayerCtor func(ev *event.Manager, log ILogger) IPlayer
type IPlayer interface {
// Start the player
Start()
// Stop the player
Stop()
// Play play a media
Play(media *model.Media) error
// GetPlaying get playing media
// if player is idle, return nil
GetPlaying() *model.Media
// IsPaused return true if player is paused
IsPaused() bool
// Pause pause player
Pause() error
// Unpause unpause player
Unpause() error
// SetVolume set volume
SetVolume(volume float64) error
// IsIdle return true if player is playing anything
IsIdle() bool
// Seek to position, if absolute is true, position is absolute time, otherwise position is relative time
Seek(position float64, absolute bool) error
// SetWindowHandle set window handle for video output
SetWindowHandle(handle uintptr) error
// ObserveProperty observe player property change
ObserveProperty(property model.PlayerProperty, name string, handler event.HandlerFunc) error
// GetAudioDeviceList get audio device list
GetAudioDeviceList() ([]model.AudioDevice, error)
// SetAudioDevice set audio device
SetAudioDevice(device string) error
}

View File

@@ -1,22 +0,0 @@
package adapter
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/model"
)
type IPlaylist interface {
Identifier() string // must unique for each playlist
Model() *model.Playlist // mutable model (not a copy)
EventManager() *event.Manager
DisplayName() string
Size() int
Get(index int) *model.Media
Pop() *model.Media
Replace(medias []*model.Media)
Push(media *model.Media)
Insert(index int, media *model.Media)
Delete(index int) *model.Media
Move(src int, dst int)
Next() *model.Media
}

View File

@@ -1,17 +0,0 @@
package adapter
import "AynaLivePlayer/core/model"
type MediaProviderConfig map[string]string
type MediaProviderCtor func(config MediaProviderConfig) MediaProvider
type MediaProvider interface {
GetName() string
MatchMedia(keyword string) *model.Media
GetPlaylist(playlist *model.Meta) ([]*model.Media, error)
FormatPlaylistUrl(uri string) string
Search(keyword string) ([]*model.Media, error)
UpdateMedia(media *model.Media) error
UpdateMediaUrl(media *model.Media) error
UpdateMediaLyric(media *model.Media) error
}

View File

@@ -1,91 +0,0 @@
package adapter
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/model"
)
// IControlBridge is the interface for all controller and
// all system use cases.
type IControlBridge interface {
App() IApplication
LiveRooms() ILiveRoomController
PlayControl() IPlayController
Playlists() IPlaylistController
Provider() IProviderController
Plugin() IPluginController
LoadPlugins(plugins ...Plugin)
Logger() ILogger
CloseAndSave()
}
type ILiveRoomController interface {
Size() int
Get(index int) ILiveRoom
GetRoomStatus(index int) bool
Connect(index int) error
Disconnect(index int) error
AddRoom(clientName, roomId string) (*model.LiveRoom, error)
DeleteRoom(index int) error
AddDanmuCommand(executor LiveRoomExecutor)
GetAllClientNames() []string
}
type IProviderController interface {
GetPriority() []string
PrepareMedia(media *model.Media) error
MediaMatch(keyword string) *model.Media
Search(keyword string) ([]*model.Media, error)
SearchWithProvider(keyword string, provider string) ([]*model.Media, error)
PreparePlaylist(playlist IPlaylist) error
}
type IPluginController interface {
LoadPlugin(plugin Plugin)
LoadPlugins(plugins ...Plugin)
ClosePlugins()
}
type IPlaylistController interface {
Size() int
GetHistory() IPlaylist
AddToHistory(media *model.Media)
GetDefault() IPlaylist
GetCurrent() IPlaylist
Get(index int) IPlaylist
Add(pname string, uri string) (IPlaylist, error)
Remove(index int) (IPlaylist, error)
SetDefault(index int) error
PreparePlaylistByIndex(index int) error
}
type IPlayControlConfig struct {
SkipPlaylist bool
AutoNextWhenFail bool
}
type IPlayController interface {
EventManager() *event.Manager
GetPlaying() *model.Media
GetPlayer() IPlayer
PlayNext()
Play(media *model.Media) error
Add(keyword string, user interface{})
AddWithProvider(keyword string, provider string, user interface{})
Seek(position float64, absolute bool)
Toggle() bool
SetVolume(volume float64)
Destroy()
GetCurrentAudioDevice() string
GetAudioDevices() []model.AudioDevice
SetAudioDevice(device string)
GetLyric() ILyricLoader
Config() *IPlayControlConfig
}
type ILyricLoader interface {
EventManager() *event.Manager
Get() *model.Lyric
Reload(lyric string)
Update(time float64)
}

View File

@@ -1,54 +1,56 @@
package events
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/model"
)
//const (
// EventPlay event.EventId = "player.play"
// EventPlayed event.EventId = "player.played"
// EventPlaylistPreInsert event.EventId = "playlist.insert.pre"
// EventPlaylistInsert event.EventId = "playlist.insert.after"
// EventPlaylistUpdate event.EventId = "playlist.update"
// EventLyricUpdate event.EventId = "lyric.update"
// EventLyricReload event.EventId = "lyric.reload"
//)
const (
EventPlay event.EventId = "player.play"
EventPlayed event.EventId = "player.played"
EventPlaylistPreInsert event.EventId = "playlist.insert.pre"
EventPlaylistInsert event.EventId = "playlist.insert.after"
EventPlaylistUpdate event.EventId = "playlist.update"
EventLyricUpdate event.EventId = "lyric.update"
EventLyricReload event.EventId = "lyric.reload"
)
const ErrorUpdate = "update.error"
func EventPlayerPropertyUpdate(property model.PlayerProperty) event.EventId {
return event.EventId("player.property.update." + string(property))
type ErrorUpdateEvent struct {
Error error
}
type PlaylistInsertEvent struct {
Playlist *model.Playlist
Index int
Media *model.Media
}
type PlaylistUpdateEvent struct {
Playlist *model.Playlist // Playlist is a copy of the playlist
}
type PlayEvent struct {
Media *model.Media
}
type LyricUpdateEvent struct {
Lyrics *model.Lyric
Time float64
Lyric *model.LyricContext
}
type LyricReloadEvent struct {
Lyrics *model.Lyric
}
type PlayerPropertyUpdateEvent struct {
Property model.PlayerProperty
Value model.PlayerPropertyValue
}
type LiveRoomStatusUpdateEvent struct {
RoomTitle string
Status bool
}
//
//func EventPlayerPropertyUpdate(property model.PlayerProperty) event.EventId {
// return event.EventId("player.property.update." + string(property))
//}
//
//type PlaylistInsertEvent struct {
// Playlist *model.Playlist
// Index int
// Media *model.Media
//}
//
//type PlaylistUpdateEvent struct {
// Playlist *model.Playlist // Playlist is a copy of the playlist
//}
//
//type PlayEvent struct {
// Media *model.Media
//}
//
//type LyricUpdateEvent struct {
// Lyrics *model.Lyric
// Time float64
// Lyric *model.LyricContext
//}
//
//type LyricReloadEvent struct {
// Lyrics *model.Lyric
//}
//
//type PlayerPropertyUpdateEvent struct {
// Property model.PlayerProperty
// Value model.PlayerPropertyValue
//}
//
//type LiveRoomStatusUpdateEvent struct {
// RoomTitle string
// Status bool
//}

View File

@@ -1,16 +1,73 @@
package events
import (
"AynaLivePlayer/common/event"
"AynaLivePlayer/core/adapter"
"AynaLivePlayer/core/model"
liveroomsdk "github.com/AynaLivePlayer/liveroom-sdk"
)
const (
LiveRoomStatusChange event.EventId = "liveclient.status.change"
LiveRoomMessageReceive event.EventId = "liveclient.message.receive"
)
//const (
// LiveRoomStatusChange event.EventId = "liveclient.status.change"
// LiveRoomMessageReceive event.EventId = "liveclient.message.receive"
//)
//
//type StatusChangeEvent struct {
// Connected bool
// Client adapter.LiveClient
//}
type StatusChangeEvent struct {
Connected bool
Client adapter.LiveClient
const LiveRoomAddCmd = "cmd.liveroom.add"
type LiveRoomAddCmdEvent struct {
Title string
Provider string
RoomKey string
}
const LiveRoomProviderUpdate = "update.liveroom.provider"
type LiveRoomProviderUpdateEvent struct {
Providers []model.LiveRoomProviderInfo
}
const LiveRoomRemoveCmd = "cmd.liveroom.remove"
type LiveRoomRemoveCmdEvent struct {
Identifier string
}
const LiveRoomRoomsUpdate = "update.liveroom.rooms"
type LiveRoomRoomsUpdateEvent struct {
Rooms []model.LiveRoom
}
const LiveRoomStatusUpdate = "update.liveroom.status"
type LiveRoomStatusUpdateEvent struct {
Room model.LiveRoom
}
const LiveRoomConfigChangeCmd = "cmd.liveroom.config.change"
type LiveRoomConfigChangeCmdEvent struct {
Identifier string
Config model.LiveRoomConfig
}
const LiveRoomOperationCmd = "cmd.liveroom.operation"
type LiveRoomOperationCmdEvent struct {
Identifier string
SetConnect bool // connect or disconnect
}
const LiveRoomOperationFinish = "update.liveroom.operation"
type LiveRoomOperationFinishEvent struct {
}
const LiveRoomMessageReceive = "update.liveroom.message"
type LiveRoomMessageReceiveEvent struct {
Message *liveroomsdk.Message
}

View File

@@ -0,0 +1,37 @@
package events
import (
"AynaLivePlayer/core/model"
)
const PlayerVolumeChangeCmd = "cmd.player.op.change_volume"
type PlayerVolumeChangeCmdEvent struct {
Volume float64 // Volume from 0-100
}
const PlayerPlayCmd = "cmd.player.op.play"
type PlayerPlayCmdEvent struct {
Media model.Media
}
const PlayerSeekCmd = "cmd.player.op.seek"
type PlayerSeekCmdEvent struct {
Position float64
// Absolute is the seek mode.
// if absolute = true : position is the time in second
// if absolute = false: position is in percentage eg 0.1 0.2
Absolute bool
}
const PlayerToggleCmd = "cmd.player.op.toggle"
type PlayerToggleCmdEvent struct {
}
const PlayerPlayNextCmd = "cmd.player.op.next"
type PlayerPlayNextCmdEvent struct {
}

View File

@@ -0,0 +1,23 @@
package events
import "github.com/AynaLivePlayer/miaosic"
const PlayerLyricRequestCmd = "cmd.player.lyric.request"
type PlayerLyricRequestCmdEvent struct {
}
const PlayerLyricReload = "update.player.lyric.reload"
type PlayerLyricReloadEvent struct {
Lyrics miaosic.Lyrics
}
const PlayerLyricPosUpdate = "update.player.lyric.pos"
type PlayerLyricPosUpdateEvent struct {
Time float64
CurrentIndex int // -1 means no lyric
CurrentLine miaosic.LyricLine
Total int // total lyric count
}

View File

@@ -0,0 +1,46 @@
package events
import "AynaLivePlayer/core/model"
const PlayerPlayingUpdate = "update.player.playing"
type PlayerPlayingUpdateEvent struct {
Media model.Media
Removed bool // if no media is playing, removed is true
}
const PlayerPropertyPauseUpdate = "update.player.property.pause"
type PlayerPropertyPauseUpdateEvent struct {
Paused bool
}
const PlayerPropertyPercentPosUpdate = "update.player.property.percent_pos"
type PlayerPropertyPercentPosUpdateEvent struct {
PercentPos float64
}
const PlayerPropertyIdleActiveUpdate = "update.player.property.idle_active"
type PlayerPropertyIdleActiveUpdateEvent struct {
IsIdle bool
}
const PlayerPropertyTimePosUpdate = "update.player.property.time_pos"
type PlayerPropertyTimePosUpdateEvent struct {
TimePos float64 // Time in seconds
}
const PlayerPropertyDurationUpdate = "update.player.property.duration"
type PlayerPropertyDurationUpdateEvent struct {
Duration float64 // Duration in seconds
}
const PlayerPropertyVolumeUpdate = "update.player.property.volume"
type PlayerPropertyVolumeUpdateEvent struct {
Volume float64 // Volume from 0-100
}

View File

@@ -0,0 +1,22 @@
package events
import "AynaLivePlayer/core/model"
const PlayerVideoPlayerSetWindowHandleCmd = "cmd.player.videoplayer.set_window_handle"
type PlayerVideoPlayerSetWindowHandleCmdEvent struct {
Handle uintptr
}
const PlayerSetAudioDeviceCmd = "cmd.player.set_audio_device"
type PlayerSetAudioDeviceCmdEvent struct {
Device string
}
const PlayerAudioDeviceUpdate = "update.player.audio_device"
type PlayerAudioDeviceUpdateEvent struct {
Current string
Devices []model.AudioDevice
}

81
core/events/playlist.go Normal file
View File

@@ -0,0 +1,81 @@
package events
import (
"AynaLivePlayer/core/model"
"AynaLivePlayer/pkg/event"
)
func PlaylistDetailUpdate(id model.PlaylistID) event.EventId {
return event.EventId("update.playlist.detail." + id)
}
type PlaylistDetailUpdateEvent struct {
Medias []model.Media
}
func PlaylistMoveCmd(id model.PlaylistID) event.EventId {
return event.EventId("cmd.playlist.move." + id)
}
type PlaylistMoveCmdEvent struct {
From int
To int
}
func PlaylistDeleteCmd(id model.PlaylistID) event.EventId {
return event.EventId("cmd.playlist.delete." + id)
}
type PlaylistDeleteCmdEvent struct {
Index int
}
func PlaylistInsertCmd(id model.PlaylistID) event.EventId {
return event.EventId("cmd.playlist.insert." + id)
}
type PlaylistInsertCmdEvent struct {
Position int // position to insert, -1 means last one
Media model.Media
}
func PlaylistInsertUpdate(id model.PlaylistID) event.EventId {
return event.EventId("update.playlist.insert." + id)
}
type PlaylistInsertUpdateEvent struct {
Position int // position to insert, -1 means last one
Media model.Media
}
func PlaylistNextCmd(id model.PlaylistID) event.EventId {
return event.EventId("cmd.playlist.next." + id)
}
type PlaylistNextCmdEvent struct {
Remove bool // remove the media after next
}
func PlaylistNextUpdate(id model.PlaylistID) event.EventId {
return event.EventId("update.playlist.next." + id)
}
type PlaylistNextUpdateEvent struct {
Media model.Media
}
func PlaylistModeChangeCmd(id model.PlaylistID) event.EventId {
return event.EventId("cmd.playlist.mode." + id)
}
type PlaylistModeChangeCmdEvent struct {
Mode model.PlaylistMode
}
func PlaylistModeChangeUpdate(id model.PlaylistID) event.EventId {
return event.EventId("update.playlist.mode." + id)
}
type PlaylistModeChangeUpdateEvent struct {
Mode model.PlaylistMode
}

54
core/events/playlists.go Normal file
View File

@@ -0,0 +1,54 @@
package events
import (
"AynaLivePlayer/core/model"
)
const PlaylistManagerSetSystemCmd = "cmd.playlist.manager.set.system"
type PlaylistManagerSetSystemCmdEvent struct {
PlaylistID string
}
const PlaylistManagerSystemUpdate = "update.playlist.manager.system"
type PlaylistManagerSystemUpdateEvent struct {
Info model.PlaylistInfo
}
const PlaylistManagerRefreshCurrentCmd = "cmd.playlist.manager.refresh.current"
type PlaylistManagerRefreshCurrentCmdEvent struct {
PlaylistID string
}
const PlaylistManagerGetCurrentCmd = "cmd.playlist.manager.get.current"
type PlaylistManagerGetCurrentCmdEvent struct {
PlaylistID string
}
const PlaylistManagerCurrentUpdate = "update.playlist.manager.current"
type PlaylistManagerCurrentUpdateEvent struct {
Medias []model.Media
}
const PlaylistManagerInfoUpdate = "update.playlist.manager.info"
type PlaylistManagerInfoUpdateEvent struct {
Playlists []model.PlaylistInfo
}
const PlaylistManagerAddPlaylistCmd = "cmd.playlist.manager.add"
type PlaylistManagerAddPlaylistCmdEvent struct {
Provider string
URL string
}
const PlaylistManagerRemovePlaylistCmd = "cmd.playlist.manager.remove"
type PlaylistManagerRemovePlaylistCmdEvent struct {
PlaylistID string
}

7
core/events/provider.go Normal file
View File

@@ -0,0 +1,7 @@
package events
const MediaProviderUpdate = "update.media.provider.update"
type MediaProviderUpdateEvent struct {
Providers []string
}

18
core/events/search.go Normal file
View File

@@ -0,0 +1,18 @@
package events
import (
"AynaLivePlayer/core/model"
)
const SearchCmd = "cmd.search"
type SearchCmdEvent struct {
Keyword string
Provider string
}
const SearchResultUpdate = "update.search_result"
type SearchResultUpdateEvent struct {
Medias []model.Media
}

View File

@@ -1,40 +1,26 @@
package model
import (
"fmt"
)
import "github.com/AynaLivePlayer/liveroom-sdk"
type LiveRoomConfig struct {
AutoConnect bool `json:"auto_connect"`
}
type LiveRoom struct {
ClientName string
ID string
Title string
AutoConnect bool
AutoReconnect bool
LiveRoom liveroom.LiveRoom `json:"live_room"`
Config LiveRoomConfig `json:"config"`
Title string `json:"title"`
Status bool `json:"-"`
}
func (r *LiveRoom) String() string {
return fmt.Sprintf("<LiveRooms %s:%s>", r.ClientName, r.ID)
func (r *LiveRoom) DisplayName() string {
if r.Title != "" {
return r.Title
}
return r.LiveRoom.Identifier()
}
func (r *LiveRoom) Identifier() string {
return fmt.Sprintf("%s_%s", r.ClientName, r.ID)
}
type UserMedal struct {
Name string
Level int
RoomID string
}
type DanmuUser struct {
Uid string
Username string
Medal UserMedal
Admin bool
Privilege int
}
type DanmuMessage struct {
User DanmuUser
Message string
type LiveRoomProviderInfo struct {
Name string
Description string
}

View File

@@ -1,117 +0,0 @@
package model
import (
"github.com/spf13/cast"
"regexp"
"sort"
"strings"
)
var timeTagRegex = regexp.MustCompile("\\[[0-9]+:[0-9]+(\\.[0-9]+)?\\]")
type LyricLine struct {
Time float64 // in seconds
Lyric string
Translation string
}
type LyricContext struct {
Now *LyricLine
Index int
Total int
Prev []*LyricLine
Next []*LyricLine
}
type Lyric struct {
Lyrics []*LyricLine
}
func LoadLyric(lyric string) *Lyric {
tmp := make(map[float64]*LyricLine)
times := make([]float64, 0)
for _, line := range strings.Split(lyric, "\n") {
lrc := timeTagRegex.ReplaceAllString(line, "")
if len(lrc) > 0 && lrc[len(lrc)-1] == '\r' {
lrc = lrc[:len(lrc)-1]
}
for _, time := range timeTagRegex.FindAllString(line, -1) {
ts := strings.Split(time[1:len(time)-1], ":")
t := cast.ToFloat64(ts[0])*60 + cast.ToFloat64(ts[1])
times = append(times, t)
tmp[t] = &LyricLine{
Time: t,
Lyric: lrc,
}
}
}
sort.Float64s(times)
lrcs := make([]*LyricLine, len(times))
for index, time := range times {
lrcs[index] = tmp[time]
}
if len(lrcs) == 0 {
lrcs = append(lrcs, &LyricLine{Time: 0, Lyric: ""})
}
lrcs = append(lrcs, &LyricLine{
Time: 99999999999,
Lyric: "",
})
return &Lyric{Lyrics: lrcs}
}
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 i
}
}
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 {
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],
}
}

View File

@@ -1,31 +0,0 @@
package model
import (
"fmt"
"github.com/magiconair/properties/assert"
"testing"
)
var testLyric = "[ti:双截棍]\n[ar:周杰伦]\n[al:范特西]\n[00:03.85]双截棍\n[00:07.14]\n[00:30.13]岩烧店的烟味弥漫隔壁是国术馆\n[00:32.57]店里面的妈妈桑茶道有三段\n[00:34.61]教拳脚武术的老板练铁沙掌耍杨家枪\n[00:37.34]硬底子功夫最擅长还会金钟罩铁步衫\n[00:39.67]他们儿子我习惯从小就耳濡目染\n[00:41.96]什么刀枪跟棍棒我都耍的有模有样\n[00:44.22]什么兵器最喜欢双截棍柔中带刚\n[00:46.73]想要去河南嵩山学少林跟武当\n[00:49.24]干什么(客)干什么(客)呼吸吐纳心自在\n[00:51.28]干什么(客)干什么(客)气沉丹田手心开\n[00:53.44]干什么(客)干什么(客)日行千里系沙袋\n[00:56.13]飞檐走壁莫奇怪去去就来\n[00:58.35]一个马步向前一记左钩拳右钩拳\n[01:01.26]一句惹毛我的人有危险一再重演\n[01:04.02]一根我不抽的菸一放好多年它一直在身边\n[01:07.28]干什么(客)干什么(客)我打开任督二脉\n[01:10.27]干什么(客)干什么(客)东亚病夫的招牌\n[01:12.75]干什么(客)干什么(客)已被我一脚踢开\n[02:32.62][01:54.69][01:15.40]快使用双截棍哼哼哈兮\n[02:34.52][01:56.63][01:18.40]快使用双截棍哼哼哈兮\n[02:36.88][01:58.98][01:20.71]习武之人切记仁者无敌\n[02:39.45][02:01.66][01:23.27]是谁在练太极风生水起\n[02:41.97][02:03.93][01:25.74]快使用双截棍哼哼哈兮\n[02:44.42][02:06.11][01:27.75]快使用双截棍哼哼哈兮\n[02:47.01][02:08.54][01:30.13]如果我有轻功飞檐走壁\n[02:49.36][02:11.03][01:32.67]为人耿直不屈一身正气\n[02:53.81]快使用双截棍哼\n[02:56.30]我用手刀防御哼\n[02:58.52]漂亮的回旋踢\n[02:59.52]"
func TestLyric(t *testing.T) {
lryic := LoadLyric(testLyric)
for _, lrc := range lryic.Lyrics {
fmt.Println(lrc)
}
}
func TestLyricFind(t *testing.T) {
lryic := LoadLyric(testLyric)
fmt.Println(lryic.Find(90.4))
for _, l := range lryic.FindContext(90.4, -2, 2).Next {
fmt.Println(l)
}
}
func TestLyricFindV2(t *testing.T) {
lryic := LoadLyric(testLyric)
for i := 0.0; i < 170; i += 0.01 {
assert.Equal(t, lryic.Find(i), lryic.Find(i))
}
}

View File

@@ -1,46 +1,37 @@
package model
import (
"github.com/jinzhu/copier"
"github.com/AynaLivePlayer/liveroom-sdk"
"github.com/AynaLivePlayer/miaosic"
)
type Picture struct {
Url string
Data []byte
type User struct {
Name string
}
func (p Picture) Exists() bool {
return p.Url != "" || p.Data != nil
}
var PlaylistUser = User{Name: "Playlists"}
var SystemUser = User{Name: "System"}
type Media struct {
Title string
Artist string
Cover Picture
Album string
Lyric string
Url string
Header map[string]string
User interface{}
Meta interface{}
Info miaosic.MediaInfo
User interface{}
}
func (m *Media) ToUser() *User {
if u, ok := m.User.(*User); ok {
func (m *Media) IsLiveRoomUser() bool {
_, ok := m.User.(liveroom.User)
return ok
}
func (m *Media) ToUser() User {
if u, ok := m.User.(User); ok {
return u
}
return &User{Name: m.DanmuUser().Username}
return User{Name: m.DanmuUser().Username}
}
func (m *Media) DanmuUser() *DanmuUser {
if u, ok := m.User.(*DanmuUser); ok {
func (m *Media) DanmuUser() liveroom.User {
if u, ok := m.User.(liveroom.User); ok {
return u
}
return nil
}
func (m *Media) Copy() *Media {
newMedia := &Media{}
copier.Copy(newMedia, m)
return newMedia
return liveroom.User{}
}

View File

@@ -1,30 +0,0 @@
package model
import (
"fmt"
"testing"
)
type A struct {
A string
}
type B struct {
B string
}
func TestStruct(t *testing.T) {
var x interface{} = &A{A: "123"}
y, ok := x.(*A)
fmt.Println(y, ok)
z, ok := x.(*B)
fmt.Println(z, ok)
}
func TestMedia_Copy(t *testing.T) {
m := &Media{Title: "asdf", User: &User{Name: "123"}}
m2 := m.Copy()
fmt.Println(m, m2)
m2.User.(*User).Name = "456"
fmt.Println(m.User.(*User).Name, m2)
}

View File

@@ -4,15 +4,3 @@ type AudioDevice struct {
Name string
Description string
}
type PlayerPropertyValue any
type PlayerProperty string
const (
PlayerPropIdleActive PlayerProperty = "idle-active"
PlayerPropTimePos PlayerProperty = "time-pos"
PlayerPropDuration PlayerProperty = "duration"
PlayerPropPercentPos PlayerProperty = "percent-pos"
PlayerPropPause PlayerProperty = "pause"
PlayerPropVolume PlayerProperty = "volume"
)

View File

@@ -1,6 +1,6 @@
package model
import "fmt"
import "github.com/AynaLivePlayer/miaosic"
type PlaylistMode int
@@ -10,28 +10,22 @@ const (
PlaylistModeRepeat
)
type Playlist struct {
Title string // can be same, display name
Medias []*Media
Mode PlaylistMode
Meta Meta
type PlaylistID string
const (
PlaylistIDPlayer PlaylistID = "player"
PlaylistIDSystem PlaylistID = "system"
PlaylistIDHistory PlaylistID = "history"
)
type PlaylistInfo struct {
Meta miaosic.MetaData
Title string
}
func (p Playlist) String() string {
return fmt.Sprintf("<Playlist %s len:%d>", p.Title, len(p.Medias))
}
func (p *Playlist) Size() int {
return len(p.Medias)
}
func (p *Playlist) Copy() *Playlist {
medias := make([]*Media, len(p.Medias))
copy(medias, p.Medias)
return &Playlist{
Title: p.Title,
Medias: medias,
Mode: p.Mode,
Meta: p.Meta,
func (p PlaylistInfo) DisplayName() string {
if p.Title != "" {
return p.Title
}
return p.Meta.ID()
}

View File

@@ -1,4 +1,4 @@
package adapter
package model
type Plugin interface {
Name() string

View File

@@ -1,16 +0,0 @@
package model
import "fmt"
type Meta struct {
Name string
Id string
}
func (m Meta) String() string {
return fmt.Sprintf("<Meta %s:%s>", m.Name, m.Id)
}
func (m Meta) Identifier() string {
return fmt.Sprintf("%s_%s", m.Name, m.Id)
}

View File

@@ -1,17 +0,0 @@
package model
type User struct {
Name string
}
func ApplyUser(medias []*Media, user interface{}) {
for _, m := range medias {
m.User = user
}
}
func ToSpMedia(media *Media, user *User) *Media {
media = media.Copy()
media.User = user
return media
}