Upgrade from Wt3 to Wt4

This commit is contained in:
emeric
2018-04-16 16:56:51 +02:00
parent b743127076
commit 9cb4918f44
87 changed files with 954 additions and 2057 deletions
+1 -6
View File
@@ -10,13 +10,10 @@ lms_SOURCES = \
$(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Playlist.cpp \
$(srcdir)/database/Release.cpp \
$(srcdir)/database/SearchFilter.cpp \
$(srcdir)/database/Setting.cpp \
$(srcdir)/database/SqlQuery.cpp \
$(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \
$(srcdir)/feature/FeatureExtractor.cpp \
$(srcdir)/feature/FeatureStore.cpp \
$(srcdir)/image/Image.cpp \
$(srcdir)/metadata/AvFormat.cpp \
$(srcdir)/metadata/TagLibParser.cpp \
@@ -31,8 +28,6 @@ lms_SOURCES = \
$(srcdir)/ui/admin/InitWizardView.cpp \
$(srcdir)/ui/admin/UserView.cpp \
$(srcdir)/ui/admin/UsersView.cpp \
$(srcdir)/ui/common/InputRange.cpp \
$(srcdir)/ui/common/LineEdit.cpp \
$(srcdir)/ui/common/Validators.cpp \
$(srcdir)/ui/explore/ArtistView.cpp \
$(srcdir)/ui/explore/ArtistsView.cpp \
@@ -48,6 +43,6 @@ lms_SOURCES = \
$(srcdir)/utils/Path.cpp \
$(srcdir)/utils/Utils.cpp
lms_CXXFLAGS=-std=c++11 -Wall -I$(srcdir)/third-party -I$(srcdir)/ui $(MAGICKXX_CFLAGS) -D_REENTRANT -DBOOST_SPIRIT_THREADSAFE
lms_CXXFLAGS=-std=c++14 -Wall -I$(srcdir)/third-party -I$(srcdir)/ui $(MAGICKXX_CFLAGS) -D_REENTRANT
lms_LDADD=$(MAGICKXX_LIBS)
+7 -7
View File
@@ -57,14 +57,14 @@ MediaFile::MediaFile(const boost::filesystem::path& p)
int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr);
if (error < 0)
{
LMS_LOG(AV, ERROR) << "Cannot open " << _p << ": " << averror_to_string(error);
LMS_LOG(AV, ERROR) << "Cannot open " << _p.string() << ": " << averror_to_string(error);
throw MediaFileException(error);
}
error = avformat_find_stream_info(_context, nullptr);
if (error < 0)
{
LMS_LOG(AV, ERROR) << "Cannot find stream information on " << _p << ": " << averror_to_string(error);
LMS_LOG(AV, ERROR) << "Cannot find stream information on " << _p.string() << ": " << averror_to_string(error);
avformat_close_input(&_context);
throw MediaFileException(error);
}
@@ -75,13 +75,13 @@ MediaFile::~MediaFile()
avformat_close_input(&_context);
}
boost::posix_time::time_duration
std::chrono::milliseconds
MediaFile::getDuration() const
{
if (static_cast<int>(_context->duration) != AV_NOPTS_VALUE )
return boost::posix_time::seconds(_context->duration / AV_TIME_BASE);
else
return boost::posix_time::seconds(0); // TODO, do something better?
if (_context->duration == AV_NOPTS_VALUE)
return std::chrono::milliseconds(0); // TODO estimate
return std::chrono::milliseconds(_context->duration / AV_TIME_BASE * 1000);
}
void
+2 -2
View File
@@ -33,10 +33,10 @@ extern "C"
#include <string>
#include <cstdint>
#include <map>
#include <chrono>
#include <boost/optional.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp> //no i/o just types
#include "utils/Exception.hpp"
@@ -75,7 +75,7 @@ class MediaFile
const boost::filesystem::path& getPath() const {return _p;};
boost::posix_time::time_duration getDuration() const;
std::chrono::milliseconds getDuration() const;
std::map<std::string, std::string> getMetaData(void);
std::vector<StreamInfo> getStreamInfo() const;
+2 -2
View File
@@ -101,7 +101,7 @@ Transcoder::init()
}
if (!avConvPath.empty())
LMS_LOG(TRANSCODE, INFO) << "Using transcoder " << avConvPath;
LMS_LOG(TRANSCODE, INFO) << "Using transcoder " << avConvPath.string();
else
throw std::runtime_error("Cannot find any transcoder binary!");
}
@@ -123,7 +123,7 @@ Transcoder::start()
else if (!boost::filesystem::is_regular( _filePath) )
return false;
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath << "'";
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath.string() << "'";
std::vector<std::string> args;
+4 -4
View File
@@ -65,7 +65,7 @@ getFromAvMediaFile(const Av::MediaFile& input, std::size_t nbMaxCovers)
if (image.load(picture.data))
res.push_back( image );
else
LMS_LOG(COVER, ERROR) << "Cannot load embedded cover file in '" << input.getPath() << "'";
LMS_LOG(COVER, ERROR) << "Cannot load embedded cover file in '" << input.getPath().string() << "'";
}
return res;
@@ -87,7 +87,7 @@ Grabber::getFromDirectory(const boost::filesystem::path& p, std::size_t nbMaxCov
if (image.load(coverPath))
res.push_back(image);
else
LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath << "'";
LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath.string() << "'";
}
return res;
@@ -116,7 +116,7 @@ Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t
if (boost::filesystem::file_size(path) > _maxFileSize)
{
LMS_LOG(COVER, INFO) << "Cover file '" << path << " is too big (" << boost::filesystem::file_size(path) << "), limit is " << _maxFileSize;
LMS_LOG(COVER, INFO) << "Cover file '" << path.string() << " is too big (" << boost::filesystem::file_size(path) << "), limit is " << _maxFileSize;
continue;
}
@@ -138,7 +138,7 @@ Grabber::getFromTrack(const boost::filesystem::path& p, std::size_t nbMaxCovers)
}
catch (Av::MediaFileException& e)
{
LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p << ": " << e.what();
LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what();
}
return std::vector<Image::Image>();
+30 -28
View File
@@ -19,17 +19,17 @@
#include <boost/make_unique.hpp>
#include <Wt/Dbo/FixedSqlConnectionPool>
#include <Wt/Dbo/backend/Sqlite3>
#include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3.h>
#include <Wt/Auth/Dbo/AuthInfo>
#include <Wt/Auth/Dbo/UserDatabase>
#include <Wt/Auth/AuthService>
#include <Wt/Auth/HashFunction>
#include <Wt/Auth/Identity>
#include <Wt/Auth/PasswordService>
#include <Wt/Auth/PasswordStrengthValidator>
#include <Wt/Auth/PasswordVerifier>
#include <Wt/Auth/Dbo/AuthInfo.h>
#include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/AuthService.h>
#include <Wt/Auth/HashFunction.h>
#include <Wt/Auth/Identity.h>
#include <Wt/Auth/PasswordService.h>
#include <Wt/Auth/PasswordStrengthValidator.h>
#include <Wt/Auth/PasswordVerifier.h>
#include "Setting.hpp"
@@ -51,24 +51,24 @@ Handler::configureAuth(void)
{
authService.setEmailVerificationEnabled(false);
authService.setAuthTokensEnabled(true, "lmsauth");
authService.setIdentityPolicy(Wt::Auth::LoginNameIdentity);
authService.setIdentityPolicy(Wt::Auth::IdentityPolicy::LoginName);
authService.setRandomTokenLength(32);
authService.setTokenHashFunction(new Wt::Auth::BCryptHashFunction(8));
Wt::Auth::PasswordVerifier *verifier = new Wt::Auth::PasswordVerifier();
verifier->addHashFunction(new Wt::Auth::BCryptHashFunction(8));
passwordService.setVerifier(verifier);
auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(8));
passwordService.setVerifier(std::move(verifier));
passwordService.setAttemptThrottlingEnabled(true);
Wt::Auth::PasswordStrengthValidator* strengthValidator = new Wt::Auth::PasswordStrengthValidator();
auto strengthValidator = std::make_unique<Wt::Auth::PasswordStrengthValidator>();
// Reduce some constraints...
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::PassPhrase, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::OneCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::TwoCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::ThreeCharClass, 4 );
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 4 );
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::PassPhrase, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::OneCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::TwoCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::ThreeCharClass, 4 );
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::FourCharClass, 4 );
passwordService.setStrengthValidator(strengthValidator);
passwordService.setStrengthValidator(std::move(strengthValidator));
}
const Wt::Auth::AuthService&
@@ -182,24 +182,26 @@ Handler::createUser(const Wt::Auth::User& authUser)
return User::pointer();
}
User::pointer user = _session.add(new User());
User::pointer user = _session.add(std::make_unique<User>());
Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
authInfo.modify()->setUser(user);
return user;
}
Wt::Dbo::SqlConnectionPool*
std::unique_ptr<Wt::Dbo::SqlConnectionPool>
Handler::createConnectionPool(boost::filesystem::path p)
{
LMS_LOG(DB, INFO) << "Creating connection pool on file " << p;
Wt::Dbo::backend::Sqlite3 *connection = new Wt::Dbo::backend::Sqlite3(p.string());
LMS_LOG(DB, INFO) << "Creating connection pool on file " << p.string();
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(p.string());
connection->executeSql("pragma journal_mode=WAL");
connection->setProperty("show-queries", "true");
return new Wt::Dbo::FixedSqlConnectionPool(connection, 1);
auto pool = std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 1);
pool->setTimeout(std::chrono::seconds(1));
return pool;
}
+6 -6
View File
@@ -23,12 +23,12 @@
#include <boost/filesystem.hpp>
#include <memory>
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/SqlConnectionPool>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Auth/Dbo/UserDatabase>
#include <Wt/Auth/Login>
#include <Wt/Auth/PasswordService>
#include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/Login.h>
#include <Wt/Auth/PasswordService.h>
#include "Types.hpp"
@@ -60,7 +60,7 @@ class Handler
static const Wt::Auth::AuthService& getAuthService();
static const Wt::Auth::PasswordService& getPasswordService();
static Wt::Dbo::SqlConnectionPool* createConnectionPool(boost::filesystem::path db);
static std::unique_ptr<Wt::Dbo::SqlConnectionPool> createConnectionPool(boost::filesystem::path db);
private:
+2 -2
View File
@@ -54,7 +54,7 @@ Artist::getById(Wt::Dbo::Session& session, Artist::id_type id)
Artist::pointer
Artist::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
{
return session.add(new Artist(name, MBID));
return session.add(std::make_unique<Artist>(name, MBID));
}
std::vector<Artist::pointer>
@@ -75,7 +75,7 @@ Artist::getAllOrphans(Wt::Dbo::Session& session)
static
Wt::Dbo::Query<Artist::pointer>
getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds,
const std::set<Artist::id_type>& clusterIds,
const std::vector<std::string>& keywords)
{
WhereClause where;
+2 -4
View File
@@ -23,10 +23,8 @@
#include <string>
#include <vector>
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/QueryModel>
#include "SearchFilter.hpp"
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/QueryModel.h>
namespace Database
{
+1 -1
View File
@@ -31,7 +31,7 @@ MediaDirectory::MediaDirectory(boost::filesystem::path p)
MediaDirectory::pointer
MediaDirectory::create(Wt::Dbo::Session& session, boost::filesystem::path p)
{
return session.add( new MediaDirectory(p) );
return session.add( std::make_unique<MediaDirectory>(p) );
}
void
+3 -2
View File
@@ -24,8 +24,9 @@
#include <boost/filesystem/path.hpp>
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/WtSqlTraits>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits.h>
namespace Database {
class MediaDirectory
+2 -2
View File
@@ -38,7 +38,7 @@ Playlist::Playlist(std::string name, bool isPublic, Wt::Dbo::ptr<User> user)
Playlist::pointer
Playlist::create(Wt::Dbo::Session& session, std::string name, bool isPublic, Wt::Dbo::ptr<User> user)
{
return session.add( new Playlist(name, isPublic, user) );
return session.add( std::make_unique<Playlist>(name, isPublic, user) );
}
PlaylistEntry::PlaylistEntry()
@@ -75,7 +75,7 @@ PlaylistEntry::PlaylistEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Playlist> p
PlaylistEntry::pointer
PlaylistEntry::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Playlist> playlist)
{
return session.add( new PlaylistEntry( track, playlist) );
return session.add( std::make_unique<PlaylistEntry>( track, playlist) );
}
+1 -1
View File
@@ -20,7 +20,7 @@
#ifndef DATABASE_PLAYLIST_HPP
#define DATABASE_PLAYLIST_HPP
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/Dbo.h>
#include <string>
+7 -8
View File
@@ -18,7 +18,6 @@
*/
#include "Types.hpp"
#include "SearchFilter.hpp"
#include "SqlQuery.hpp"
namespace Database
@@ -53,7 +52,7 @@ Release::getById(Wt::Dbo::Session& session, Release::id_type id)
Release::pointer
Release::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID)
{
return session.add(new Release(name, MBID));
return session.add(std::make_unique<Release>(name, MBID));
}
std::vector<Release::pointer>
@@ -74,7 +73,7 @@ Release::getAllOrphans(Wt::Dbo::Session& session)
static
Wt::Dbo::Query<Release::pointer>
getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds,
const std::set<Release::id_type>& clusterIds,
const std::vector<std::string> keywords)
{
WhereClause where;
@@ -140,22 +139,22 @@ Release::getReleaseYear(bool original) const
{
assert(session());
Wt::Dbo::collection<boost::posix_time::ptime> times = session()->query<boost::posix_time::ptime>(
Wt::Dbo::collection<Wt::WDate> dates = session()->query<Wt::WDate>(
std::string("SELECT ") + (original ? "t.original_date" : "t.date") + " FROM track t INNER JOIN release r ON r.id = t.release_id")
.where("r.id = ?")
.groupBy("t.date")
.bind(this->id());
/* various dates, no date */
if (times.empty() || times.size() > 1)
if (dates.empty() || dates.size() > 1)
return boost::none;
boost::gregorian::date date = times.front().date();
auto date = dates.front();
if (date.is_special())
if (!date.isValid())
return boost::none;
return boost::make_optional<int>(date.year());
return date.year();
}
std::vector<Wt::Dbo::ptr<Artist>>
+2 -4
View File
@@ -21,9 +21,7 @@
#include <boost/optional.hpp>
#include <Wt/Dbo/Dbo>
#include "SearchFilter.hpp"
#include <Wt/Dbo/Dbo.h>
namespace Database
{
@@ -68,7 +66,7 @@ class Release : public Wt::Dbo::Dbo<Release>
// Accessors
std::string getName() const { return _name; }
std::string getMBID() const { return _MBID; }
boost::posix_time::time_duration getDuration(void) const;
std::chrono::seconds getDuration(void) const;
// Get the artists of this release
std::vector<Wt::Dbo::ptr<Artist> > getArtists() const;
-40
View File
@@ -1,40 +0,0 @@
/*
* Copyright (C) 2015 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 "utils/Logger.hpp"
#include "SearchFilter.hpp"
namespace Database
{
void
SearchFilter::operator+=(const SearchFilter& filter)
{
}
SqlQuery
SearchFilter::generatePartialQuery()
{
SqlQuery sqlQuery;
return sqlQuery;
}
} // namespace Database
-68
View File
@@ -1,68 +0,0 @@
/*
* Copyright (C) 2015 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/>.
*/
#ifndef _DB_SEARCH_FILTER_HPP_
#define _DB_SEARCH_FILTER_HPP_
#include <string>
#include <vector>
#include <map>
#include <Wt/Dbo/Dbo>
#include "SqlQuery.hpp"
namespace Database
{
typedef Wt::Dbo::dbo_default_traits::IdType id_type;
class SearchFilter
{
public:
typedef int id_type;
SearchFilter() {}
static SearchFilter Artist(std::string name) {return SearchFilter();}
static SearchFilter Artist(id_type id) {return SearchFilter();}
static SearchFilter Release(std::string name) {return SearchFilter();}
static SearchFilter Release(id_type id) {return SearchFilter();}
static SearchFilter Track(std::string name) {return SearchFilter();}
static SearchFilter Cluster(id_type id) {return SearchFilter();}
// Combine search filters by add operation
// Caution: multiple filters on different artist/release/track
// values may lead to empty results
void operator+=(const SearchFilter& filter);
SqlQuery generatePartialQuery();
private:
};
} // namespace Database
#endif // _DB_SEARCH_FILTER_HPP_
+6 -25
View File
@@ -52,8 +52,8 @@ Setting::getBool(Wt::Dbo::Session& session, std::string setting, bool defaultVal
return (res->_value == "true");
}
boost::posix_time::time_duration
Setting::getDuration(Wt::Dbo::Session& session, std::string setting, boost::posix_time::time_duration defaultValue)
Wt::WTime
Setting::getTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime defaultValue)
{
Wt::Dbo::Transaction transaction(session);
@@ -61,19 +61,7 @@ Setting::getDuration(Wt::Dbo::Session& session, std::string setting, boost::posi
if (!res)
return defaultValue;
return boost::posix_time::duration_from_string(res->_value);
}
boost::posix_time::ptime
Setting::getTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime defaultValue)
{
Wt::Dbo::Transaction transaction(session);
pointer res = getByName(session, setting);
if (!res)
return defaultValue;
return boost::posix_time::time_from_string(res->_value);
return Wt::WTime::fromString(res->_value);
}
int
@@ -93,7 +81,7 @@ Setting::getInt(Wt::Dbo::Session& session, std::string setting, int defaultValue
Setting::pointer
Setting::create(Wt::Dbo::Session& session, std::string name)
{
return session.add<Setting>(new Setting(name));
return session.add<Setting>(std::make_unique<Setting>(name));
}
Setting::pointer
@@ -127,17 +115,10 @@ Setting::setBool(Wt::Dbo::Session& session, std::string setting, bool value)
}
void
Setting::setDuration(Wt::Dbo::Session& session, std::string setting, boost::posix_time::time_duration value)
Setting::setTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime value)
{
Wt::Dbo::Transaction transaction(session);
getOrCreateByName(session, setting).modify()->_value = boost::posix_time::to_simple_string(value);
}
void
Setting::setTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime value)
{
Wt::Dbo::Transaction transaction(session);
getOrCreateByName(session, setting).modify()->_value = boost::posix_time::to_simple_string(value);
getOrCreateByName(session, setting).modify()->_value = value.toString().toUTF8();
}
void
+7 -11
View File
@@ -19,9 +19,8 @@
#pragma once
#include <Wt/Dbo/Dbo>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <Wt/Dbo/Dbo.h>
#include <Wt/WTime.h>
namespace Database {
@@ -29,7 +28,10 @@ namespace Database {
class Setting
{
public:
using pointer = Wt::Dbo::ptr<Setting>;
Setting() {}
Setting(std::string name) : _name(name) {}
// check if a setting exists or not
static bool exists(Wt::Dbo::Session& session, std::string setting);
@@ -38,16 +40,14 @@ class Setting
// Nested transactions
static std::string getString(Wt::Dbo::Session& session, std::string setting, std::string defaultValue = "");
static bool getBool(Wt::Dbo::Session& session, std::string setting, bool defaultValue = false);
static boost::posix_time::time_duration getDuration(Wt::Dbo::Session& session, std::string setting, boost::posix_time::time_duration defaultDuration = boost::posix_time::seconds(0) );
static boost::posix_time::ptime getTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime defaultTime = boost::posix_time::ptime());
static Wt::WTime getTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime defaultValue = Wt::WTime());
static int getInt(Wt::Dbo::Session& session, std::string setting, int defaultValue = 0);
// Setters
// Nested transactions
static void setString(Wt::Dbo::Session& session, std::string setting, std::string value);
static void setBool(Wt::Dbo::Session& session, std::string setting, bool value);
static void setDuration(Wt::Dbo::Session& session, std::string setting, boost::posix_time::time_duration value);
static void setTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime time);
static void setTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime value);
static void setInt(Wt::Dbo::Session& session, std::string setting, int value);
template<class Action>
@@ -58,10 +58,6 @@ class Setting
}
private:
Setting(std::string name) : _name(name) {}
typedef Wt::Dbo::ptr<Setting> pointer;
static pointer getByName(Wt::Dbo::Session& session, std::string name);
static pointer create(Wt::Dbo::Session& session, std::string name);
static pointer getOrCreateByName(Wt::Dbo::Session& session, std::string name);
+4 -44
View File
@@ -17,10 +17,6 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/property_tree/json_parser.hpp>
#include <Wt/Dbo/QueryModel>
#include "utils/Logger.hpp"
#include "SqlQuery.hpp"
@@ -75,7 +71,7 @@ Track::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
Track::pointer
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p)
{
return session.add(new Track(p) );
return session.add(std::make_unique<Track>(p));
}
std::vector<boost::filesystem::path>
@@ -111,7 +107,7 @@ Track::getClusters(void) const
static
Wt::Dbo::Query< Track::pointer >
getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds,
const std::set<Cluster::id_type>& clusterIds,
const std::vector<std::string> keywords)
{
WhereClause where;
@@ -149,20 +145,6 @@ getQuery(Wt::Dbo::Session& session,
return query;
}
Track::StatsQueryResult
Track::getStats(Wt::Dbo::Session& session, SearchFilter filter)
{
SqlQuery sqlQuery = filter.generatePartialQuery();
Wt::Dbo::Query<StatsQueryResult> query = session.query<StatsQueryResult>( "SELECT COUNT(\"id\"), SUM(\"dur\") FROM (SELECT t.id as \"id\", t.duration as \"dur\" FROM track t INNER JOIN artist a ON t.artist_id = a.id INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN release r ON r.id = t.release_id " + sqlQuery.where().get() + " GROUP BY t.id)");
for (const std::string& bindArg : sqlQuery.where().getBindArgs())
query.bind(bindArg);
return query;
}
std::vector<Track::pointer>
Track::getByFilter(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds,
@@ -233,7 +215,7 @@ Cluster::Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name)
Cluster::pointer
Cluster::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name)
{
return session.add(new Cluster(type, name));
return session.add(std::make_unique<Cluster>(type, name));
}
std::vector<Cluster::pointer>
@@ -244,28 +226,6 @@ Cluster::getAll(Wt::Dbo::Session& session)
return std::vector<Cluster::pointer>(res.begin(), res.end());
}
Wt::Dbo::Query<Cluster::pointer>
Cluster::getQuery(Wt::Dbo::Session& session, SearchFilter filter)
{
SqlQuery sqlQuery = filter.generatePartialQuery();
Wt::Dbo::Query<pointer> query
= session.query<pointer>( "SELECT g FROM cluster c INNER JOIN track_cluster t_c ON t_c.cluster_id = c.id INNER JOIN artist a ON t.artist_id = a.id INNER JOIN release r ON r.id = t.release_id INNER JOIN track t ON t.id = t_c.track_id " + sqlQuery.where().get()).groupBy("c.name").orderBy("c.name");
for (const std::string& bindArg : sqlQuery.where().getBindArgs())
query.bind(bindArg);
return query;
}
std::vector<Cluster::pointer>
Cluster::getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size)
{
Wt::Dbo::collection<pointer> res = getQuery(session, filter).limit(size).offset(offset);
return std::vector<pointer>(res.begin(), res.end());
}
ClusterType::ClusterType(std::string name)
: _name(name)
{
@@ -297,7 +257,7 @@ ClusterType::getAll(Wt::Dbo::Session& session)
ClusterType::pointer
ClusterType::create(Wt::Dbo::Session& session, std::string name)
{
return session.add(new ClusterType(name));
return session.add(std::make_unique<ClusterType>(name));
}
Cluster::pointer
+19 -31
View File
@@ -21,17 +21,15 @@
#include <string>
#include <vector>
#include <chrono>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/optional.hpp>
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/WtSqlTraits>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits.h>
#include <Wt/WDateTime>
#include "SearchFilter.hpp"
#include <Wt/WDateTime.h>
namespace Database {
@@ -52,7 +50,6 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name);
// Find utility
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
// Create utility
@@ -75,7 +72,6 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
}
private:
static Wt::Dbo::Query<pointer> getQuery(Wt::Dbo::Session& session, SearchFilter filter);
static const std::size_t _maxNameLength = 128;
@@ -159,14 +155,6 @@ class Track
static std::vector<pointer> getMBIDDuplicates(Wt::Dbo::Session& session);
static std::vector<pointer> getChecksumDuplicates(Wt::Dbo::Session& session);
// Utility fonctions
// Stats for a given search filter
typedef boost::tuple<
int, // Total tracks
boost::posix_time::time_duration // Total duration
> StatsQueryResult;
static StatsQueryResult getStats(Wt::Dbo::Session& session, SearchFilter filter);
// Create utility
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
@@ -179,12 +167,12 @@ class Track
void setDiscNumber(int num) { _discNumber = num; }
void setTotalDiscNumber(int num) { _totalDiscNumber = num; }
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
void setDuration(boost::posix_time::time_duration duration) { _duration = duration; }
void setLastWriteTime(boost::posix_time::ptime time) { _fileLastWrite = time; }
void setAddedTime(boost::posix_time::ptime time) { _fileAdded = time; }
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
void setChecksum(const std::vector<unsigned char>& checksum) { _fileChecksum = checksum; }
void setDate(const boost::posix_time::ptime& date) { _date = date; }
void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; }
void setDate(Wt::WDate date) { _date = date; }
void setOriginalDate(Wt::WDate date) { _originalDate = date; }
void setGenres(const std::string& genreList) { _genreList = genreList; }
void setCoverType(CoverType coverType) { _coverType = coverType; }
void setMBID(const std::string& MBID) { _MBID = MBID; }
@@ -197,11 +185,11 @@ class Track
boost::optional<std::size_t> getTotalDiscNumber(void) const;
std::string getName(void) const { return _name; }
boost::filesystem::path getPath(void) const { return _filePath; }
boost::posix_time::time_duration getDuration(void) const { return _duration; }
boost::posix_time::ptime getDate(void) const { return _date; }
boost::posix_time::ptime getOriginalDate(void) const { return _originalDate; }
boost::posix_time::ptime getLastWriteTime(void) const { return _fileLastWrite; }
boost::posix_time::ptime getAddedTime(void) const { return _fileAdded; }
std::chrono::milliseconds getDuration(void) const { return _duration; }
Wt::WDate getDate(void) const { return _date; }
Wt::WDate getOriginalDate(void) const { return _originalDate; }
Wt::WDateTime getLastWriteTime(void) const { return _fileLastWrite; }
Wt::WDateTime getAddedTime(void) const { return _fileAdded; }
const std::vector<unsigned char>& getChecksum(void) const { return _fileChecksum; }
CoverType getCoverType(void) const { return _coverType; }
const std::string& getMBID(void) const { return _MBID; }
@@ -244,14 +232,14 @@ class Track
std::string _name;
std::string _artistName;
std::string _releaseName;
boost::posix_time::time_duration _duration;
boost::posix_time::ptime _date;
boost::posix_time::ptime _originalDate; // original date time
std::chrono::duration<int, std::milli> _duration;
Wt::WDate _date;
Wt::WDate _originalDate; // original date time
std::string _genreList;
std::string _filePath;
std::vector<unsigned char> _fileChecksum;
boost::posix_time::ptime _fileLastWrite;
boost::posix_time::ptime _fileAdded;
Wt::WDateTime _fileLastWrite;
Wt::WDateTime _fileAdded;
CoverType _coverType;
std::string _MBID; // Musicbrainz Identifier
+1 -1
View File
@@ -51,7 +51,7 @@ User::getAll(Wt::Dbo::Session& session)
User::pointer
User::create(Wt::Dbo::Session& session)
{
return session.add(new User());
return session.add(std::make_unique<User>());
}
User::pointer
+2 -2
View File
@@ -22,8 +22,8 @@
#include <vector>
#include <Wt/Dbo/Dbo>
#include <Wt/Auth/Dbo/AuthInfo>
#include <Wt/Dbo/Dbo.h>
#include <Wt/Auth/Dbo/AuthInfo.h>
namespace Database {
-216
View File
@@ -1,216 +0,0 @@
/*
* Copyright (C) 2016 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 <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>
#include <pstreams/pstream.h>
#include <boost/property_tree/json_parser.hpp>
#include "utils/Logger.hpp"
#include "utils/Path.hpp"
#include "FeatureExtractor.hpp"
namespace Feature {
static boost::filesystem::path extractorPath = boost::filesystem::path();
bool
Extractor::init(void)
{
static const std::string execName = "streaming_extractor_music";
extractorPath = searchExecPath(execName);
if (extractorPath.empty())
{
LMS_LOG(FEATURE, ERROR) << "Failed to find path to " << execName;
return false;
}
return true;
}
Extractor::Extractor()
{ }
static bool fetchJSONData(boost::property_tree::ptree& pt, std::string url)
{
try
{
curlpp::Cleanup myCleanup;
std::ostringstream os;
os << curlpp::options::Url(url);
std::istringstream iss(os.str());
boost::property_tree::ptree res;
boost::property_tree::json_parser::read_json(iss, res);
pt = res;
}
catch( curlpp::RuntimeError &e )
{
LMS_LOG(FEATURE, ERROR) << "curlpp error: " << e.what();
return false;
}
catch( curlpp::LogicError &e )
{
LMS_LOG(FEATURE, ERROR) << "curlpp error: " << e.what();
return false;
}
catch ( boost::property_tree::ptree_error& e)
{
LMS_LOG(FEATURE, ERROR) << "JSON paring failed: " << e.what();
return false;
}
return true;
}
bool
Extractor::getLowLevel(boost::property_tree::ptree& pt, std::string mbid)
{
LMS_LOG(FEATURE, DEBUG) << "Trying to fetch low level metadata for track '" << mbid << "' on AcousticBrainz";
boost::property_tree::ptree res;
if (!fetchJSONData(res, "https://acousticbrainz.org/" + mbid + "/low-level"))
return false;
auto message = res.get_child_optional("message");
if (message)
{
LMS_LOG(FEATURE, ERROR) << "Track '" << mbid << "': cannot get data on AcousticBrainz: " << message->data();
return false;
}
auto lowlevel = res.get_child_optional("lowlevel");
if (!lowlevel)
{
LMS_LOG(FEATURE, ERROR) << "Track '" << mbid << "': low level data not found!";
return false;
}
// Keep metadata to ease debugging
// res.erase("metadata");
pt = res;
return true;
}
bool
Extractor::getHighLevel(boost::property_tree::ptree& pt, std::string mbid)
{
LMS_LOG(FEATURE, DEBUG) << "Trying to fetch high level metadata for track '" << mbid << "' on AcousticBrainz";
// TODO check MBID
if (mbid.empty())
return false;
boost::property_tree::ptree res;
if (!fetchJSONData(res, "https://acousticbrainz.org/" + mbid + "/high-level"))
return false;
auto message = res.get_child_optional("message");
if (message)
{
LMS_LOG(FEATURE, ERROR) << "Track '" << mbid << "': cannot get data on AcousticBrainz: " << message->data();
return false;
}
auto lowlevel = res.get_child_optional("highlevel");
if (!lowlevel)
{
LMS_LOG(FEATURE, ERROR) << "Track '" << mbid << "': high level data not found!";
return false;
}
res.erase("metadata");
pt = res;
return true;
}
bool
Extractor::getLowLevel(boost::property_tree::ptree& pt, boost::filesystem::path path)
{
LMS_LOG(FEATURE, DEBUG) << "Extracting low level data from '" << path << "'";
if (extractorPath.empty())
return false;
std::vector<std::string> args;
args.push_back(extractorPath.string());
args.push_back(path.string());
args.push_back("-"); // output to stdout
std::string jsonData;
{
redi::ipstream in;
in.open(extractorPath.string(), args);
if (!in.is_open())
{
LMS_LOG(FEATURE, ERROR) << "Exec failed!";
return false;
}
bool firstLineHit = false;
std::string line;
while(std::getline(in, line))
{
if (!firstLineHit)
{
if (line != "{")
continue;
firstLineHit = true;
}
jsonData += line;
jsonData += '\n';
}
}
try
{
std::istringstream iss(jsonData);
boost::property_tree::json_parser::read_json(iss, pt);
pt.erase("metadata");
}
catch ( boost::property_tree::ptree_error& e)
{
LMS_LOG(FEATURE, ERROR) << "JSON parsing failed: " << e.what();
return false;
}
return true;
}
} // namespace Feature
-43
View File
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2016 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 <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#pragma once
namespace Feature {
class Extractor
{
public:
static bool init(void);
static bool getLowLevel(boost::property_tree::ptree& pt, boost::filesystem::path path);
static bool getLowLevel(boost::property_tree::ptree& pt, std::string mbid);
static bool getHighLevel(boost::property_tree::ptree& pt, std::string mbid);
private:
Extractor();
};
} // namespace Feature
-126
View File
@@ -1,126 +0,0 @@
/*
* Copyright (C) 2016 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 <boost/property_tree/json_parser.hpp>
#include "utils/Config.hpp"
#include "utils/Logger.hpp"
#include "utils/Path.hpp"
#include "FeatureStore.hpp"
namespace Feature {
Store::Store()
{
}
Store&
Store::instance(void)
{
static Store instance;
return instance;
}
static boost::filesystem::path
getPath(std::string mbid, std::string type)
{
return Config::instance().getPath("working-dir") / "features" / (mbid + "_" + type);
}
bool
Store::exists(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type)
{
Wt::Dbo::Transaction transaction(session);
auto track = Database::Track::getById(session, trackId);
std::string mbid = track->getMBID();
transaction.commit();
if (mbid.empty())
return false;
boost::filesystem::path path = getPath(mbid, type);
return boost::filesystem::exists(path);
}
bool
Store::get(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type, boost::property_tree::ptree& feature)
{
Wt::Dbo::Transaction transaction(session);
auto track = Database::Track::getById(session, trackId);
std::string mbid = track->getMBID();
transaction.commit();
if (mbid.empty())
return false;
boost::filesystem::path path = getPath(mbid, type);
if (!boost::filesystem::exists(path))
return false;
try
{
std::ifstream iss(path.string().c_str(), std::ios::in);
boost::property_tree::json_parser::read_json(iss, feature);
}
catch (boost::property_tree::ptree_error& e)
{
LMS_LOG(FEATURE, ERROR) << "JSON parsing failed: " << e.what();
return false;
}
return true;
}
bool
Store::set(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type, const boost::property_tree::ptree& feature)
{
Wt::Dbo::Transaction transaction(session);
auto track = Database::Track::getById(session, trackId);
std::string mbid = track->getMBID();
transaction.commit();
if (mbid.empty())
return false;
boost::filesystem::path path = getPath(mbid, type);
try
{
std::ofstream oss(path.string().c_str(), std::ios::out);
boost::property_tree::json_parser::write_json(oss, feature);
}
catch (boost::property_tree::ptree_error& e)
{
LMS_LOG(FEATURE, ERROR) << "JSON writing failed: " << e.what();
return false;
}
return true;
}
} // namespace CoverArt
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2016 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 <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree.hpp>
#include "database/Types.hpp"
namespace Feature {
class Store
{
public:
Store(const Store&) = delete;
Store& operator=(const Store&) = delete;
static Store& instance();
bool exists(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type);
bool get(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type, boost::property_tree::ptree& feature);
bool set(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::string type, const boost::property_tree::ptree& feature);
private:
Store();
};
} // namespace CoverArt
+1 -1
View File
@@ -79,7 +79,7 @@ Image::load(boost::filesystem::path p)
}
catch (Magick::Exception& e)
{
LMS_LOG(COVER, ERROR) << "Caught Magick exception while loading image from file '" << p << "': " << e.what();
LMS_LOG(COVER, ERROR) << "Caught Magick exception while loading image from file '" << p.string() << "': " << e.what();
return false;
}
}
+7 -8
View File
@@ -20,14 +20,14 @@
#include <boost/filesystem.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <Wt/WServer>
#include <Wt/WServer.h>
#include <Wt/WApplication.h>
#include "utils/Config.hpp"
#include "utils/Logger.hpp"
#include "av/AvInfo.hpp"
#include "av/AvTranscoder.hpp"
#include "image/Image.hpp"
#include "feature/FeatureExtractor.hpp"
#include "scanner/MediaScanner.hpp"
@@ -117,17 +117,16 @@ int main(int argc, char* argv[])
Av::AvInit();
Av::Transcoder::init();
Database::Handler::configureAuth();
Feature::Extractor::init();
// Initializing a connection pool to the database that will be shared along services
std::unique_ptr<Wt::Dbo::SqlConnectionPool>
connectionPool( Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db"));
auto connectionPool = Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db");
Scanner::MediaScanner scanner(*connectionPool);
// bind entry point
server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create,
_1, boost::ref(*connectionPool), boost::ref(scanner)));
server.addEntryPoint(Wt::EntryPointType::Application,
std::bind(UserInterface::LmsApplication::create,
std::placeholders::_1, std::ref(*connectionPool), std::ref(scanner)));
// Start
LMS_LOG(MAIN, INFO) << "Starting Media scanner...";
@@ -138,7 +137,7 @@ int main(int argc, char* argv[])
// Wait
LMS_LOG(MAIN, INFO) << "Now running...";
Wt::WServer::waitForShutdown(argv[0]);
Wt::WServer::waitForShutdown();
// Stop
LMS_LOG(MAIN, INFO) << "Stopping server...";
+25 -25
View File
@@ -77,11 +77,11 @@ AvFormat::parse(const boost::filesystem::path& p)
std::cout << "TAG = " << tag << ", VAL = " << value << std::endl;
#endif
if (tag == "ARTIST")
items.insert( std::make_pair(MetaData::Type::Artist, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::Artist, stringTrim( value) ));
else if (tag == "ALBUM")
items.insert( std::make_pair(MetaData::Type::Album, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::Album, stringTrim( value) ));
else if (tag == "TITLE")
items.insert( std::make_pair(MetaData::Type::Title, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::Title, stringTrim( value) ));
else if (tag == "TRACK")
{
// Expecting 'Number/Total'
@@ -89,15 +89,15 @@ AvFormat::parse(const boost::filesystem::path& p)
if (strings.size() > 0)
{
std::size_t number;
if (readAs<std::size_t>(strings[0], number))
items.insert( std::make_pair(MetaData::Type::TrackNumber, number ));
auto number = readAs<std::size_t>(strings[0]);
if (number)
items.insert( std::make_pair(MetaData::Type::TrackNumber, *number ));
if (strings.size() > 1)
{
std::size_t totalNumber;
if (readAs<std::size_t>(strings[1], totalNumber))
items.insert( std::make_pair(MetaData::Type::TotalTrack, totalNumber ));
auto totalNumber = readAs<std::size_t>(strings[1]);
if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalTrack, *totalNumber ));
}
}
}
@@ -108,15 +108,15 @@ AvFormat::parse(const boost::filesystem::path& p)
if (strings.size() > 0)
{
std::size_t number;
if (readAs<std::size_t>(strings[0], number))
items.insert( std::make_pair(MetaData::Type::DiscNumber, number ));
auto number = readAs<std::size_t>(strings[0]);
if (number)
items.insert( std::make_pair(MetaData::Type::DiscNumber, *number ));
if (strings.size() > 1)
{
std::size_t totalNumber;
if (readAs<std::size_t>(strings[1], totalNumber))
items.insert( std::make_pair(MetaData::Type::TotalDisc, totalNumber ));
auto totalNumber = readAs<std::size_t>(strings[1]);
if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalDisc, *totalNumber ));
}
}
}
@@ -124,36 +124,36 @@ AvFormat::parse(const boost::filesystem::path& p)
|| tag == "YEAR"
|| tag == "WM/Year")
{
boost::posix_time::ptime p;
if (readAsPosixTime(value, p))
items.insert( std::make_pair(MetaData::Type::Date, p));
auto date = readAs<Wt::WDate>(value);
if (date)
items.insert( std::make_pair(MetaData::Type::Date, *date));
}
else if (tag == "TDOR" // Original release time (ID3v2 2.4)
|| tag == "TORY") // Original release year
{
boost::posix_time::ptime p;
if (readAsPosixTime(value, p))
items.insert( std::make_pair(MetaData::Type::OriginalDate, p));
auto date = readAs<Wt::WDate>(value);
if (date)
items.insert( std::make_pair(MetaData::Type::OriginalDate, *date));
}
else if (tag == "MUSICBRAINZ ARTIST ID"
|| tag == "MUSICBRAINZ_ARTISTID")
{
items.insert( std::make_pair(MetaData::Type::MusicBrainzArtistID, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::MusicBrainzArtistID, stringTrim(value)) );
}
else if (tag == "MUSICBRAINZ ALBUM ID"
|| tag == "MUSICBRAINZ_ALBUMID")
{
items.insert( std::make_pair(MetaData::Type::MusicBrainzAlbumID, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::MusicBrainzAlbumID, stringTrim(value)) );
}
else if (tag == "MUSICBRAINZ RELEASE TRACK ID"
|| tag == "MUSICBRAINZ_RELEASETRACKID"
|| tag == "MUSICBRAINZ_TRACKID")
{
items.insert( std::make_pair(MetaData::Type::MusicBrainzTrackID, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::MusicBrainzTrackID, stringTrim(value)) );
}
else if (tag == "ACOUSTID ID")
{
items.insert( std::make_pair(MetaData::Type::AcoustID, stringTrim( stringToUTF8(value)) ));
items.insert( std::make_pair(MetaData::Type::AcoustID, stringTrim(value)) );
}
else if (_clusterMap.find(tag) != _clusterMap.end())
{
+3 -3
View File
@@ -37,13 +37,13 @@ namespace MetaData
Title, // string
Album, // string
Clusters, // Clusters, ex: { "genre", {"death metal", "brutal death"} }, { "albumgrouping", {"metal"} }
Duration, // boost::posix_time::time_duration
Duration, // std::chrono::milliseconds
TrackNumber, // size_t
DiscNumber, // size_t
TotalTrack, // size_t
TotalDisc, // size_t
Date, // boost::posix_time::ptime
OriginalDate, // boost::posix_time::ptime
Date, // Wt::WDate
OriginalDate, // Wt::WDate
HasCover, // bool
AudioStreams, // vector<AudioStream>
MusicBrainzArtistID, // string
+22 -23
View File
@@ -55,8 +55,7 @@ TagLibParser::parse(const boost::filesystem::path& p)
{
TagLib::AudioProperties *properties = f.audioProperties();
boost::posix_time::time_duration duration = boost::posix_time::seconds(properties->length());
std::chrono::milliseconds duration(properties->length() * 1000);
items.insert( std::make_pair(MetaData::Type::Duration, duration) );
MetaData::AudioStream audioStream = { .bitRate = static_cast<std::size_t>(properties->bitrate() * 1000) };
@@ -124,15 +123,15 @@ TagLibParser::parse(const boost::filesystem::path& p)
if (!strings.empty())
{
std::size_t number;
if (readAs<std::size_t>(strings[0], number))
items.insert( std::make_pair(MetaData::Type::TrackNumber, number ));
auto number = readAs<std::size_t>(strings[0]);
if (number)
items.insert( std::make_pair(MetaData::Type::TrackNumber, *number ));
if (strings.size() > 1)
{
std::size_t totalNumber;
if (readAs<std::size_t>(strings[1], totalNumber))
items.insert( std::make_pair(MetaData::Type::TotalTrack, totalNumber ));
auto totalNumber = readAs<std::size_t>(strings[1]);
if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalTrack, *totalNumber ));
}
}
}
@@ -143,32 +142,32 @@ TagLibParser::parse(const boost::filesystem::path& p)
if (!strings.empty())
{
std::size_t number;
if (readAs<std::size_t>(strings[0], number))
items.insert( std::make_pair(MetaData::Type::DiscNumber, number ));
auto number = readAs<std::size_t>(strings[0]);
if (number)
items.insert( std::make_pair(MetaData::Type::DiscNumber, *number));
if (strings.size() > 1)
{
std::size_t totalNumber;
if (readAs<std::size_t>(strings[1], totalNumber))
items.insert( std::make_pair(MetaData::Type::TotalDisc, totalNumber ));
auto totalNumber = readAs<std::size_t>(strings[1]);
if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalDisc, *totalNumber ));
}
}
}
else if (tag == "DATE")
{
boost::posix_time::ptime p;
if (readAsPosixTime(values.front().to8Bit(), p))
items.insert( std::make_pair(MetaData::Type::Date, p));
auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (timePoint)
items.insert( std::make_pair(MetaData::Type::Date, *timePoint));
}
else if (tag == "ORIGINALDATE")
{
boost::posix_time::ptime p;
if (readAsPosixTime(values.front().to8Bit(), p))
auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (timePoint)
{
// Take priority on original year
items.erase( MetaData::Type::OriginalDate );
items.insert( std::make_pair(MetaData::Type::OriginalDate, p));
items.insert( std::make_pair(MetaData::Type::OriginalDate, *timePoint));
}
}
else if (tag == "ORIGINALYEAR")
@@ -176,9 +175,9 @@ TagLibParser::parse(const boost::filesystem::path& p)
// lower priority than original date
if (items.find(MetaData::Type::OriginalDate) == items.end())
{
boost::posix_time::ptime p;
if (readAsPosixTime(values.front().to8Bit(), p))
items.insert( std::make_pair(MetaData::Type::OriginalDate, p));
auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (timePoint)
items.insert( std::make_pair(MetaData::Type::OriginalDate, *timePoint));
}
}
else if (tag == "METADATA_BLOCK_PICTURE")
+61 -76
View File
@@ -20,9 +20,10 @@
#include <stdexcept>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <boost/asio/placeholders.hpp>
#include <Wt/WLocalDateTime.h>
#include "cover/CoverArtGrabber.hpp"
#include "database/Setting.hpp"
@@ -36,40 +37,28 @@
namespace {
boost::gregorian::date
getNextDay(const boost::gregorian::date& current)
Wt::WDate
getNextMonday(Wt::WDate current)
{
boost::gregorian::day_iterator it(current);
return *(++it);
do
{
current.addDays(1);
} while (current.dayOfWeek() != 1);
return current;
}
boost::gregorian::date
getNextMonday(const boost::gregorian::date& current)
Wt::WDate
getNextFirstOfMonth(Wt::WDate current)
{
boost::gregorian::day_iterator it(current);
do
{
current.addDays(1);
} while (current.day() != 1);
++it;
// While it's not monday
while( it->day_of_week() != 1 )
++it;
return *(it);
return current;
}
boost::gregorian::date
getNextFirstOfMonth(const boost::gregorian::date& current)
{
boost::gregorian::day_iterator it(current);
++it;
// While it's not the 1st of the month
while( it->day() != 1 )
++it;
return (*it);
}
bool
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions)
{
@@ -119,14 +108,14 @@ setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod)
Setting::setInt(session, "update_period", static_cast<int>(updatePeriod));
}
boost::posix_time::time_duration getUpdateStartTime(Wt::Dbo::Session& session)
Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session)
{
return Setting::getDuration(session, "update_start_time");
return Setting::getTime(session, "update_start_time");
}
void setUpdateStartTime(Wt::Dbo::Session& session, boost::posix_time::time_duration startTime)
void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime startTime)
{
Setting::setDuration(session, "update_start_time", startTime);
Setting::setTime(session, "update_start_time", startTime);
}
MediaScanner::MediaScanner(Wt::Dbo::SqlConnectionPool& connectionPool)
@@ -176,7 +165,7 @@ MediaScanner::scheduleImmediateScan()
_ioService.post([=]()
{
LMS_LOG(DBUPDATER, INFO) << "Schedule immediate scan";
scheduleScan( boost::posix_time::seconds(0) );
scheduleScan(std::chrono::seconds(0));
});
}
@@ -193,29 +182,31 @@ MediaScanner::reschedule()
void
MediaScanner::scheduleScan()
{
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
boost::posix_time::time_duration startTime = getUpdateStartTime(_db.getSession());
using namespace std::chrono_literals;
boost::gregorian::date nextScanDate;
Wt::WTime startTime = getUpdateStartTime(_db.getSession());
Wt::WDateTime now = Wt::WLocalDateTime::currentServerDateTime().toUTC();
Wt::WDate nextScanDate;
switch ( getUpdatePeriod(_db.getSession()) )
{
case UpdatePeriod::Daily:
if (now.time_of_day() < startTime)
if (now.time() < startTime)
nextScanDate = now.date();
else
nextScanDate = getNextDay(now.date());
nextScanDate = now.date().addDays(1);
break;
case UpdatePeriod::Weekly:
if (now.time_of_day() < startTime && now.date().day_of_week() == 1)
if (now.time() < startTime && now.date().dayOfWeek() == 1)
nextScanDate = now.date();
else
nextScanDate = getNextMonday(now.date());
break;
case UpdatePeriod::Monthly:
if (now.time_of_day() < startTime && now.date().day() == 1)
if (now.time() < startTime && now.date().day() == 1)
nextScanDate = now.date();
else
nextScanDate = getNextFirstOfMonth(now.date());
@@ -226,24 +217,25 @@ MediaScanner::scheduleScan()
break;
}
if (!nextScanDate.is_special())
scheduleScan( boost::posix_time::ptime (nextScanDate, startTime) );
if (nextScanDate.isValid())
scheduleScan( Wt::WDateTime(nextScanDate, startTime).toTimePoint() );
}
void
MediaScanner::scheduleScan( boost::posix_time::time_duration duration)
MediaScanner::scheduleScan(std::chrono::seconds duration)
{
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan in " << duration;
_scheduleTimer.expires_from_now(duration);
_scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) );
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan in " << duration.count() << " seconds";
_scheduleTimer.expires_from_now(std::chrono::seconds(5)); //duration);
_scheduleTimer.async_wait( std::bind( &MediaScanner::scan, this, std::placeholders::_1) );
}
void
MediaScanner::scheduleScan( boost::posix_time::ptime time)
MediaScanner::scheduleScan(std::chrono::system_clock::time_point timePoint)
{
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << time;
_scheduleTimer.expires_at(time);
_scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) );
std::time_t t = std::chrono::system_clock::to_time_t(timePoint);
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << std::string(std::ctime(&t));
_scheduleTimer.expires_at(timePoint);
_scheduleTimer.async_wait(std::bind(&MediaScanner::scan, this, std::placeholders::_1));
}
void
@@ -263,9 +255,9 @@ MediaScanner::scan(boost::system::error_code err)
if (!_running)
break;
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "'...";
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "'...";
scanRootDirectory(rootDirectory, stats);
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "' DONE";
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "' DONE";
}
if (_running)
@@ -273,16 +265,9 @@ MediaScanner::scan(boost::system::error_code err)
LMS_LOG(DBUPDATER, INFO) << "Scan " << (_running ? "complete" : "aborted") << ". Changes = " << stats.nbChanges() << " (added = " << stats.additions << ", removed = " << stats.deletions << ", updated = " << stats.updates << "), Not changed = " << stats.skips << ", Scanned = " << stats.scans << " (errors = " << stats.scanErrors << ", not imported = " << stats.incompleteScans << "), duplicates = " << stats.nbDuplicates() << " (hash = " << stats.duplicateHashes << ", mbid = " << stats.duplicateMBID << ")";
// Update database stats
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
if (stats.nbChanges() > 0)
Setting::setTime(_db.getSession(), "last_update", now);
// Save the last scan only if it has been completed
if (_running)
{
Setting::setTime(_db.getSession(), "last_scan", now);
scheduleScan();
scanComplete().emit(stats);
@@ -408,7 +393,7 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames)
void
MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
{
boost::posix_time::ptime lastWriteTime (boost::posix_time::from_time_t( boost::filesystem::last_write_time( file ) ) );
auto lastWriteTime = Wt::WDateTime::fromTime_t(boost::filesystem::last_write_time(file));
// Skip file if last write is the same
{
@@ -445,7 +430,7 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
if ((*items).find(MetaData::Type::AudioStreams) == (*items).end()
|| boost::any_cast<std::vector<MetaData::AudioStream>> ((*items)[MetaData::Type::AudioStreams]).empty())
{
LMS_LOG(DBUPDATER, INFO) << "Skipped '" << file << "' (no audio stream found)";
LMS_LOG(DBUPDATER, INFO) << "Skipped '" << file.string() << "' (no audio stream found)";
// If Track exists here, delete it!
if (track)
@@ -457,9 +442,9 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
return;
}
if ((*items).find(MetaData::Type::Duration) == (*items).end()
|| boost::any_cast<boost::posix_time::time_duration>((*items)[MetaData::Type::Duration]).total_seconds() <= 0)
|| boost::any_cast<std::chrono::milliseconds>((*items)[MetaData::Type::Duration]).count() <= 0)
{
LMS_LOG(DBUPDATER, INFO) << "Skipped '" << file << "' (no duration or duration <= 0)";
LMS_LOG(DBUPDATER, INFO) << "Skipped '" << file.string() << "' (no duration or duration <= 0)";
// If Track exists here, delete it!
if (track)
@@ -534,12 +519,12 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
{
// Create a new song
track = Track::create(_db.getSession(), file);
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file << "'";
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file.string() << "'";
stats.additions++;
}
else
{
LMS_LOG(DBUPDATER, INFO) << "Updating '" << file << "'";
LMS_LOG(DBUPDATER, INFO) << "Updating '" << file.string() << "'";
// Remove the songs from its clusters
for (auto cluster : track->getClusters())
@@ -555,8 +540,8 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
track.modify()->setRelease(release);
track.modify()->setLastWriteTime(lastWriteTime);
track.modify()->setName(title);
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>((*items)[MetaData::Type::Duration]) );
track.modify()->setAddedTime( boost::posix_time::second_clock::local_time() );
track.modify()->setDuration( boost::any_cast<std::chrono::milliseconds>((*items)[MetaData::Type::Duration]) );
track.modify()->setAddedTime( Wt::WLocalDateTime::currentServerDateTime().toUTC() );
{
std::string trackClusterList;
@@ -586,15 +571,15 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
track.modify()->setTotalDiscNumber( boost::any_cast<std::size_t>((*items)[MetaData::Type::TotalDisc]) );
if ((*items).find(MetaData::Type::Date) != (*items).end())
track.modify()->setDate( boost::any_cast<boost::posix_time::ptime>((*items)[MetaData::Type::Date]) );
track.modify()->setDate( boost::any_cast<Wt::WDate>((*items)[MetaData::Type::Date]) );
if ((*items).find(MetaData::Type::OriginalDate) != (*items).end())
{
track.modify()->setOriginalDate( boost::any_cast<boost::posix_time::ptime>((*items)[MetaData::Type::OriginalDate]) );
track.modify()->setOriginalDate( boost::any_cast<Wt::WDate>((*items)[MetaData::Type::OriginalDate]) );
// If a file has an OriginalDate but no date, set the date to ease filtering
if ((*items).find(MetaData::Type::Date) == (*items).end())
track.modify()->setDate( boost::any_cast<boost::posix_time::ptime>((*items)[MetaData::Type::OriginalDate]) );
track.modify()->setDate( boost::any_cast<Wt::WDate>((*items)[MetaData::Type::OriginalDate]) );
}
if ((*items).find(MetaData::Type::MusicBrainzRecordingID) != (*items).end())
@@ -652,7 +637,7 @@ checkFile(const boost::filesystem::path& p, const std::vector<boost::filesystem:
if (!boost::filesystem::exists( p )
|| !boost::filesystem::is_regular( p ) )
{
LMS_LOG(DBUPDATER, INFO) << "Missing file '" << p << "'";
LMS_LOG(DBUPDATER, INFO) << "Missing file '" << p.string() << "'";
status = false;
}
else
@@ -670,12 +655,12 @@ checkFile(const boost::filesystem::path& p, const std::vector<boost::filesystem:
if (!foundRoot)
{
LMS_LOG(DBUPDATER, INFO) << "Out of root file '" << p << "'";
LMS_LOG(DBUPDATER, INFO) << "Out of root file '" << p.string() << "'";
status = false;
}
else if (!isFileSupported(p, extensions))
{
LMS_LOG(DBUPDATER, INFO) << "File format no longer supported for '" << p << "'";
LMS_LOG(DBUPDATER, INFO) << "File format no longer supported for '" << p.string() << "'";
status = false;
}
}
@@ -685,7 +670,7 @@ checkFile(const boost::filesystem::path& p, const std::vector<boost::filesystem:
}
catch (boost::filesystem::filesystem_error& e)
{
LMS_LOG(DBUPDATER, ERROR) << "Caught exception while checking file '" << p << "': " << e.what();
LMS_LOG(DBUPDATER, ERROR) << "Caught exception while checking file '" << p.string() << "': " << e.what();
return false;
}
@@ -779,14 +764,14 @@ MediaScanner::checkDuplicatedAudioFiles(Stats& stats)
std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db.getSession());
for (Track::pointer track : tracks)
{
LMS_LOG(DBUPDATER, INFO) << "Found duplicated MBID [" << track->getMBID() << "], file: " << track->getPath() << " - " << track->getName();
LMS_LOG(DBUPDATER, INFO) << "Found duplicated MBID [" << track->getMBID() << "], file: " << track->getPath().string() << " - " << track->getName();
stats.duplicateMBID++;
}
tracks = Database::Track::getChecksumDuplicates(_db.getSession());
for (Track::pointer track : tracks)
{
LMS_LOG(DBUPDATER, INFO) << "Found duplicated checksum [" << bufferToString(track->getChecksum()) << "], file: " << track->getPath() << " - " << track->getName();
LMS_LOG(DBUPDATER, INFO) << "Found duplicated checksum [" << bufferToString(track->getChecksum()) << "], file: " << track->getPath().string() << " - " << track->getName();
stats.duplicateHashes++;
}
+10 -8
View File
@@ -19,10 +19,12 @@
#pragma once
#include <boost/asio/deadline_timer.hpp>
#include <chrono>
#include <Wt/WIOService>
#include <Wt/WSignal>
#include <Wt/WIOService.h>
#include <Wt/WSignal.h>
#include <boost/asio/system_timer.hpp>
#include "metadata/TagLibParser.hpp"
@@ -40,8 +42,8 @@ enum class UpdatePeriod {
UpdatePeriod getUpdatePeriod(Wt::Dbo::Session& session);
void setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod);
boost::posix_time::time_duration getUpdateStartTime(Wt::Dbo::Session& session);
void setUpdateStartTime(Wt::Dbo::Session& session, boost::posix_time::time_duration);
Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session);
void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime time);
class MediaScanner
{
@@ -92,8 +94,8 @@ class MediaScanner
// Job handling
void scheduleScan();
void scheduleScan(boost::posix_time::time_duration duration);
void scheduleScan(boost::posix_time::ptime time);
void scheduleScan(std::chrono::seconds duration);
void scheduleScan(std::chrono::system_clock::time_point time);
// Update database (scheduled callback)
void scan(boost::system::error_code ec);
@@ -119,7 +121,7 @@ class MediaScanner
Wt::Signal<Database::Track::pointer> _sigAddedTrack;
Wt::Signal<Database::Track::pointer> _sigRemovedTrack;
boost::asio::deadline_timer _scheduleTimer;
boost::asio::system_timer _scheduleTimer;
Database::Handler _db;
+22 -25
View File
@@ -17,10 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WFormModel>
#include <Wt/WLineEdit>
#include <Wt/WCheckBox>
#include <Wt/WPushButton>
#include <Wt/WFormModel.h>
#include <Wt/WLineEdit.h>
#include <Wt/WCheckBox.h>
#include <Wt/WPushButton.h>
#include "utils/Logger.hpp"
@@ -31,10 +31,10 @@
namespace UserInterface {
Auth::Auth(Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
Auth::Auth()
: Wt::WTemplateFormView()
{
_model = new Wt::Auth::AuthModel(DbHandler().getAuthService(), DbHandler().getUserDatabase());
_model = std::make_shared<Wt::Auth::AuthModel>(LmsApp->getDb().getAuthService(), LmsApp->getDb().getUserDatabase());
_model->addPasswordAuth(&Database::Handler::getPasswordService());
@@ -43,50 +43,47 @@ Auth::Auth(Wt::WContainerWidget *parent)
addFunction("id", &WTemplate::Functions::id);
// LoginName
auto loginName = new Wt::WLineEdit();
setFormWidget(Wt::Auth::AuthModel::LoginNameField, loginName);
setFormWidget(Wt::Auth::AuthModel::LoginNameField, std::make_unique<Wt::WLineEdit>());
// Password
auto password = new Wt::WLineEdit();
setFormWidget(Wt::Auth::AuthModel::PasswordField, password);
password->setEchoMode(Wt::WLineEdit::Password);
auto password = std::make_unique<Wt::WLineEdit>();
password->setEchoMode(Wt::EchoMode::Password);
password->enterPressed().connect(this, &Auth::processAuth);
setFormWidget(Wt::Auth::AuthModel::PasswordField, std::move(password));
// Remember Me
auto rememberMe = new Wt::WCheckBox();
setFormWidget(Wt::Auth::AuthModel::RememberMeField, rememberMe);
setFormWidget(Wt::Auth::AuthModel::RememberMeField, std::make_unique<Wt::WCheckBox>());
auto loginBtn = new Wt::WPushButton(Wt::WString::tr("Lms.login"));
bindWidget("login-btn", loginBtn);
Wt::WPushButton* loginBtn = bindNew<Wt::WPushButton>("login-btn", Wt::WString::tr("Lms.login"));
loginBtn->clicked().connect(this, &Auth::processAuth);
password->enterPressed().connect(this, &Auth::processAuth);
DbHandler().getLogin().changed().connect(std::bind([=]
LmsApp->getDb().getLogin().changed().connect(std::bind([=]
{
if (DbHandler().getLogin().loggedIn())
if (LmsApp->getDb().getLogin().loggedIn())
this->setHidden(true);
}));
Wt::Auth::User user = _model->processAuthToken();
_model->loginUser(DbHandler().getLogin(), user, Wt::Auth::WeakLogin);
_model->loginUser(LmsApp->getDb().getLogin(), user, Wt::Auth::LoginState::Weak);
updateView(_model);
updateView(_model.get());
}
void
Auth::processAuth()
{
updateModel(_model);
updateModel(_model.get());
if (_model->validate())
_model->login(DbHandler().getLogin());
_model->login(LmsApp->getDb().getLogin());
else
updateView(_model);
updateView(_model.get());
}
void
Auth::logout()
{
_model->logout(DbHandler().getLogin());
_model->logout(LmsApp->getDb().getLogin());
}
} // namespace UserInterface
+4 -6
View File
@@ -19,24 +19,22 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/Auth/Login>
#include <Wt/Auth/AuthModel>
#include <Wt/WTemplateFormView.h>
#include <Wt/Auth/AuthModel.h>
namespace UserInterface {
class Auth : public Wt::WTemplateFormView
{
public:
Auth(Wt::WContainerWidget *parent = 0);
Auth();
void logout();
private:
void processAuth();
Wt::Auth::AuthModel* _model;
std::shared_ptr<Wt::Auth::AuthModel> _model;
};
+5 -4
View File
@@ -17,7 +17,7 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WTemplate>
#include <Wt/WTemplate.h>
#include "utils/Logger.hpp"
@@ -28,12 +28,13 @@
namespace UserInterface {
Home::Home(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent)
Home::Home()
: Wt::WContainerWidget()
{
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Home.template"), this);
auto t = std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.Home.template"));
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
this->addWidget(std::move(t));
}
} // namespace UserInterface
+2 -2
View File
@@ -19,14 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WContainerWidget.h>
namespace UserInterface {
class Home : public Wt::WContainerWidget
{
public:
Home(Wt::WContainerWidget *parent = 0);
Home();
private:
+60 -110
View File
@@ -17,15 +17,16 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WAnchor>
#include <Wt/WBootstrapTheme>
#include <Wt/WEnvironment>
#include <Wt/WMenu>
#include <Wt/WNavigationBar>
#include <Wt/WPopupMenu>
#include <Wt/WStackedWidget>
#include <Wt/WText>
#include <Wt/Auth/Identity>
#include <Wt/WAnchor.h>
#include <Wt/WBootstrapTheme.h>
#include <Wt/WEnvironment.h>
#include <Wt/WMenu.h>
#include <Wt/WNavigationBar.h>
#include <Wt/WPopupMenu.h>
#include <Wt/WServer.h>
#include <Wt/WStackedWidget.h>
#include <Wt/WText.h>
#include <Wt/Auth/Identity.h>
#include "config/config.h"
#include "utils/Logger.hpp"
@@ -49,14 +50,14 @@
namespace UserInterface {
Wt::WApplication*
std::unique_ptr<Wt::WApplication>
LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner)
{
/*
* You could read information from the environment to decide whether
* the user has permission to start a new application
*/
return new LmsApplication(env, connectionPool, scanner);
return std::make_unique<LmsApplication>(env, connectionPool, scanner);
}
LmsApplication*
@@ -78,10 +79,10 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
_imageResource(nullptr),
_transcodeResource(nullptr)
{
Wt::WBootstrapTheme *bootstrapTheme = new Wt::WBootstrapTheme(this);
bootstrapTheme->setVersion(Wt::WBootstrapTheme::Version3);
auto bootstrapTheme = std::make_unique<Wt::WBootstrapTheme>();
bootstrapTheme->setVersion(Wt::BootstrapVersion::v3);
bootstrapTheme->setResponsive(true);
setTheme(bootstrapTheme);
setTheme(std::move(bootstrapTheme));
useStyleSheet("css/lms.css");
useStyleSheet("resources/font-awesome/css/font-awesome.min.css");
@@ -90,7 +91,7 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
messageResourceBundle().use(appRoot() + "admin-database");
messageResourceBundle().use(appRoot() + "admin-user");
messageResourceBundle().use(appRoot() + "admin-users");
messageResourceBundle().use(appRoot() + "admin-wizard");
messageResourceBundle().use(appRoot() + "admin-initwizard");
messageResourceBundle().use(appRoot() + "artist");
messageResourceBundle().use(appRoot() + "artists");
messageResourceBundle().use(appRoot() + "home");
@@ -113,20 +114,22 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
// If here is no account in the database, launch the first connection wizard
bool firstConnection;
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
firstConnection = (Database::User::getAll(DboSession()).size() == 0);
firstConnection = (Database::User::getAll(LmsApp->getDboSession()).size() == 0);
}
LMS_LOG(UI, DEBUG) << "Creating root widget. First connection = " << std::boolalpha << firstConnection;
if (firstConnection)
{
root()->addWidget(new InitWizardView());
root()->addWidget(std::make_unique<InitWizardView>());
}
else
{
DbHandler().getLogin().changed().connect(std::bind([=]
_auth = root()->addNew<Auth>();
LmsApp->getDb().getLogin().changed().connect(std::bind([=]
{
try
{
@@ -138,68 +141,31 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
throw std::runtime_error("Internal error");
}
}));
_auth = new Auth();
root()->addWidget(_auth);
}
}
Database::Handler& DbHandler()
{
return LmsApplication::instance()->getDbHandler();
}
Wt::Dbo::Session& DboSession()
{
return DbHandler().getSession();
}
const Wt::Auth::User& CurrentAuthUser()
{
return DbHandler().getLogin().user();
}
Database::User::pointer CurrentUser()
{
return DbHandler().getCurrentUser();
}
ImageResource* SessionImageResource()
{
return LmsApplication::instance()->getImageResource();
}
TranscodeResource* SessionTranscodeResource()
{
return LmsApplication::instance()->getTranscodeResource();
}
Scanner::MediaScanner& MediaScanner()
{
return LmsApplication::instance()->getMediaScanner();
}
Wt::WAnchor*
std::unique_ptr<Wt::WAnchor>
LmsApplication::createArtistAnchor(Database::Artist::pointer artist, bool addText)
{
auto res = new Wt::WAnchor(Wt::WLink(Wt::WLink::InternalPath, "/artist/" + std::to_string(artist.id())));
auto res = std::make_unique<Wt::WAnchor>(Wt::WLink(Wt::LinkType::InternalPath, "/artist/" + std::to_string(artist.id())));
if (addText)
{
res->setTextFormat(Wt::PlainText);
res->setTextFormat(Wt::TextFormat::Plain);
res->setText(Wt::WString::fromUTF8(artist->getName()));
}
return res;
}
Wt::WAnchor*
std::unique_ptr<Wt::WAnchor>
LmsApplication::createReleaseAnchor(Database::Release::pointer release, bool addText)
{
auto res = new Wt::WAnchor(Wt::WLink(Wt::WLink::InternalPath, "/release/" + std::to_string(release.id())));
auto res = std::make_unique<Wt::WAnchor>(Wt::WLink(Wt::LinkType::InternalPath, "/release/" + std::to_string(release.id())));
if (addText)
{
res->setTextFormat(Wt::PlainText);
res->setTextFormat(Wt::TextFormat::Plain);
res->setText(Wt::WString::fromUTF8(release->getName()));
}
@@ -274,7 +240,7 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
void
LmsApplication::handleAuthEvent(void)
{
if (!DbHandler().getLogin().loggedIn())
if (!LmsApp->getDb().getLogin().loggedIn())
{
LMS_LOG(UI, INFO) << "User logged out, session = " << Wt::WApplication::instance()->sessionId();
@@ -282,73 +248,69 @@ LmsApplication::handleAuthEvent(void)
return;
}
LMS_LOG(UI, INFO) << "User '" << CurrentAuthUser().identity(Wt::Auth::Identity::LoginName) << "' logged in from '" << Wt::WApplication::instance()->environment().clientAddress() << "', user agent = " << Wt::WApplication::instance()->environment().agent() << ", session = " << Wt::WApplication::instance()->sessionId();
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();
{
Wt::Dbo::Transaction transaction (DboSession());
_isAdmin = CurrentUser()->isAdmin();
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
_isAdmin = LmsApp->getCurrentUser()->isAdmin();
}
_imageResource = new ImageResource(_db, root());
_transcodeResource = new TranscodeResource(_db, root());
_imageResource = std::make_shared<ImageResource>(_db);
_transcodeResource = std::make_shared<TranscodeResource>(_db);
setConfirmCloseMessage(Wt::WString::tr("Lms.quit-confirm"));
auto main = new Wt::WTemplate(Wt::WString::tr("Lms.template"), root());
Wt::WTemplate* main = root()->addWidget(std::make_unique<Wt::WTemplate>(Wt::WString::tr("Lms.template")));
// Navbar
auto navbar = new Wt::WNavigationBar();
navbar->setTitle("LMS", Wt::WLink(Wt::WLink::InternalPath, "/home"));
Wt::WNavigationBar* navbar = main->bindNew<Wt::WNavigationBar>("navbar-top");
navbar->setTitle("LMS", Wt::WLink(Wt::LinkType::InternalPath, "/home"));
navbar->setResponsive(true);
main->bindWidget("navbar-top", navbar);
auto menu = new Wt::WMenu();
Wt::WMenu* menu = navbar->addMenu(std::make_unique<Wt::WMenu>());
{
auto menuItem = menu->insertItem(0, Wt::WString::tr("Lms.Explore.artists"));
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/artists"));
menuItem->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/artists"));
menuItem->setSelectable(false);
}
{
auto menuItem = menu->insertItem(1, Wt::WString::tr("Lms.Explore.releases"));
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/releases"));
menuItem->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/releases"));
menuItem->setSelectable(false);
}
{
auto menuItem = menu->insertItem(2, Wt::WString::tr("Lms.Explore.tracks"));
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/tracks"));
menuItem->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/tracks"));
menuItem->setSelectable(false);
}
{
auto menuItem = menu->insertItem(3, Wt::WString::tr("Lms.PlayQueue.playqueue"));
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/playqueue"));
menuItem->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/playqueue"));
menuItem->setSelectable(false);
}
navbar->addMenu(menu);
auto rightMenu = new Wt::WMenu();
Wt::WMenu* rightMenu = navbar->addMenu(std::make_unique<Wt::WMenu>(), Wt::AlignmentFlag::Right);
std::size_t itemCounter = 0;
if (_isAdmin)
{
auto menuItem = rightMenu->insertItem(itemCounter++, Wt::WString::tr("Lms.administration"));
menuItem->setSelectable(false);
Wt::WPopupMenu *admin = new Wt::WPopupMenu();
auto admin = std::make_unique<Wt::WPopupMenu>();
auto dbSettings = admin->insertItem(0, Wt::WString::tr("Lms.Admin.Database.database"));
dbSettings->setLink(Wt::WLink(Wt::WLink::InternalPath, "/admin/database"));
dbSettings->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/admin/database"));
dbSettings->setSelectable(false);
auto usersSettings = admin->insertItem(1, Wt::WString::tr("Lms.Admin.Users.users"));
usersSettings->setLink(Wt::WLink(Wt::WLink::InternalPath, "/admin/users"));
usersSettings->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/admin/users"));
usersSettings->setSelectable(false);
menuItem->setMenu(admin);
menuItem->setMenu(std::move(admin));
}
{
auto menuItem = rightMenu->insertItem(itemCounter++, Wt::WString::tr("Lms.Settings.settings"));
menuItem->setLink(Wt::WLink(Wt::WLink::InternalPath, "/settings"));
menuItem->setLink(Wt::WLink(Wt::LinkType::InternalPath, "/settings"));
menuItem->setSelectable(false);
}
{
@@ -360,34 +322,22 @@ LmsApplication::handleAuthEvent(void)
_auth->logout();
}));
}
navbar->addMenu(rightMenu, Wt::AlignRight);
// Contents
Wt::WStackedWidget* mainStack = new Wt::WStackedWidget();
main->bindWidget("contents", mainStack);
// Order is important in mainStack, see IdxRoot!
Wt::WStackedWidget* mainStack = main->bindNew<Wt::WStackedWidget>("contents");
mainStack->addWidget(new Home());
auto explore = new Explore();
mainStack->addWidget(explore);
auto playqueue = new PlayQueue();
mainStack->addWidget(playqueue);
auto settings = new SettingsView();
mainStack->addWidget(settings);
mainStack->addNew<Home>();
Explore* explore = mainStack->addNew<Explore>();
PlayQueue* playqueue = mainStack->addNew<PlayQueue>();
mainStack->addNew<SettingsView>();
// Admin stuff
if (_isAdmin)
{
auto databaseSettings = new DatabaseSettingsView();
mainStack->addWidget(databaseSettings);
auto users = new UsersView();
mainStack->addWidget(users);
auto user = new UserView();
mainStack->addWidget(user);
mainStack->addNew<DatabaseSettingsView>();
mainStack->addNew<UsersView>();
mainStack->addNew<UserView>();
}
explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
@@ -400,9 +350,9 @@ LmsApplication::handleAuthEvent(void)
playqueue->playTracks(tracks);
}, std::placeholders::_1));
// MediaPlayer
auto player = new MediaPlayer();
main->bindWidget("player", player);
MediaPlayer* player = main->bindNew<MediaPlayer>("player");
// Events from MediaPlayer
player->playNext.connect(std::bind([=]
@@ -468,7 +418,7 @@ void
LmsApplication::notifyMsg(const Wt::WString& message)
{
LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "'";
root()->addWidget(new Wt::WText(message));
root()->addNew<Wt::WText>(message);
}
} // namespace UserInterface
+18 -19
View File
@@ -20,8 +20,8 @@
#ifndef LMS_APPLICATION_HPP
#define LMS_APPLICATION_HPP
#include <Wt/WApplication>
#include <Wt/Dbo/SqlConnectionPool>
#include <Wt/WApplication.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include "database/DatabaseHandler.hpp"
#include "scanner/MediaScanner.hpp"
@@ -36,13 +36,20 @@ class ImageResource;
class LmsApplication : public Wt::WApplication
{
public:
static Wt::WApplication *create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
static std::unique_ptr<Wt::WApplication> create(const Wt::WEnvironment& env,
Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
static LmsApplication* instance();
// Session application data
ImageResource* getImageResource() { return _imageResource; }
TranscodeResource* getTranscodeResource() { return _transcodeResource; }
Database::Handler& getDbHandler() { return _db;}
std::shared_ptr<ImageResource> getImageResource() { return _imageResource; }
std::shared_ptr<TranscodeResource> getTranscodeResource() { return _transcodeResource; }
Database::Handler& getDb() { return _db;}
Wt::Dbo::Session& getDboSession() { return _db.getSession();}
const Wt::Auth::User& getCurrentAuthUser() { return _db.getLogin().user(); }
Database::User::pointer getCurrentUser() { return _db.getCurrentUser(); }
Scanner::MediaScanner& getMediaScanner() { return _scanner; }
@@ -51,33 +58,25 @@ class LmsApplication : public Wt::WApplication
void goHomeAndQuit();
void notifyMsg(const Wt::WString& message);
static Wt::WAnchor* createArtistAnchor(Database::Artist::pointer artist, bool addText = true);
static Wt::WAnchor* createReleaseAnchor(Database::Release::pointer release, bool addText = true);
static std::unique_ptr<Wt::WAnchor> createArtistAnchor(Database::Artist::pointer artist, bool addText = true);
static std::unique_ptr<Wt::WAnchor> createReleaseAnchor(Database::Release::pointer release, bool addText = true);
private:
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
void handleAuthEvent(void);
void notify(const Wt::WEvent& event) override;
Database::Handler _db;
Auth* _auth;
Scanner::MediaScanner& _scanner;
ImageResource* _imageResource;
TranscodeResource* _transcodeResource;
std::shared_ptr<ImageResource> _imageResource;
std::shared_ptr<TranscodeResource> _transcodeResource;
bool _isAdmin = false;
};
// Helpers to get session data
// Helper to get session data
#define LmsApp LmsApplication::instance()
Database::Handler& DbHandler();
Wt::Dbo::Session& DboSession();
const Wt::Auth::User& CurrentAuthUser();
Database::User::pointer CurrentUser();
} // namespace UserInterface
#endif
+5 -5
View File
@@ -30,8 +30,8 @@
namespace UserInterface {
MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent)
: Wt::WTemplate(Wt::WString::tr("template-mediaplayer"), parent),
MediaPlayer::MediaPlayer()
: Wt::WTemplate(Wt::WString::tr("template-mediaplayer")),
playbackEnded(this, "playbackEnded"),
playPrevious(this, "playPrevious"),
playNext(this, "playNext")
@@ -49,8 +49,8 @@ MediaPlayer::playTrack(Database::Track::id_type trackId)
{
LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId;
Wt::Dbo::Transaction transaction(DboSession());
auto track = Database::Track::getById(DboSession(), trackId);
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto track = Database::Track::getById(LmsApp->getDboSession(), trackId);
try
{
@@ -66,7 +66,7 @@ MediaPlayer::playTrack(Database::Track::id_type trackId)
<< " release: " << (track->getRelease() ? "\"" + escape(track->getRelease()->getName()) + "\"" : "undefined" ) << ","
<< " artist: " << (track->getArtist() ? "\"" + escape(track->getArtist()->getName()) + "\"" : "undefined" ) << ","
<< " resource: \"" << resource << "\","
<< " duration: " << track->getDuration().total_seconds() << ","
<< " duration: " << std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count() << ","
<< " imgResource: \"" << imgResource << "\","
<< "};";
oss << "LMS.mediaplayer.loadTrack(params, true)"; // true to autoplay
+6 -6
View File
@@ -19,8 +19,8 @@
#pragma once
#include <Wt/WJavaScript>
#include <Wt/WTemplate>
#include <Wt/WJavaScript.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
@@ -29,15 +29,15 @@ namespace UserInterface {
class MediaPlayer : public Wt::WTemplate
{
public:
MediaPlayer(Wt::WContainerWidget* parent = 0);
MediaPlayer();
void stop();
void playTrack(Database::Track::id_type);
// Signals
Wt::JSignal<void> playbackEnded;
Wt::JSignal<void> playPrevious;
Wt::JSignal<void> playNext;
Wt::JSignal<> playbackEnded;
Wt::JSignal<> playPrevious;
Wt::JSignal<> playNext;
private:
+47 -58
View File
@@ -17,9 +17,9 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WAnchor>
#include <Wt/WImage>
#include <Wt/WText>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WText.h>
#include "utils/Logger.hpp"
@@ -30,26 +30,39 @@ namespace UserInterface {
static const std::string currentPlayQueueName = "__current__playqueue__";
PlayQueue::PlayQueue(Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent)
PlayQueue::PlayQueue()
: Wt::WTemplate(Wt::WString::tr("Lms.PlayQueue.template"))
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.PlayQueue.template"), this);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("tr", &Wt::WTemplate::Functions::tr);
auto saveBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.save-to-playlist"), Wt::XHTMLText);
container->bindWidget("save-btn", saveBtn);
bindNew<Wt::WText>("save-btn", Wt::WString::tr("Lms.PlayQueue.save-to-playlist"), Wt::TextFormat::XHTML);
bindNew<Wt::WText>("load-btn", Wt::WString::tr("Lms.PlayQueue.load-from-playlist"), Wt::TextFormat::XHTML);
Wt::WText* clearBtn = bindNew<Wt::WText>("clear-btn", Wt::WString::tr("Lms.PlayQueue.clear"), Wt::TextFormat::XHTML);
auto loadBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.load-from-playlist"), Wt::XHTMLText);
container->bindWidget("load-btn", loadBtn);
_entriesContainer = bindNew<Wt::WContainerWidget>("entries");
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
_showMore->setHidden(true);
_nbTracks = bindNew<Wt::WText>("nb-tracks");
{
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
if (!playlist)
playlist = Database::Playlist::create(LmsApp->getDboSession(), currentPlayQueueName, false, LmsApp->getCurrentUser());
_trackPos = LmsApp->getCurrentUser()->getCurPlayingTrackPos();
}
auto clearBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.clear"), Wt::XHTMLText);
container->bindWidget("clear-btn", clearBtn);
clearBtn->clicked().connect(std::bind([=]
{
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
playlist.modify()->clear();
_showMore->setHidden(true);
}
@@ -58,27 +71,6 @@ PlayQueue::PlayQueue(Wt::WContainerWidget* parent)
updateInfo();
}));
_entriesContainer = new Wt::WContainerWidget();
container->bindWidget("entries", _entriesContainer);
_showMore = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
_showMore->setHidden(true);
container->bindWidget("show-more", _showMore);
_nbTracks = new Wt::WText();
container->bindWidget("nb-tracks", _nbTracks);
{
Wt::Dbo::Transaction transaction (DboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
if (!playlist)
playlist = Database::Playlist::create(DboSession(), currentPlayQueueName, false, CurrentUser());
_trackPos = CurrentUser()->getCurPlayingTrackPos();
}
_showMore->clicked().connect(std::bind([=]
{
addSome();
@@ -103,9 +95,9 @@ PlayQueue::play(std::size_t pos)
Database::Track::id_type trackId;
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
// If out of range, stop playing
if (pos >= playlist->getCount())
@@ -149,9 +141,9 @@ PlayQueue::playNext()
void
PlayQueue::updateInfo()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(playlist->getCount()));
}
@@ -179,10 +171,10 @@ PlayQueue::addTracks(const std::vector<Database::Track::pointer>& tracks)
LMS_LOG(UI, DEBUG) << "Adding tracks to the current queue";
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
for (auto track : tracks)
Database::PlaylistEntry::create(DboSession(), track, playlist);
Database::PlaylistEntry::create(LmsApp->getDboSession(), track, playlist);
updateInfo();
addSome();
@@ -193,9 +185,9 @@ PlayQueue::playTracks(const std::vector<Database::Track::pointer>& tracks)
{
LMS_LOG(UI, DEBUG) << "Emptying current queue to play new tracks";
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto playqueue = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playqueue = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
playqueue.modify()->clear();
_entriesContainer->clear();
@@ -209,9 +201,9 @@ PlayQueue::playTracks(const std::vector<Database::Track::pointer>& tracks)
void
PlayQueue::addSome()
{
Wt::Dbo::Transaction transaction (DboSession());
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
auto playlist = Database::Playlist::get(DboSession(), currentPlayQueueName, CurrentUser());
auto playlist = Database::Playlist::get(LmsApp->getDboSession(), currentPlayQueueName, LmsApp->getCurrentUser());
bool moreResults;
auto playlistEntries = playlist->getEntries(_entriesContainer->count(), 50, moreResults);
@@ -220,27 +212,24 @@ PlayQueue::addSome()
auto playlistEntryId = playlistEntry.id();
auto track = playlistEntry->getTrack();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.PlayQueue.template.entry"), _entriesContainer);
Wt::WTemplate* entry = _entriesContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.PlayQueue.template.entry"));
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText);
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
auto artist = track->getArtist();
if (artist)
{
entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist());
entry->bindWidget("artist-name", artistAnchor);
entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
}
auto release = track->getRelease();
if (release)
{
entry->setCondition("if-has-release", true);
Wt::WAnchor *releaseAnchor = LmsApplication::createReleaseAnchor(track->getRelease());
entry->bindWidget("release-name", releaseAnchor);
entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(track->getRelease()));
}
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.PlayQueue.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
auto pos = _entriesContainer->indexOf(entry);
@@ -248,15 +237,14 @@ PlayQueue::addSome()
play(pos);
}));
auto delBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.delete"), Wt::XHTMLText);
entry->bindWidget("del-btn", delBtn);
Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.delete"), Wt::TextFormat::XHTML);
delBtn->clicked().connect(std::bind([=]
{
// Remove the entry n both the widget tree and the playqueue
{
Wt::Dbo::Transaction transaction (DboSession());
Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
auto entryToRemove = Database::PlaylistEntry::getById(DboSession(), playlistEntryId);
auto entryToRemove = Database::PlaylistEntry::getById(LmsApp->getDboSession(), playlistEntryId);
entryToRemove.remove();
}
@@ -271,6 +259,7 @@ PlayQueue::addSome()
updateInfo();
}));
}
_showMore->setHidden(!moreResults);
+7 -7
View File
@@ -19,10 +19,10 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WSignal>
#include <Wt/WTemplate>
#include <Wt/WText>
#include <Wt/WContainerWidget.h>
#include <Wt/WSignal.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>
#include <boost/optional.hpp>
@@ -30,10 +30,10 @@
namespace UserInterface {
class PlayQueue : public Wt::WContainerWidget
class PlayQueue : public Wt::WTemplate
{
public:
PlayQueue(Wt::WContainerWidget* parent = 0);
PlayQueue();
void addTracks(const std::vector<Database::Track::pointer>& tracks);
void playTracks(const std::vector<Database::Track::pointer>& tracks);
@@ -48,7 +48,7 @@ class PlayQueue : public Wt::WContainerWidget
Wt::Signal<Database::Track::id_type> playTrack;
// Signal emitted when play has to be stopped
Wt::Signal<void> playbackStop;
Wt::Signal<> playbackStop;
private:
void addSome();
+61 -68
View File
@@ -17,13 +17,13 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WComboBox>
#include <Wt/WLineEdit>
#include <Wt/WString.h>
#include <Wt/WPushButton.h>
#include <Wt/WComboBox.h>
#include <Wt/WLineEdit.h>
#include <Wt/WFormModel>
#include <Wt/WStringListModel>
#include <Wt/WFormModel.h>
#include <Wt/WStringListModel.h>
#include "common/Validators.hpp"
@@ -45,8 +45,8 @@ class SettingsModel : public Wt::WFormModel
static const Field PasswordField;
static const Field PasswordConfirmField;
SettingsModel(Wt::WObject *parent = 0)
: Wt::WFormModel(parent)
SettingsModel()
: Wt::WFormModel()
{
initializeModels();
@@ -61,18 +61,18 @@ class SettingsModel : public Wt::WFormModel
loadData();
}
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; }
Wt::WAbstractItemModel *encodingModel() { return _encodingModel; }
std::shared_ptr<Wt::WAbstractItemModel> bitrateModel() { return _bitrateModel; }
std::shared_ptr<Wt::WAbstractItemModel> encodingModel() { return _encodingModel; }
void loadData()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto bitrate = getBitrateRow(CurrentUser()->getAudioBitrate());
auto bitrate = getBitrateRow(LmsApp->getCurrentUser()->getAudioBitrate());
if (bitrate)
setValue(BitrateField, bitrateString(*bitrate));
auto encodingRow = getEncodingRow(CurrentUser()->getAudioEncoding());
auto encodingRow = getEncodingRow(LmsApp->getCurrentUser()->getAudioEncoding());
if (encodingRow)
setValue(EncodingField, encodingString(*encodingRow));
@@ -80,18 +80,18 @@ class SettingsModel : public Wt::WFormModel
void saveData()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField)));
assert(bitrateRow);
CurrentUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
LmsApp->getCurrentUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField)));
CurrentUser().modify()->setAudioEncoding(encoding(*encodingRow));
LmsApp->getCurrentUser().modify()->setAudioEncoding(encoding(*encodingRow));
if (!valueText(PasswordField).empty())
{
Database::Handler::getPasswordService().updatePassword(CurrentAuthUser(), valueText(PasswordField));
Database::Handler::getPasswordService().updatePassword(LmsApp->getCurrentAuthUser(), valueText(PasswordField));
}
}
@@ -105,7 +105,7 @@ class SettingsModel : public Wt::WFormModel
{
// Evaluate the strength of the password
auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField),
CurrentAuthUser().identity(Wt::Auth::Identity::LoginName), "");
LmsApp->getCurrentAuthUser().identity(Wt::Auth::Identity::LoginName), "");
if (!res.isValid())
error = res.message();
@@ -115,7 +115,7 @@ class SettingsModel : public Wt::WFormModel
}
else if (field == PasswordConfirmField)
{
if (validation(PasswordField).state() == Wt::WValidator::Valid)
if (validation(PasswordField).state() == Wt::ValidationState::Valid)
{
if (valueText(PasswordField) != valueText(PasswordConfirmField))
error = Wt::WString::tr("Lms.passwords-dont-match");
@@ -126,9 +126,9 @@ class SettingsModel : public Wt::WFormModel
return Wt::WFormModel::validateField(field);
}
setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::WValidator::Valid : Wt::WValidator::Invalid, error));
setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::ValidationState::Valid : Wt::ValidationState::Invalid, error));
return (validation(field).state() == Wt::WValidator::Valid);
return (validation(field).state() == Wt::ValidationState::Valid);
}
boost::optional<int> getBitrateRow(Wt::WString value)
@@ -155,14 +155,14 @@ class SettingsModel : public Wt::WFormModel
std::size_t bitrate(int row)
{
return boost::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole));
return Wt::cpp17::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
}
Wt::WString bitrateString(int row)
{
return boost::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole));
return Wt::cpp17::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
}
boost::optional<int> getEncodingRow(Wt::WString value)
@@ -189,14 +189,14 @@ class SettingsModel : public Wt::WFormModel
Database::AudioEncoding encoding(int row)
{
return boost::any_cast<Database::AudioEncoding>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::UserRole));
return Wt::cpp17::any_cast<Database::AudioEncoding>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::User));
}
Wt::WString encodingString(int row)
{
return boost::any_cast<Wt::WString>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::DisplayRole));
return Wt::cpp17::any_cast<Wt::WString>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::Display));
}
@@ -204,42 +204,42 @@ class SettingsModel : public Wt::WFormModel
void initializeModels()
{
_bitrateModel = new Wt::WStringListModel(this);
_bitrateModel = std::make_shared<Wt::WStringListModel>();
std::size_t maxAudioBitrate;
{
Wt::Dbo::Transaction transaction(DboSession());
maxAudioBitrate = CurrentUser()->getMaxAudioBitrate();
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
maxAudioBitrate = LmsApp->getCurrentUser()->getMaxAudioBitrate();
}
std::size_t id = 0;
for (auto bitrate : Database::User::audioBitrates)
for (std::size_t bitrate : Database::User::audioBitrates)
{
if (bitrate > maxAudioBitrate)
break;
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
_bitrateModel->setData( id++, 0, bitrate, Wt::UserRole);
_bitrateModel->setData( id, 0, bitrate, Wt::ItemDataRole::User);
id++;
}
_encodingModel = new Wt::WStringListModel(this);
_encodingModel = std::make_shared<Wt::WStringListModel>();
_encodingModel->addString(Wt::WString::tr("Lms.Settings.auto"));
_encodingModel->setData(0, 0, Database::AudioEncoding::AUTO, Wt::UserRole);
_encodingModel->setData(0, 0, Database::AudioEncoding::AUTO, Wt::ItemDataRole::User);
_encodingModel->addString(Wt::WString::tr("Lms.Settings.mp3"));
_encodingModel->setData(1, 0, Database::AudioEncoding::MP3, Wt::UserRole);
_encodingModel->setData(1, 0, Database::AudioEncoding::MP3, Wt::ItemDataRole::User);
_encodingModel->addString(Wt::WString::tr("Lms.Settings.oga"));
_encodingModel->setData(2, 0, Database::AudioEncoding::OGA, Wt::UserRole);
_encodingModel->setData(2, 0, Database::AudioEncoding::OGA, Wt::ItemDataRole::User);
_encodingModel->addString(Wt::WString::tr("Lms.Settings.webma"));
_encodingModel->setData(3, 0, Database::AudioEncoding::WEBMA, Wt::UserRole);
_encodingModel->setData(3, 0, Database::AudioEncoding::WEBMA, Wt::ItemDataRole::User);
}
Wt::WStringListModel* _bitrateModel;
Wt::WStringListModel* _encodingModel;
std::shared_ptr<Wt::WStringListModel> _bitrateModel;
std::shared_ptr<Wt::WStringListModel> _encodingModel;
};
@@ -248,45 +248,38 @@ const Wt::WFormModel::Field SettingsModel::EncodingField = "encoding";
const Wt::WFormModel::Field SettingsModel::PasswordField = "password";
const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm";
SettingsView::SettingsView(Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
SettingsView::SettingsView()
: Wt::WTemplateFormView(Wt::WString::tr("Lms.Settings.template"))
{
auto model = new SettingsModel(this);
setTemplateText(tr("Lms.Settings.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
auto model = std::make_shared<SettingsModel>();
// Password
Wt::WLineEdit *password = new Wt::WLineEdit();
setFormWidget(SettingsModel::PasswordField, password);
password->setEchoMode(Wt::WLineEdit::Password);
auto password = std::make_unique<Wt::WLineEdit>();
password->setEchoMode(Wt::EchoMode::Password);
setFormWidget(SettingsModel::PasswordField, std::move(password));
// Password confirm
Wt::WLineEdit *passwordConfirm = new Wt::WLineEdit();
setFormWidget(SettingsModel::PasswordConfirmField, passwordConfirm);
passwordConfirm->setEchoMode(Wt::WLineEdit::Password);
auto passwordConfirm = std::make_unique<Wt::WLineEdit>();
passwordConfirm->setEchoMode(Wt::EchoMode::Password);
setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
// Bitrate
Wt::WComboBox *bitrate = new Wt::WComboBox();
setFormWidget(SettingsModel::BitrateField, bitrate);
auto bitrate = std::make_unique<Wt::WComboBox>();
bitrate->setModel(model->bitrateModel());
setFormWidget(SettingsModel::BitrateField, std::move(bitrate));
// Encoding
Wt::WComboBox *encoding = new Wt::WComboBox();
setFormWidget(SettingsModel::EncodingField, encoding);
auto encoding = std::make_unique<Wt::WComboBox>();
encoding->setModel(model->encodingModel());
setFormWidget(SettingsModel::EncodingField, std::move(encoding));
// Buttons
Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("Lms.apply"));
bindWidget("apply-btn", saveBtn);
Wt::WPushButton *discardBtn = new Wt::WPushButton(Wt::WString::tr("Lms.discard"));
bindWidget("discard-btn", discardBtn);
Wt::WPushButton *saveBtn = bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
Wt::WPushButton *discardBtn = bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
saveBtn->clicked().connect(std::bind([=] ()
{
updateModel(model);
updateModel(model.get());
if (model->validate())
{
@@ -295,17 +288,17 @@ SettingsView::SettingsView(Wt::WContainerWidget *parent)
}
// Udate the view: Delete any validation message in the view, etc.
updateView(model);
updateView(model.get());
}));
discardBtn->clicked().connect(std::bind([=] ()
{
model->loadData();
model->validate();
updateView(model);
updateView(model.get());
}));
updateView(model);
updateView(model.get());
}
} // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WTemplateFormView.h>
namespace UserInterface {
class SettingsView : public Wt::WTemplateFormView
{
public:
SettingsView(Wt::WContainerWidget *parent = 0);
SettingsView();
};
} // namespace UserInterface
+60 -78
View File
@@ -17,13 +17,13 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WString>
#include <Wt/WPushButton>
#include <Wt/WComboBox>
#include <Wt/WLineEdit>
#include <Wt/WString.h>
#include <Wt/WPushButton.h>
#include <Wt/WComboBox.h>
#include <Wt/WLineEdit.h>
#include <Wt/WFormModel>
#include <Wt/WStringListModel>
#include <Wt/WFormModel.h>
#include <Wt/WStringListModel.h>
#include "common/Validators.hpp"
#include "database/MediaDirectory.hpp"
@@ -46,8 +46,8 @@ class DatabaseSettingsModel : public Wt::WFormModel
static const Field UpdatePeriodField;
static const Field UpdateStartTimeField;
DatabaseSettingsModel(Wt::WObject *parent = 0)
: Wt::WFormModel(parent)
DatabaseSettingsModel()
: Wt::WFormModel()
{
initializeModels();
@@ -55,7 +55,7 @@ class DatabaseSettingsModel : public Wt::WFormModel
addField(UpdatePeriodField);
addField(UpdateStartTimeField);
DirectoryValidator* dirValidator = new DirectoryValidator();
auto dirValidator = std::make_shared<DirectoryValidator>();
dirValidator->setMandatory(true);
setValidator(MediaDirectoryField, dirValidator);
@@ -66,42 +66,42 @@ class DatabaseSettingsModel : public Wt::WFormModel
loadData();
}
Wt::WAbstractItemModel *updatePeriodModel() { return _updatePeriodModel; }
Wt::WAbstractItemModel *updateStartTimeModel() { return _updateStartTimeModel; }
std::shared_ptr<Wt::WAbstractItemModel> updatePeriodModel() { return _updatePeriodModel; }
std::shared_ptr<Wt::WAbstractItemModel> updateStartTimeModel() { return _updateStartTimeModel; }
void loadData()
{
using namespace Database;
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
std::vector<MediaDirectory::pointer> mediaDirectories = MediaDirectory::getAll(DboSession());
std::vector<MediaDirectory::pointer> mediaDirectories = MediaDirectory::getAll(LmsApp->getDboSession());
if (!mediaDirectories.empty())
setValue(MediaDirectoryField, mediaDirectories.front()->getPath().string());
auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(DboSession()) );
auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(LmsApp->getDboSession()) );
if (periodRow)
setValue(UpdatePeriodField, updatePeriodString(*periodRow));
auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(DboSession()) );
auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(LmsApp->getDboSession()) );
if (startTimeRow)
setValue(UpdateStartTimeField, updateStartTimeString(*startTimeRow) );
}
void saveData()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
MediaDirectory::eraseAll(DboSession());
MediaDirectory::create(DboSession(), boost::any_cast<Wt::WString>(value(MediaDirectoryField)).toUTF8());
MediaDirectory::eraseAll(LmsApp->getDboSession());
MediaDirectory::create(LmsApp->getDboSession(), Wt::cpp17::any_cast<Wt::WString>(value(MediaDirectoryField)).toUTF8());
auto updatePeriodRow = getUpdatePeriodModelRow( boost::any_cast<Wt::WString>(value(UpdatePeriodField)));
auto updatePeriodRow = getUpdatePeriodModelRow( Wt::cpp17::any_cast<Wt::WString>(value(UpdatePeriodField)));
assert(updatePeriodRow);
Scanner::setUpdatePeriod(DboSession(), updatePeriod(*updatePeriodRow));
Scanner::setUpdatePeriod(LmsApp->getDboSession(), updatePeriod(*updatePeriodRow));
auto startTimeRow = getUpdateStartTimeModelRow( boost::any_cast<Wt::WString>(value(UpdateStartTimeField)));
auto startTimeRow = getUpdateStartTimeModelRow( Wt::cpp17::any_cast<Wt::WString>(value(UpdateStartTimeField)));
assert(startTimeRow);
Scanner::setUpdateStartTime(DboSession(), updateStartTime(*startTimeRow));
Scanner::setUpdateStartTime(LmsApp->getDboSession(), updateStartTime(*startTimeRow));
}
boost::optional<int> getUpdatePeriodModelRow(Wt::WString value)
@@ -128,14 +128,14 @@ class DatabaseSettingsModel : public Wt::WFormModel
Scanner::UpdatePeriod updatePeriod(int row)
{
return boost::any_cast<Scanner::UpdatePeriod>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole));
return Wt::cpp17::any_cast<Scanner::UpdatePeriod>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::ItemDataRole::User));
}
Wt::WString updatePeriodString(int row)
{
return boost::any_cast<Wt::WString>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::DisplayRole));
return Wt::cpp17::any_cast<Wt::WString>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::ItemDataRole::Display));
}
@@ -150,7 +150,7 @@ class DatabaseSettingsModel : public Wt::WFormModel
return boost::none;
}
boost::optional<int> getUpdateStartTimeModelRow(boost::posix_time::time_duration startTime)
boost::optional<int> getUpdateStartTimeModelRow(Wt::WTime startTime)
{
for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{
@@ -161,16 +161,16 @@ class DatabaseSettingsModel : public Wt::WFormModel
return boost::none;
}
boost::posix_time::time_duration updateStartTime(int row)
Wt::WTime updateStartTime(int row)
{
return boost::any_cast<boost::posix_time::time_duration>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole));
return Wt::cpp17::any_cast<Wt::WTime>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::ItemDataRole::User));
}
Wt::WString updateStartTimeString(int row)
{
return boost::any_cast<Wt::WString>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::DisplayRole));
return Wt::cpp17::any_cast<Wt::WString>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::ItemDataRole::Display));
}
@@ -179,43 +179,35 @@ class DatabaseSettingsModel : public Wt::WFormModel
void initializeModels()
{
_updatePeriodModel = new Wt::WStringListModel(this);
_updatePeriodModel = std::make_shared<Wt::WStringListModel>();
_updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.never"));
_updatePeriodModel->setData(0, 0, Scanner::UpdatePeriod::Never, Wt::UserRole);
_updatePeriodModel->setData(0, 0, Scanner::UpdatePeriod::Never, Wt::ItemDataRole::User);
_updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.daily"));
_updatePeriodModel->setData(1, 0, Scanner::UpdatePeriod::Daily, Wt::UserRole);
_updatePeriodModel->setData(1, 0, Scanner::UpdatePeriod::Daily, Wt::ItemDataRole::User);
_updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.weekly"));
_updatePeriodModel->setData(2, 0, Scanner::UpdatePeriod::Weekly, Wt::UserRole);
_updatePeriodModel->setData(2, 0, Scanner::UpdatePeriod::Weekly, Wt::ItemDataRole::User);
_updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.monthly"));
_updatePeriodModel->setData(3, 0, Scanner::UpdatePeriod::Monthly, Wt::UserRole);
_updatePeriodModel->setData(3, 0, Scanner::UpdatePeriod::Monthly, Wt::ItemDataRole::User);
_updateStartTimeModel = new Wt::WStringListModel(this);
_updateStartTimeModel = std::make_shared<Wt::WStringListModel>();
for (std::size_t i = 0; i < 24; ++i)
{
boost::posix_time::time_duration dur = boost::posix_time::hours(i);
Wt::WTime time(i, 0);
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%H:%M");
facet->time_duration_format("%H:%M");
std::ostringstream oss;
oss.imbue(std::locale(oss.getloc(), facet));
oss << dur;
_updateStartTimeModel->addString( oss.str() );
_updateStartTimeModel->setData(i, 0, dur, Wt::UserRole);
_updateStartTimeModel->addString( time.toString() );
_updateStartTimeModel->setData(i, 0, time, Wt::ItemDataRole::User);
}
}
Wt::WStringListModel* _updatePeriodModel;
Wt::WStringListModel* _updateStartTimeModel;
std::shared_ptr<Wt::WStringListModel> _updatePeriodModel;
std::shared_ptr<Wt::WStringListModel> _updateStartTimeModel;
};
@@ -224,43 +216,33 @@ const Wt::WFormModel::Field DatabaseSettingsModel::UpdatePeriodField = "update-
const Wt::WFormModel::Field DatabaseSettingsModel::UpdateStartTimeField = "update-start-time";
DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
DatabaseSettingsView::DatabaseSettingsView()
: Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.Database.template"))
{
auto model = new DatabaseSettingsModel(this);
setTemplateText(tr("Lms.Admin.Database.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
auto model = std::make_shared<DatabaseSettingsModel>();
// Media Directory
Wt::WLineEdit *mediaDirectoryEdit = new Wt::WLineEdit();
setFormWidget(DatabaseSettingsModel::MediaDirectoryField, mediaDirectoryEdit);
setFormWidget(DatabaseSettingsModel::MediaDirectoryField, std::make_unique<Wt::WLineEdit>());
// Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox();
setFormWidget(DatabaseSettingsModel::UpdatePeriodField, updatePeriodCB);
updatePeriodCB->setModel(model->updatePeriodModel());
auto updatePeriod = std::make_unique<Wt::WComboBox>();
updatePeriod->setModel(model->updatePeriodModel());
setFormWidget(DatabaseSettingsModel::UpdatePeriodField, std::move(updatePeriod));
// Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox();
setFormWidget(DatabaseSettingsModel::UpdateStartTimeField, updateStartTimeCB);
updateStartTimeCB->setModel(model->updateStartTimeModel());
auto updateStartTime = std::make_unique<Wt::WComboBox>();
updateStartTime->setModel(model->updateStartTimeModel());
setFormWidget(DatabaseSettingsModel::UpdateStartTimeField, std::move(updateStartTime));
// Buttons
Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("Lms.apply"));
bindWidget("apply-btn", saveBtn);
Wt::WPushButton *discardBtn = new Wt::WPushButton(Wt::WString::tr("Lms.discard"));
bindWidget("discard-btn", discardBtn);
Wt::WPushButton *immScanBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Database.immediate-scan"));
bindWidget("immediate-scan-btn", immScanBtn);
Wt::WPushButton *saveBtn = bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
Wt::WPushButton *discardBtn = bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
Wt::WPushButton *immScanBtn = bindWidget("immediate-scan-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.Admin.Database.immediate-scan")));
saveBtn->clicked().connect(std::bind([=] ()
{
updateModel(model);
updateModel(model.get());
if (model->validate())
{
@@ -271,14 +253,14 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
}
// Udate the view: Delete any validation message in the view, etc.
updateView(model);
updateView(model.get());
}));
discardBtn->clicked().connect(std::bind([=] ()
{
model->loadData();
model->validate();
updateView(model);
updateView(model.get());
}));
immScanBtn->clicked().connect(std::bind([=] ()
@@ -287,7 +269,7 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.Database.scan-launched"));
}));
updateView(model);
updateView(model.get());
}
} // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WTemplateFormView.h>
namespace UserInterface {
class DatabaseSettingsView : public Wt::WTemplateFormView
{
public:
DatabaseSettingsView(Wt::WContainerWidget *parent = 0);
DatabaseSettingsView();
};
+26 -33
View File
@@ -17,10 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WFormModel>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/Auth/Identity>
#include <Wt/WFormModel.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/Auth/Identity.h>
#include "utils/Logger.hpp"
@@ -40,8 +40,7 @@ class InitWizardModel : public Wt::WFormModel
static const Field PasswordField;
static const Field PasswordConfirmField;
InitWizardModel(Wt::WObject *parent = 0)
: Wt::WFormModel(parent)
InitWizardModel() : Wt::WFormModel()
{
addField(AdminLoginField);
addField(PasswordField);
@@ -54,16 +53,16 @@ class InitWizardModel : public Wt::WFormModel
void saveData()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
// Check if a user already exist
// If it's the case, just do nothing
if (!Database::User::getAll(DboSession()).empty())
if (!Database::User::getAll(LmsApp->getDboSession()).empty())
throw std::runtime_error("Admin user already created");
// Create user
Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew();
Database::User::pointer user = DbHandler().getUser(authUser);
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().registerNew();
Database::User::pointer user = LmsApp->getDb().createUser(authUser);
// Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(AdminLoginField));
@@ -94,7 +93,7 @@ class InitWizardModel : public Wt::WFormModel
}
else if (field == PasswordConfirmField)
{
if (validation(PasswordField).state() == Wt::WValidator::Valid)
if (validation(PasswordField).state() == Wt::ValidationState::Valid)
{
if (valueText(PasswordField) != valueText(PasswordConfirmField))
error = Wt::WString::tr("Lms.passwords-dont-match");
@@ -105,9 +104,9 @@ class InitWizardModel : public Wt::WFormModel
return Wt::WFormModel::validateField(field);
}
setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::WValidator::Valid : Wt::WValidator::Invalid, error));
setValidation(field, Wt::WValidator::Result( error.empty() ? Wt::ValidationState::Valid : Wt::ValidationState::Invalid, error));
return (validation(field).state() == Wt::WValidator::Valid);
return (validation(field).state() == Wt::ValidationState::Valid);
}
};
@@ -116,34 +115,28 @@ const Wt::WFormModel::Field InitWizardModel::AdminLoginField = "admin-login";
const Wt::WFormModel::Field InitWizardModel::PasswordField = "password";
const Wt::WFormModel::Field InitWizardModel::PasswordConfirmField = "password-confirm";
InitWizardView::InitWizardView(Wt::WContainerWidget *parent)
: Wt::WTemplateFormView(parent)
InitWizardView::InitWizardView()
: Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.InitWizard.template"))
{
auto model = new InitWizardModel(this);
setTemplateText(Wt::WString::tr("Lms.Admin.InitWizard.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
auto model = std::make_shared<InitWizardModel>();
// AdminLogin
Wt::WLineEdit* accountEdit = new Wt::WLineEdit();
setFormWidget(InitWizardModel::AdminLoginField, accountEdit);
setFormWidget(InitWizardModel::AdminLoginField, std::make_unique<Wt::WLineEdit>());
// Password
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit();
setFormWidget(InitWizardModel::PasswordField, passwordEdit );
passwordEdit->setEchoMode(Wt::WLineEdit::Password);
auto passwordEdit = std::make_unique<Wt::WLineEdit>();
passwordEdit->setEchoMode(Wt::EchoMode::Password);
setFormWidget(InitWizardModel::PasswordField, std::move(passwordEdit) );
// Password confirmation
Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit();
setFormWidget(InitWizardModel::PasswordConfirmField, passwordConfirmEdit);
passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password);
auto passwordConfirmEdit = std::make_unique<Wt::WLineEdit>();
passwordConfirmEdit->setEchoMode(Wt::EchoMode::Password);
setFormWidget(InitWizardModel::PasswordConfirmField, std::move(passwordConfirmEdit));
auto saveButton = new Wt::WPushButton(Wt::WString::tr("Lms.create"));
bindWidget("create-btn", saveButton);
Wt::WPushButton* saveButton = bindNew<Wt::WPushButton>("create-btn", Wt::WString::tr("Lms.create"));
saveButton->clicked().connect(std::bind([=]
{
updateModel(model);
updateModel(model.get());
if (model->validate())
{
@@ -152,10 +145,10 @@ InitWizardView::InitWizardView(Wt::WContainerWidget *parent)
saveButton->setEnabled(false);
}
updateView(model);
updateView(model.get());
}));
updateView(model);
updateView(model.get());
}
} // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplateFormView>
#include <Wt/WTemplateFormView.h>
namespace UserInterface {
class InitWizardView : public Wt::WTemplateFormView
{
public:
InitWizardView(Wt::WContainerWidget *parent = 0);
InitWizardView();
};
+47 -52
View File
@@ -17,15 +17,15 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WApplication>
#include <Wt/WCheckBox>
#include <Wt/WComboBox>
#include <Wt/WLineEdit>
#include <Wt/WPushButton>
#include <Wt/WTemplateFormView>
#include <Wt/WApplication.h>
#include <Wt/WCheckBox.h>
#include <Wt/WComboBox.h>
#include <Wt/WLineEdit.h>
#include <Wt/WPushButton.h>
#include <Wt/WTemplateFormView.h>
#include <Wt/WFormModel>
#include <Wt/WStringListModel>
#include <Wt/WFormModel.h>
#include <Wt/WStringListModel.h>
#include "common/Validators.hpp"
#include "utils/Utils.hpp"
@@ -44,8 +44,8 @@ class UserModel : public Wt::WFormModel
static const Field PasswordField;
static const Field BitrateLimitField;
UserModel(boost::optional<Database::User::id_type> userId, Wt::WObject *parent = 0)
: Wt::WFormModel(parent),
UserModel(boost::optional<Database::User::id_type> userId)
: Wt::WFormModel(),
_userId(userId)
{
if (!_userId)
@@ -72,12 +72,12 @@ class UserModel : public Wt::WFormModel
if (!_userId)
return;
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = DbHandler().getUser(authUser);
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = LmsApp->getDb().getUser(authUser);
if (user == CurrentUser())
if (user == LmsApp->getCurrentUser())
throw std::runtime_error("Cannot edit ourselves");
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
@@ -87,13 +87,13 @@ class UserModel : public Wt::WFormModel
void saveData()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
if (_userId)
{
// Update user
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = DbHandler().getUser( authUser );
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = LmsApp->getDb().getUser( authUser );
// Account
if (!valueText(PasswordField).empty())
@@ -106,8 +106,8 @@ class UserModel : public Wt::WFormModel
else
{
// Create user
Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew();
Database::User::pointer user = DbHandler().createUser(authUser);
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().registerNew();
Database::User::pointer user = LmsApp->getDb().createUser(authUser);
// Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField));
@@ -142,35 +142,35 @@ class UserModel : public Wt::WFormModel
std::size_t bitrateLimit(int row)
{
return boost::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole));
return Wt::cpp17::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
}
Wt::WString bitrateLimitString(int row)
{
return boost::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole));
return Wt::cpp17::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
}
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; }
std::shared_ptr<Wt::WAbstractItemModel> bitrateModel() { return _bitrateModel; }
private:
void initializeModels()
{
_bitrateModel = new Wt::WStringListModel(this);
_bitrateModel = std::make_shared<Wt::WStringListModel>();
std::size_t id = 0;
for (auto bitrate : Database::User::audioBitrates)
{
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) );
_bitrateModel->setData( id++, 0, bitrate, Wt::UserRole);
_bitrateModel->setData( id++, 0, bitrate, Wt::ItemDataRole::User);
}
}
Wt::WStringListModel* _bitrateModel;
std::shared_ptr<Wt::WStringListModel> _bitrateModel;
boost::optional<Database::User::id_type> _userId;
};
@@ -178,8 +178,7 @@ const Wt::WFormModel::Field UserModel::LoginField = "login";
const Wt::WFormModel::Field UserModel::PasswordField = "password";
const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit";
UserView::UserView(Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent)
UserView::UserView()
{
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -195,47 +194,44 @@ UserView::refreshView()
if (!wApp->internalPathMatches("/admin/user"))
return;
auto userId = readLong(wApp->internalPathNextPart("/admin/user/"));
auto userId = readAs<Database::User::id_type>(wApp->internalPathNextPart("/admin/user/"));
LMS_LOG(UI, DEBUG) << "userId = " << (userId ? std::to_string(*userId) : "none");
clear();
auto t = new Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.User.template"), this);
Wt::WTemplateFormView* t = addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.Admin.User.template"));
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
t->addFunction("id", &Wt::WTemplate::Functions::id);
auto model = new UserModel(userId ? boost::make_optional<Database::User::id_type>(*userId) : boost::none, this);
auto model = std::make_shared<UserModel>(userId);
if (userId)
{
auto authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*userId) );
auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*userId) );
auto name = authUser.identity(Wt::Auth::Identity::LoginName);
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-edit").arg(name), Wt::PlainText);
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-edit").arg(name), Wt::TextFormat::Plain);
}
else
{
// Login
t->setCondition("if-has-login", true);
t->setFormWidget(UserModel::LoginField, new Wt::WLineEdit());
t->setFormWidget(UserModel::LoginField, std::make_unique<Wt::WLineEdit>());
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-create"));
}
// Password
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit();
t->setFormWidget(UserModel::PasswordField, passwordEdit );
passwordEdit->setEchoMode(Wt::WLineEdit::Password);
auto passwordEdit = std::make_unique<Wt::WLineEdit>();
passwordEdit->setEchoMode(Wt::EchoMode::Password);
t->setFormWidget(UserModel::PasswordField, std::move(passwordEdit));
// AudioBitrate
Wt::WComboBox *bitrateCB = new Wt::WComboBox();
t->setFormWidget(UserModel::BitrateLimitField, bitrateCB);
bitrateCB->setModel(model->bitrateModel());
// Bitrate
auto bitrate = std::make_unique<Wt::WComboBox>();
bitrate->setModel(model->bitrateModel());
t->setFormWidget(UserModel::BitrateLimitField, std::move(bitrate));
auto saveBtn = new Wt::WPushButton(Wt::WString::tr(userId ? "Lms.save" : "Lms.create"));
t->bindWidget("save-btn", saveBtn);
Wt::WPushButton* saveBtn = t->bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr(userId ? "Lms.save" : "Lms.create"));
saveBtn->clicked().connect(std::bind([=]
{
t->updateModel(model);
t->updateModel(model.get());
if (model->validate())
{
@@ -245,12 +241,11 @@ UserView::refreshView()
}
else
{
t->updateView(model);
t->updateView(model.get());
}
}));
t->updateView(model);
t->updateView(model.get());
}
} // namespace UserInterface
+2 -2
View File
@@ -19,14 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WContainerWidget.h>
namespace UserInterface {
class UserView : public Wt::WContainerWidget
{
public:
UserView(Wt::WContainerWidget *parent = 0);
UserView();
private:
void refreshView();
+19 -26
View File
@@ -17,8 +17,8 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WTemplate>
#include <Wt/WPushButton>
#include <Wt/WTemplate.h>
#include <Wt/WPushButton.h>
#include "database/Types.hpp"
#include "utils/Logger.hpp"
@@ -28,18 +28,14 @@
namespace UserInterface {
UsersView::UsersView(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent)
UsersView::UsersView()
: Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template"))
{
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template"), this);
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("tr", &Wt::WTemplate::Functions::tr);
_container = new Wt::WContainerWidget();
t->bindWidget("users", _container);
auto addBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.add"));
t->bindWidget("add-btn", addBtn);
_container = bindNew<Wt::WContainerWidget>("users");
Wt::WPushButton* addBtn = bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.Admin.Users.add"));
addBtn->clicked().connect(std::bind([=]
{
LmsApp->setInternalPath("/admin/user", true);
@@ -61,44 +57,41 @@ UsersView::refreshView()
_container->clear();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto users = Database::User::getAll(DboSession());
auto users = Database::User::getAll(LmsApp->getDboSession());
for (auto user : users)
{
auto userId = std::to_string(user.id());
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template.entry"), _container);
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId(userId);
Wt::WTemplate* entry = _container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Admin.Users.template.entry"));
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId(userId);
if (!authUser.isValid()) {
LMS_LOG(UI, ERROR) << "Skipping invalid userId = " << user.id();
continue;
}
entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::PlainText);
entry->bindString("name", authUser.identity(Wt::Auth::Identity::LoginName), Wt::TextFormat::Plain);
// Don't edit ourself this way
if (CurrentUser() == user)
if (LmsApp->getCurrentUser() == user)
continue;
entry->setCondition("if-edit", true);
auto editBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.edit"));
entry->bindWidget("edit-btn", editBtn);
Wt::WPushButton* editBtn = entry->bindNew<Wt::WPushButton>("edit-btn", Wt::WString::tr("Lms.Admin.Users.edit"));
editBtn->clicked().connect(std::bind([=]
{
LmsApp->setInternalPath("/admin/user/" + std::to_string(user.id()), true);
}));
auto delBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.del"));
entry->bindWidget("del-btn", delBtn);
Wt::WPushButton* delBtn = entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.Admin.Users.del"));
delBtn->clicked().connect(std::bind([=]
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto authUser = DbHandler().getUserDatabase().findWithId(userId);
auto user = DbHandler().getUser(authUser);
DbHandler().getUserDatabase().deleteUser( authUser );
auto authUser = LmsApp->getDb().getUserDatabase().findWithId(userId);
auto user = LmsApp->getDb().getUser(authUser);
LmsApp->getDb().getUserDatabase().deleteUser( authUser );
user.remove();
_container->removeWidget(entry);
}));
+3 -3
View File
@@ -19,14 +19,14 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WContainerWidget.h>
namespace UserInterface {
class UsersView : public Wt::WContainerWidget
class UsersView : public Wt::WTemplate
{
public:
UsersView(Wt::WContainerWidget *parent = 0);
UsersView();
private:
void refreshView();
-56
View File
@@ -1,56 +0,0 @@
/*
* Copyright (C) 2015 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 <Wt/WConfig.h>
#if WT_VERSION < 0X03030500
#include <Wt/WTemplate>
#include <Wt/WText>
#endif
#include "InputRange.hpp"
#if WT_VERSION >= 0X03030500
InputRange::InputRange(Wt::WContainerWidget *parent)
: Wt::WWebWidget(parent)
{
setHtmlTagName("input");
setAttributeValue("type", "range");
}
std::string
InputRange::jsRef(void) const
{
return Wt::WWebWidget::jsRef();
}
#else
InputRange::InputRange(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent)
{
addWidget(new Wt::WTemplate("<input type=\"range\"></input>"));
}
std::string
InputRange::jsRef(void) const
{
return Wt::WWebWidget::jsRef() + ".getElementsByTagName(\"input\")[0]";
}
#endif
-51
View File
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2015 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 <Wt/WContainerWidget>
#include <Wt/WWebWidget>
#include <Wt/WConfig.h>
#if WT_VERSION >= 0X03030500
class InputRange : public Wt::WWebWidget
{
public:
InputRange(Wt::WContainerWidget *parent = 0);
Wt::DomElementType domElementType() const
{
return Wt::DomElement_INPUT;
}
std::string jsRef() const;
};
#else
class InputRange : public Wt::WContainerWidget
{
public:
InputRange(Wt::WContainerWidget *parent = 0);
std::string jsRef() const;
};
#endif
-55
View File
@@ -1,55 +0,0 @@
/*
* Copyright (C) 2015 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 <Wt/WTimer>
#include "LineEdit.hpp"
namespace UserInterface {
LineEdit::LineEdit(std::size_t ms, Wt::WContainerWidget* parent)
: Wt::WLineEdit(parent)
{
Wt::WTimer *timer = new Wt::WTimer(this);
timer->setSingleShot(true);
timer->setInterval(ms);
this->keyWentUp().connect(std::bind([=] (Wt::WKeyEvent keyEvent)
{
if (timer->isActive())
timer->stop();
if (keyEvent.key() == Wt::Key_Enter)
_sigTimedChanged.emit(this->text());
else
timer->start();
}, std::placeholders::_1));
timer->timeout().connect(std::bind([=] ()
{
_sigTimedChanged.emit(this->text());
}));
}
} // namespace UserInterface
-44
View File
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2015 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/>.
*/
#ifndef UI_LINE_EDIT_HPP
#define UI_LINE_EDIT_HPP
#include <Wt/WLineEdit>
#include <Wt/WContainerWidget>
#include <Wt/WSignal>
namespace UserInterface {
class LineEdit : public Wt::WLineEdit
{
public:
LineEdit(std::size_t ms, Wt::WContainerWidget* parent = 0);
Wt::Signal<Wt::WString>& timedChanged() { return _sigTimedChanged; }
private:
Wt::Signal<Wt::WString> _sigTimedChanged;
};
} // namespace UserInterface
#endif
+12 -9
View File
@@ -19,28 +19,31 @@
#include <boost/filesystem.hpp>
#include <Wt/WLengthValidator.h>
#include "Validators.hpp"
namespace UserInterface {
Wt::WValidator* createNameValidator()
std::shared_ptr<Wt::WValidator>
createNameValidator()
{
Wt::WLengthValidator *v = new Wt::WLengthValidator();
auto v = std::make_shared<Wt::WLengthValidator>();
v->setMandatory(true);
v->setMinimumLength(::Database::User::MinNameLength);
v->setMaximumLength(::Database::User::MaxNameLength);
return v;
}
Wt::WValidator* createMandatoryValidator()
std::shared_ptr<Wt::WValidator>
createMandatoryValidator()
{
auto v = new Wt::WValidator();
auto v = std::make_shared<Wt::WValidator>();
v->setMandatory(true);
return v;
}
DirectoryValidator::DirectoryValidator(Wt::WObject *parent)
: Wt::WValidator(parent)
DirectoryValidator::DirectoryValidator() : Wt::WValidator()
{
}
@@ -56,11 +59,11 @@ DirectoryValidator::validate(const Wt::WString& input) const
// TODO check rights
bool res = boost::filesystem::is_directory(p, ec);
if (ec)
return Wt::WValidator::Result(Wt::WValidator::Invalid, ec.message()); // TODO translate common errors
return Wt::WValidator::Result(Wt::ValidationState::Invalid, ec.message()); // TODO translate common errors
else if (res)
return Wt::WValidator::Result(Wt::WValidator::Valid);
return Wt::WValidator::Result(Wt::ValidationState::Valid);
else
return Wt::WValidator::Result(Wt::WValidator::Invalid, Wt::WString::tr("Lms.not-a-directory"));
return Wt::WValidator::Result(Wt::ValidationState::Invalid, Wt::WString::tr("Lms.not-a-directory"));
}
+4 -4
View File
@@ -19,19 +19,19 @@
#pragma once
#include <Wt/WLengthValidator>
#include <Wt/WValidator.h>
#include "database/User.hpp"
namespace UserInterface {
Wt::WValidator* createNameValidator();
Wt::WValidator* createMandatoryValidator();
std::shared_ptr<Wt::WValidator> createNameValidator();
std::shared_ptr<Wt::WValidator> createMandatoryValidator();
class DirectoryValidator : public Wt::WValidator
{
public:
DirectoryValidator(Wt::WObject *parent = 0);
DirectoryValidator();
Wt::WValidator::Result validate(const Wt::WString& input) const override;
+28 -40
View File
@@ -17,11 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WApplication>
#include <Wt/WAnchor>
#include <Wt/WImage>
#include <Wt/WTemplate>
#include <Wt/WText>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>
#include "database/Types.hpp"
@@ -36,9 +35,8 @@
namespace UserInterface {
Artist::Artist(Filters* filters, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_filters(filters)
Artist::Artist(Filters* filters)
: _filters(filters)
{
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -60,81 +58,73 @@ Artist::refresh()
clear();
Database::Artist::id_type artistId;
if (!readAs(wApp->internalPathNextPart("/artist/"), artistId))
auto artistId = readAs<Database::Artist::id_type>(wApp->internalPathNextPart("/artist/"));
if (!artistId)
return;
Wt::Dbo::Transaction transaction(DboSession());
auto artist = Database::Artist::getById(DboSession(), artistId);
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto artist = Database::Artist::getById(LmsApp->getDboSession(), *artistId);
if (!artist)
{
LmsApp->goHome();
return;
}
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artist.template"), this);
Wt::WTemplate* t = addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artist.template"));
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
auto clusterContainers = new Wt::WContainerWidget();
t->bindWidget("clusters", clusterContainers);
Wt::WContainerWidget* clusterContainers = t->bindNew<Wt::WContainerWidget>("clusters");
{
auto clusters = artist->getClusters(3);
for (auto cluster : clusters)
{
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artist.template.cluster-entry"), clusterContainers);
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::PlainText);
Wt::WTemplate* entry = clusterContainers->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artist.template.cluster-entry"));
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::TextFormat::Plain);
}
}
t->bindString("name", Wt::WString::fromUTF8(artist->getName()), Wt::PlainText);
t->bindString("name", Wt::WString::fromUTF8(artist->getName()), Wt::TextFormat::Plain);
{
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Artist.play"), Wt::XHTMLText);
t->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = t->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Artist.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
artistPlay.emit(artistId);
artistPlay.emit(*artistId);
}));
}
{
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Artist.add"), Wt::XHTMLText);
t->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = t->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Artist.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
artistAdd.emit(artistId);
artistAdd.emit(*artistId);
}));
}
auto releasesContainer = new Wt::WContainerWidget();
t->bindWidget("releases", releasesContainer);
Wt::WContainerWidget* releasesContainer = t->bindNew<Wt::WContainerWidget>("releases");
auto releases = artist->getReleases(_filters->getClusterIds());
for (auto release : releases)
{
auto releaseId = release.id();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artist.template.entry"), releasesContainer);
Wt::WTemplate* entry = releasesContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artist.template.entry"));
entry->addFunction("tr", Wt::WTemplate::Functions::tr);
{
Wt::WAnchor* coverAnchor = LmsApplication::createReleaseAnchor(release, false);
Wt::WImage* cover = new Wt::WImage(coverAnchor);
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
auto cover = std::make_unique<Wt::WImage>();
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 128));
// Some images may not be square
cover->setWidth(128);
entry->bindWidget("cover", coverAnchor);
anchor->setImage(std::move(cover));
}
{
Wt::WAnchor* releaseAnchor = LmsApplication::createReleaseAnchor(release);
entry->bindWidget("name", releaseAnchor);
}
entry->bindWidget("name", LmsApplication::createReleaseAnchor(release));
if (release->hasVariousArtists())
entry->setCondition("if-has-various-artists", true);
@@ -153,15 +143,13 @@ Artist::refresh()
}
}
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Artist.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Artist.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
releasePlay.emit(releaseId);
}));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Artist.add"), Wt::XHTMLText);
entry->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Artist.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
releaseAdd.emit(releaseId);
+3 -3
View File
@@ -19,8 +19,8 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WSignal>
#include <Wt/WContainerWidget.h>
#include <Wt/WSignal.h>
namespace UserInterface {
@@ -29,7 +29,7 @@ class Filters;
class Artist : public Wt::WContainerWidget
{
public:
Artist(Filters* filters, Wt::WContainerWidget* parent = 0);
Artist(Filters* filters);
Wt::Signal<Database::id_type> artistAdd;
Wt::Signal<Database::id_type> artistPlay;
+13 -21
View File
@@ -17,9 +17,9 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WAnchor>
#include <Wt/WTemplate>
#include <Wt/WLineEdit>
#include <Wt/WAnchor.h>
#include <Wt/WTemplate.h>
#include <Wt/WLineEdit.h>
#include "database/Types.hpp"
@@ -34,24 +34,20 @@ namespace UserInterface {
using namespace Database;
Artists::Artists(Filters* filters, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
Artists::Artists(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artists.template")),
_filters(filters)
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artists.template"), this);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit();
container->bindWidget("search", _search);
_search = bindNew<Wt::WLineEdit>("search");
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Artists::refresh);
_artistsContainer = new Wt::WContainerWidget();
container->bindWidget("artists", _artistsContainer);
_artistsContainer = bindNew<Wt::WContainerWidget>("artists");
_showMore = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.show-more"));
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.template.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=]
{
@@ -77,24 +73,20 @@ Artists::addSome()
auto clusterIds = _filters->getClusterIds();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults;
auto artists = Artist::getByFilter(DboSession(),
auto artists = Artist::getByFilter(LmsApp->getDboSession(),
clusterIds,
searchKeywords,
_artistsContainer->count(), 20, moreResults);
for (auto artist : artists)
{
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artists.template.entry"), _artistsContainer);
Wt::WTemplate* entry = _artistsContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Artists.template.entry"));
entry->bindInt("nb-release", artist->getReleases(clusterIds).size());
{
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(artist);
entry->bindWidget("name", artistAnchor);
}
entry->bindWidget("name", LmsApplication::createArtistAnchor(artist));
}
_showMore->setHidden(!moreResults);
+6 -5
View File
@@ -19,18 +19,19 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WSignal>
#include <Wt/WContainerWidget.h>
#include <Wt/WTemplate.h>
#include <Wt/WLineEdit.h>
#include <Wt/WSignal.h>
namespace UserInterface {
class Filters;
class Artists : public Wt::WContainerWidget
class Artists : public Wt::WTemplate
{
public:
Artists(Filters* filters, Wt::WContainerWidget* parent = 0);
Artists(Filters* filters);
Wt::Signal<Database::id_type> artistAdd;
Wt::Signal<Database::id_type> artistPlay;
+29 -39
View File
@@ -17,9 +17,9 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WStackedWidget>
#include <Wt/WTemplate>
#include <Wt/WText>
#include <Wt/WStackedWidget.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>
#include "utils/Logger.hpp"
@@ -70,54 +70,44 @@ handlePathChange(Wt::WStackedWidget* stack)
}
}
Explore::Explore(Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent)
Explore::Explore()
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.template"))
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template"), this);
_filters = new Filters();
container->bindWidget("filters", _filters);
_filters = bindNew<Filters>("filters");
// Contents
Wt::WStackedWidget* stack = new Wt::WStackedWidget();
container->bindWidget("contents", stack);
auto artists = new Artists(_filters);
stack->addWidget(artists);
Wt::WStackedWidget* stack = bindNew<Wt::WStackedWidget>("contents");
auto artists = std::make_unique<Artists>(_filters);
artists->artistAdd.connect(this, &Explore::handleArtistAdd);
artists->artistPlay.connect(this, &Explore::handleArtistPlay);
stack->addWidget(std::move(artists));
auto artist = new Artist(_filters);
stack->addWidget(artist);
auto artist = std::make_unique<Artist>(_filters);
artist->artistAdd.connect(this, &Explore::handleArtistAdd);
artist->artistPlay.connect(this, &Explore::handleArtistPlay);
artist->releaseAdd.connect(this, &Explore::handleReleaseAdd);
artist->releasePlay.connect(this, &Explore::handleReleasePlay);
stack->addWidget(std::move(artist));
auto releases = new Releases(_filters);
stack->addWidget(releases);
auto releases = std::make_unique<Releases>(_filters);
releases->releaseAdd.connect(this, &Explore::handleReleaseAdd);
releases->releasePlay.connect(this, &Explore::handleReleasePlay);
stack->addWidget(std::move(releases));
auto release = new Release(_filters);
stack->addWidget(release);
auto release = std::make_unique<Release>(_filters);
release->releaseAdd.connect(this, &Explore::handleReleaseAdd);
release->releasePlay.connect(this, &Explore::handleReleasePlay);
release->trackAdd.connect(this, &Explore::handleTrackAdd);
release->trackPlay.connect(this, &Explore::handleTrackPlay);
stack->addWidget(std::move(release));
auto tracks = new Tracks(_filters);
stack->addWidget(tracks);
auto tracks = std::make_unique<Tracks>(_filters);
tracks->trackAdd.connect(this, &Explore::handleTrackAdd);
tracks->trackPlay.connect(this, &Explore::handleTrackPlay);
tracks->tracksAdd.connect(this, &Explore::handleTracksAdd);
tracks->tracksPlay.connect(this, &Explore::handleTracksPlay);
stack->addWidget(std::move(tracks));
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -175,49 +165,49 @@ static std::vector<Database::Track::pointer> getTrack(Wt::Dbo::Session& session,
void
Explore::handleArtistAdd(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksAdd.emit(getArtistTracks(DboSession(), id, _filters->getClusterIds()));
tracksAdd.emit(getArtistTracks(LmsApp->getDboSession(), id, _filters->getClusterIds()));
}
void
Explore::handleArtistPlay(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksPlay.emit(getArtistTracks(DboSession(), id, _filters->getClusterIds()));
tracksPlay.emit(getArtistTracks(LmsApp->getDboSession(), id, _filters->getClusterIds()));
}
void
Explore::handleReleaseAdd(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksAdd.emit(getReleaseTracks(DboSession(), id, _filters->getClusterIds()));
tracksAdd.emit(getReleaseTracks(LmsApp->getDboSession(), id, _filters->getClusterIds()));
}
void
Explore::handleReleasePlay(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksPlay.emit(getReleaseTracks(DboSession(), id, _filters->getClusterIds()));
tracksPlay.emit(getReleaseTracks(LmsApp->getDboSession(), id, _filters->getClusterIds()));
}
void
Explore::handleTrackAdd(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksAdd.emit(getTrack(DboSession(), id));
tracksAdd.emit(getTrack(LmsApp->getDboSession(), id));
}
void
Explore::handleTrackPlay(Database::id_type id)
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksPlay.emit(getTrack(DboSession(), id));
tracksPlay.emit(getTrack(LmsApp->getDboSession(), id));
}
void
+9 -9
View File
@@ -19,7 +19,7 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
@@ -27,22 +27,22 @@ namespace UserInterface {
class Filters;
class Explore : public Wt::WContainerWidget
class Explore : public Wt::WTemplate
{
public:
Explore(Wt::WContainerWidget *parent = 0);
Explore();
Wt::Signal<std::vector<Database::Track::pointer>> tracksAdd;
Wt::Signal<std::vector<Database::Track::pointer>> tracksPlay;
private:
void handleArtistAdd(Database::id_type id);
void handleArtistPlay(Database::id_type id);
void handleReleaseAdd(Database::id_type id);
void handleReleasePlay(Database::id_type id);
void handleTrackAdd(Database::id_type id);
void handleTrackPlay(Database::id_type id);
void handleArtistAdd(Database::Artist::id_type id);
void handleArtistPlay(Database::Artist::id_type id);
void handleReleaseAdd(Database::Release::id_type id);
void handleReleasePlay(Database::Release::id_type id);
void handleTrackAdd(Database::Track::id_type id);
void handleTrackPlay(Database::Track::id_type id);
void handleTracksAdd(std::vector<Database::Track::pointer> tracks);
void handleTracksPlay(std::vector<Database::Track::pointer> tracks);
+25 -44
View File
@@ -17,10 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WComboBox>
#include <Wt/WDialog>
#include <Wt/WPushButton>
#include <Wt/WTemplate>
#include <Wt/WComboBox.h>
#include <Wt/WDialog.h>
#include <Wt/WPushButton.h>
#include <Wt/WTemplate.h>
#include "Filters.hpp"
@@ -31,32 +31,25 @@ namespace UserInterface {
void
Filters::showDialog()
{
auto dialog = new Wt::WDialog(Wt::WString::tr("Lms.Explore.add-filter"));
auto dialog = std::make_shared<Wt::WDialog>(Wt::WString::tr("Lms.Explore.add-filter"));
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.add-filter"));
Wt::WTemplate* container = dialog->contents()->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.template.add-filter"));
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
dialog->contents()->addWidget(container);
Wt::WComboBox* typeCombo = container->bindNew<Wt::WComboBox>("type");
Wt::WComboBox* valueCombo = container->bindNew<Wt::WComboBox>("value");
auto typeCombo = new Wt::WComboBox();
container->bindWidget("type", typeCombo);
Wt::WPushButton* addBtn = container->bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.add"));
addBtn->clicked().connect(dialog.get(), &Wt::WDialog::accept);
auto valueCombo = new Wt::WComboBox();
container->bindWidget("value", valueCombo);
auto addBtn = new Wt::WPushButton(Wt::WString::tr("Lms.add"));
container->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(dialog, &Wt::WDialog::accept);
auto cancelBtn = new Wt::WPushButton(Wt::WString::tr("Lms.cancel"));
container->bindWidget("cancel-btn", cancelBtn);
cancelBtn->clicked().connect(dialog, &Wt::WDialog::reject);
Wt::WPushButton* cancelBtn = container->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"));
cancelBtn->clicked().connect(dialog.get(), &Wt::WDialog::reject);
// Populate data
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto types = Database::ClusterType::getAll(DboSession());
auto types = Database::ClusterType::getAll(LmsApp->getDboSession());
for (auto type : types)
typeCombo->addItem(Wt::WString::fromUTF8(type->getName()));
@@ -81,9 +74,9 @@ Filters::showDialog()
valueCombo->clear();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto clusterType = Database::ClusterType::getByName(DboSession(), name);
auto clusterType = Database::ClusterType::getByName(LmsApp->getDboSession(), name);
auto values = clusterType->getClusters();
for (auto value : values)
@@ -94,24 +87,22 @@ Filters::showDialog()
}));
dialog->setModal(true);
#if WT_VERSION >= 0x03030700
dialog->setMovable(false);
#endif
dialog->setResizable(false);
dialog->setClosable(false);
dialog->finished().connect(std::bind([=]
{
if (dialog->result() != Wt::WDialog::Accepted)
if (dialog->result() != Wt::DialogCode::Accepted)
return;
auto type = typeCombo->valueText().toUTF8();
auto value = valueCombo->valueText().toUTF8();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto clusterType = Database::ClusterType::getByName(DboSession(), type);
auto clusterType = Database::ClusterType::getByName(LmsApp->getDboSession(), type);
if (!clusterType)
return;
@@ -123,8 +114,7 @@ Filters::showDialog()
_filterIds.insert(clusterId);
_sigUpdated.emit();
auto filterBtn = new Wt::WPushButton(Wt::WString::fromUTF8(value));
_filters->addWidget(filterBtn);
Wt::WPushButton* filterBtn = _filters->addNew<Wt::WPushButton>(Wt::WString::fromUTF8(value), Wt::TextFormat::Plain);
filterBtn->clicked().connect(std::bind([=]
{
@@ -137,23 +127,14 @@ Filters::showDialog()
dialog->show();
}
Filters::Filters(Wt::WContainerWidget *parent)
: Wt::WContainerWidget(parent)
Filters::Filters()
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.filters"))
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.filters"), this);
// Filters
Wt::WPushButton *addFilterBtn = new Wt::WPushButton(Wt::WText::tr("Lms.Explore.add-filter"));
container->bindWidget("add-filter", addFilterBtn);
_filters = new Wt::WContainerWidget();
container->bindWidget("filters", _filters);
addFilterBtn->clicked().connect(std::bind([this]
{
showDialog();
}));
Wt::WPushButton *addFilterBtn = bindNew<Wt::WPushButton>("add-filter", Wt::WText::tr("Lms.Explore.add-filter"));
addFilterBtn->clicked().connect(this, &Filters::showDialog);
_filters = bindNew<Wt::WContainerWidget>("filters");
}
+7 -6
View File
@@ -19,8 +19,9 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WSignal>
#include <Wt/WContainerWidget.h>
#include <Wt/WSignal.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
@@ -28,21 +29,21 @@
namespace UserInterface {
class Filters : public Wt::WContainerWidget
class Filters : public Wt::WTemplate
{
public:
Filters(Wt::WContainerWidget *parent = 0);
Filters();
std::set<Database::Cluster::id_type> getClusterIds() const { return _filterIds; }
Wt::Signal<void>& updated() { return _sigUpdated; }
Wt::Signal<>& updated() { return _sigUpdated; }
private:
void showDialog();
Wt::WContainerWidget *_filters;
Wt::Signal<void> _sigUpdated;
Wt::Signal<> _sigUpdated;
std::set<Database::Cluster::id_type> _filterIds;
};
+28 -43
View File
@@ -17,11 +17,11 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WApplication>
#include <Wt/WAnchor>
#include <Wt/WImage>
#include <Wt/WTemplate>
#include <Wt/WText>
#include <Wt/WApplication.h>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WTemplate.h>
#include <Wt/WText.h>
#include "database/Types.hpp"
@@ -36,9 +36,8 @@
namespace UserInterface {
Release::Release(Filters* filters, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_filters(filters)
Release::Release(Filters* filters)
: _filters(filters)
{
wApp->internalPathChanged().connect(std::bind([=]
{
@@ -60,24 +59,23 @@ Release::refresh()
clear();
Database::Release::id_type releaseId;
if (!readAs(wApp->internalPathNextPart("/release/"), releaseId))
auto releaseId = readAs<Database::Release::id_type>(wApp->internalPathNextPart("/release/"));
if (!releaseId)
return;
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto release = Database::Release::getById(DboSession(), releaseId);
auto release = Database::Release::getById(LmsApp->getDboSession(), *releaseId);
if (!release)
{
LmsApp->goHome();
return;
}
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Release.template"), this);
Wt::WTemplate* t = addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Release.template"));
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
t->bindString("name", Wt::WString::fromUTF8(release->getName()), Wt::PlainText);
t->bindString("name", Wt::WString::fromUTF8(release->getName()), Wt::TextFormat::Plain);
boost::optional<int> year = release->getReleaseYear();
if (year)
@@ -103,49 +101,40 @@ Release::refresh()
else if (artists.size() == 1)
{
t->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(artists.front());
t->bindWidget("artist-name", artistAnchor);
t->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
}
}
Wt::WImage *cover = new Wt::WImage();
cover->setImageLink(Wt::WLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 512)));
t->bindWidget("cover", cover);
auto clusterContainers = new Wt::WContainerWidget();
t->bindWidget("clusters", clusterContainers);
t->bindNew<Wt::WImage>("cover", Wt::WLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 512)));
Wt::WContainerWidget* clusterContainers = t->bindNew<Wt::WContainerWidget>("clusters");
{
auto clusters = release->getClusters(3);
for (auto cluster : clusters)
{
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Release.template.cluster-entry"), clusterContainers);
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::PlainText);
Wt::WTemplate* entry = clusterContainers->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Release.template.cluster-entry"));
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::TextFormat::Plain);
}
}
{
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.play"), Wt::XHTMLText);
t->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = t->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Release.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
releasePlay.emit(releaseId);
releasePlay.emit(*releaseId);
}));
}
{
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.add"), Wt::XHTMLText);
t->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = t->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Release.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
releaseAdd.emit(releaseId);
releaseAdd.emit(*releaseId);
}));
}
auto tracksContainer = new Wt::WContainerWidget();
t->bindWidget("tracks", tracksContainer);
Wt::WContainerWidget* tracksContainer = t->bindNew<Wt::WContainerWidget>("tracks");
auto clusterIds = _filters->getClusterIds();
auto tracks = release->getTracks(clusterIds);
@@ -156,15 +145,13 @@ Release::refresh()
{
auto trackId = track.id();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Release.template.entry"), tracksContainer);
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText);
Wt::WTemplate* entry = tracksContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Release.template.entry"));
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
if (variousArtists && track->getArtist())
{
entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist());
entry->bindWidget("artist-name", artistAnchor);
entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
}
auto trackNumber = track->getTrackNumber();
@@ -182,15 +169,13 @@ Release::refresh()
entry->bindInt("disc-number", *discNumber);
}
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Release.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
trackPlay.emit(trackId);
}));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.add"), Wt::XHTMLText);
entry->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Release.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
trackAdd.emit(trackId);
+2 -2
View File
@@ -19,7 +19,7 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WSignal.h>
namespace UserInterface {
@@ -27,7 +27,7 @@ class Filters;
class Release : public Wt::WContainerWidget
{
public:
Release(Filters* filters, Wt::WContainerWidget* parent = 0);
Release(Filters* filters);
Wt::Signal<Database::id_type> releaseAdd;
Wt::Signal<Database::id_type> releasePlay;
+23 -36
View File
@@ -17,11 +17,11 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WApplication>
#include <Wt/WAnchor>
#include <Wt/WImage>
#include <Wt/WText>
#include <Wt/WTemplate>
#include <Wt/WApplication.h>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WText.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp"
@@ -38,25 +38,20 @@ namespace UserInterface {
using namespace Database;
Releases::Releases(Filters* filters, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_filters(filters)
Releases::Releases(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template")),
_filters(filters)
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template"), this);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit();
container->bindWidget("search", _search);
_search = bindNew<Wt::WLineEdit>("search");
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Releases::refresh);
_releasesContainer = new Wt::WContainerWidget();
container->bindWidget("releases", _releasesContainer);
_releasesContainer = bindNew<Wt::WContainerWidget>("releases");
_showMore = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.show-more"));
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=]
{
addSome();
@@ -81,29 +76,24 @@ Releases::addSome()
auto clusterIds = _filters->getClusterIds();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults;
auto releases = Release::getByFilter(DboSession(), clusterIds, searchKeywords, _releasesContainer->count(), 20, moreResults);
auto releases = Release::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, _releasesContainer->count(), 20, moreResults);
for (auto release : releases)
{
auto releaseId = release.id();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template.entry"), _releasesContainer);
Wt::WTemplate* entry = _releasesContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Releases.template.entry"));
entry->addFunction("tr", Wt::WTemplate::Functions::tr);
Wt::WAnchor* coverAnchor = LmsApplication::createReleaseAnchor(release, false);
Wt::WImage* cover = new Wt::WImage(coverAnchor);
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(releaseId, 128));
// Some images may not be square
cover->setWidth(128);
entry->bindWidget("cover", coverAnchor);
Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
auto cover = std::make_unique<Wt::WImage>();
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 128));
anchor->setImage(std::move(cover));
{
Wt::WAnchor* releaseAnchor = LmsApplication::createReleaseAnchor(release);
entry->bindWidget("release-name", releaseAnchor);
}
entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(release));
auto artists = release->getArtists();
if (artists.size() > 1)
@@ -114,19 +104,16 @@ Releases::addSome()
else if (artists.size() == 1)
{
entry->setCondition("if-has-artist", true);
Wt::WAnchor* artistAnchor = LmsApplication::createArtistAnchor(artists.front());
entry->bindWidget("artist-name", artistAnchor);
entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
}
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Releases.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Releases.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
releasePlay.emit(releaseId);
}));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Releases.add"), Wt::XHTMLText);
entry->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Releases.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
releaseAdd.emit(releaseId);
+6 -5
View File
@@ -19,18 +19,19 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WText>
#include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit.h>
#include <Wt/WText.h>
#include <Wt/WTemplate.h>
namespace UserInterface {
class Filters;
class Releases : public Wt::WContainerWidget
class Releases : public Wt::WTemplate
{
public:
Releases(Filters* filters, Wt::WContainerWidget* parent = 0);
Releases(Filters* filters);
Wt::Signal<Database::id_type> releaseAdd;
Wt::Signal<Database::id_type> releasePlay;
+25 -38
View File
@@ -17,10 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WAnchor>
#include <Wt/WImage>
#include <Wt/WLineEdit>
#include <Wt/WText>
#include <Wt/WAnchor.h>
#include <Wt/WImage.h>
#include <Wt/WLineEdit.h>
#include <Wt/WText.h>
#include "database/Types.hpp"
@@ -37,41 +37,34 @@ namespace UserInterface {
using namespace Database;
Tracks::Tracks(Filters* filters, Wt::WContainerWidget* parent)
: Wt::WContainerWidget(parent),
_filters(filters)
Tracks::Tracks(Filters* filters)
: Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template")),
_filters(filters)
{
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template"), this);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit();
container->bindWidget("search", _search);
_search = bindNew<Wt::WLineEdit>("search");
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Tracks::refresh);
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::XHTMLText);
container->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksPlay.emit(getTracks());
}));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::XHTMLText);
container->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksAdd.emit(getTracks());
}));
_tracksContainer = new Wt::WContainerWidget();
container->bindWidget("tracks", _tracksContainer);
_tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
_showMore = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.show-more"));
_showMore = bindNew<Wt::WTemplate>("show-more", Wt::WString::tr("Lms.Explore.template.show-more"));
_showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=]
{
addSome();
@@ -88,9 +81,9 @@ Tracks::getTracks(int offset, int size, bool& moreResults)
auto searchKeywords = splitString(_search->text().toUTF8(), " ");
auto clusterIds = _filters->getClusterIds();
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
return Track::getByFilter(DboSession(), clusterIds, searchKeywords, offset, size, moreResults);
return Track::getByFilter(LmsApp->getDboSession(), clusterIds, searchKeywords, offset, size, moreResults);
}
std::vector<Database::Track::pointer>
@@ -110,7 +103,7 @@ Tracks::refresh()
void
Tracks::addSome()
{
Wt::Dbo::Transaction transaction(DboSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults;
auto tracks = getTracks(_tracksContainer->count(), 20, moreResults);
@@ -118,41 +111,35 @@ Tracks::addSome()
for (auto track : tracks)
{
auto trackId = track.id();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template.entry"), _tracksContainer);
Wt::WTemplate* entry = _tracksContainer->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Explore.Tracks.template.entry"));
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText);
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::TextFormat::Plain);
auto artist = track->getArtist();
if (artist)
{
entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist());
entry->bindWidget("artist-name", artistAnchor);
entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
}
auto release = track->getRelease();
if (release)
{
entry->setCondition("if-has-release", true);
Wt::WAnchor *releaseAnchor = LmsApplication::createReleaseAnchor(track->getRelease());
entry->bindWidget("release-name", releaseAnchor);
entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(track->getRelease()));
}
Wt::WImage *cover = new Wt::WImage();
cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(track.id(), 64));
Wt::WImage* cover = entry->bindNew<Wt::WImage>("cover", LmsApp->getImageResource()->getTrackUrl(track.id(), 64));
// Some images may not be square
cover->setWidth(64);
entry->bindWidget("cover", cover);
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::XHTMLText);
entry->bindWidget("play-btn", playBtn);
Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::TextFormat::XHTML);
playBtn->clicked().connect(std::bind([=]
{
trackPlay.emit(trackId);
}));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::XHTMLText);
entry->bindWidget("add-btn", addBtn);
Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::TextFormat::XHTML);
addBtn->clicked().connect(std::bind([=]
{
trackAdd.emit(trackId);
+5 -5
View File
@@ -19,17 +19,17 @@
#pragma once
#include <Wt/WContainerWidget>
#include <Wt/WLineEdit>
#include <Wt/WTemplate>
#include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit.h>
#include <Wt/WTemplate.h>
namespace UserInterface {
class Filters;
class Tracks : public Wt::WContainerWidget
class Tracks : public Wt::WTemplate
{
public:
Tracks(Filters* filters, Wt::WContainerWidget* parent = 0);
Tracks(Filters* filters);
Wt::Signal<Database::id_type> trackAdd;
Wt::Signal<Database::id_type> trackPlay;
+17 -22
View File
@@ -17,8 +17,8 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/WApplication>
#include <Wt/Http/Response>
#include <Wt/WApplication.h>
#include <Wt/Http/Response.h>
#include "utils/Logger.hpp"
#include "utils/Utils.hpp"
@@ -34,9 +34,8 @@ namespace UserInterface {
static const std::string unknownCoverPath = "/images/unknown-cover.jpg";
static const std::string unknownArtistImagePath = "/images/unknown-artist.jpg";
ImageResource::ImageResource(Database::Handler& db, Wt::WObject *parent)
: Wt::WResource(parent),
_db(db)
ImageResource::ImageResource(Database::Handler& db)
: _db(db)
{
}
@@ -154,14 +153,14 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
if (!sizeStr)
return;
std::size_t size;
if (!readAs(*sizeStr, size) || size > maxSize)
auto size = readAs<std::size_t>(*sizeStr);
if (!size || *size > maxSize)
return;
if (trackIdStr)
{
Database::Track::id_type trackId;
if (!readAs(*trackIdStr, trackId))
auto trackId = readAs<Database::Track::id_type>(*trackIdStr);
if (!trackId)
return;
boost::filesystem::path path;
@@ -172,7 +171,7 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
Wt::Dbo::Transaction transaction(_db.getSession());
Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
Database::Track::pointer track = Database::Track::getById(_db.getSession(), *trackId);
if (track)
{
coverType = track->getCoverType();
@@ -195,17 +194,16 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
break;
}
putCover(response, covers, size);
putCover(response, covers, *size);
return;
}
putImage(response, getDefaultCover(size));
return;
putImage(response, getDefaultCover(*size));
}
else if (releaseIdStr)
{
Database::Release::id_type releaseId;
if (!readAs(*releaseIdStr, releaseId))
auto releaseId = readAs<Database::Release::id_type>(*releaseIdStr);
if (!releaseId)
return;
std::vector<Image::Image> covers;
@@ -213,21 +211,18 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
// transactions are not thread safe
{
Wt::WApplication::UpdateLock lock(LmsApplication::instance());
covers = CoverArt::Grabber::instance().getFromRelease(_db.getSession(), releaseId);
covers = CoverArt::Grabber::instance().getFromRelease(_db.getSession(), *releaseId);
}
putCover(response, covers, size);
return;
putCover(response, covers, *size);
}
else if (artistIdStr)
{
putImage(response, getDefaultArtistImage(size));
return;
putImage(response, getDefaultArtistImage(*size));
}
else
{
putImage(response, getDefaultCover(size));
return;
putImage(response, getDefaultCover(*size));
}
}
+2 -2
View File
@@ -22,7 +22,7 @@
#include <mutex>
#include <Wt/WResource>
#include <Wt/WResource.h>
#include "database/DatabaseHandler.hpp"
#include "image/Image.hpp"
@@ -35,7 +35,7 @@ class ImageResource : public Wt::WResource
public:
static const std::size_t maxSize = 512;
ImageResource(Database::Handler& db, Wt::WObject *parent = 0);
ImageResource(Database::Handler& db);
~ImageResource();
std::string getReleaseUrl(Database::Release::id_type releaseId, size_t size) const;
+3 -4
View File
@@ -17,7 +17,7 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/Http/Response>
#include <Wt/Http/Response.h>
#include "utils/Logger.hpp"
@@ -27,9 +27,8 @@
namespace UserInterface {
TranscodeResource::TranscodeResource(Database::Handler& db, Wt::WObject *parent)
: Wt::WResource(parent),
_db(db)
TranscodeResource::TranscodeResource(Database::Handler& db)
: _db(db)
{
LMS_LOG(UI, DEBUG) << "CONSTRUCTING RESOURCE";
}
+2 -2
View File
@@ -21,7 +21,7 @@
#include <mutex>
#include <Wt/WResource>
#include <Wt/WResource.h>
#include "av/AvTranscoder.hpp"
@@ -32,7 +32,7 @@ namespace UserInterface {
class TranscodeResource : public Wt::WResource
{
public:
TranscodeResource(Database::Handler& db, Wt::WObject *parent);
TranscodeResource(Database::Handler& db);
~TranscodeResource();
std::string getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset) const;
+2 -4
View File
@@ -19,12 +19,10 @@
#pragma once
#include <Wt/WServer>
#include <Wt/WApplication>
#include <Wt/WLogger>
#include <string>
#include <Wt/WApplication.h>
#include <Wt/WLogger.h>
enum class Severity
{
+1 -1
View File
@@ -73,7 +73,7 @@ void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& cr
}
else
{
LMS_LOG(DBUPDATER, ERROR) << "Failed to open file '" << p << "'";
LMS_LOG(DBUPDATER, ERROR) << "Failed to open file '" << p.string() << "'";
throw std::runtime_error("Failed to open file '" + p.string() + "'" );
}
+20 -41
View File
@@ -19,6 +19,7 @@
#include <string>
#include <sstream>
#include <iomanip>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/string/split.hpp>
@@ -26,42 +27,28 @@
#include "Utils.hpp"
boost::optional<long>
readLong(const std::string& str)
template<>
boost::optional<Wt::WDate> readAs(const std::string& str)
{
try
{
return std::stol(str);
}
catch (std::exception& e)
{
return boost::none;
}
}
bool readAsPosixTime(const std::string& str, boost::posix_time::ptime& time)
{
const std::locale formats[] = {
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y-%m-%d")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y-%b-%d")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y-%B-%d")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y/%m/%d")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%d.%m.%Y")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y-%m")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y/%m")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y.%m")),
std::locale(std::locale::classic(), new boost::posix_time::time_input_facet("%Y")),
const std::vector<std::string> formats = {
"yyyy-MM-dd",
"yyyy/MM/dd",
"yyyy-MM",
"yyyy/MM",
"yyyy"
};
for(size_t i=0; i < sizeof(formats)/sizeof(formats[0]); ++i)
for (auto format : formats)
{
std::istringstream iss(str);
iss.imbue(formats[i]);
if (iss >> time)
return true;
auto date = Wt::WDate::fromString(str, format);
if (!date.isValid())
continue;
return date;
}
return false;
return boost::none;
}
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
@@ -72,7 +59,7 @@ bool readList(const std::string& str, const std::string& separators, std::list<s
{
if (separators.find(c) != std::string::npos) {
if (!curStr.empty()) {
results.push_back(stringToUTF8(curStr));
results.push_back(curStr);
curStr.clear();
}
}
@@ -85,7 +72,7 @@ bool readList(const std::string& str, const std::string& separators, std::list<s
}
if (!curStr.empty())
results.push_back(stringToUTF8(curStr));
results.push_back(curStr);
return !str.empty();
}
@@ -131,14 +118,6 @@ stringTrimEnd(const std::string& str, const std::string& whitespace)
return str.substr(0, str.find_last_not_of(whitespace)+1);
}
std::string
stringToUTF8(const std::string& str)
{
return boost::locale::conv::to_utf<char>(str, "UTF-8");
}
std::string
bufferToString(const std::vector<unsigned char>& data)
{
+19 -18
View File
@@ -19,26 +19,21 @@
#pragma once
#include <string>
#include <vector>
#include <chrono>
#include <list>
#include <string>
#include <sstream>
#include <vector>
#include <boost/locale.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
boost::optional<long>
readLong(const std::string& str);
bool
readAsPosixTime(const std::string& str, boost::posix_time::ptime& time);
#include <Wt/WDate.h>
bool
readList(const std::string& str, const std::string& separators, std::list<std::string>& results);
std::string
durationToString(boost::posix_time::time_duration duration, std::string format);
//std::string
//durationToString(boost::posix_time::time_duration duration, std::string format);
std::vector<std::string>
splitString(std::string string, std::string separators);
@@ -49,17 +44,23 @@ stringTrim(const std::string& str, const std::string& whitespaces = " \t");
std::string
stringTrimEnd(const std::string& str, const std::string& whitespaces = " \t");
std::string
stringToUTF8(const std::string& str);
std::string
bufferToString(const std::vector<unsigned char>& data);
template<typename T>
static inline bool readAs(const std::string& str, T& data)
boost::optional<T> readAs(const std::string& str)
{
T res;
std::istringstream iss ( str );
iss >> data;
return !iss.fail();
iss >> res;
if (iss.fail())
return boost::none;
return res;
}
template<>
boost::optional<Wt::WDate> readAs(const std::string& str);