refactored many events to use wails bindings, reducing boilerplate
This commit is contained in:
+19
-7
@@ -103,11 +103,22 @@ func NewYellowJacketApp(
|
||||
yjApp.logger, yjApp.database, yjApp.appConfig,
|
||||
)
|
||||
|
||||
// create queue (before wails.Run so it can be bound)
|
||||
yjApp.queue = queue.NewQueue(yjApp.logger, yjApp.database)
|
||||
|
||||
// create player (before wails.Run so it can be bound;
|
||||
// speaker hardware is initialized later in OnStartup)
|
||||
yjApp.player = player.NewPlayer(
|
||||
yjApp.logger.WithGroup("player"), yjApp.database,
|
||||
)
|
||||
|
||||
yjApp.FEBindings = []any{
|
||||
yjApp.FrontendUtil,
|
||||
yjApp.appConfig,
|
||||
yjApp.library,
|
||||
yjApp.playlist,
|
||||
yjApp.queue,
|
||||
yjApp.player,
|
||||
}
|
||||
|
||||
return yjApp, nil
|
||||
@@ -134,17 +145,18 @@ func (yj *YellowJacketApp) OnStartup(ctx context.Context) {
|
||||
yj.library.SetContext(ctx)
|
||||
yj.playlist.SetContext(ctx)
|
||||
|
||||
var err error
|
||||
// create player
|
||||
yj.player, err = player.NewPlayer(ctx, yj.logger.WithGroup("player"), yj.database)
|
||||
if err != nil {
|
||||
startupErr = errors.Join(startupErr, fmt.Errorf("could not create player: %w", err))
|
||||
// Initialize speaker hardware (player struct created in
|
||||
// NewYellowJacketApp for Wails binding registration).
|
||||
if err := yj.player.InitSpeaker(); err != nil {
|
||||
startupErr = errors.Join(
|
||||
startupErr,
|
||||
fmt.Errorf("could not initialize speaker: %w", err),
|
||||
)
|
||||
}
|
||||
|
||||
yj.player.SetContext(ctx)
|
||||
|
||||
// create queue
|
||||
yj.queue = queue.NewQueue(yj.logger, yj.database)
|
||||
// Wire queue (created in NewYellowJacketApp for Wails binding)
|
||||
yj.queue.SetContext(ctx)
|
||||
yj.queue.SetPlayer(yj.player)
|
||||
yj.queue.RestoreState()
|
||||
|
||||
@@ -3,53 +3,21 @@
|
||||
// the corresponding event names in the TypeScript frontend.
|
||||
package events
|
||||
|
||||
// Playback control events.
|
||||
// Playback events (backend → frontend push).
|
||||
const (
|
||||
PlaybackStateChanged = "PlaybackStateChanged"
|
||||
PlaybackFinished = "PlaybackFinished"
|
||||
RequestPlay = "RequestPlay"
|
||||
RequestPause = "RequestPause"
|
||||
RequestLoadFile = "RequestLoadFile"
|
||||
TrackChanged = "TrackChanged"
|
||||
SeekFailed = "SeekFailed"
|
||||
VolumeChanged = "VolumeChanged"
|
||||
)
|
||||
|
||||
// Track events.
|
||||
// Queue events (backend → frontend push).
|
||||
const (
|
||||
TrackChanged = "TrackChanged"
|
||||
)
|
||||
|
||||
// Seek events.
|
||||
const (
|
||||
Seek = "Seek"
|
||||
SeekFailed = "SeekFailed"
|
||||
)
|
||||
|
||||
// Volume events.
|
||||
const (
|
||||
RequestSetVolume = "RequestSetVolume"
|
||||
VolumeChanged = "VolumeChanged"
|
||||
)
|
||||
|
||||
// Queue events.
|
||||
const (
|
||||
QueueChanged = "QueueChanged"
|
||||
QueueIndexChanged = "QueueIndexChanged"
|
||||
QueueModeChanged = "QueueModeChanged"
|
||||
QueueTracksModified = "QueueTracksModified"
|
||||
RequestNext = "RequestNext"
|
||||
RequestPrevious = "RequestPrevious"
|
||||
RequestSetQueue = "RequestSetQueue"
|
||||
RequestAddToQueue = "RequestAddToQueue"
|
||||
RequestPlayNext = "RequestPlayNext"
|
||||
RequestRemoveFromQueue = "RequestRemoveFromQueue"
|
||||
RequestToggleShuffle = "RequestToggleShuffle"
|
||||
RequestCycleRepeat = "RequestCycleRepeat"
|
||||
RequestAddTracksToQueue = "RequestAddTracksToQueue"
|
||||
RequestPlayTracksNext = "RequestPlayTracksNext"
|
||||
RequestPlayQueueIndex = "RequestPlayQueueIndex"
|
||||
RequestRemoveTracksFromQueue = "RequestRemoveTracksFromQueue"
|
||||
RequestInsertTracksAtIndex = "RequestInsertTracksAtIndex"
|
||||
RequestMoveQueueTracks = "RequestMoveQueueTracks"
|
||||
RequestClearQueue = "RequestClearQueue"
|
||||
QueueChanged = "QueueChanged"
|
||||
QueueIndexChanged = "QueueIndexChanged"
|
||||
QueueModeChanged = "QueueModeChanged"
|
||||
QueueTracksModified = "QueueTracksModified"
|
||||
)
|
||||
|
||||
// Config events.
|
||||
|
||||
+24
-143
@@ -94,16 +94,10 @@ var (
|
||||
|
||||
var speakerSampleRate = beep.SampleRate(44100)
|
||||
|
||||
// NewPlayer creates a player and initializes the audio speaker.
|
||||
func NewPlayer(
|
||||
ctx context.Context,
|
||||
logger *slog.Logger,
|
||||
db *database.DB,
|
||||
) (*Player, error) {
|
||||
defer profiling.TimeOp(logger, "player.NewPlayer")()
|
||||
|
||||
player := &Player{
|
||||
ctx: ctx,
|
||||
// NewPlayer creates a player. Call InitSpeaker separately to
|
||||
// initialize the audio output device.
|
||||
func NewPlayer(logger *slog.Logger, db *database.DB) *Player {
|
||||
return &Player{
|
||||
logger: logger,
|
||||
db: db,
|
||||
state: Stopped,
|
||||
@@ -112,19 +106,27 @@ func NewPlayer(
|
||||
SampleRate: speakerSampleRate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: allow user to change buffer size and speaker sample rate
|
||||
// InitSpeaker initializes the audio output device. This is
|
||||
// separated from NewPlayer so the player struct can be created
|
||||
// before wails.Run (for binding registration) while deferring
|
||||
// hardware initialization to OnStartup.
|
||||
func (p *Player) InitSpeaker() error {
|
||||
defer profiling.TimeOp(p.logger, "player.InitSpeaker")()
|
||||
|
||||
// TODO: allow user to change buffer size and speaker sample rate.
|
||||
err := speaker.Init(
|
||||
player.format.SampleRate,
|
||||
player.format.SampleRate.N(time.Second/10),
|
||||
p.format.SampleRate,
|
||||
p.format.SampleRate.N(time.Second/10),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"failed to initialize speaker: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
return player, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPlaybackFinishedHandler sets a callback invoked when a track
|
||||
@@ -137,140 +139,18 @@ func (p *Player) SetPlaybackFinishedHandler(handler func()) {
|
||||
p.playbackFinishedHandler = handler
|
||||
}
|
||||
|
||||
// SetContext sets the Wails context, registers event handlers, and
|
||||
// restores persisted state.
|
||||
// SetContext sets the Wails runtime context and restores persisted
|
||||
// state.
|
||||
func (p *Player) SetContext(ctx context.Context) {
|
||||
p.mu.Lock()
|
||||
p.ctx = ctx
|
||||
p.mu.Unlock()
|
||||
|
||||
p.registerEventHandlers()
|
||||
|
||||
p.mu.Lock()
|
||||
p.restoreStateLocked()
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
func (p *Player) registerEventHandlers() {
|
||||
p.mu.Lock()
|
||||
ctx := p.ctx
|
||||
p.mu.Unlock()
|
||||
|
||||
if ctx == nil {
|
||||
p.logger.Error(
|
||||
"Context is nil, cannot register event handlers",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
runtime.EventsOn(
|
||||
ctx,
|
||||
events.RequestPause,
|
||||
func(_ ...any) {
|
||||
p.logger.Info("Received RequestPauseEvent")
|
||||
|
||||
if err := p.Pause(); err != nil {
|
||||
p.logger.Error("failed to pause", "err", err)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
ctx,
|
||||
events.RequestLoadFile,
|
||||
func(data ...any) {
|
||||
p.logger.Info("Received RequestLoadFileEvent")
|
||||
|
||||
if len(data) < 1 {
|
||||
p.logger.Warn(
|
||||
"RequestLoadFile: missing file path argument",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePath, ok := data[0].(string)
|
||||
if !ok {
|
||||
p.logger.Warn(
|
||||
"RequestLoadFile: invalid file path type",
|
||||
"got", fmt.Sprintf("%T", data[0]),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err := p.LoadFile(filePath)
|
||||
if err != nil {
|
||||
p.logger.Error(err.Error())
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(ctx, events.Seek, func(data ...any) {
|
||||
p.logger.Info("Received SeekEvent")
|
||||
|
||||
if len(data) < 1 {
|
||||
p.logger.Warn("Seek: missing seek value argument")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
seekFloat, ok := data[0].(float64)
|
||||
if !ok {
|
||||
p.logger.Warn(
|
||||
"Seek: invalid seek value type",
|
||||
"got", fmt.Sprintf("%T", data[0]),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
seekValue := int(seekFloat)
|
||||
|
||||
err := p.Seek(seekValue)
|
||||
if err != nil {
|
||||
p.logger.Error("cannot seek", "error", err)
|
||||
}
|
||||
})
|
||||
|
||||
runtime.EventsOn(
|
||||
ctx,
|
||||
events.RequestSetVolume,
|
||||
func(data ...any) {
|
||||
if len(data) < 1 {
|
||||
p.logger.Warn(
|
||||
"RequestSetVolume: missing volume argument",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
volFloat, ok := data[0].(float64)
|
||||
if !ok {
|
||||
p.logger.Warn(
|
||||
"RequestSetVolume: invalid volume type",
|
||||
"got", fmt.Sprintf("%T", data[0]),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
desiredVolume := UserVolume(volFloat)
|
||||
p.logger.Info(
|
||||
"Received RequestSetVolumeEvent",
|
||||
"volume", desiredVolume,
|
||||
)
|
||||
|
||||
p.mu.Lock()
|
||||
p.setVolumeLocked(desiredVolume)
|
||||
p.emitVolumeChanged()
|
||||
p.saveState()
|
||||
p.mu.Unlock()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Emit helpers (must be called with p.mu held)
|
||||
// ---------------------------------------------------------------
|
||||
@@ -695,14 +575,15 @@ func (p *Player) UnloadTrack() {
|
||||
// Volume
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// SetVolume sets the playback volume (0-100).
|
||||
func (p *Player) SetVolume(desiredVolume UserVolume) error {
|
||||
// SetVolume sets the playback volume (0-100), emits a
|
||||
// VolumeChanged event, and persists the new level.
|
||||
func (p *Player) SetVolume(desiredVolume UserVolume) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
p.setVolumeLocked(desiredVolume)
|
||||
|
||||
return nil
|
||||
p.emitVolumeChanged()
|
||||
p.saveState()
|
||||
}
|
||||
|
||||
func (p *Player) setVolumeLocked(desiredVolume UserVolume) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package player
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -15,8 +14,8 @@ var testQueue = []string{
|
||||
|
||||
func TestPlayer(t *testing.T) {
|
||||
// This is an integration test that requires:
|
||||
// 1. A Wails runtime context (SetContext calls runtime.EventsOn)
|
||||
// 2. An audio output device (speaker.Init)
|
||||
// 1. A Wails runtime context (SetContext restores persisted state)
|
||||
// 2. An audio output device (InitSpeaker)
|
||||
//
|
||||
// Skip unless the caller explicitly opts in via YELLOWJACKET_INTEGRATION=1.
|
||||
if os.Getenv("YELLOWJACKET_INTEGRATION") == "" {
|
||||
@@ -27,25 +26,24 @@ func TestPlayer(t *testing.T) {
|
||||
|
||||
t.Logf("Starting test")
|
||||
|
||||
p, err := NewPlayer(context.Background(), slog.Default(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create player: %s", err.Error())
|
||||
p := NewPlayer(slog.Default(), nil)
|
||||
|
||||
if err := p.InitSpeaker(); err != nil {
|
||||
t.Fatalf("could not initialize speaker: %s", err.Error())
|
||||
}
|
||||
|
||||
// SetContext registers Wails event handlers; only works with a real Wails context.
|
||||
// SetContext restores persisted state; only works with a real Wails context.
|
||||
p.SetContext(t.Context())
|
||||
t.Logf("initializing player")
|
||||
|
||||
for _, track := range testQueue {
|
||||
t.Logf("loading file: %s", track)
|
||||
|
||||
err = p.LoadFile(track)
|
||||
if err != nil {
|
||||
if err := p.LoadFile(track); err != nil {
|
||||
t.Fatalf("could not load file %s: %s", track, err.Error())
|
||||
}
|
||||
|
||||
err = p.Play()
|
||||
if err != nil {
|
||||
if err := p.Play(); err != nil {
|
||||
t.Fatalf("could not play file %s: %s", track, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package queue
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
|
||||
"yellowjacket/backend/events"
|
||||
)
|
||||
|
||||
// OnPlaybackFinished is called when a track finishes playing naturally.
|
||||
// This drives the auto-advance behavior.
|
||||
func (q *Queue) OnPlaybackFinished() {
|
||||
@@ -36,426 +30,3 @@ func (q *Queue) OnPlaybackFinished() {
|
||||
q.playCurrentTrack()
|
||||
q.emitIndexChanged()
|
||||
}
|
||||
|
||||
// registerEventHandlers sets up Wails event listeners for queue commands.
|
||||
func (q *Queue) registerEventHandlers() {
|
||||
if q.ctx == nil {
|
||||
q.logger.Error("Context is nil, cannot register event handlers")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestPlay, func(_ ...any) {
|
||||
q.logger.Info("Received RequestPlay")
|
||||
q.Play()
|
||||
})
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestNext, func(_ ...any) {
|
||||
q.logger.Info("Received RequestNext")
|
||||
q.Next()
|
||||
})
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestPrevious, func(_ ...any) {
|
||||
q.logger.Info("Received RequestPrevious")
|
||||
q.Previous()
|
||||
})
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestSetQueue, func(data ...any) {
|
||||
q.logger.Info("Received RequestSetQueue")
|
||||
q.handleSetQueue(data...)
|
||||
})
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestAddToQueue, func(data ...any) {
|
||||
q.logger.Info("Received RequestAddToQueue")
|
||||
q.handleAddToQueue(data...)
|
||||
})
|
||||
|
||||
runtime.EventsOn(q.ctx, events.RequestPlayNext, func(data ...any) {
|
||||
q.logger.Info("Received RequestPlayNext")
|
||||
q.handlePlayNext(data...)
|
||||
})
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestRemoveFromQueue,
|
||||
func(data ...any) {
|
||||
q.logger.Info("Received RequestRemoveFromQueue")
|
||||
q.handleRemoveFromQueue(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestToggleShuffle,
|
||||
func(_ ...any) {
|
||||
q.logger.Info("Received RequestToggleShuffle")
|
||||
q.ToggleShuffle()
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestCycleRepeat,
|
||||
func(_ ...any) {
|
||||
q.logger.Info("Received RequestCycleRepeat")
|
||||
q.CycleRepeat()
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestAddTracksToQueue,
|
||||
func(data ...any) {
|
||||
q.logger.Info("Received RequestAddTracksToQueue")
|
||||
q.handleAddTracksToQueue(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestPlayTracksNext,
|
||||
func(data ...any) {
|
||||
q.logger.Info("Received RequestPlayTracksNext")
|
||||
q.handlePlayTracksNext(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestPlayQueueIndex,
|
||||
func(data ...any) {
|
||||
q.logger.Info("Received RequestPlayQueueIndex")
|
||||
q.handlePlayQueueIndex(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestRemoveTracksFromQueue,
|
||||
func(data ...any) {
|
||||
q.logger.Info(
|
||||
"Received RequestRemoveTracksFromQueue",
|
||||
)
|
||||
q.handleRemoveTracksFromQueue(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestInsertTracksAtIndex,
|
||||
func(data ...any) {
|
||||
q.logger.Info(
|
||||
"Received RequestInsertTracksAtIndex",
|
||||
)
|
||||
q.handleInsertTracksAtIndex(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestMoveQueueTracks,
|
||||
func(data ...any) {
|
||||
q.logger.Info(
|
||||
"Received RequestMoveQueueTracks",
|
||||
)
|
||||
q.handleMoveQueueTracks(data...)
|
||||
},
|
||||
)
|
||||
|
||||
runtime.EventsOn(
|
||||
q.ctx,
|
||||
events.RequestClearQueue,
|
||||
func(_ ...any) {
|
||||
q.logger.Info("Received RequestClearQueue")
|
||||
q.Clear()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// toStringSlice extracts strings from a Wails event argument.
|
||||
func toStringSlice(raw []interface{}) []string {
|
||||
result := make([]string, 0, len(raw))
|
||||
|
||||
for _, v := range raw {
|
||||
if s, ok := v.(string); ok {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// toIntSlice extracts ints (from float64) from a Wails event argument.
|
||||
func toIntSlice(raw []interface{}) []int {
|
||||
result := make([]int, 0, len(raw))
|
||||
|
||||
for _, v := range raw {
|
||||
if f, ok := v.(float64); ok {
|
||||
result = append(result, int(f))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// handleSetQueue processes the RequestSetQueue event payload.
|
||||
// Expects data[0] = []interface{} of file path strings,
|
||||
// data[1] = float64 start index, data[2] = bool shuffleStart (optional).
|
||||
func (q *Queue) handleSetQueue(data ...any) {
|
||||
if len(data) < 2 {
|
||||
q.logger.Error("RequestSetQueue: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePathsRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error("RequestSetQueue: invalid filePaths type")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePaths := toStringSlice(filePathsRaw)
|
||||
|
||||
startIndex := 0
|
||||
|
||||
if si, ok := data[1].(float64); ok {
|
||||
startIndex = int(si)
|
||||
}
|
||||
|
||||
shuffleStart := false
|
||||
|
||||
if len(data) > 2 {
|
||||
if ss, ok := data[2].(bool); ok {
|
||||
shuffleStart = ss
|
||||
}
|
||||
}
|
||||
|
||||
q.SetQueue(filePaths, startIndex, shuffleStart)
|
||||
}
|
||||
|
||||
// handleAddToQueue processes the RequestAddToQueue event payload.
|
||||
// Expects data[0] = string file path.
|
||||
func (q *Queue) handleAddToQueue(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestAddToQueue: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePath, ok := data[0].(string)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestAddToQueue: invalid filePath type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.AddTrack(filePath)
|
||||
}
|
||||
|
||||
// handlePlayNext processes the RequestPlayNext event payload.
|
||||
// Expects data[0] = string file path.
|
||||
func (q *Queue) handlePlayNext(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestPlayNext: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePath, ok := data[0].(string)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestPlayNext: invalid filePath type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.InsertNext(filePath)
|
||||
}
|
||||
|
||||
// handleRemoveFromQueue processes the RequestRemoveFromQueue event payload.
|
||||
// Expects data[0] = float64 position.
|
||||
func (q *Queue) handleRemoveFromQueue(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestRemoveFromQueue: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
position, ok := data[0].(float64)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestRemoveFromQueue: invalid position type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.RemoveTrack(int(position))
|
||||
}
|
||||
|
||||
// handleRemoveTracksFromQueue processes the RequestRemoveTracksFromQueue
|
||||
// event payload. Expects data[0] = []interface{} of float64 positions.
|
||||
func (q *Queue) handleRemoveTracksFromQueue(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error(
|
||||
"RequestRemoveTracksFromQueue: missing data",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
positionsRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestRemoveTracksFromQueue: invalid positions type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.RemoveTracks(toIntSlice(positionsRaw))
|
||||
}
|
||||
|
||||
// handleAddTracksToQueue processes the RequestAddTracksToQueue event payload.
|
||||
// Expects data[0] = []interface{} of file path strings.
|
||||
func (q *Queue) handleAddTracksToQueue(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestAddTracksToQueue: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePathsRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestAddTracksToQueue: invalid filePaths type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.AddTracks(toStringSlice(filePathsRaw))
|
||||
}
|
||||
|
||||
// handleInsertTracksAtIndex processes the RequestInsertTracksAtIndex event
|
||||
// payload. Expects data[0] = []interface{} of file path strings,
|
||||
// data[1] = float64 target index.
|
||||
func (q *Queue) handleInsertTracksAtIndex(data ...any) {
|
||||
if len(data) < 2 {
|
||||
q.logger.Error(
|
||||
"RequestInsertTracksAtIndex: missing data",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePathsRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestInsertTracksAtIndex: invalid filePaths type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
idx, ok := data[1].(float64)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestInsertTracksAtIndex: invalid index type",
|
||||
"got", data[1],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.InsertTracksAt(toStringSlice(filePathsRaw), int(idx))
|
||||
}
|
||||
|
||||
// handleMoveQueueTracks processes the RequestMoveQueueTracks event payload.
|
||||
// Expects data[0] = []interface{} of float64 source indices,
|
||||
// data[1] = float64 target index.
|
||||
func (q *Queue) handleMoveQueueTracks(data ...any) {
|
||||
if len(data) < 2 {
|
||||
q.logger.Error(
|
||||
"RequestMoveQueueTracks: missing data",
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
indicesRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestMoveQueueTracks: invalid indices type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
toIdx, ok := data[1].(float64)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestMoveQueueTracks: invalid toIndex type",
|
||||
"got", data[1],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.MoveQueueTracks(toIntSlice(indicesRaw), int(toIdx))
|
||||
}
|
||||
|
||||
// handlePlayQueueIndex processes the RequestPlayQueueIndex event payload.
|
||||
// Expects data[0] = float64 index.
|
||||
func (q *Queue) handlePlayQueueIndex(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestPlayQueueIndex: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
index, ok := data[0].(float64)
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestPlayQueueIndex: invalid index type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.PlayIndex(int(index))
|
||||
}
|
||||
|
||||
// handlePlayTracksNext processes the RequestPlayTracksNext event payload.
|
||||
// Expects data[0] = []interface{} of file path strings.
|
||||
func (q *Queue) handlePlayTracksNext(data ...any) {
|
||||
if len(data) < 1 {
|
||||
q.logger.Error("RequestPlayTracksNext: missing data")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filePathsRaw, ok := data[0].([]interface{})
|
||||
if !ok {
|
||||
q.logger.Error(
|
||||
"RequestPlayTracksNext: invalid filePaths type",
|
||||
"got", data[0],
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
q.InsertNextTracks(toStringSlice(filePathsRaw))
|
||||
}
|
||||
|
||||
@@ -130,10 +130,9 @@ func NewQueue(logger *slog.Logger, db *database.DB) *Queue {
|
||||
}
|
||||
}
|
||||
|
||||
// SetContext sets the Wails runtime context and registers event handlers.
|
||||
// SetContext sets the Wails runtime context for event emission.
|
||||
func (q *Queue) SetContext(ctx context.Context) {
|
||||
q.ctx = ctx
|
||||
q.registerEventHandlers()
|
||||
}
|
||||
|
||||
// SetPlayer provides the queue with a reference to the player for auto-advance.
|
||||
|
||||
Reference in New Issue
Block a user