Files
go-mxl-player/internal/playback/video_slot.go
T
2026-08-27 21:31:00 +03:00

93 lines
1.6 KiB
Go

package playback
import (
"context"
"errors"
"fmt"
)
var ErrVideoWorkerRequired = errors.New("video worker is required")
type VideoSlot struct {
worker *VideoWorker
}
func NewVideoSlot(worker *VideoWorker) (*VideoSlot, error) {
if worker == nil {
return nil, ErrVideoWorkerRequired
}
return &VideoSlot{worker: worker}, nil
}
func (s *VideoSlot) Run(
ctx context.Context,
initial FeedConfig,
commands <-chan FeedConfig,
) error {
if err := initial.Validate(); err != nil {
return fmt.Errorf("validate initial video config: %w", err)
}
var (
workerCancel context.CancelFunc
workerDone chan error
)
start := func(config FeedConfig) {
workerCtx, cancel := context.WithCancel(ctx)
done := make(chan error, 1)
workerCancel = cancel
workerDone = done
go func() {
done <- s.worker.Run(workerCtx, config)
}()
}
stop := func() {
if workerCancel == nil {
return
}
workerCancel()
<-workerDone
workerCancel = nil
workerDone = nil
}
if initial.Active {
start(initial)
}
for {
select {
case <-ctx.Done():
stop()
return ctx.Err()
case config, ok := <-commands:
if !ok {
stop()
return nil
}
if err := config.Validate(); err != nil {
// Ignore invalid commands without disturbing the current worker.
continue
}
stop()
if config.Active {
start(config)
}
case <-workerDone:
// The worker stopped naturally or exhausted its retries.
// Clear its lifecycle, but keep the slot alive for future commands.
workerCancel()
workerCancel = nil
workerDone = nil
}
}
}