mirror of
https://github.com/navidrome/navidrome.git
synced 2025-05-31 15:49:38 +03:00
Jukebox cleanup (#2554)
* Fixing typo FFmpegPath -> MPVPath * Fixing panic by applying afontenot patch * Using mpv audio-device flag and naming for config and playback
This commit is contained in:
parent
2cd4358172
commit
59f0c487e7
@ -24,7 +24,6 @@ type PlaybackDevice struct {
|
|||||||
Default bool
|
Default bool
|
||||||
User string
|
User string
|
||||||
Name string
|
Name string
|
||||||
Method string
|
|
||||||
DeviceName string
|
DeviceName string
|
||||||
PlaybackQueue *Queue
|
PlaybackQueue *Queue
|
||||||
Gain float32
|
Gain float32
|
||||||
@ -60,12 +59,11 @@ func (pd *PlaybackDevice) getStatus() DeviceStatus {
|
|||||||
// NewPlaybackDevice creates a new playback device which implements all the basic Jukebox mode commands defined here:
|
// NewPlaybackDevice creates a new playback device which implements all the basic Jukebox mode commands defined here:
|
||||||
// http://www.subsonic.org/pages/api.jsp#jukeboxControl
|
// http://www.subsonic.org/pages/api.jsp#jukeboxControl
|
||||||
// Starts the trackSwitcher goroutine for the device.
|
// Starts the trackSwitcher goroutine for the device.
|
||||||
func NewPlaybackDevice(playbackServer PlaybackServer, name string, method string, deviceName string) *PlaybackDevice {
|
func NewPlaybackDevice(playbackServer PlaybackServer, name string, deviceName string) *PlaybackDevice {
|
||||||
return &PlaybackDevice{
|
return &PlaybackDevice{
|
||||||
ParentPlaybackServer: playbackServer,
|
ParentPlaybackServer: playbackServer,
|
||||||
User: "",
|
User: "",
|
||||||
Name: name,
|
Name: name,
|
||||||
Method: method,
|
|
||||||
DeviceName: deviceName,
|
DeviceName: deviceName,
|
||||||
Gain: DefaultGain,
|
Gain: DefaultGain,
|
||||||
PlaybackQueue: NewQueue(),
|
PlaybackQueue: NewQueue(),
|
||||||
@ -146,12 +144,12 @@ func (pd *PlaybackDevice) Skip(ctx context.Context, index int, offset int) (Devi
|
|||||||
pd.ActiveTrack.Pause()
|
pd.ActiveTrack.Pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
if index != pd.PlaybackQueue.Index {
|
if index != pd.PlaybackQueue.Index && pd.ActiveTrack != nil {
|
||||||
if pd.ActiveTrack != nil {
|
pd.ActiveTrack.Close()
|
||||||
pd.ActiveTrack.Close()
|
pd.ActiveTrack = nil
|
||||||
pd.ActiveTrack = nil
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if pd.ActiveTrack == nil {
|
||||||
err := pd.switchActiveTrackByIndex(index)
|
err := pd.switchActiveTrackByIndex(index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pd.getStatus(), err
|
return pd.getStatus(), err
|
||||||
@ -278,7 +276,7 @@ func (pd *PlaybackDevice) switchActiveTrackByIndex(index int) error {
|
|||||||
return fmt.Errorf("could not get current track")
|
return fmt.Errorf("could not get current track")
|
||||||
}
|
}
|
||||||
|
|
||||||
track, err := mpv.NewTrack(pd.PlaybackDone, *currentTrack)
|
track, err := mpv.NewTrack(pd.PlaybackDone, pd.DeviceName, *currentTrack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
|
|
||||||
// mpv --no-audio-display --pause 'Jack Johnson/On And On/01 Times Like These.m4a' --input-ipc-server=/tmp/gonzo.socket
|
// mpv --no-audio-display --pause 'Jack Johnson/On And On/01 Times Like These.m4a' --input-ipc-server=/tmp/gonzo.socket
|
||||||
const (
|
const (
|
||||||
mpvComdTemplate = "mpv --no-audio-display --pause %f --input-ipc-server=%s"
|
mpvComdTemplate = "mpv --audio-device=%d --no-audio-display --pause %f --input-ipc-server=%s"
|
||||||
)
|
)
|
||||||
|
|
||||||
func start(args []string) (Executor, error) {
|
func start(args []string) (Executor, error) {
|
||||||
@ -81,9 +81,10 @@ func (j *Executor) wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Path will always be an absolute path
|
// Path will always be an absolute path
|
||||||
func createMPVCommand(cmd, filename string, socketName string) []string {
|
func createMPVCommand(cmd, deviceName string, filename string, socketName string) []string {
|
||||||
split := strings.Split(fixCmd(cmd), " ")
|
split := strings.Split(fixCmd(cmd), " ")
|
||||||
for i, s := range split {
|
for i, s := range split {
|
||||||
|
s = strings.ReplaceAll(s, "%d", deviceName)
|
||||||
s = strings.ReplaceAll(s, "%f", filename)
|
s = strings.ReplaceAll(s, "%f", filename)
|
||||||
s = strings.ReplaceAll(s, "%s", socketName)
|
s = strings.ReplaceAll(s, "%s", socketName)
|
||||||
split[i] = s
|
split[i] = s
|
||||||
@ -110,7 +111,7 @@ func fixCmd(cmd string) string {
|
|||||||
func mpvCommand() (string, error) {
|
func mpvCommand() (string, error) {
|
||||||
mpvOnce.Do(func() {
|
mpvOnce.Do(func() {
|
||||||
if conf.Server.MPVPath != "" {
|
if conf.Server.MPVPath != "" {
|
||||||
mpvPath = conf.Server.FFmpegPath
|
mpvPath = conf.Server.MPVPath
|
||||||
mpvPath, mpvErr = exec.LookPath(mpvPath)
|
mpvPath, mpvErr = exec.LookPath(mpvPath)
|
||||||
} else {
|
} else {
|
||||||
mpvPath, mpvErr = exec.LookPath("mpv")
|
mpvPath, mpvErr = exec.LookPath("mpv")
|
||||||
|
@ -24,7 +24,7 @@ type MpvTrack struct {
|
|||||||
CloseCalled bool
|
CloseCalled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTrack(playbackDoneChannel chan bool, mf model.MediaFile) (*MpvTrack, error) {
|
func NewTrack(playbackDoneChannel chan bool, deviceName string, mf model.MediaFile) (*MpvTrack, error) {
|
||||||
log.Debug("loading track", "trackname", mf.Path, "mediatype", mf.ContentType())
|
log.Debug("loading track", "trackname", mf.Path, "mediatype", mf.ContentType())
|
||||||
|
|
||||||
if _, err := mpvCommand(); err != nil {
|
if _, err := mpvCommand(); err != nil {
|
||||||
@ -33,7 +33,7 @@ func NewTrack(playbackDoneChannel chan bool, mf model.MediaFile) (*MpvTrack, err
|
|||||||
|
|
||||||
tmpSocketName := TempFileName("mpv-ctrl-", ".socket")
|
tmpSocketName := TempFileName("mpv-ctrl-", ".socket")
|
||||||
|
|
||||||
args := createMPVCommand(mpvComdTemplate, mf.Path, tmpSocketName)
|
args := createMPVCommand(mpvComdTemplate, deviceName, mf.Path, tmpSocketName)
|
||||||
exe, err := start(args)
|
exe, err := start(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error starting mpv process", "error", err)
|
log.Error("error starting mpv process", "error", err)
|
||||||
|
@ -46,8 +46,11 @@ func (ps *playbackServer) Run(ctx context.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info(ctx, fmt.Sprintf("%d audio devices found", len(conf.Server.Jukebox.Devices)))
|
log.Info(ctx, fmt.Sprintf("%d audio devices found", len(devices)))
|
||||||
log.Info(ctx, "Using default audio device: "+conf.Server.Jukebox.Default)
|
|
||||||
|
defaultDevice, _ := ps.getDefaultDevice()
|
||||||
|
|
||||||
|
log.Info(ctx, "Using audio device: "+defaultDevice.DeviceName)
|
||||||
|
|
||||||
ps.ctx = &ctx
|
ps.ctx = &ctx
|
||||||
|
|
||||||
@ -61,15 +64,37 @@ func (ps *playbackServer) GetCtx() *context.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ps *playbackServer) initDeviceStatus(devices []conf.AudioDeviceDefinition, defaultDevice string) ([]PlaybackDevice, error) {
|
func (ps *playbackServer) initDeviceStatus(devices []conf.AudioDeviceDefinition, defaultDevice string) ([]PlaybackDevice, error) {
|
||||||
pbDevices := make([]PlaybackDevice, len(devices))
|
pbDevices := make([]PlaybackDevice, max(1, len(devices)))
|
||||||
defaultDeviceFound := false
|
defaultDeviceFound := false
|
||||||
|
|
||||||
for idx, audioDevice := range devices {
|
if defaultDevice == "" {
|
||||||
if len(audioDevice) != 3 {
|
// if there are no devices given and no default device, we create a sythetic device named "auto"
|
||||||
return []PlaybackDevice{}, fmt.Errorf("audio device definition ought to contain 3 fields, found: %d ", len(audioDevice))
|
if len(devices) == 0 {
|
||||||
|
pbDevices[0] = *NewPlaybackDevice(ps, "auto", "auto")
|
||||||
}
|
}
|
||||||
|
|
||||||
pbDevices[idx] = *NewPlaybackDevice(ps, audioDevice[0], audioDevice[1], audioDevice[2])
|
// if there is but only one entry and no default given, just use that.
|
||||||
|
if len(devices) == 1 {
|
||||||
|
if len(devices[0]) != 2 {
|
||||||
|
return []PlaybackDevice{}, fmt.Errorf("audio device definition ought to contain 2 fields, found: %d ", len(devices[0]))
|
||||||
|
}
|
||||||
|
pbDevices[0] = *NewPlaybackDevice(ps, devices[0][0], devices[0][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(devices) > 1 {
|
||||||
|
return []PlaybackDevice{}, fmt.Errorf("number of audio device found is %d, but no default device defined. Set Jukebox.Default", len(devices))
|
||||||
|
}
|
||||||
|
|
||||||
|
pbDevices[0].Default = true
|
||||||
|
return pbDevices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, audioDevice := range devices {
|
||||||
|
if len(audioDevice) != 2 {
|
||||||
|
return []PlaybackDevice{}, fmt.Errorf("audio device definition ought to contain 2 fields, found: %d ", len(audioDevice))
|
||||||
|
}
|
||||||
|
|
||||||
|
pbDevices[idx] = *NewPlaybackDevice(ps, audioDevice[0], audioDevice[1])
|
||||||
|
|
||||||
if audioDevice[0] == defaultDevice {
|
if audioDevice[0] == defaultDevice {
|
||||||
pbDevices[idx].Default = true
|
pbDevices[idx].Default = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user