diff --git a/src/database/User.cpp b/src/database/User.cpp index 39e8165f..21aa8310 100644 --- a/src/database/User.cpp +++ b/src/database/User.cpp @@ -71,7 +71,8 @@ _isAdmin(false), _audioBitrate(defaultAudioBitrate), _audioEncoding(AudioEncoding::AUTO), _videoBitrate(defaultVideoBitrate), -_videoEncoding(VideoEncoding::AUTO) +_videoEncoding(VideoEncoding::AUTO), +_curPlayingTrackPos(0) { } diff --git a/src/database/User.hpp b/src/database/User.hpp index 9615b572..2450aa1f 100644 --- a/src/database/User.hpp +++ b/src/database/User.hpp @@ -78,6 +78,7 @@ class User void setVideoEncoding(VideoEncoding encoding) { _videoEncoding = encoding; } void setMaxAudioBitrate(std::size_t bitrate); void setMaxVideoBitrate(std::size_t bitrate); + void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; } // read bool isAdmin() const {return _isAdmin;} @@ -87,6 +88,7 @@ class User VideoEncoding getVideoEncoding() const { return _videoEncoding;} std::size_t getMaxAudioBitrate() const; std::size_t getMaxVideoBitrate() const; + std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; } template void persist(Action& a) @@ -98,6 +100,8 @@ class User Wt::Dbo::field(a, _audioEncoding, "audio_encoding"); Wt::Dbo::field(a, _videoBitrate, "video_bitrate"); Wt::Dbo::field(a, _videoEncoding, "video_encoding"); + // User's dynamic data + Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos"); Wt::Dbo::hasMany(a, _playlists, Wt::Dbo::ManyToOne, "user"); } @@ -120,6 +124,9 @@ class User int _videoBitrate; VideoEncoding _videoEncoding; + // User's dynamic data + int _curPlayingTrackPos; // Current track position in queue + Wt::Dbo::collection< Wt::Dbo::ptr > _playlists; }; diff --git a/src/ui/audio/desktop/DesktopAudio.cpp b/src/ui/audio/desktop/DesktopAudio.cpp index 9d046041..bd9b9d49 100644 --- a/src/ui/audio/desktop/DesktopAudio.cpp +++ b/src/ui/audio/desktop/DesktopAudio.cpp @@ -51,6 +51,10 @@ void WPopupMenuClear(Wt::WPopupMenu* menu) namespace UserInterface { namespace Desktop { +// Special playlist generated each time the playque gets changed +// Restored at the beginning of the session +static const std::string CurrentQueuePlaylistName = "__current__"; + Audio::Audio(SessionData& sessionData, Wt::WContainerWidget* parent) : UserInterface::Audio(parent), _db(sessionData.getDatabaseHandler()), @@ -180,6 +184,11 @@ _playQueue(nullptr) playQueueLayout->addLayout(playlistControls); mainLayout->addWidget(playQueueContainer, 0, 0, 2, 1); + + // Load the last known queue + playlistLoadToPlayqueue(CurrentQueuePlaylistName); + // Select the last known playing track + _playQueue->select(user->getCurPlayingTrackPos()); } mainLayout->setRowStretch(1, 1); @@ -212,6 +221,13 @@ _playQueue(nullptr) _mediaPlayer->shuffle().connect(boost::bind(&PlayQueue::setShuffle, _playQueue, _1)); _mediaPlayer->loop().connect(boost::bind(&PlayQueue::setLoop,_playQueue, _1)); + _playQueue->tracksUpdated().connect(std::bind([=] () { + LMS_LOG(MOD_UI, SEV_INFO) << "Playqueue updated!"; + + playlistSaveFromPlayqueue(CurrentQueuePlaylistName); + })); + + playlistRefreshMenus(); } @@ -337,17 +353,21 @@ Audio::playlistLoadToPlayqueue(std::string playlistName) { LMS_LOG(MOD_UI, SEV_DEBUG) << "Loading playlist '" << playlistName << "' to playqueue"; - Wt::Dbo::Transaction transaction(_db.getSession()); + std::vector entries; - Database::User::pointer user = _db.getCurrentUser(); - if (!user) - return; + { + Wt::Dbo::Transaction transaction(_db.getSession()); - Database::Playlist::pointer playlist = Database::Playlist::get(_db.getSession(), playlistName, user); - if (!playlist) - return; + Database::User::pointer user = _db.getCurrentUser(); + if (!user) + return; - std::vector entries = Database::PlaylistEntry::getEntries(_db.getSession(), playlist); + Database::Playlist::pointer playlist = Database::Playlist::get(_db.getSession(), playlistName, user); + if (!playlist) + return; + + entries = Database::PlaylistEntry::getEntries(_db.getSession(), playlist); + } _playQueue->clear(); _playQueue->addTracks(entries); @@ -414,6 +434,9 @@ Audio::playlistRefreshMenus() BOOST_FOREACH(Database::Playlist::pointer playlist, playlists) { + if (playlist->getName() == CurrentQueuePlaylistName) + continue; + // Add playlists in each menu _popupMenuDelete->addItem(playlist->getName())->triggered().connect(std::bind([=] () { @@ -496,7 +519,7 @@ Audio::playSelectedTracks(PlayQueueAddType addType) } void -Audio::playTrack(boost::filesystem::path p) +Audio::playTrack(boost::filesystem::path p, int pos) { LMS_LOG(MOD_UI, SEV_DEBUG) << "play track '" << p << "'"; try { @@ -508,7 +531,10 @@ Audio::playTrack(boost::filesystem::path p) Wt::Dbo::Transaction transaction(_db.getSession()); Database::User::pointer user = _db.getCurrentUser(); if (user) + { bitrate = user->getAudioBitrate(); + user.modify()->setCurPlayingTrackPos(pos); + } else { LMS_LOG(MOD_UI, SEV_ERROR) << "Can't play: user does not exists!"; diff --git a/src/ui/audio/desktop/DesktopAudio.hpp b/src/ui/audio/desktop/DesktopAudio.hpp index 19b70f5f..2f967fad 100644 --- a/src/ui/audio/desktop/DesktopAudio.hpp +++ b/src/ui/audio/desktop/DesktopAudio.hpp @@ -55,7 +55,7 @@ class Audio : public UserInterface::Audio void playlistShowDeleteDialog(std::string name); void playlistRefreshMenus(); - void playTrack(boost::filesystem::path p); + void playTrack(boost::filesystem::path p, int pos); enum PlayQueueAddType { diff --git a/src/ui/audio/desktop/PlayQueue.cpp b/src/ui/audio/desktop/PlayQueue.cpp index 2c1977b2..eaa57b2f 100644 --- a/src/ui/audio/desktop/PlayQueue.cpp +++ b/src/ui/audio/desktop/PlayQueue.cpp @@ -357,14 +357,25 @@ PlayQueue::play(int rowId) playNext(); } +void +PlayQueue::select(int rowId) +{ + // Update the track selector to use the requested track as current position + _trackSelector->setPosByRowId(rowId); + + Wt::WTableView::select(_model->index(_trackSelector->getCurrent(), 0)); + + this->scrollTo( _model->index(_trackSelector->getCurrent(), 0)); +} + void PlayQueue::addTracks(const std::vector& trackIds) { // Add tracks to model - Wt::Dbo::Transaction transaction(_db.getSession()); - + // BOOST_FOREACH(Database::Track::id_type trackId, trackIds) { + Wt::Dbo::Transaction transaction(_db.getSession()); Database::Track::pointer track (Database::Track::getById(_db.getSession(), trackId)); if (track) @@ -391,6 +402,8 @@ PlayQueue::addTracks(const std::vector& trackIds) } _trackSelector->setSize( _model->rowCount() ); + + _sigTracksUpdated.emit(); } void @@ -401,6 +414,8 @@ PlayQueue::clear(void) // Reset play id _curPlayedTrackPos = trackPosInvalid; _trackSelector->setSize( 0 ); + + _sigTracksUpdated.emit(); } void @@ -458,7 +473,7 @@ PlayQueue::readTrack(int rowPos) { setPlayingTrackPos(rowPos); - _sigTrackPlay.emit(track->getPath()); + _sigTrackPlay.emit(track->getPath(), rowPos); this->scrollTo( _model->index(_trackSelector->getCurrent(), 0)); @@ -504,6 +519,7 @@ PlayQueue::delSelected(void) _trackSelector->setSize(_model->rowCount()); renumber(minId, _model->rowCount() - 1); + _sigTracksUpdated.emit(); } void @@ -511,6 +527,7 @@ PlayQueue::delAll(void) { _model->removeRows(0, _model->rowCount()); _trackSelector->setSize(0); + _sigTracksUpdated.emit(); } void @@ -551,6 +568,8 @@ PlayQueue::moveSelectedUp(void) this->setSelectedIndexes( newIndexSet ); renumber(minId, maxId); + + _sigTracksUpdated.emit(); } @@ -592,6 +611,8 @@ PlayQueue::moveSelectedDown(void) this->setSelectedIndexes( newIndexSet ); renumber(minId, maxId); + + _sigTracksUpdated.emit(); } void diff --git a/src/ui/audio/desktop/PlayQueue.hpp b/src/ui/audio/desktop/PlayQueue.hpp index 9d150a41..a36e6c3c 100644 --- a/src/ui/audio/desktop/PlayQueue.hpp +++ b/src/ui/audio/desktop/PlayQueue.hpp @@ -49,6 +49,7 @@ class PlayQueue : public Wt::WTableView // Play functions void play(void); // Play the queue from the beginning void play(int rowId); // Play the queue from the given rowId + void select(int rowId); // Select the given rowId void playNext(void); // Play the next track void playPrevious(void); // Play the previous track @@ -59,7 +60,10 @@ class PlayQueue : public Wt::WTableView void moveSelectedDown(void); // Signals - Wt::Signal< boost::filesystem::path >& playTrack() { return _sigTrackPlay; } + // Emitted when a song has to be played + Wt::Signal< boost::filesystem::path, int >& playTrack() { return _sigTrackPlay; } + // Emitted when the list has changed + Wt::Signal< void >& tracksUpdated() { return _sigTracksUpdated; } // Slots void handlePlaybackComplete(void); @@ -72,7 +76,8 @@ class PlayQueue : public Wt::WTableView void setPlayingTrackPos(int newRowPos); void renumber(int firstId, int lastId); - Wt::Signal< boost::filesystem::path > _sigTrackPlay; + Wt::Signal< boost::filesystem::path, int > _sigTrackPlay; + Wt::Signal< void > _sigTracksUpdated; Database::Handler& _db; diff --git a/src/ui/settings/Settings.cpp b/src/ui/settings/Settings.cpp index 1a9ad4bc..a88eb695 100644 --- a/src/ui/settings/Settings.cpp +++ b/src/ui/settings/Settings.cpp @@ -59,12 +59,17 @@ _sessionData(sessionData) hLayout->addWidget(menu); hLayout->addWidget(contents, 1); - Wt::Dbo::Transaction transaction( sessionData.getDatabaseHandler().getSession()); - - ::Database::User::pointer user = sessionData.getDatabaseHandler().getCurrentUser(); + std::string userId; + bool userIsAdmin; + { + Wt::Dbo::Transaction transaction( sessionData.getDatabaseHandler().getSession()); + ::Database::User::pointer user = sessionData.getDatabaseHandler().getCurrentUser(); + userId = Database::User::getId(user); + userIsAdmin = user->isAdmin(); + } menu->addItem("Audio", new AudioFormView(sessionData)); - if (user->isAdmin()) + if (userIsAdmin) { MediaDirectories* mediaDirectory = new MediaDirectories(sessionData); mediaDirectory->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged); @@ -78,7 +83,7 @@ _sessionData(sessionData) } else { - menu->addItem("Account", new AccountFormView(sessionData, Database::User::getId(user))); + menu->addItem("Account", new AccountFormView(sessionData, userId)); } }