Added an ApplicationGroup system to track the multiple sessions open by a user
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
<message id="Lms.password-confirm">Confirm password</message>
|
<message id="Lms.password-confirm">Confirm password</message>
|
||||||
<message id="Lms.passwords-dont-match">Passwords don't match</message>
|
<message id="Lms.passwords-dont-match">Passwords don't match</message>
|
||||||
<message id="Lms.quit-confirm">Are you sure?</message>
|
<message id="Lms.quit-confirm">Are you sure?</message>
|
||||||
|
<message id="Lms.quit-other-session">Another session has been open. Reopen this one?</message>
|
||||||
<message id="Lms.save">Save</message>
|
<message id="Lms.save">Save</message>
|
||||||
|
|
||||||
<!--Administration-->
|
<!--Administration-->
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ lms_SOURCES = \
|
|||||||
$(srcdir)/scanner/MediaScanner.cpp \
|
$(srcdir)/scanner/MediaScanner.cpp \
|
||||||
$(srcdir)/ui/Auth.cpp \
|
$(srcdir)/ui/Auth.cpp \
|
||||||
$(srcdir)/ui/LmsApplication.cpp \
|
$(srcdir)/ui/LmsApplication.cpp \
|
||||||
|
$(srcdir)/ui/LmsApplicationGroup.cpp \
|
||||||
$(srcdir)/ui/MediaPlayer.cpp \
|
$(srcdir)/ui/MediaPlayer.cpp \
|
||||||
$(srcdir)/ui/PlayQueueView.cpp \
|
$(srcdir)/ui/PlayQueueView.cpp \
|
||||||
$(srcdir)/ui/SettingsView.cpp \
|
$(srcdir)/ui/SettingsView.cpp \
|
||||||
|
|||||||
+2
-1
@@ -122,12 +122,13 @@ int main(int argc, char* argv[])
|
|||||||
// Initializing a connection pool to the database that will be shared along services
|
// Initializing a connection pool to the database that will be shared along services
|
||||||
auto connectionPool = Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db");
|
auto connectionPool = Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db");
|
||||||
|
|
||||||
|
UserInterface::LmsApplicationGroups appGroups;
|
||||||
Scanner::MediaScanner scanner(*connectionPool);
|
Scanner::MediaScanner scanner(*connectionPool);
|
||||||
|
|
||||||
// bind entry point
|
// bind entry point
|
||||||
server.addEntryPoint(Wt::EntryPointType::Application,
|
server.addEntryPoint(Wt::EntryPointType::Application,
|
||||||
std::bind(UserInterface::LmsApplication::create,
|
std::bind(UserInterface::LmsApplication::create,
|
||||||
std::placeholders::_1, std::ref(*connectionPool), std::ref(scanner)));
|
std::placeholders::_1, std::ref(*connectionPool), std::ref(appGroups), std::ref(scanner)));
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
LMS_LOG(MAIN, INFO) << "Starting Media scanner...";
|
LMS_LOG(MAIN, INFO) << "Starting Media scanner...";
|
||||||
|
|||||||
+80
-34
@@ -50,13 +50,13 @@
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
std::unique_ptr<Wt::WApplication>
|
std::unique_ptr<Wt::WApplication>
|
||||||
LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner)
|
LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, LmsApplicationGroups& appGroups, Scanner::MediaScanner& scanner)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* You could read information from the environment to decide whether
|
* You could read information from the environment to decide whether
|
||||||
* the user has permission to start a new application
|
* the user has permission to start a new application
|
||||||
*/
|
*/
|
||||||
return std::make_unique<LmsApplication>(env, connectionPool, scanner);
|
return std::make_unique<LmsApplication>(env, connectionPool, appGroups, scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
LmsApplication*
|
LmsApplication*
|
||||||
@@ -71,9 +71,10 @@ LmsApplication::instance()
|
|||||||
* constructor so it is typically also an argument for your custom
|
* constructor so it is typically also an argument for your custom
|
||||||
* application constructor.
|
* application constructor.
|
||||||
*/
|
*/
|
||||||
LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner)
|
LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, LmsApplicationGroups& appGroups, Scanner::MediaScanner& scanner)
|
||||||
: Wt::WApplication(env),
|
: Wt::WApplication(env),
|
||||||
_db(connectionPool),
|
_db(connectionPool),
|
||||||
|
_appGroups(appGroups),
|
||||||
_scanner(scanner),
|
_scanner(scanner),
|
||||||
_imageResource(nullptr),
|
_imageResource(nullptr),
|
||||||
_transcodeResource(nullptr)
|
_transcodeResource(nullptr)
|
||||||
@@ -125,23 +126,23 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LmsApp->getDb().getLogin().changed().connect(std::bind([=]
|
LmsApp->getDb().getLogin().changed().connect(this, &LmsApplication::handleAuthEvent);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handleAuthEvent();
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
LMS_LOG(UI, ERROR) << "Error while handling auth event: " << e.what();
|
|
||||||
throw std::runtime_error("Internal error");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
_auth = root()->addNew<Auth>();
|
_auth = root()->addNew<Auth>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LmsApplication::~LmsApplication()
|
||||||
|
{
|
||||||
|
LmsApplicationInfo info = LmsApplicationInfo::fromEnvironment(environment());
|
||||||
|
|
||||||
|
getApplicationGroup().postOthers([info]
|
||||||
|
{
|
||||||
|
LmsApp->getGroupEvents().appClosed(info);
|
||||||
|
});
|
||||||
|
|
||||||
|
getApplicationGroup().leave();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Wt::WAnchor>
|
std::unique_ptr<Wt::WAnchor>
|
||||||
LmsApplication::createArtistAnchor(Database::Artist::pointer artist, bool addText)
|
LmsApplication::createArtistAnchor(Database::Artist::pointer artist, bool addText)
|
||||||
{
|
{
|
||||||
@@ -250,25 +251,56 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
|
|||||||
wApp->setInternalPath("/artists", true);
|
wApp->setInternalPath("/artists", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
LmsApplicationGroup&
|
||||||
LmsApplication::handleAuthEvent(void)
|
LmsApplication::getApplicationGroup()
|
||||||
{
|
{
|
||||||
if (!LmsApp->getDb().getLogin().loggedIn())
|
return _appGroups[_userIdentity];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LmsApplication::handleAuthEvent()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
LMS_LOG(UI, INFO) << "User logged out, session = " << Wt::WApplication::instance()->sessionId();
|
if (!getDb().getLogin().loggedIn())
|
||||||
|
{
|
||||||
|
LMS_LOG(UI, INFO) << "User '" << _userIdentity << " 'logged out, session = " << sessionId();
|
||||||
|
|
||||||
goHomeAndQuit();
|
goHomeAndQuit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_userIdentity = getAuthUser().identity(Wt::Auth::Identity::LoginName);
|
||||||
|
LmsApplicationInfo info = LmsApplicationInfo::fromEnvironment(environment());
|
||||||
|
|
||||||
LMS_LOG(UI, INFO) << "User '" << LmsApp->getCurrentAuthUser().identity(Wt::Auth::Identity::LoginName) << "' logged in from '" << Wt::WApplication::instance()->environment().clientAddress() << "', user agent = " << Wt::WApplication::instance()->environment().userAgent() << ", session = " << Wt::WApplication::instance()->sessionId();
|
LMS_LOG(UI, INFO) << "User '" << _userIdentity << "' logged in from '" << environment().clientAddress() << "', user agent = " << environment().userAgent() << ", session = " << sessionId();
|
||||||
|
getApplicationGroup().join(info);
|
||||||
|
|
||||||
// Since we plug into the Media Scanner events, we need to enable updates
|
getApplicationGroup().postOthers([info]
|
||||||
|
{
|
||||||
|
LmsApp->getGroupEvents().appOpen(info);
|
||||||
|
});
|
||||||
|
|
||||||
|
createHome();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
LMS_LOG(UI, ERROR) << "Error while handling auth event: " << e.what();
|
||||||
|
throw std::runtime_error("Internal error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LmsApplication::createHome()
|
||||||
|
{
|
||||||
|
// Handle Media Scanner events and other session events
|
||||||
enableUpdates(true);
|
enableUpdates(true);
|
||||||
|
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
||||||
_isAdmin = LmsApp->getCurrentUser()->isAdmin();
|
_isAdmin = LmsApp->getUser()->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
_imageResource = std::make_shared<ImageResource>(_db);
|
_imageResource = std::make_shared<ImageResource>(_db);
|
||||||
@@ -355,33 +387,33 @@ LmsApplication::handleAuthEvent(void)
|
|||||||
mainStack->addNew<UserView>();
|
mainStack->addNew<UserView>();
|
||||||
}
|
}
|
||||||
|
|
||||||
explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
|
explore->tracksAdd.connect([=] (std::vector<Database::Track::pointer> tracks)
|
||||||
{
|
{
|
||||||
playqueue->addTracks(tracks);
|
playqueue->addTracks(tracks);
|
||||||
}, std::placeholders::_1));
|
});
|
||||||
|
|
||||||
explore->tracksPlay.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
|
explore->tracksPlay.connect([=] (std::vector<Database::Track::pointer> tracks)
|
||||||
{
|
{
|
||||||
playqueue->playTracks(tracks);
|
playqueue->playTracks(tracks);
|
||||||
}, std::placeholders::_1));
|
});
|
||||||
|
|
||||||
|
|
||||||
// MediaPlayer
|
// MediaPlayer
|
||||||
MediaPlayer* player = main->bindNew<MediaPlayer>("player");
|
MediaPlayer* player = main->bindNew<MediaPlayer>("player");
|
||||||
|
|
||||||
// Events from MediaPlayer
|
// Events from MediaPlayer
|
||||||
player->playNext.connect(std::bind([=]
|
player->playNext.connect([=]
|
||||||
{
|
{
|
||||||
playqueue->playNext();
|
playqueue->playNext();
|
||||||
}));
|
});
|
||||||
player->playPrevious.connect(std::bind([=]
|
player->playPrevious.connect([=]
|
||||||
{
|
{
|
||||||
playqueue->playPrevious();
|
playqueue->playPrevious();
|
||||||
}));
|
});
|
||||||
player->playbackEnded.connect(std::bind([=]
|
player->playbackEnded.connect([=]
|
||||||
{
|
{
|
||||||
playqueue->playNext();
|
playqueue->playNext();
|
||||||
}));
|
});
|
||||||
|
|
||||||
// Events from the PlayQueue
|
// Events from the PlayQueue
|
||||||
playqueue->playTrack.connect(explore, &Explore::handleTrackPlayed);
|
playqueue->playTrack.connect(explore, &Explore::handleTrackPlayed);
|
||||||
@@ -390,7 +422,7 @@ LmsApplication::handleAuthEvent(void)
|
|||||||
playqueue->playbackStop.connect(player, &MediaPlayer::stop);
|
playqueue->playbackStop.connect(player, &MediaPlayer::stop);
|
||||||
|
|
||||||
// Events from MediaScanner
|
// Events from MediaScanner
|
||||||
std::string sessionId = this->sessionId();
|
std::string sessionId = LmsApp->sessionId();
|
||||||
_scanner.scanComplete().connect([=] (Scanner::MediaScanner::Stats stats)
|
_scanner.scanComplete().connect([=] (Scanner::MediaScanner::Stats stats)
|
||||||
{
|
{
|
||||||
Wt::WServer::instance()->post(sessionId, [=]
|
Wt::WServer::instance()->post(sessionId, [=]
|
||||||
@@ -420,6 +452,19 @@ LmsApplication::handleAuthEvent(void)
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Events from Application group
|
||||||
|
_groupEvents.appOpen.connect([=] (LmsApplicationInfo info)
|
||||||
|
{
|
||||||
|
// Only one active session by user
|
||||||
|
setConfirmCloseMessage("");
|
||||||
|
quit(Wt::WString::tr("Lms.quit-other-session"));
|
||||||
|
});
|
||||||
|
|
||||||
|
_groupEvents.appClosed.connect([=] (LmsApplicationInfo info)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
});
|
||||||
|
|
||||||
internalPathChanged().connect(std::bind([=]
|
internalPathChanged().connect(std::bind([=]
|
||||||
{
|
{
|
||||||
handlePathChange(mainStack, _isAdmin);
|
handlePathChange(mainStack, _isAdmin);
|
||||||
@@ -449,6 +494,7 @@ LmsApplication::notifyMsg(const Wt::WString& message)
|
|||||||
root()->addNew<Wt::WText>(message);
|
root()->addNew<Wt::WText>(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "database/Cluster.hpp"
|
#include "database/Cluster.hpp"
|
||||||
#include "database/Release.hpp"
|
#include "database/Release.hpp"
|
||||||
|
|
||||||
|
#include "LmsApplicationGroup.hpp"
|
||||||
#include "Auth.hpp"
|
#include "Auth.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
@@ -37,13 +38,20 @@ namespace UserInterface {
|
|||||||
class TranscodeResource;
|
class TranscodeResource;
|
||||||
class ImageResource;
|
class ImageResource;
|
||||||
|
|
||||||
|
struct GroupEvents
|
||||||
|
{
|
||||||
|
Wt::Signal<LmsApplicationInfo> appOpen;
|
||||||
|
Wt::Signal<LmsApplicationInfo> appClosed;
|
||||||
|
};
|
||||||
|
|
||||||
class LmsApplication : public Wt::WApplication
|
class LmsApplication : public Wt::WApplication
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
|
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, LmsApplicationGroups& appGroups, Scanner::MediaScanner& scanner);
|
||||||
|
~LmsApplication();
|
||||||
|
|
||||||
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env,
|
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env,
|
||||||
Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
|
Wt::Dbo::SqlConnectionPool& connectionPool, LmsApplicationGroups& appGroups, Scanner::MediaScanner& scanner);
|
||||||
static LmsApplication* instance();
|
static LmsApplication* instance();
|
||||||
|
|
||||||
// Session application data
|
// Session application data
|
||||||
@@ -52,11 +60,14 @@ class LmsApplication : public Wt::WApplication
|
|||||||
Database::Handler& getDb() { return _db;}
|
Database::Handler& getDb() { return _db;}
|
||||||
Wt::Dbo::Session& getDboSession() { return _db.getSession();}
|
Wt::Dbo::Session& getDboSession() { return _db.getSession();}
|
||||||
|
|
||||||
const Wt::Auth::User& getCurrentAuthUser() { return _db.getLogin().user(); }
|
const Wt::Auth::User& getAuthUser() { return _db.getLogin().user(); }
|
||||||
Database::User::pointer getCurrentUser() { return _db.getCurrentUser(); }
|
Database::User::pointer getUser() { return _db.getCurrentUser(); }
|
||||||
|
Wt::WString getUserIdentity() { return _userIdentity; }
|
||||||
|
|
||||||
Scanner::MediaScanner& getMediaScanner() { return _scanner; }
|
Scanner::MediaScanner& getMediaScanner() { return _scanner; }
|
||||||
|
|
||||||
|
GroupEvents& getGroupEvents() { return _groupEvents; }
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
void goHome();
|
void goHome();
|
||||||
void goHomeAndQuit();
|
void goHomeAndQuit();
|
||||||
@@ -68,10 +79,18 @@ class LmsApplication : public Wt::WApplication
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void handleAuthEvent(void);
|
LmsApplicationGroup& getApplicationGroup();
|
||||||
|
|
||||||
|
// Events
|
||||||
|
void handleAuthEvent();
|
||||||
void notify(const Wt::WEvent& event) override;
|
void notify(const Wt::WEvent& event) override;
|
||||||
|
|
||||||
|
void createHome();
|
||||||
|
|
||||||
Database::Handler _db;
|
Database::Handler _db;
|
||||||
|
LmsApplicationGroups& _appGroups;
|
||||||
|
GroupEvents _groupEvents;
|
||||||
|
Wt::WString _userIdentity;
|
||||||
Auth* _auth;
|
Auth* _auth;
|
||||||
Scanner::MediaScanner& _scanner;
|
Scanner::MediaScanner& _scanner;
|
||||||
std::shared_ptr<ImageResource> _imageResource;
|
std::shared_ptr<ImageResource> _imageResource;
|
||||||
@@ -79,7 +98,8 @@ class LmsApplication : public Wt::WApplication
|
|||||||
bool _isAdmin = false;
|
bool _isAdmin = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to get session data
|
|
||||||
|
// Helper to get session instance
|
||||||
#define LmsApp LmsApplication::instance()
|
#define LmsApp LmsApplication::instance()
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LmsApplicationGroup.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <Wt/WServer.h>
|
||||||
|
#include <Wt/WApplication.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace UserInterface {
|
||||||
|
|
||||||
|
LmsApplicationInfo
|
||||||
|
LmsApplicationInfo::fromEnvironment(const Wt::WEnvironment& env)
|
||||||
|
{
|
||||||
|
LmsApplicationInfo info = {.userAgent = wApp->environment().agent()};
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LmsApplicationGroup::join(LmsApplicationInfo info)
|
||||||
|
{
|
||||||
|
|
||||||
|
_apps.emplace(wApp->sessionId(), std::move(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LmsApplicationGroup::leave()
|
||||||
|
{
|
||||||
|
_apps.erase(wApp->sessionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
LmsApplicationGroup::getOtherSessionIds() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
|
||||||
|
for (auto const& app : _apps)
|
||||||
|
{
|
||||||
|
if (app.first != wApp->sessionId())
|
||||||
|
res.push_back(app.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LmsApplicationGroup::postOthers(std::function<void()> func) const
|
||||||
|
{
|
||||||
|
for (auto const& sessionId : getOtherSessionIds())
|
||||||
|
{
|
||||||
|
Wt::WServer::instance()->post(sessionId, [=]
|
||||||
|
{
|
||||||
|
func();
|
||||||
|
wApp->triggerUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // UserInterface
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Emeric Poupon
|
||||||
|
*
|
||||||
|
* This file is part of LMS.
|
||||||
|
*
|
||||||
|
* LMS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* LMS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <Wt/WSignal.h>
|
||||||
|
#include <Wt/WEnvironment.h>
|
||||||
|
|
||||||
|
namespace UserInterface {
|
||||||
|
|
||||||
|
|
||||||
|
struct LmsApplicationInfo
|
||||||
|
{
|
||||||
|
Wt::UserAgent userAgent;
|
||||||
|
|
||||||
|
static LmsApplicationInfo fromEnvironment(const Wt::WEnvironment& env);
|
||||||
|
};
|
||||||
|
|
||||||
|
// LmsApplication instances are grouped by User
|
||||||
|
class LmsApplicationGroup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void join(LmsApplicationInfo info);
|
||||||
|
void leave();
|
||||||
|
|
||||||
|
std::vector<LmsApplicationInfo> list() const;
|
||||||
|
|
||||||
|
void postOthers(std::function<void()> func) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<std::string> getOtherSessionIds() const;
|
||||||
|
|
||||||
|
std::map<std::string, LmsApplicationInfo> _apps;
|
||||||
|
};
|
||||||
|
|
||||||
|
using LmsApplicationGroups = std::map<Wt::WString, LmsApplicationGroup>;
|
||||||
|
|
||||||
|
} // UserInterface
|
||||||
+12
-12
@@ -58,11 +58,11 @@ PlayQueue::PlayQueue()
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
if (!playlist)
|
if (!playlist)
|
||||||
playlist = Database::Playlist::create(LmsApp->getDboSession(), currentPlayQueueName, false, LmsApp->getCurrentUser());
|
playlist = Database::Playlist::create(LmsApp->getDboSession(), currentPlayQueueName, false, LmsApp->getUser());
|
||||||
|
|
||||||
_trackPos = LmsApp->getCurrentUser()->getCurPlayingTrackPos();
|
_trackPos = LmsApp->getUser()->getCurPlayingTrackPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearBtn->clicked().connect(std::bind([=]
|
clearBtn->clicked().connect(std::bind([=]
|
||||||
@@ -70,7 +70,7 @@ PlayQueue::PlayQueue()
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
playlist.modify()->clear();
|
playlist.modify()->clear();
|
||||||
_showMore->setHidden(true);
|
_showMore->setHidden(true);
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ PlayQueue::PlayQueue()
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
playlist.modify()->shuffle();
|
playlist.modify()->shuffle();
|
||||||
}
|
}
|
||||||
_entriesContainer->clear();
|
_entriesContainer->clear();
|
||||||
@@ -117,7 +117,7 @@ PlayQueue::play(std::size_t pos)
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
|
|
||||||
// If out of range, stop playing
|
// If out of range, stop playing
|
||||||
if (pos >= playlist->getCount())
|
if (pos >= playlist->getCount())
|
||||||
@@ -135,7 +135,7 @@ PlayQueue::play(std::size_t pos)
|
|||||||
|
|
||||||
trackId = track.id();
|
trackId = track.id();
|
||||||
|
|
||||||
auto stats = Database::TrackStats::get(LmsApp->getDboSession(), track, LmsApp->getCurrentUser());
|
auto stats = Database::TrackStats::get(LmsApp->getDboSession(), track, LmsApp->getUser());
|
||||||
|
|
||||||
stats.modify()->incPlayCount();
|
stats.modify()->incPlayCount();
|
||||||
stats.modify()->setLastPlayed(Wt::WLocalDateTime::currentServerDateTime().toUTC());
|
stats.modify()->setLastPlayed(Wt::WLocalDateTime::currentServerDateTime().toUTC());
|
||||||
@@ -175,7 +175,7 @@ PlayQueue::updateInfo()
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(static_cast<unsigned>(playlist->getCount())));
|
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(static_cast<unsigned>(playlist->getCount())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ PlayQueue::enqueueTracks(const std::vector<Database::Track::pointer>& tracks)
|
|||||||
// Use a "session" playqueue in order to store the current playqueue
|
// Use a "session" playqueue in order to store the current playqueue
|
||||||
// so that the user can disconnect and get its playqueue back
|
// so that the user can disconnect and get its playqueue back
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
|
|
||||||
for (auto track : tracks)
|
for (auto track : tracks)
|
||||||
Database::PlaylistEntry::create(LmsApp->getDboSession(), track, playlist);
|
Database::PlaylistEntry::create(LmsApp->getDboSession(), track, playlist);
|
||||||
@@ -228,7 +228,7 @@ PlayQueue::playTracks(const std::vector<Database::Track::pointer>& tracks)
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playqueue = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playqueue = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
playqueue.modify()->clear();
|
playqueue.modify()->clear();
|
||||||
|
|
||||||
_entriesContainer->clear();
|
_entriesContainer->clear();
|
||||||
@@ -245,7 +245,7 @@ PlayQueue::addSome()
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
|
|
||||||
bool moreResults;
|
bool moreResults;
|
||||||
auto playlistEntries = playlist->getEntries(_entriesContainer->count(), 50, moreResults);
|
auto playlistEntries = playlist->getEntries(_entriesContainer->count(), 50, moreResults);
|
||||||
@@ -313,7 +313,7 @@ PlayQueue::addRadioTrack()
|
|||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
std::mt19937 randGenerator(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
|
std::mt19937 randGenerator(std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
|
||||||
|
|
||||||
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
|
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getUser());
|
||||||
|
|
||||||
std::set<Database::IdType> playlistTrackIds;
|
std::set<Database::IdType> playlistTrackIds;
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto bitrate = getBitrateRow(LmsApp->getCurrentUser()->getAudioBitrate());
|
auto bitrate = getBitrateRow(LmsApp->getUser()->getAudioBitrate());
|
||||||
if (bitrate)
|
if (bitrate)
|
||||||
setValue(BitrateField, bitrateString(*bitrate));
|
setValue(BitrateField, bitrateString(*bitrate));
|
||||||
|
|
||||||
auto encodingRow = getEncodingRow(LmsApp->getCurrentUser()->getAudioEncoding());
|
auto encodingRow = getEncodingRow(LmsApp->getUser()->getAudioEncoding());
|
||||||
if (encodingRow)
|
if (encodingRow)
|
||||||
setValue(EncodingField, encodingString(*encodingRow));
|
setValue(EncodingField, encodingString(*encodingRow));
|
||||||
|
|
||||||
@@ -84,14 +84,14 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
|
|
||||||
auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField)));
|
auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField)));
|
||||||
assert(bitrateRow);
|
assert(bitrateRow);
|
||||||
LmsApp->getCurrentUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
|
LmsApp->getUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
|
||||||
|
|
||||||
auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField)));
|
auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField)));
|
||||||
LmsApp->getCurrentUser().modify()->setAudioEncoding(encoding(*encodingRow));
|
LmsApp->getUser().modify()->setAudioEncoding(encoding(*encodingRow));
|
||||||
|
|
||||||
if (!valueText(PasswordField).empty())
|
if (!valueText(PasswordField).empty())
|
||||||
{
|
{
|
||||||
Database::Handler::getPasswordService().updatePassword(LmsApp->getCurrentAuthUser(), valueText(PasswordField));
|
Database::Handler::getPasswordService().updatePassword(LmsApp->getAuthUser(), valueText(PasswordField));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,8 +104,7 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
if (!valueText(PasswordField).empty())
|
if (!valueText(PasswordField).empty())
|
||||||
{
|
{
|
||||||
// Evaluate the strength of the password
|
// Evaluate the strength of the password
|
||||||
auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField),
|
auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField), LmsApp->getUserIdentity(), "");
|
||||||
LmsApp->getCurrentAuthUser().identity(Wt::Auth::Identity::LoginName), "");
|
|
||||||
|
|
||||||
if (!res.isValid())
|
if (!res.isValid())
|
||||||
error = res.message();
|
error = res.message();
|
||||||
@@ -209,7 +208,7 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
std::size_t maxAudioBitrate;
|
std::size_t maxAudioBitrate;
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
maxAudioBitrate = LmsApp->getCurrentUser()->getMaxAudioBitrate();
|
maxAudioBitrate = LmsApp->getUser()->getMaxAudioBitrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t id = 0;
|
std::size_t id = 0;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class UserModel : public Wt::WFormModel
|
|||||||
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
|
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
|
||||||
Database::User::pointer user = LmsApp->getDb().getUser(authUser);
|
Database::User::pointer user = LmsApp->getDb().getUser(authUser);
|
||||||
|
|
||||||
if (user == LmsApp->getCurrentUser())
|
if (user == LmsApp->getUser())
|
||||||
throw std::runtime_error("Cannot edit ourselves");
|
throw std::runtime_error("Cannot edit ourselves");
|
||||||
|
|
||||||
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
|
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ UsersView::refreshView()
|
|||||||
entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::TextFormat::Plain);
|
entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::TextFormat::Plain);
|
||||||
|
|
||||||
// Don't edit ourself this way
|
// Don't edit ourself this way
|
||||||
if (LmsApp->getCurrentUser() == user)
|
if (LmsApp->getUser() == user)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
entry->setCondition("if-edit", true);
|
entry->setCondition("if-edit", true);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ void
|
|||||||
Artists::refreshRecentlyPlayed()
|
Artists::refreshRecentlyPlayed()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
auto artists = TrackStats::getLastPlayedArtists(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
|
auto artists = TrackStats::getLastPlayedArtists(LmsApp->getDboSession(), LmsApp->getUser(), 5);
|
||||||
|
|
||||||
_recentlyPlayedContainer->clear();
|
_recentlyPlayedContainer->clear();
|
||||||
addCompactEntries(_recentlyPlayedContainer, artists);
|
addCompactEntries(_recentlyPlayedContainer, artists);
|
||||||
@@ -112,7 +112,7 @@ void
|
|||||||
Artists::refreshMostPlayed()
|
Artists::refreshMostPlayed()
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
auto artists = TrackStats::getMostPlayedArtists(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
|
auto artists = TrackStats::getMostPlayedArtists(LmsApp->getDboSession(), LmsApp->getUser(), 5);
|
||||||
|
|
||||||
_mostPlayedContainer->clear();
|
_mostPlayedContainer->clear();
|
||||||
addCompactEntries(_mostPlayedContainer, artists);
|
addCompactEntries(_mostPlayedContainer, artists);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ Releases::refreshRecentlyPlayed()
|
|||||||
|
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto releases = TrackStats::getLastPlayedReleases(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
|
auto releases = TrackStats::getLastPlayedReleases(LmsApp->getDboSession(), LmsApp->getUser(), 5);
|
||||||
|
|
||||||
_recentlyPlayedContainer->clear();
|
_recentlyPlayedContainer->clear();
|
||||||
addCompactEntries(_recentlyPlayedContainer, releases);
|
addCompactEntries(_recentlyPlayedContainer, releases);
|
||||||
@@ -138,7 +138,7 @@ Releases::refreshMostPlayed()
|
|||||||
|
|
||||||
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
|
||||||
|
|
||||||
auto releases = TrackStats::getMostPlayedReleases(LmsApp->getDboSession(), LmsApp->getCurrentUser(), 5);
|
auto releases = TrackStats::getMostPlayedReleases(LmsApp->getDboSession(), LmsApp->getUser(), 5);
|
||||||
|
|
||||||
_mostPlayedContainer->clear();
|
_mostPlayedContainer->clear();
|
||||||
addCompactEntries(_mostPlayedContainer, releases);
|
addCompactEntries(_mostPlayedContainer, releases);
|
||||||
|
|||||||
Reference in New Issue
Block a user