From 8daa4a2e7b9aad1478efa985e2c1e0d585962710 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 12 Jul 2026 11:17:58 +0200 Subject: [PATCH] Improved play next behavior, now store a play next position, fixes #864 --- src/lms/ui/PlayQueue.cpp | 62 ++++++++++++++++++++++++++-------------- src/lms/ui/PlayQueue.hpp | 12 ++++++-- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/lms/ui/PlayQueue.cpp b/src/lms/ui/PlayQueue.cpp index 73cf2057..a1d0eb23 100644 --- a/src/lms/ui/PlayQueue.cpp +++ b/src/lms/ui/PlayQueue.cpp @@ -152,6 +152,7 @@ namespace lms::ui LmsApp->getDbSession().create(entry->getTrack(), queue); } _entriesContainer->reset(); + _nextPlayPos.reset(); addSome(); }); @@ -234,6 +235,7 @@ namespace lms::ui _entriesContainer->reset(); _trackPos.reset(); + _nextPlayPos.reset(); updateInfo(); } @@ -241,11 +243,12 @@ namespace lms::ui { updateCurrentTrack(false); _trackPos.reset(); + _nextPlayPos.reset(); _isTrackSelected = false; trackUnselected.emit(); } - void PlayQueue::loadTrack(std::size_t pos, bool play) + void PlayQueue::loadTrack(std::size_t pos, bool play, ResetNextPlayPos resetNextPlayPos) { updateCurrentTrack(false); @@ -269,6 +272,8 @@ namespace lms::ui } _trackPos = pos; + if (resetNextPlayPos.value()) + _nextPlayPos.reset(); const db::Track::pointer track{ queue->getEntry(*_trackPos)->getTrack() }; trackId = track->getId(); @@ -296,18 +301,23 @@ namespace lms::ui void PlayQueue::playNext() { - if (!_trackPos) - { - loadTrack(0, true); - return; - } - - loadTrack(*_trackPos + 1, true); + advanceTrack(ResetNextPlayPos{ true }); } void PlayQueue::onPlaybackEnded() { - playNext(); + advanceTrack(ResetNextPlayPos{ false }); + } + + void PlayQueue::advanceTrack(ResetNextPlayPos resetNextPlayPos) + { + if (!_trackPos) + { + loadTrack(0, true, resetNextPlayPos); + return; + } + + loadTrack(*_trackPos + 1, true, resetNextPlayPos); } std::size_t PlayQueue::getCount() @@ -396,14 +406,14 @@ namespace lms::ui _entriesContainer->setHasMore(); } - std::vector PlayQueue::getAndClearNextTracks() + std::vector PlayQueue::getAndClearTracksFrom(std::size_t pos) { std::vector tracks; auto transaction{ LmsApp->getDbSession().createWriteTransaction() }; db::TrackList::pointer queue{ getQueue() }; - auto entries{ queue->getEntries(db::Range{ _trackPos ? *_trackPos + 1 : 0, getCapacity() }) }; + auto entries{ queue->getEntries(db::Range{ pos, getCapacity() }) }; tracks.reserve(entries.size()); for (db::TrackListEntry::pointer& entry : entries) { @@ -411,16 +421,15 @@ namespace lms::ui entry.remove(); } - if (_trackPos) - { - // entries may have been cleared - if (*_trackPos + 1 < _entriesContainer->getCount()) - _entriesContainer->remove(*_trackPos + 1, _entriesContainer->getCount() - 1); - } - else + if (pos == 0) { _entriesContainer->reset(); } + else if (pos < _entriesContainer->getCount()) + { + // entries may have been cleared + _entriesContainer->remove(pos, _entriesContainer->getCount() - 1); + } return tracks; } @@ -432,9 +441,19 @@ namespace lms::ui void PlayQueue::playNext(std::span trackIds) { - std::vector nextTracks{ getAndClearNextTracks() }; - nextTracks.insert(std::cbegin(nextTracks), std::cbegin(trackIds), std::cend(trackIds)); - playOrAddLast(nextTracks); + const std::size_t defaultInsertPos{ _trackPos ? *_trackPos + 1 : 0 }; + if (!_nextPlayPos || *_nextPlayPos < defaultInsertPos) + _nextPlayPos = defaultInsertPos; + + const std::size_t insertPos{ *_nextPlayPos }; + + std::vector tracksToInsert{ getAndClearTracksFrom(insertPos) }; + tracksToInsert.insert(std::cbegin(tracksToInsert), std::cbegin(trackIds), std::cend(trackIds)); + + playOrAddLast(tracksToInsert); + + // set after playOrAddLast: it may call loadTrack() (queue was empty), which resets _nextPlayPos + _nextPlayPos = insertPos + trackIds.size(); } void PlayQueue::playShuffled(std::span trackIds) @@ -549,6 +568,7 @@ namespace lms::ui else if (*_trackPos >= _entriesContainer->getCount()) _trackPos.reset(); } + _nextPlayPos.reset(); _entriesContainer->remove(*entry); diff --git a/src/lms/ui/PlayQueue.hpp b/src/lms/ui/PlayQueue.hpp index 3fb718a1..bc0eb26e 100644 --- a/src/lms/ui/PlayQueue.hpp +++ b/src/lms/ui/PlayQueue.hpp @@ -27,6 +27,8 @@ #include #include +#include "core/TaggedType.hpp" + #include "database/Object.hpp" #include "database/objects/TrackId.hpp" #include "database/objects/TrackListId.hpp" @@ -50,6 +52,8 @@ namespace lms::ui public: PlayQueue(); + using ResetNextPlayPos = core::TaggedBool; + void play(std::span trackIds); void playNext(std::span trackIds); void playShuffled(std::span trackIds); @@ -85,7 +89,8 @@ namespace lms::ui void clearTracks(); void enqueueTracks(std::span trackIds); - std::vector getAndClearNextTracks(); + std::vector getAndClearTracksFrom(std::size_t pos); + void advanceTrack(ResetNextPlayPos resetNextPlayPos); void addSome(); void addEntry(const db::ObjectPtr& entry); void enqueueRadioTracksIfNeeded(); @@ -95,7 +100,7 @@ namespace lms::ui bool isRepeatAllSet() const; bool isRadioModeSet() const; - void loadTrack(std::size_t pos, bool play); + void loadTrack(std::size_t pos, bool play, ResetNextPlayPos resetNextPlayPos = ResetNextPlayPos{ true }); void stop(); std::optional getReplayGain(std::size_t pos, const db::ObjectPtr& track) const; @@ -114,7 +119,8 @@ namespace lms::ui Wt::WText* _duration{}; Wt::WCheckBox* _repeatBtn{}; Wt::WCheckBox* _radioBtn{}; - std::optional _trackPos; // current track position, if set + std::optional _trackPos; // current track position, if set + std::optional _nextPlayPos; // where the next "play next" insertion happens, if set bool _isTrackSelected{}; };