Added date/time field for tracklist, added a LmsApplicationManager to make scrobbler register for played tracks
This commit is contained in:
@@ -39,10 +39,11 @@
|
||||
#include "database/TrackFeatures.hpp"
|
||||
#include "database/User.hpp"
|
||||
|
||||
namespace Database {
|
||||
namespace Database
|
||||
{
|
||||
|
||||
using Version = std::size_t;
|
||||
static constexpr Version LMS_DATABASE_VERSION {29};
|
||||
static constexpr Version LMS_DATABASE_VERSION {30};
|
||||
|
||||
class VersionInfo
|
||||
{
|
||||
@@ -78,7 +79,7 @@ namespace Database {
|
||||
|
||||
private:
|
||||
int _version {LMS_DATABASE_VERSION};
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
Session::doDatabaseMigrationIfNeeded()
|
||||
@@ -315,6 +316,11 @@ CREATE TABLE "user_backup" (
|
||||
_session.execute("DROP TABLE user");
|
||||
_session.execute("ALTER TABLE user_backup RENAME TO user");
|
||||
}
|
||||
else if (version == 29)
|
||||
{
|
||||
// new field data_time in tracklist_entry (used by async scrobble or to make stats)
|
||||
_session.execute("ALTER TABLE tracklist_entry ADD date_time TEXT");
|
||||
}
|
||||
else
|
||||
{
|
||||
LMS_LOG(DB, ERROR) << "Database version " << version << " cannot be handled using migration";
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
#include <Wt/WDateTime.h>
|
||||
|
||||
#include "Types.hpp"
|
||||
|
||||
@@ -142,13 +144,16 @@ class TrackListEntry : public Wt::Dbo::Dbo<TrackListEntry>
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::belongsTo(a, _tracklist, "tracklist", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
|
||||
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::belongsTo(a, _tracklist, "tracklist", Wt::Dbo::OnDeleteCascade);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Wt::Dbo::ptr<Track> _track;
|
||||
Wt::WDateTime _dateTime;
|
||||
Wt::Dbo::ptr<Track> _track;
|
||||
Wt::Dbo::ptr<TrackList> _tracklist;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ add_executable(lms
|
||||
main.cpp
|
||||
ui/Auth.cpp
|
||||
ui/LmsApplication.cpp
|
||||
ui/LmsApplicationGroup.cpp
|
||||
ui/LmsApplicationManager.cpp
|
||||
ui/LmsTheme.cpp
|
||||
ui/MediaPlayer.cpp
|
||||
ui/PlayQueue.cpp
|
||||
|
||||
+3
-2
@@ -34,6 +34,7 @@
|
||||
#include "recommendation/IEngine.hpp"
|
||||
#include "subsonic/SubsonicResource.hpp"
|
||||
#include "ui/LmsApplication.hpp"
|
||||
#include "ui/LmsApplicationManager.hpp"
|
||||
#include "utils/IChildProcessManager.hpp"
|
||||
#include "utils/IConfig.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
@@ -221,7 +222,7 @@ int main(int argc, char* argv[])
|
||||
session.optimize();
|
||||
}
|
||||
|
||||
UserInterface::LmsApplicationGroupContainer appGroups;
|
||||
UserInterface::LmsApplicationManager appManager;
|
||||
|
||||
// Service initialization order is important (reverse-order for deinit)
|
||||
Service<IChildProcessManager> childProcessManagerService {createChildProcessManager()};
|
||||
@@ -268,7 +269,7 @@ int main(int argc, char* argv[])
|
||||
server.addEntryPoint(Wt::EntryPointType::Application,
|
||||
[&](const Wt::WEnvironment &env)
|
||||
{
|
||||
return UserInterface::LmsApplication::create(env, database, appGroups);
|
||||
return UserInterface::LmsApplication::create(env, database, appManager);
|
||||
});
|
||||
|
||||
proxyScannerEventsToApplication(*scannerService, server);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#include <Wt/WEnvironment.h>
|
||||
#include <Wt/WFormModel.h>
|
||||
#include <Wt/WLineEdit.h>
|
||||
#include <Wt/WCheckBox.h>
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "resource/CoverResource.hpp"
|
||||
#include "Auth.hpp"
|
||||
#include "LmsApplicationException.hpp"
|
||||
#include "LmsApplicationManager.hpp"
|
||||
#include "LmsTheme.hpp"
|
||||
#include "MediaPlayer.hpp"
|
||||
#include "PlayQueue.hpp"
|
||||
@@ -63,7 +64,7 @@ namespace UserInterface {
|
||||
static constexpr const char* defaultPath {"/releases"};
|
||||
|
||||
std::unique_ptr<Wt::WApplication>
|
||||
LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups)
|
||||
LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager)
|
||||
{
|
||||
if (auto *authEnvService {Service<::Auth::IEnvService>::get()})
|
||||
{
|
||||
@@ -75,10 +76,10 @@ LmsApplication::create(const Wt::WEnvironment& env, Database::Db& db, LmsApplica
|
||||
return std::make_unique<Wt::WApplication>(env);
|
||||
}
|
||||
|
||||
return std::make_unique<LmsApplication>(env, db, appGroups, checkResult.userId);
|
||||
return std::make_unique<LmsApplication>(env, db, appManager, checkResult.userId);
|
||||
}
|
||||
|
||||
return std::make_unique<LmsApplication>(env, db, appGroups);
|
||||
return std::make_unique<LmsApplication>(env, db, appManager);
|
||||
}
|
||||
|
||||
LmsApplication*
|
||||
@@ -102,6 +103,12 @@ LmsApplication::getUser()
|
||||
return Database::User::getById(getDbSession(), _authenticatedUser->userId);
|
||||
}
|
||||
|
||||
Database::IdType
|
||||
LmsApplication::getUserId()
|
||||
{
|
||||
return _authenticatedUser->userId;
|
||||
}
|
||||
|
||||
bool
|
||||
LmsApplication::isUserAuthStrong() const
|
||||
{
|
||||
@@ -134,11 +141,11 @@ LmsApplication::getUserLoginName()
|
||||
|
||||
LmsApplication::LmsApplication(const Wt::WEnvironment& env,
|
||||
Database::Db& db,
|
||||
LmsApplicationGroupContainer& appGroups,
|
||||
LmsApplicationManager& appManager,
|
||||
std::optional<Database::IdType> userId)
|
||||
: Wt::WApplication {env}
|
||||
, _db {db}
|
||||
, _appGroups {appGroups}
|
||||
, _appManager {appManager}
|
||||
, _authenticatedUser {userId ? std::make_optional<UserAuthInfo>(UserAuthInfo {*userId, false}) : std::nullopt}
|
||||
{
|
||||
try
|
||||
@@ -261,16 +268,7 @@ void
|
||||
LmsApplication::finalize()
|
||||
{
|
||||
if (_authenticatedUser)
|
||||
{
|
||||
LmsApplicationInfo info = LmsApplicationInfo::fromEnvironment(environment());
|
||||
|
||||
getApplicationGroup().postOthers([info]
|
||||
{
|
||||
LmsApp->getEvents().appClosed(info);
|
||||
});
|
||||
|
||||
getApplicationGroup().leave();
|
||||
}
|
||||
_appManager.unregisterApplication(*this);
|
||||
|
||||
preQuit().emit();
|
||||
}
|
||||
@@ -427,12 +425,6 @@ handlePathChange(Wt::WStackedWidget& stack, bool isAdmin)
|
||||
wApp->setInternalPath(defaultPath, true);
|
||||
}
|
||||
|
||||
LmsApplicationGroup&
|
||||
LmsApplication::getApplicationGroup()
|
||||
{
|
||||
return _appGroups.get(_authenticatedUser->userId);
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplication::logoutUser()
|
||||
{
|
||||
@@ -451,14 +443,19 @@ LmsApplication::onUserLoggedIn()
|
||||
setTheme();
|
||||
root()->clear();
|
||||
|
||||
const LmsApplicationInfo info {LmsApplicationInfo::fromEnvironment(environment())};
|
||||
|
||||
LMS_LOG(UI, INFO) << "User '" << getUserLoginName() << "' logged in from '" << environment().clientAddress() << "', user agent = " << environment().userAgent();
|
||||
getApplicationGroup().join(info);
|
||||
|
||||
getApplicationGroup().postOthers([info]
|
||||
_appManager.registerApplication(*this);
|
||||
_appManager.applicationRegistered.connect(this, [this] (LmsApplication& otherApplication)
|
||||
{
|
||||
LmsApp->getEvents().appOpen(info);
|
||||
// Only one active session by user
|
||||
if (otherApplication.getUserId() == getUserId())
|
||||
{
|
||||
if (!LmsApp->isUserDemo())
|
||||
{
|
||||
quit(Wt::WString::tr("Lms.quit-other-session"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
createHome();
|
||||
@@ -576,16 +573,6 @@ LmsApplication::createHome()
|
||||
});
|
||||
}
|
||||
|
||||
// Events from Application group
|
||||
_events.appOpen.connect([=]
|
||||
{
|
||||
// Only one active session by user
|
||||
if (!LmsApp->isUserDemo())
|
||||
{
|
||||
quit(Wt::WString::tr("Lms.quit-other-session"));
|
||||
}
|
||||
});
|
||||
|
||||
internalPathChanged().connect([=]
|
||||
{
|
||||
handlePathChange(*mainStack, isUserAdmin());
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include "scanner/ScannerEvents.hpp"
|
||||
|
||||
#include "LmsApplicationGroup.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Artist;
|
||||
@@ -47,23 +45,16 @@ class CoverResource;
|
||||
class LmsApplicationException;
|
||||
class MediaPlayer;
|
||||
class PlayQueue;
|
||||
|
||||
// Events that can be listen from anywhere in the application
|
||||
struct Events
|
||||
{
|
||||
// Events relative to group
|
||||
Wt::Signal<LmsApplicationInfo> appOpen;
|
||||
Wt::Signal<LmsApplicationInfo> appClosed;
|
||||
};
|
||||
class LmsApplicationManager;
|
||||
|
||||
class LmsApplication : public Wt::WApplication
|
||||
{
|
||||
public:
|
||||
|
||||
LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups, std::optional<Database::IdType> userId = std::nullopt);
|
||||
LmsApplication(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager, std::optional<Database::IdType> userId = std::nullopt);
|
||||
~LmsApplication();
|
||||
|
||||
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationGroupContainer& appGroups);
|
||||
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env, Database::Db& db, LmsApplicationManager& appManager);
|
||||
static LmsApplication* instance();
|
||||
|
||||
|
||||
@@ -71,13 +62,14 @@ class LmsApplication : public Wt::WApplication
|
||||
std::shared_ptr<CoverResource> getCoverResource() { return _coverResource; }
|
||||
Database::Session& getDbSession(); // always thread safe
|
||||
|
||||
Wt::Dbo::ptr<Database::User> getUser();
|
||||
Wt::Dbo::ptr<Database::User> getUser();
|
||||
Database::IdType getUserId();
|
||||
bool isUserAuthStrong() const; // user must be logged in prior this call
|
||||
bool isUserAdmin(); // user must be logged in prior this call
|
||||
bool isUserDemo(); // user must be logged in prior this call
|
||||
std::string getUserLoginName(); // user must be logged in prior this call
|
||||
|
||||
Events& getEvents() { return _events; }
|
||||
// Proxified scanner events
|
||||
Scanner::Events& getScannerEvents() { return _scannerEvents; }
|
||||
|
||||
// Utils
|
||||
@@ -113,8 +105,6 @@ class LmsApplication : public Wt::WApplication
|
||||
void handleException(LmsApplicationException& e);
|
||||
void goHomeAndQuit();
|
||||
|
||||
LmsApplicationGroup& getApplicationGroup();
|
||||
|
||||
// Signal slots
|
||||
void logoutUser();
|
||||
void onUserLoggedIn();
|
||||
@@ -126,8 +116,7 @@ class LmsApplication : public Wt::WApplication
|
||||
|
||||
Database::Db& _db;
|
||||
Wt::Signal<> _preQuit;
|
||||
LmsApplicationGroupContainer& _appGroups;
|
||||
Events _events;
|
||||
LmsApplicationManager& _appManager;
|
||||
Scanner::Events _scannerEvents;
|
||||
struct UserAuthInfo
|
||||
{
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* 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 = {env.agent()};
|
||||
return info;
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplicationGroup::join(LmsApplicationInfo info)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock {_mutex};
|
||||
|
||||
_apps.emplace(wApp->sessionId(), std::move(info));
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplicationGroup::leave()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock {_mutex};
|
||||
|
||||
_apps.erase(wApp->sessionId());
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
LmsApplicationGroup::getOtherSessionIds() const
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
|
||||
std::unique_lock<std::mutex> lock {_mutex};
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LmsApplicationGroup&
|
||||
LmsApplicationGroupContainer::get(Database::IdType userId)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock {_mutex};
|
||||
|
||||
return _apps[userId];
|
||||
}
|
||||
|
||||
} // UserInterface
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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 <mutex>
|
||||
|
||||
#include <Wt/WSignal.h>
|
||||
#include <Wt/WEnvironment.h>
|
||||
|
||||
#include "database/Types.hpp"
|
||||
|
||||
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();
|
||||
|
||||
void postOthers(std::function<void()> func) const;
|
||||
|
||||
private:
|
||||
|
||||
mutable std::mutex _mutex;
|
||||
|
||||
std::vector<std::string> getOtherSessionIds() const;
|
||||
|
||||
std::map<std::string, LmsApplicationInfo> _apps;
|
||||
};
|
||||
|
||||
class LmsApplicationGroupContainer
|
||||
{
|
||||
public:
|
||||
LmsApplicationGroup& get(Database::IdType userId);
|
||||
|
||||
private:
|
||||
std::map<Database::IdType /* userId */, LmsApplicationGroup> _apps;
|
||||
std::mutex _mutex;
|
||||
};
|
||||
|
||||
} // UserInterface
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 "LmsApplicationManager.hpp"
|
||||
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
void
|
||||
LmsApplicationManager::registerApplication(LmsApplication& application)
|
||||
{
|
||||
{
|
||||
std::scoped_lock lock {_mutex};
|
||||
m_applications[application.getUserId()].insert(&application);
|
||||
}
|
||||
|
||||
applicationRegistered.emit(application);
|
||||
}
|
||||
|
||||
void
|
||||
LmsApplicationManager::unregisterApplication(LmsApplication& application)
|
||||
{
|
||||
{
|
||||
std::scoped_lock lock {_mutex};
|
||||
m_applications[application.getUserId()].erase(&application);
|
||||
}
|
||||
|
||||
applicationUnregistered.emit(application);
|
||||
}
|
||||
|
||||
} // UserInterface
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 <mutex>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <Wt/WSignal.h>
|
||||
|
||||
#include "database/Types.hpp"
|
||||
|
||||
namespace UserInterface
|
||||
{
|
||||
class LmsApplication;
|
||||
class LmsApplicationManager
|
||||
{
|
||||
public:
|
||||
Wt::Signal<LmsApplication&> applicationRegistered;
|
||||
Wt::Signal<LmsApplication&> applicationUnregistered;
|
||||
|
||||
private:
|
||||
|
||||
friend class LmsApplication;
|
||||
|
||||
void registerApplication(LmsApplication& application);
|
||||
void unregisterApplication(LmsApplication& application);
|
||||
|
||||
std::mutex _mutex;
|
||||
std::unordered_map<Database::IdType /* user */, std::unordered_set<LmsApplication*>> m_applications;
|
||||
};
|
||||
} // UserInterface
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "PasswordValidator.hpp"
|
||||
|
||||
#include <Wt/WEnvironment.h>
|
||||
|
||||
#include "auth/IPasswordService.hpp"
|
||||
#include "utils/Service.hpp"
|
||||
#include "LmsApplication.hpp"
|
||||
|
||||
Reference in New Issue
Block a user