Merge branch 'develop'

This commit is contained in:
emeric
2018-09-13 20:52:05 +02:00
16 changed files with 123 additions and 96 deletions
+9
View File
@@ -51,6 +51,10 @@ a:hover {
cursor: pointer; cursor: pointer;
} }
.Lms-cluster:hover {
filter: brightness(85%);
}
.Lms-cluster-type-0 { .Lms-cluster-type-0 {
background-color: darkslategrey; background-color: darkslategrey;
margin-bottom: 4px; margin-bottom: 4px;
@@ -146,6 +150,7 @@ a:hover {
.Lms-explore-release-entry-name { .Lms-explore-release-entry-name {
margin-top: 4px; margin-top: 4px;
margin-bottom: 4px; margin-bottom: 4px;
font-weight: inherit;
} }
.Lms-explore-release-entry:hover { .Lms-explore-release-entry:hover {
@@ -156,6 +161,10 @@ a:hover {
text-align: center; text-align: center;
} }
.Lms-explore-release-entry-playing {
font-weight: bold;
}
.Lms-explore-artists-entry { .Lms-explore-artists-entry {
min-height: 48px; min-height: 48px;
} }
+32 -37
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2013 Emeric Poupon * Copyright (C) 2018 Emeric Poupon
* *
* This file is part of LMS. * This file is part of LMS.
* *
@@ -145,7 +145,7 @@ LmsApplication::finalize()
getApplicationGroup().postOthers([info] getApplicationGroup().postOthers([info]
{ {
LmsApp->getGroupEvents().appClosed(info); LmsApp->getEvents().appClosed(info);
}); });
getApplicationGroup().leave(); getApplicationGroup().leave();
@@ -304,7 +304,7 @@ LmsApplication::handleAuthEvent()
getApplicationGroup().postOthers([info] getApplicationGroup().postOthers([info]
{ {
LmsApp->getGroupEvents().appOpen(info); LmsApp->getEvents().appOpen(info);
}); });
createHome(); createHome();
@@ -407,7 +407,7 @@ LmsApplication::createHome()
Explore* explore = mainStack->addNew<Explore>(); Explore* explore = mainStack->addNew<Explore>();
PlayQueue* playqueue = mainStack->addNew<PlayQueue>(); PlayQueue* playqueue = mainStack->addNew<PlayQueue>();
PlayHistory* playhistory = mainStack->addNew<PlayHistory>(); mainStack->addNew<PlayHistory>();
mainStack->addNew<SettingsView>(); mainStack->addNew<SettingsView>();
// Admin stuff // Admin stuff
@@ -446,46 +446,46 @@ LmsApplication::createHome()
playqueue->playNext(); playqueue->playNext();
}); });
// Events from the PlayQueue playqueue->loadTrack.connect([=] (Database::IdType trackId, bool play)
playqueue->playTrack.connect(playhistory, &PlayHistory::addTrack); {
playqueue->playTrack.connect(explore, &Explore::handleTrackPlayed); _events.lastLoadedTrackId = trackId;
playqueue->playTrack.connect(player, &MediaPlayer::playTrack); _events.trackLoaded(trackId, play);
});
playqueue->playbackStop.connect(player, &MediaPlayer::stop); playqueue->trackUnload.connect([=]
{
_events.lastLoadedTrackId.reset();
_events.trackUnloaded();
});
// Events from MediaScanner // Events from MediaScanner
std::string sessionId = LmsApp->sessionId(); std::string sessionId = LmsApp->sessionId();
_scanner.scanComplete().connect([=] (Scanner::MediaScanner::Stats stats) _scanner.scanComplete().connect([=] (Scanner::MediaScanner::Stats stats)
{ {
// Runs from media scanner context
Wt::WServer::instance()->post(sessionId, [=] Wt::WServer::instance()->post(sessionId, [=]
{ {
bool changes = false; _events.dbScanned.emit(stats);
triggerUpdate();
if (_isAdmin)
{
notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete")
.arg(static_cast<unsigned>(stats.nbFiles()))
.arg(static_cast<unsigned>(stats.additions))
.arg(static_cast<unsigned>(stats.updates))
.arg(static_cast<unsigned>(stats.deletions))
.arg(static_cast<unsigned>(stats.nbDuplicates()))
.arg(static_cast<unsigned>(stats.nbErrors())));
changes = true;
}
if (stats.nbChanges() > 0)
{
explore->handleDbChanged();
changes = true;
}
if (changes)
triggerUpdate();
}); });
}); });
_events.dbScanned.connect([=] (Scanner::MediaScanner::Stats stats)
{
if (_isAdmin)
{
notifyMsg(MsgType::Info, Wt::WString::tr("Lms.Admin.Database.scan-complete")
.arg(static_cast<unsigned>(stats.nbFiles()))
.arg(static_cast<unsigned>(stats.additions))
.arg(static_cast<unsigned>(stats.updates))
.arg(static_cast<unsigned>(stats.deletions))
.arg(static_cast<unsigned>(stats.nbDuplicates()))
.arg(static_cast<unsigned>(stats.nbErrors())));
}
});
// Events from Application group // Events from Application group
_groupEvents.appOpen.connect([=] (LmsApplicationInfo info) _events.appOpen.connect([=] (LmsApplicationInfo info)
{ {
// Only one active session by user // Only one active session by user
if (!LmsApp->getUser()->isDemo()) if (!LmsApp->getUser()->isDemo())
@@ -495,11 +495,6 @@ LmsApplication::createHome()
} }
}); });
_groupEvents.appClosed.connect([=] (LmsApplicationInfo info)
{
;
});
internalPathChanged().connect(std::bind([=] internalPathChanged().connect(std::bind([=]
{ {
handlePathChange(mainStack, _isAdmin); handlePathChange(mainStack, _isAdmin);
+15 -3
View File
@@ -38,12 +38,24 @@ namespace UserInterface {
class TranscodeResource; class TranscodeResource;
class ImageResource; class ImageResource;
struct GroupEvents // Events that can be listen to anywhere in the application
struct Events
{ {
// Events relative to group
Wt::Signal<LmsApplicationInfo> appOpen; Wt::Signal<LmsApplicationInfo> appOpen;
Wt::Signal<LmsApplicationInfo> appClosed; Wt::Signal<LmsApplicationInfo> appClosed;
// A track is being loaded
Wt::Signal<Database::IdType /* trackId */, bool /* play */> trackLoaded;
boost::optional<Database::IdType> lastLoadedTrackId;
// Unload current track
Wt::Signal<> trackUnloaded;
// A database scan is complete
Wt::Signal<Scanner::MediaScanner::Stats> dbScanned;
}; };
// Used to classify the message sent to the user
enum class MsgType enum class MsgType
{ {
Success, Success,
@@ -73,7 +85,7 @@ class LmsApplication : public Wt::WApplication
Scanner::MediaScanner& getMediaScanner() { return _scanner; } Scanner::MediaScanner& getMediaScanner() { return _scanner; }
GroupEvents& getGroupEvents() { return _groupEvents; } Events& getEvents() { return _events; }
// Utils // Utils
void goHome(); void goHome();
@@ -105,7 +117,7 @@ class LmsApplication : public Wt::WApplication
Wt::Signal<> _preQuit; Wt::Signal<> _preQuit;
Database::Handler _db; Database::Handler _db;
LmsApplicationGroupContainer& _appGroups; LmsApplicationGroupContainer& _appGroups;
GroupEvents _groupEvents; Events _events;
Wt::WString _userIdentity; Wt::WString _userIdentity;
Auth* _auth; Auth* _auth;
Scanner::MediaScanner& _scanner; Scanner::MediaScanner& _scanner;
+5 -2
View File
@@ -48,10 +48,13 @@ MediaPlayer::MediaPlayer()
_release->setTextFormat(Wt::TextFormat::Plain); _release->setTextFormat(Wt::TextFormat::Plain);
wApp->doJavaScript("LMS.mediaplayer.init(" + jsRef() + ")"); wApp->doJavaScript("LMS.mediaplayer.init(" + jsRef() + ")");
LmsApp->getEvents().trackLoaded.connect(this, &MediaPlayer::loadTrack);
LmsApp->getEvents().trackUnloaded.connect(this, &MediaPlayer::stop);
} }
void void
MediaPlayer::playTrack(Database::IdType trackId) MediaPlayer::loadTrack(Database::IdType trackId, bool play)
{ {
LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId; LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId;
@@ -72,7 +75,7 @@ MediaPlayer::playTrack(Database::IdType trackId)
<< " duration: " << std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count() << "," << " duration: " << std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count() << ","
<< " imgResource: \"" << imgResource << "\"," << " 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() << "'"; LMS_LOG(UI, DEBUG) << "Runing js = '" << oss.str() << "'";
+2 -3
View File
@@ -33,15 +33,14 @@ class MediaPlayer : public Wt::WTemplate
public: public:
MediaPlayer(); MediaPlayer();
void stop();
void playTrack(Database::IdType trackId);
// Signals // Signals
Wt::JSignal<> playbackEnded; Wt::JSignal<> playbackEnded;
Wt::JSignal<> playPrevious; Wt::JSignal<> playPrevious;
Wt::JSignal<> playNext; Wt::JSignal<> playNext;
private: private:
void stop();
void loadTrack(Database::IdType trackId, bool play);
Wt::WText* _title; Wt::WText* _title;
Wt::WAnchor* _release; Wt::WAnchor* _release;
+8 -10
View File
@@ -74,19 +74,17 @@ PlayHistory::PlayHistory()
addSome(); 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(); 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 void
PlayHistory::addSome() PlayHistory::addSome()
{ {
-2
View File
@@ -32,8 +32,6 @@ class PlayHistory : public Wt::WTemplate
public: public:
PlayHistory(); PlayHistory();
void addTrack(Database::IdType trackId);
private: private:
void addSome(); void addSome();
+9 -9
View File
@@ -92,7 +92,7 @@ PlayQueue::PlayQueue()
{ {
LmsApp->post([=] LmsApp->post([=]
{ {
play(LmsApp->getUser()->getCurPlayingTrackPos()); load(LmsApp->getUser()->getCurPlayingTrackPos(), false);
}); });
} }
} }
@@ -135,11 +135,11 @@ PlayQueue::stop()
{ {
updateCurrentTrack(false); updateCurrentTrack(false);
_trackPos.reset(); _trackPos.reset();
playbackStop.emit(); trackUnload.emit();
} }
void void
PlayQueue::play(std::size_t pos) PlayQueue::load(std::size_t pos, bool play)
{ {
updateCurrentTrack(false); updateCurrentTrack(false);
@@ -171,7 +171,7 @@ PlayQueue::play(std::size_t pos)
LmsApp->getUser().modify()->setCurPlayingTrackPos(pos); LmsApp->getUser().modify()->setCurPlayingTrackPos(pos);
} }
playTrack.emit(trackId); loadTrack.emit(trackId, play);
} }
void void
@@ -183,7 +183,7 @@ PlayQueue::playPrevious()
if (*_trackPos == 0) if (*_trackPos == 0)
stop(); stop();
else else
play(*_trackPos - 1); load(*_trackPos - 1, true);
} }
void void
@@ -191,11 +191,11 @@ PlayQueue::playNext()
{ {
if (!_trackPos) if (!_trackPos)
{ {
play(0); load(0, true);
return; return;
} }
play(*_trackPos + 1); load(*_trackPos + 1, true);
} }
void void
@@ -257,7 +257,7 @@ PlayQueue::playTracks(const std::vector<Database::Track::pointer>& tracks)
clearTracks(); clearTracks();
enqueueTracks(tracks); 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)); 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); auto pos = _entriesContainer->indexOf(entry);
if (pos >= 0) if (pos >= 0)
play(pos); load(pos, true);
})); }));
Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.delete"), Wt::TextFormat::XHTML); Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.delete"), Wt::TextFormat::XHTML);
+4 -4
View File
@@ -47,11 +47,11 @@ class PlayQueue : public Wt::WTemplate
// play the previous track in the queue // play the previous track in the queue
void playPrevious(); void playPrevious();
// Signal emitted when a track is to be played // Signal emitted when a track is to be load(and optionally played)
Wt::Signal<Database::IdType> playTrack; Wt::Signal<Database::IdType /*trackId*/, bool /*play*/> loadTrack;
// Signal emitted when play has to be stopped // Signal emitted when play has to be stopped
Wt::Signal<> playbackStop; Wt::Signal<> trackUnload;
private: private:
Database::TrackList::pointer getTrackList(); Database::TrackList::pointer getTrackList();
@@ -64,7 +64,7 @@ class PlayQueue : public Wt::WTemplate
void updateInfo(); void updateInfo();
void updateCurrentTrack(bool selected); void updateCurrentTrack(bool selected);
void play(std::size_t pos); void load(std::size_t pos, bool play);
void stop(); void stop();
boost::optional<Database::IdType> _tracklistId; boost::optional<Database::IdType> _tracklistId;
+10
View File
@@ -57,6 +57,16 @@ ArtistsInfo::ArtistsInfo()
_mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played"); _mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played");
_recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added"); _recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added");
LmsApp->getEvents().dbScanned.connect([=]
{
refreshRecentlyAdded();
});
LmsApp->getEvents().trackLoaded.connect([=]
{
refreshMostPlayed();
});
refreshMostPlayed(); refreshMostPlayed();
refreshRecentlyAdded(); refreshRecentlyAdded();
} }
-18
View File
@@ -152,32 +152,14 @@ Explore::Explore()
Wt::WStackedWidget* infoStack = bindNew<Wt::WStackedWidget>("info"); Wt::WStackedWidget* infoStack = bindNew<Wt::WStackedWidget>("info");
auto artistsInfo = std::make_unique<ArtistsInfo>(); auto artistsInfo = std::make_unique<ArtistsInfo>();
auto artistsInfoRaw = artistsInfo.get();
infoStack->addWidget(std::move(artistsInfo)); infoStack->addWidget(std::move(artistsInfo));
auto releasesInfo = std::make_unique<ReleasesInfo>(); auto releasesInfo = std::make_unique<ReleasesInfo>();
auto releasesInfoRaw = releasesInfo.get();
infoStack->addWidget(std::move(releasesInfo)); infoStack->addWidget(std::move(releasesInfo));
auto tracksInfo = std::make_unique<TracksInfo>(); auto tracksInfo = std::make_unique<TracksInfo>();
auto tracksInfoRaw = tracksInfo.get();
infoStack->addWidget(std::move(tracksInfo)); 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([=] wApp->internalPathChanged().connect(std::bind([=]
{ {
handleContentsPathChange(contentsStack); handleContentsPathChange(contentsStack);
-6
View File
@@ -36,9 +36,6 @@ class Explore : public Wt::WTemplate
Wt::Signal<std::vector<Database::Track::pointer>> tracksAdd; Wt::Signal<std::vector<Database::Track::pointer>> tracksAdd;
Wt::Signal<std::vector<Database::Track::pointer>> tracksPlay; Wt::Signal<std::vector<Database::Track::pointer>> tracksPlay;
void handleDbChanged() { _dbChanged.emit(); };
void handleTrackPlayed(Database::IdType trackId) { _trackPlayed.emit(); }
private: private:
void handleArtistAdd(Database::IdType id); void handleArtistAdd(Database::IdType id);
@@ -51,9 +48,6 @@ class Explore : public Wt::WTemplate
void handleTracksPlay(std::vector<Database::Track::pointer> tracks); void handleTracksPlay(std::vector<Database::Track::pointer> tracks);
Filters* _filters; Filters* _filters;
Wt::Signal<> _dbChanged;
Wt::Signal<> _trackPlayed;
}; };
} // namespace UserInterface } // namespace UserInterface
+8 -1
View File
@@ -62,7 +62,6 @@ Release::refresh()
return; return;
clear(); clear();
auto releaseId = readAs<Database::IdType>(wApp->internalPathNextPart("/release/")); auto releaseId = readAs<Database::IdType>(wApp->internalPathNextPart("/release/"));
if (!releaseId) if (!releaseId)
return; return;
@@ -192,6 +191,14 @@ Release::refresh()
{ {
trackAdd.emit(trackId); 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");
} }
} }
+10
View File
@@ -77,6 +77,16 @@ ReleasesInfo::ReleasesInfo()
_mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played"); _mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played");
_recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added"); _recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added");
LmsApp->getEvents().dbScanned.connect([=]
{
refreshRecentlyAdded();
});
LmsApp->getEvents().trackLoaded.connect([=]
{
refreshMostPlayed();
});
refreshRecentlyAdded(); refreshRecentlyAdded();
refreshMostPlayed(); refreshMostPlayed();
} }
+1 -1
View File
@@ -29,10 +29,10 @@ class ReleasesInfo : public Wt::WTemplate
public: public:
ReleasesInfo(); ReleasesInfo();
private:
void refreshRecentlyAdded(); void refreshRecentlyAdded();
void refreshMostPlayed(); void refreshMostPlayed();
private:
Wt::WContainerWidget* _mostPlayedContainer; Wt::WContainerWidget* _mostPlayedContainer;
Wt::WContainerWidget* _recentlyAddedContainer; Wt::WContainerWidget* _recentlyAddedContainer;
}; };
+10
View File
@@ -57,6 +57,16 @@ TracksInfo::TracksInfo()
_mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played"); _mostPlayedContainer = bindNew<Wt::WContainerWidget>("most-played");
_recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added"); _recentlyAddedContainer = bindNew<Wt::WContainerWidget>("recently-added");
LmsApp->getEvents().dbScanned.connect([=]
{
refreshRecentlyAdded();
});
LmsApp->getEvents().trackLoaded.connect([=]
{
refreshMostPlayed();
});
refreshMostPlayed(); refreshMostPlayed();
refreshRecentlyAdded(); refreshRecentlyAdded();
} }