diff --git a/backend/playbackengine.go b/backend/playbackengine.go index fa0d15f..ae0dbdd 100644 --- a/backend/playbackengine.go +++ b/backend/playbackengine.go @@ -65,6 +65,8 @@ type playbackEngine struct { isRadio bool loopMode LoopMode + stopAfterCurrent bool // flag to stop playback after current track ends + // flags for handleOnTrackChange / handleOnStopped callbacks - reset to false in the callbacks wasStopped bool // true iff player was stopped before handleOnTrackChange invocation alreadyScrobbled bool // true iff the previously-playing track was already scrobbled @@ -334,6 +336,15 @@ func (p *playbackEngine) Stop() error { return p.player.Stop(false) } +func (p *playbackEngine) SetStopAfterCurrent(stopAfterCurrent bool) { + p.stopAfterCurrent = stopAfterCurrent + if p.stopAfterCurrent { + p.setNextTrack(-1) // clear next playing track from internal player, if any + } else if p.loopMode != LoopNone || p.nowPlayingIdx < len(p.playQueue)-1 { + p.needToSetNextTrack = true // need to restore next track to internal player queue + } +} + func (p *playbackEngine) Pause() error { return p.player.Pause() } @@ -634,6 +645,7 @@ func (p *playbackEngine) handleOnStopped() { p.alreadyScrobbled = false p.wasStopped = true p.nowPlayingIdx = -1 + p.stopAfterCurrent = false } // to be invoked as soon as the next item in the queue that should play changes @@ -855,7 +867,7 @@ func (p *playbackEngine) handleTimePosUpdate(seeked bool) { meta = np.Metadata() } isNearEnd := meta.Type != mediaprovider.MediaItemTypeRadioStation && s.TimePos > meta.Duration.Seconds()-10 - if p.needToSetNextTrack && isNearEnd { + if p.needToSetNextTrack && !p.stopAfterCurrent && isNearEnd { p.needToSetNextTrack = false p.setNextTrack(p.nextPlayingIndex()) } diff --git a/backend/playbackmanager.go b/backend/playbackmanager.go index 72109f7..14dbede 100644 --- a/backend/playbackmanager.go +++ b/backend/playbackmanager.go @@ -669,6 +669,14 @@ func (p *PlaybackManager) PlayPause() { } } +func (p *PlaybackManager) SetStopAfterCurrent(stopAfterCurrent bool) { + p.engine.SetStopAfterCurrent(stopAfterCurrent) +} + +func (p *PlaybackManager) IsStopAfterCurrent() bool { + return p.engine.stopAfterCurrent +} + func (p *PlaybackManager) enqueueAutoplayTracks() { nowPlaying := p.NowPlaying() if nowPlaying == nil { diff --git a/ui/controller/controller.go b/ui/controller/controller.go index b6a6677..927cd6f 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -27,6 +27,7 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/lang" + "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" ) @@ -57,6 +58,7 @@ type Controller struct { popUpQueue *widget.PopUp popUpQueueList *widgets.PlayQueueList + stopAfterCurrent *widget.Check popUpQueueLastUsed int64 escapablePopUp fyne.CanvasObject haveModal bool @@ -195,7 +197,11 @@ func (m *Controller) ShowPopUpPlayQueue() { title := widget.NewRichTextWithText(lang.L("Play Queue")) title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter title.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true - ctr := container.NewBorder(title, nil, nil, nil, + m.stopAfterCurrent = widget.NewCheck(lang.L("Stop after current track"), func(b bool) { + m.App.PlaybackManager.SetStopAfterCurrent(b) + }) + bottomRow := container.NewHBox(layout.NewSpacer(), m.stopAfterCurrent) + ctr := container.NewBorder(title, bottomRow, nil, nil, container.NewPadded(m.popUpQueueList), ) m.popUpQueue = widget.NewPopUp(ctr, m.MainWindow.Canvas()) @@ -224,6 +230,7 @@ func (m *Controller) ShowPopUpPlayQueue() { fynetooltip.DestroyPopUpToolTipLayer(m.popUpQueue) m.popUpQueue = nil m.popUpQueueList = nil + m.stopAfterCurrent = nil m.popUpQueueLastUsed = 0 t.Stop() return @@ -252,6 +259,7 @@ func (m *Controller) ShowPopUpPlayQueue() { )) pop.Resize(size) popUpQueueList.ScrollToNowPlaying() // must come after resize + m.stopAfterCurrent.SetChecked(m.App.PlaybackManager.IsStopAfterCurrent()) pop.ShowAtPosition(fyne.NewPos( canvasSize.Width-size.Width-10, canvasSize.Height-size.Height-100,