diff --git a/docroot/css/lms.css b/docroot/css/lms.css index 641d288f..f4049419 100644 --- a/docroot/css/lms.css +++ b/docroot/css/lms.css @@ -51,6 +51,10 @@ a:hover { cursor: pointer; } +.Lms-cluster:hover { + filter: brightness(85%); +} + .Lms-cluster-type-0 { background-color: darkslategrey; margin-bottom: 4px; @@ -146,6 +150,7 @@ a:hover { .Lms-explore-release-entry-name { margin-top: 4px; margin-bottom: 4px; + font-weight: inherit; } .Lms-explore-release-entry:hover { @@ -156,6 +161,10 @@ a:hover { text-align: center; } +.Lms-explore-release-entry-playing { + font-weight: bold; +} + .Lms-explore-artists-entry { min-height: 48px; } diff --git a/src/ui/LmsApplication.cpp b/src/ui/LmsApplication.cpp index b231940a..354ceeb4 100644 --- a/src/ui/LmsApplication.cpp +++ b/src/ui/LmsApplication.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Emeric Poupon + * Copyright (C) 2018 Emeric Poupon * * This file is part of LMS. * @@ -145,7 +145,7 @@ LmsApplication::finalize() getApplicationGroup().postOthers([info] { - LmsApp->getGroupEvents().appClosed(info); + LmsApp->getEvents().appClosed(info); }); getApplicationGroup().leave(); @@ -304,7 +304,7 @@ LmsApplication::handleAuthEvent() getApplicationGroup().postOthers([info] { - LmsApp->getGroupEvents().appOpen(info); + LmsApp->getEvents().appOpen(info); }); createHome(); @@ -407,7 +407,7 @@ LmsApplication::createHome() Explore* explore = mainStack->addNew(); PlayQueue* playqueue = mainStack->addNew(); - PlayHistory* playhistory = mainStack->addNew(); + mainStack->addNew(); mainStack->addNew(); // Admin stuff @@ -446,46 +446,46 @@ LmsApplication::createHome() playqueue->playNext(); }); - // Events from the PlayQueue - playqueue->playTrack.connect(playhistory, &PlayHistory::addTrack); - playqueue->playTrack.connect(explore, &Explore::handleTrackPlayed); - playqueue->playTrack.connect(player, &MediaPlayer::playTrack); + playqueue->loadTrack.connect([=] (Database::IdType trackId, bool play) + { + _events.lastLoadedTrackId = trackId; + _events.trackLoaded(trackId, play); + }); - playqueue->playbackStop.connect(player, &MediaPlayer::stop); + playqueue->trackUnload.connect([=] + { + _events.lastLoadedTrackId.reset(); + _events.trackUnloaded(); + }); // Events from MediaScanner std::string sessionId = LmsApp->sessionId(); _scanner.scanComplete().connect([=] (Scanner::MediaScanner::Stats stats) { + // Runs from media scanner context Wt::WServer::instance()->post(sessionId, [=] { - bool changes = false; - - if (_isAdmin) - { - notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete") - .arg(static_cast(stats.nbFiles())) - .arg(static_cast(stats.additions)) - .arg(static_cast(stats.updates)) - .arg(static_cast(stats.deletions)) - .arg(static_cast(stats.nbDuplicates())) - .arg(static_cast(stats.nbErrors()))); - changes = true; - } - - if (stats.nbChanges() > 0) - { - explore->handleDbChanged(); - changes = true; - } - - if (changes) - triggerUpdate(); + _events.dbScanned.emit(stats); + triggerUpdate(); }); }); + _events.dbScanned.connect([=] (Scanner::MediaScanner::Stats stats) + { + if (_isAdmin) + { + notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete") + .arg(static_cast(stats.nbFiles())) + .arg(static_cast(stats.additions)) + .arg(static_cast(stats.updates)) + .arg(static_cast(stats.deletions)) + .arg(static_cast(stats.nbDuplicates())) + .arg(static_cast(stats.nbErrors()))); + } + }); + // Events from Application group - _groupEvents.appOpen.connect([=] (LmsApplicationInfo info) + _events.appOpen.connect([=] (LmsApplicationInfo info) { // Only one active session by user if (!LmsApp->getUser()->isDemo()) @@ -495,11 +495,6 @@ LmsApplication::createHome() } }); - _groupEvents.appClosed.connect([=] (LmsApplicationInfo info) - { - ; - }); - internalPathChanged().connect(std::bind([=] { handlePathChange(mainStack, _isAdmin); diff --git a/src/ui/LmsApplication.hpp b/src/ui/LmsApplication.hpp index 5bb3eb31..00b5a07d 100644 --- a/src/ui/LmsApplication.hpp +++ b/src/ui/LmsApplication.hpp @@ -38,12 +38,24 @@ namespace UserInterface { class TranscodeResource; class ImageResource; -struct GroupEvents +// Events that can be listen to anywhere in the application +struct Events { + // Events relative to group Wt::Signal appOpen; Wt::Signal appClosed; + + // A track is being loaded + Wt::Signal trackLoaded; + boost::optional lastLoadedTrackId; + // Unload current track + Wt::Signal<> trackUnloaded; + + // A database scan is complete + Wt::Signal dbScanned; }; +// Used to classify the message sent to the user enum class MsgType { Success, @@ -73,7 +85,7 @@ class LmsApplication : public Wt::WApplication Scanner::MediaScanner& getMediaScanner() { return _scanner; } - GroupEvents& getGroupEvents() { return _groupEvents; } + Events& getEvents() { return _events; } // Utils void goHome(); @@ -105,7 +117,7 @@ class LmsApplication : public Wt::WApplication Wt::Signal<> _preQuit; Database::Handler _db; LmsApplicationGroupContainer& _appGroups; - GroupEvents _groupEvents; + Events _events; Wt::WString _userIdentity; Auth* _auth; Scanner::MediaScanner& _scanner; diff --git a/src/ui/MediaPlayer.cpp b/src/ui/MediaPlayer.cpp index 521888f2..6afa3f8c 100644 --- a/src/ui/MediaPlayer.cpp +++ b/src/ui/MediaPlayer.cpp @@ -48,10 +48,13 @@ MediaPlayer::MediaPlayer() _release->setTextFormat(Wt::TextFormat::Plain); wApp->doJavaScript("LMS.mediaplayer.init(" + jsRef() + ")"); + + LmsApp->getEvents().trackLoaded.connect(this, &MediaPlayer::loadTrack); + LmsApp->getEvents().trackUnloaded.connect(this, &MediaPlayer::stop); } void -MediaPlayer::playTrack(Database::IdType trackId) +MediaPlayer::loadTrack(Database::IdType trackId, bool play) { LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId; @@ -72,7 +75,7 @@ MediaPlayer::playTrack(Database::IdType trackId) << " duration: " << std::chrono::duration_cast(track->getDuration()).count() << "," << " imgResource: \"" << imgResource << "\"," << "};"; - oss << "LMS.mediaplayer.loadTrack(params, true)"; // true to autoplay + oss << "LMS.mediaplayer.loadTrack(params, " << (play ? "true" : "false") << ")"; // true to autoplay LMS_LOG(UI, DEBUG) << "Runing js = '" << oss.str() << "'"; diff --git a/src/ui/MediaPlayer.hpp b/src/ui/MediaPlayer.hpp index d5d83315..538d64b1 100644 --- a/src/ui/MediaPlayer.hpp +++ b/src/ui/MediaPlayer.hpp @@ -33,15 +33,14 @@ class MediaPlayer : public Wt::WTemplate public: MediaPlayer(); - void stop(); - void playTrack(Database::IdType trackId); - // Signals Wt::JSignal<> playbackEnded; Wt::JSignal<> playPrevious; Wt::JSignal<> playNext; private: + void stop(); + void loadTrack(Database::IdType trackId, bool play); Wt::WText* _title; Wt::WAnchor* _release; diff --git a/src/ui/PlayHistoryView.cpp b/src/ui/PlayHistoryView.cpp index ecdee58b..ac44f95a 100644 --- a/src/ui/PlayHistoryView.cpp +++ b/src/ui/PlayHistoryView.cpp @@ -74,19 +74,17 @@ PlayHistory::PlayHistory() addSome(); }); + LmsApp->getEvents().trackLoaded.connect([=](Database::IdType trackId, bool /* play */) + { + Wt::Dbo::Transaction transaction (LmsApp->getDboSession()); + + auto trackEntry = LmsApp->getUser()->getPlayedTrackList().modify()->add(trackId); + _entriesContainer->insertWidget(0, createEntry(trackEntry->getTrack())); + }); + addSome(); } -void -PlayHistory::addTrack(Database::IdType trackId) -{ - Wt::Dbo::Transaction transaction (LmsApp->getDboSession()); - - auto trackEntry = LmsApp->getUser()->getPlayedTrackList().modify()->add(trackId); - _entriesContainer->insertWidget(0, createEntry(trackEntry->getTrack())); -} - - void PlayHistory::addSome() { diff --git a/src/ui/PlayHistoryView.hpp b/src/ui/PlayHistoryView.hpp index 7bd5c88e..36cf73c2 100644 --- a/src/ui/PlayHistoryView.hpp +++ b/src/ui/PlayHistoryView.hpp @@ -32,8 +32,6 @@ class PlayHistory : public Wt::WTemplate public: PlayHistory(); - void addTrack(Database::IdType trackId); - private: void addSome(); diff --git a/src/ui/PlayQueueView.cpp b/src/ui/PlayQueueView.cpp index 4c7bf974..6011c2ed 100644 --- a/src/ui/PlayQueueView.cpp +++ b/src/ui/PlayQueueView.cpp @@ -92,7 +92,7 @@ PlayQueue::PlayQueue() { LmsApp->post([=] { - play(LmsApp->getUser()->getCurPlayingTrackPos()); + load(LmsApp->getUser()->getCurPlayingTrackPos(), false); }); } } @@ -135,11 +135,11 @@ PlayQueue::stop() { updateCurrentTrack(false); _trackPos.reset(); - playbackStop.emit(); + trackUnload.emit(); } void -PlayQueue::play(std::size_t pos) +PlayQueue::load(std::size_t pos, bool play) { updateCurrentTrack(false); @@ -171,7 +171,7 @@ PlayQueue::play(std::size_t pos) LmsApp->getUser().modify()->setCurPlayingTrackPos(pos); } - playTrack.emit(trackId); + loadTrack.emit(trackId, play); } void @@ -183,7 +183,7 @@ PlayQueue::playPrevious() if (*_trackPos == 0) stop(); else - play(*_trackPos - 1); + load(*_trackPos - 1, true); } void @@ -191,11 +191,11 @@ PlayQueue::playNext() { if (!_trackPos) { - play(0); + load(0, true); return; } - play(*_trackPos + 1); + load(*_trackPos + 1, true); } void @@ -257,7 +257,7 @@ PlayQueue::playTracks(const std::vector& tracks) clearTracks(); enqueueTracks(tracks); - play(0); + load(0, true); LmsApp->notifyMsg(MsgType::Info, Wt::WString::tr("Lms.PlayQueue.nb-tracks-playing").arg(tracks.size()), std::chrono::milliseconds(2000)); } @@ -298,7 +298,7 @@ PlayQueue::addSome() { auto pos = _entriesContainer->indexOf(entry); if (pos >= 0) - play(pos); + load(pos, true); })); Wt::WText* delBtn = entry->bindNew("del-btn", Wt::WString::tr("Lms.PlayQueue.delete"), Wt::TextFormat::XHTML); diff --git a/src/ui/PlayQueueView.hpp b/src/ui/PlayQueueView.hpp index 7d0dacc8..596486c8 100644 --- a/src/ui/PlayQueueView.hpp +++ b/src/ui/PlayQueueView.hpp @@ -47,11 +47,11 @@ class PlayQueue : public Wt::WTemplate // play the previous track in the queue void playPrevious(); - // Signal emitted when a track is to be played - Wt::Signal playTrack; + // Signal emitted when a track is to be load(and optionally played) + Wt::Signal loadTrack; // Signal emitted when play has to be stopped - Wt::Signal<> playbackStop; + Wt::Signal<> trackUnload; private: Database::TrackList::pointer getTrackList(); @@ -64,7 +64,7 @@ class PlayQueue : public Wt::WTemplate void updateInfo(); void updateCurrentTrack(bool selected); - void play(std::size_t pos); + void load(std::size_t pos, bool play); void stop(); boost::optional _tracklistId; diff --git a/src/ui/explore/ArtistsInfoView.cpp b/src/ui/explore/ArtistsInfoView.cpp index ec389a3b..d64efadf 100644 --- a/src/ui/explore/ArtistsInfoView.cpp +++ b/src/ui/explore/ArtistsInfoView.cpp @@ -57,6 +57,16 @@ ArtistsInfo::ArtistsInfo() _mostPlayedContainer = bindNew("most-played"); _recentlyAddedContainer = bindNew("recently-added"); + LmsApp->getEvents().dbScanned.connect([=] + { + refreshRecentlyAdded(); + }); + + LmsApp->getEvents().trackLoaded.connect([=] + { + refreshMostPlayed(); + }); + refreshMostPlayed(); refreshRecentlyAdded(); } diff --git a/src/ui/explore/Explore.cpp b/src/ui/explore/Explore.cpp index b77320d2..7adbe212 100644 --- a/src/ui/explore/Explore.cpp +++ b/src/ui/explore/Explore.cpp @@ -152,32 +152,14 @@ Explore::Explore() Wt::WStackedWidget* infoStack = bindNew("info"); auto artistsInfo = std::make_unique(); - auto artistsInfoRaw = artistsInfo.get(); infoStack->addWidget(std::move(artistsInfo)); auto releasesInfo = std::make_unique(); - auto releasesInfoRaw = releasesInfo.get(); infoStack->addWidget(std::move(releasesInfo)); auto tracksInfo = std::make_unique(); - auto tracksInfoRaw = tracksInfo.get(); infoStack->addWidget(std::move(tracksInfo)); - _dbChanged.connect([=] - { - artistsInfoRaw->refreshRecentlyAdded(); - releasesInfoRaw->refreshRecentlyAdded(); - tracksInfoRaw->refreshRecentlyAdded(); - }); - - _trackPlayed.connect([=] - { - artistsInfoRaw->refreshMostPlayed(); - releasesInfoRaw->refreshMostPlayed(); - tracksInfoRaw->refreshMostPlayed(); - }); - - wApp->internalPathChanged().connect(std::bind([=] { handleContentsPathChange(contentsStack); diff --git a/src/ui/explore/Explore.hpp b/src/ui/explore/Explore.hpp index e00c8bc7..ea538876 100644 --- a/src/ui/explore/Explore.hpp +++ b/src/ui/explore/Explore.hpp @@ -36,9 +36,6 @@ class Explore : public Wt::WTemplate Wt::Signal> tracksAdd; Wt::Signal> tracksPlay; - void handleDbChanged() { _dbChanged.emit(); }; - void handleTrackPlayed(Database::IdType trackId) { _trackPlayed.emit(); } - private: void handleArtistAdd(Database::IdType id); @@ -51,9 +48,6 @@ class Explore : public Wt::WTemplate void handleTracksPlay(std::vector tracks); Filters* _filters; - - Wt::Signal<> _dbChanged; - Wt::Signal<> _trackPlayed; }; } // namespace UserInterface diff --git a/src/ui/explore/ReleaseView.cpp b/src/ui/explore/ReleaseView.cpp index e6ed8185..f6b4015f 100644 --- a/src/ui/explore/ReleaseView.cpp +++ b/src/ui/explore/ReleaseView.cpp @@ -62,7 +62,6 @@ Release::refresh() return; clear(); - auto releaseId = readAs(wApp->internalPathNextPart("/release/")); if (!releaseId) return; @@ -192,6 +191,14 @@ Release::refresh() { trackAdd.emit(trackId); })); + + LmsApp->getEvents().trackLoaded.connect(entry, [=] (Database::IdType loadedTrackId, bool /*play*/) + { + entry->toggleStyleClass("Lms-explore-release-entry-playing", loadedTrackId == trackId); + }); + + if (LmsApp->getEvents().lastLoadedTrackId && *LmsApp->getEvents().lastLoadedTrackId == trackId) + entry->addStyleClass("Lms-explore-release-entry-playing"); } } diff --git a/src/ui/explore/ReleasesInfoView.cpp b/src/ui/explore/ReleasesInfoView.cpp index f30f0dea..994f2fe5 100644 --- a/src/ui/explore/ReleasesInfoView.cpp +++ b/src/ui/explore/ReleasesInfoView.cpp @@ -77,6 +77,16 @@ ReleasesInfo::ReleasesInfo() _mostPlayedContainer = bindNew("most-played"); _recentlyAddedContainer = bindNew("recently-added"); + LmsApp->getEvents().dbScanned.connect([=] + { + refreshRecentlyAdded(); + }); + + LmsApp->getEvents().trackLoaded.connect([=] + { + refreshMostPlayed(); + }); + refreshRecentlyAdded(); refreshMostPlayed(); } diff --git a/src/ui/explore/ReleasesInfoView.hpp b/src/ui/explore/ReleasesInfoView.hpp index fe402421..a74edf84 100644 --- a/src/ui/explore/ReleasesInfoView.hpp +++ b/src/ui/explore/ReleasesInfoView.hpp @@ -29,10 +29,10 @@ class ReleasesInfo : public Wt::WTemplate public: ReleasesInfo(); + private: void refreshRecentlyAdded(); void refreshMostPlayed(); - private: Wt::WContainerWidget* _mostPlayedContainer; Wt::WContainerWidget* _recentlyAddedContainer; }; diff --git a/src/ui/explore/TracksInfoView.cpp b/src/ui/explore/TracksInfoView.cpp index ed2c9b18..509bf186 100644 --- a/src/ui/explore/TracksInfoView.cpp +++ b/src/ui/explore/TracksInfoView.cpp @@ -57,6 +57,16 @@ TracksInfo::TracksInfo() _mostPlayedContainer = bindNew("most-played"); _recentlyAddedContainer = bindNew("recently-added"); + LmsApp->getEvents().dbScanned.connect([=] + { + refreshRecentlyAdded(); + }); + + LmsApp->getEvents().trackLoaded.connect([=] + { + refreshMostPlayed(); + }); + refreshMostPlayed(); refreshRecentlyAdded(); }