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
+14 -18
View File
@@ -1,17 +1,13 @@
# LMS - Lightweight Media Server # LMS - Lightweight Media Server
LMS is a self-hosted media streaming software, released under the GPLv3 license. LMS is a self-hosted media streaming software, released under the GPLv3 license.
It allows you to access your music and your videos using an http(s) web interface. It allows you to access your music using an http(s) web interface.
## Features ## Features
- Audio transcode for maximum interoperability and low bandwith requirements - Audio transcode for maximum interoperability and low bandwith requirements
- User management
- Playlist support
- MusicBrainzID support to handle duplicated artist and release names - MusicBrainzID support to handle duplicated artist and release names
- Filter by genre and/or group
Tags are automatically created to facilitate music searching, using: - User management
- Multi genre information from metadata
- High-level information from AcousticBrainz
LMS is written entirely in C++. Therefore, it is suitable to run on embedded devices, where space and memory are limited. LMS is written entirely in C++. Therefore, it is suitable to run on embedded devices, where space and memory are limited.
@@ -19,29 +15,29 @@ LMS is written entirely in C++. Therefore, it is suitable to run on embedded dev
### Debian ### Debian
```sh ```sh
$ apt-get install g++ autoconf automake libboost-dev libboost-locale-dev libboost-iostreams-dev libavcodec-dev libavutil-dev libavformat-dev libav-tools libwtdbosqlite-dev libwthttp-dev libwtdbo-dev libwt-dev libmagick++-dev libpstreams-dev libcurlpp-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev apt-get install g++ autoconf automake libboost-filesystem-dev libboost-system-dev libavcodec-dev libavutil-dev libavformat-dev libav-tools libwtdbosqlite-dev libwthttp-dev libwtdbo-dev libwt-dev libmagick++-dev libpstreams-dev libconfig++-dev libpstreams-dev ffmpeg libtag1-dev
``` ```
## Build ## Build
```sh ```sh
$ git clone https://github.com/epoupon/lms.git lms git clone https://github.com/epoupon/lms.git lms
$ cd lms cd lms
$ autoreconf -vfi autoreconf -vfi
$ mkdir build mkdir build
$ cd build cd build
$ ../configure --prefix=/usr --sysconfdir=/etc ../configure --prefix=/usr --sysconfdir=/etc
``` ```
configure will complain if a mandatory library is missing. configure will complain if a mandatory library is missing.
```sh ```sh
$ make -j 4 make
``` ```
## Install ## Install
```sh ```sh
$ make install make install
``` ```
This command requires root privileges This command requires root privileges
@@ -49,13 +45,13 @@ This command requires root privileges
LMS uses a configuration file, installed in '/etc/lms.conf' LMS uses a configuration file, installed in '/etc/lms.conf'
It is recommended to edit this file and change the relevant settings (working directory, listen port, etc.) It is recommended to edit this file and change the relevant settings (working directory, listen port, etc.)
Other settings are set using the web interface. All other settings are set using the web interface.
It is highly recommended to run LMS as a non root user. Therefore make sure the user has write permissions on the working directory. It is highly recommended to run LMS as a non root user. Therefore make sure the user has write permissions on the working directory.
## Running ## Running
```sh ```sh
$ lms [config_file] lms [config_file]
``` ```
Logs are output in the working directory, in the file 'lms.log' Logs are output in the working directory, in the file 'lms.log'
-1
View File
@@ -37,7 +37,6 @@
- Display only the most popular cluster of each cluster type? - Display only the most popular cluster of each cluster type?
[Settings] [Settings]
- Add a audio settings: format + bitrate to be used
- Add a user management view - Add a user management view
- Add multi root directories support - Add multi root directories support
- Add a first connection wizard - Add a first connection wizard
+1 -1
View File
@@ -5,7 +5,7 @@
<message id="Lms.Admin.InitWizard.template"> <message id="Lms.Admin.InitWizard.template">
<div class="container"> <div class="container">
<div class="page-header"> <div class="page-header">
<h2>${tr:Lms.AdminWizard.header}</h2> <h2>${tr:Lms.Admin.InitWizard.header}</h2>
</div> </div>
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
+1 -31
View File
@@ -16,7 +16,7 @@ fi
AC_SUBST(MAGICKXX_CFLAGS) AC_SUBST(MAGICKXX_CFLAGS)
AC_SUBST(MAGICKXX_LIBS) AC_SUBST(MAGICKXX_LIBS)
AC_CHECK_HEADERS([Wt/WApplication pstreams/pstream.h], AC_CHECK_HEADERS([Wt/WApplication.h pstreams/pstream.h],
[], [],
[AC_MSG_ERROR([Header not found or unusable !])]) [AC_MSG_ERROR([Header not found or unusable !])])
@@ -66,36 +66,6 @@ AC_CHECK_LIB( [boost_filesystem],
, ,
[AC_MSG_ERROR([libboost_filesystem not found!])]) [AC_MSG_ERROR([libboost_filesystem not found!])])
AC_CHECK_LIB( [boost_locale],
[main],
,
[AC_MSG_ERROR([libboost_locale not found!])])
AC_CHECK_LIB( [boost_date_time],
[main],
,
[AC_MSG_ERROR([libboost_date_time not found!])])
AC_CHECK_LIB( [boost_iostreams],
[main],
,
[AC_MSG_ERROR([libboost_iostreams not found!])])
AC_CHECK_LIB( [boost_thread],
[main],
,
[AC_MSG_ERROR([libboost_thread not found!])])
AC_CHECK_LIB( [curlpp],
[main],
,
[AC_MSG_ERROR([libcurlpp not found!])])
AC_CHECK_LIB( [curl],
[curl_easy_cleanup],
,
[AC_MSG_ERROR([libcurl not found!])])
AC_CHECK_LIB( [tag], AC_CHECK_LIB( [tag],
[main], [main],
, ,
+1 -6
View File
@@ -10,13 +10,10 @@ lms_SOURCES = \
$(srcdir)/database/MediaDirectory.cpp \ $(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Playlist.cpp \ $(srcdir)/database/Playlist.cpp \
$(srcdir)/database/Release.cpp \ $(srcdir)/database/Release.cpp \
$(srcdir)/database/SearchFilter.cpp \
$(srcdir)/database/Setting.cpp \ $(srcdir)/database/Setting.cpp \
$(srcdir)/database/SqlQuery.cpp \ $(srcdir)/database/SqlQuery.cpp \
$(srcdir)/database/Track.cpp \ $(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \ $(srcdir)/database/User.cpp \
$(srcdir)/feature/FeatureExtractor.cpp \
$(srcdir)/feature/FeatureStore.cpp \
$(srcdir)/image/Image.cpp \ $(srcdir)/image/Image.cpp \
$(srcdir)/metadata/AvFormat.cpp \ $(srcdir)/metadata/AvFormat.cpp \
$(srcdir)/metadata/TagLibParser.cpp \ $(srcdir)/metadata/TagLibParser.cpp \
@@ -31,8 +28,6 @@ lms_SOURCES = \
$(srcdir)/ui/admin/InitWizardView.cpp \ $(srcdir)/ui/admin/InitWizardView.cpp \
$(srcdir)/ui/admin/UserView.cpp \ $(srcdir)/ui/admin/UserView.cpp \
$(srcdir)/ui/admin/UsersView.cpp \ $(srcdir)/ui/admin/UsersView.cpp \
$(srcdir)/ui/common/InputRange.cpp \
$(srcdir)/ui/common/LineEdit.cpp \
$(srcdir)/ui/common/Validators.cpp \ $(srcdir)/ui/common/Validators.cpp \
$(srcdir)/ui/explore/ArtistView.cpp \ $(srcdir)/ui/explore/ArtistView.cpp \
$(srcdir)/ui/explore/ArtistsView.cpp \ $(srcdir)/ui/explore/ArtistsView.cpp \
@@ -48,6 +43,6 @@ lms_SOURCES = \
$(srcdir)/utils/Path.cpp \ $(srcdir)/utils/Path.cpp \
$(srcdir)/utils/Utils.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) 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); int error = avformat_open_input(&_context, _p.string().c_str(), nullptr, nullptr);
if (error < 0) 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); throw MediaFileException(error);
} }
error = avformat_find_stream_info(_context, nullptr); error = avformat_find_stream_info(_context, nullptr);
if (error < 0) 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); avformat_close_input(&_context);
throw MediaFileException(error); throw MediaFileException(error);
} }
@@ -75,13 +75,13 @@ MediaFile::~MediaFile()
avformat_close_input(&_context); avformat_close_input(&_context);
} }
boost::posix_time::time_duration std::chrono::milliseconds
MediaFile::getDuration() const MediaFile::getDuration() const
{ {
if (static_cast<int>(_context->duration) != AV_NOPTS_VALUE ) if (_context->duration == AV_NOPTS_VALUE)
return boost::posix_time::seconds(_context->duration / AV_TIME_BASE); return std::chrono::milliseconds(0); // TODO estimate
else
return boost::posix_time::seconds(0); // TODO, do something better? return std::chrono::milliseconds(_context->duration / AV_TIME_BASE * 1000);
} }
void void
+2 -2
View File
@@ -33,10 +33,10 @@ extern "C"
#include <string> #include <string>
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <chrono>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/filesystem/path.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" #include "utils/Exception.hpp"
@@ -75,7 +75,7 @@ class MediaFile
const boost::filesystem::path& getPath() const {return _p;}; 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::map<std::string, std::string> getMetaData(void);
std::vector<StreamInfo> getStreamInfo() const; std::vector<StreamInfo> getStreamInfo() const;
+2 -2
View File
@@ -101,7 +101,7 @@ Transcoder::init()
} }
if (!avConvPath.empty()) if (!avConvPath.empty())
LMS_LOG(TRANSCODE, INFO) << "Using transcoder " << avConvPath; LMS_LOG(TRANSCODE, INFO) << "Using transcoder " << avConvPath.string();
else else
throw std::runtime_error("Cannot find any transcoder binary!"); throw std::runtime_error("Cannot find any transcoder binary!");
} }
@@ -123,7 +123,7 @@ Transcoder::start()
else if (!boost::filesystem::is_regular( _filePath) ) else if (!boost::filesystem::is_regular( _filePath) )
return false; return false;
LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath << "'"; LMS_LOG_TRANSCODE(INFO) << "Transcoding file '" << _filePath.string() << "'";
std::vector<std::string> args; 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)) if (image.load(picture.data))
res.push_back( image ); res.push_back( image );
else 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; return res;
@@ -87,7 +87,7 @@ Grabber::getFromDirectory(const boost::filesystem::path& p, std::size_t nbMaxCov
if (image.load(coverPath)) if (image.load(coverPath))
res.push_back(image); res.push_back(image);
else else
LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath << "'"; LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath.string() << "'";
} }
return res; return res;
@@ -116,7 +116,7 @@ Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t
if (boost::filesystem::file_size(path) > _maxFileSize) 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; continue;
} }
@@ -138,7 +138,7 @@ Grabber::getFromTrack(const boost::filesystem::path& p, std::size_t nbMaxCovers)
} }
catch (Av::MediaFileException& e) 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>(); return std::vector<Image::Image>();
+30 -28
View File
@@ -19,17 +19,17 @@
#include <boost/make_unique.hpp> #include <boost/make_unique.hpp>
#include <Wt/Dbo/FixedSqlConnectionPool> #include <Wt/Dbo/FixedSqlConnectionPool.h>
#include <Wt/Dbo/backend/Sqlite3> #include <Wt/Dbo/backend/Sqlite3.h>
#include <Wt/Auth/Dbo/AuthInfo> #include <Wt/Auth/Dbo/AuthInfo.h>
#include <Wt/Auth/Dbo/UserDatabase> #include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/AuthService> #include <Wt/Auth/AuthService.h>
#include <Wt/Auth/HashFunction> #include <Wt/Auth/HashFunction.h>
#include <Wt/Auth/Identity> #include <Wt/Auth/Identity.h>
#include <Wt/Auth/PasswordService> #include <Wt/Auth/PasswordService.h>
#include <Wt/Auth/PasswordStrengthValidator> #include <Wt/Auth/PasswordStrengthValidator.h>
#include <Wt/Auth/PasswordVerifier> #include <Wt/Auth/PasswordVerifier.h>
#include "Setting.hpp" #include "Setting.hpp"
@@ -51,24 +51,24 @@ Handler::configureAuth(void)
{ {
authService.setEmailVerificationEnabled(false); authService.setEmailVerificationEnabled(false);
authService.setAuthTokensEnabled(true, "lmsauth"); authService.setAuthTokensEnabled(true, "lmsauth");
authService.setIdentityPolicy(Wt::Auth::LoginNameIdentity); authService.setIdentityPolicy(Wt::Auth::IdentityPolicy::LoginName);
authService.setRandomTokenLength(32); authService.setRandomTokenLength(32);
authService.setTokenHashFunction(new Wt::Auth::BCryptHashFunction(8)); authService.setTokenHashFunction(new Wt::Auth::BCryptHashFunction(8));
Wt::Auth::PasswordVerifier *verifier = new Wt::Auth::PasswordVerifier(); auto verifier = std::make_unique<Wt::Auth::PasswordVerifier>();
verifier->addHashFunction(new Wt::Auth::BCryptHashFunction(8)); verifier->addHashFunction(std::make_unique<Wt::Auth::BCryptHashFunction>(8));
passwordService.setVerifier(verifier); passwordService.setVerifier(std::move(verifier));
passwordService.setAttemptThrottlingEnabled(true); passwordService.setAttemptThrottlingEnabled(true);
Wt::Auth::PasswordStrengthValidator* strengthValidator = new Wt::Auth::PasswordStrengthValidator(); auto strengthValidator = std::make_unique<Wt::Auth::PasswordStrengthValidator>();
// Reduce some constraints... // Reduce some constraints...
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::PassPhrase, 4); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::PassPhrase, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::OneCharClass, 4); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::OneCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::TwoCharClass, 4); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::TwoCharClass, 4);
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::ThreeCharClass, 4 ); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::ThreeCharClass, 4 );
strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthValidator::FourCharClass, 4 ); strengthValidator->setMinimumLength( Wt::Auth::PasswordStrengthType::FourCharClass, 4 );
passwordService.setStrengthValidator(strengthValidator); passwordService.setStrengthValidator(std::move(strengthValidator));
} }
const Wt::Auth::AuthService& const Wt::Auth::AuthService&
@@ -182,24 +182,26 @@ Handler::createUser(const Wt::Auth::User& authUser)
return User::pointer(); 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); Wt::Dbo::ptr<AuthInfo> authInfo = _users->find(authUser);
authInfo.modify()->setUser(user); authInfo.modify()->setUser(user);
return user; return user;
} }
Wt::Dbo::SqlConnectionPool* std::unique_ptr<Wt::Dbo::SqlConnectionPool>
Handler::createConnectionPool(boost::filesystem::path p) Handler::createConnectionPool(boost::filesystem::path p)
{ {
LMS_LOG(DB, INFO) << "Creating connection pool on file " << p; LMS_LOG(DB, INFO) << "Creating connection pool on file " << p.string();
Wt::Dbo::backend::Sqlite3 *connection = new Wt::Dbo::backend::Sqlite3(p.string());
auto connection = std::make_unique<Wt::Dbo::backend::Sqlite3>(p.string());
connection->executeSql("pragma journal_mode=WAL"); connection->executeSql("pragma journal_mode=WAL");
connection->setProperty("show-queries", "true"); 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 <boost/filesystem.hpp>
#include <memory> #include <memory>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool> #include <Wt/Dbo/SqlConnectionPool.h>
#include <Wt/Auth/Dbo/UserDatabase> #include <Wt/Auth/Dbo/UserDatabase.h>
#include <Wt/Auth/Login> #include <Wt/Auth/Login.h>
#include <Wt/Auth/PasswordService> #include <Wt/Auth/PasswordService.h>
#include "Types.hpp" #include "Types.hpp"
@@ -60,7 +60,7 @@ class Handler
static const Wt::Auth::AuthService& getAuthService(); static const Wt::Auth::AuthService& getAuthService();
static const Wt::Auth::PasswordService& getPasswordService(); 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: private:
+2 -2
View File
@@ -54,7 +54,7 @@ Artist::getById(Wt::Dbo::Session& session, Artist::id_type id)
Artist::pointer Artist::pointer
Artist::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID) 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> std::vector<Artist::pointer>
@@ -75,7 +75,7 @@ Artist::getAllOrphans(Wt::Dbo::Session& session)
static static
Wt::Dbo::Query<Artist::pointer> Wt::Dbo::Query<Artist::pointer>
getQuery(Wt::Dbo::Session& session, getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds, const std::set<Artist::id_type>& clusterIds,
const std::vector<std::string>& keywords) const std::vector<std::string>& keywords)
{ {
WhereClause where; WhereClause where;
+2 -4
View File
@@ -23,10 +23,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/QueryModel> #include <Wt/Dbo/QueryModel.h>
#include "SearchFilter.hpp"
namespace Database namespace Database
{ {
+1 -1
View File
@@ -31,7 +31,7 @@ MediaDirectory::MediaDirectory(boost::filesystem::path p)
MediaDirectory::pointer MediaDirectory::pointer
MediaDirectory::create(Wt::Dbo::Session& session, boost::filesystem::path p) 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 void
+3 -2
View File
@@ -24,8 +24,9 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits> #include <Wt/Dbo/WtSqlTraits.h>
namespace Database { namespace Database {
class MediaDirectory 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::pointer
Playlist::create(Wt::Dbo::Session& session, std::string name, bool isPublic, Wt::Dbo::ptr<User> user) 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() PlaylistEntry::PlaylistEntry()
@@ -75,7 +75,7 @@ PlaylistEntry::PlaylistEntry(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Playlist> p
PlaylistEntry::pointer PlaylistEntry::pointer
PlaylistEntry::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Playlist> playlist) 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 #ifndef DATABASE_PLAYLIST_HPP
#define DATABASE_PLAYLIST_HPP #define DATABASE_PLAYLIST_HPP
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <string> #include <string>
+7 -8
View File
@@ -18,7 +18,6 @@
*/ */
#include "Types.hpp" #include "Types.hpp"
#include "SearchFilter.hpp"
#include "SqlQuery.hpp" #include "SqlQuery.hpp"
namespace Database namespace Database
@@ -53,7 +52,7 @@ Release::getById(Wt::Dbo::Session& session, Release::id_type id)
Release::pointer Release::pointer
Release::create(Wt::Dbo::Session& session, const std::string& name, const std::string& MBID) 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> std::vector<Release::pointer>
@@ -74,7 +73,7 @@ Release::getAllOrphans(Wt::Dbo::Session& session)
static static
Wt::Dbo::Query<Release::pointer> Wt::Dbo::Query<Release::pointer>
getQuery(Wt::Dbo::Session& session, getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds, const std::set<Release::id_type>& clusterIds,
const std::vector<std::string> keywords) const std::vector<std::string> keywords)
{ {
WhereClause where; WhereClause where;
@@ -140,22 +139,22 @@ Release::getReleaseYear(bool original) const
{ {
assert(session()); 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") 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 = ?") .where("r.id = ?")
.groupBy("t.date") .groupBy("t.date")
.bind(this->id()); .bind(this->id());
/* various dates, no date */ /* various dates, no date */
if (times.empty() || times.size() > 1) if (dates.empty() || dates.size() > 1)
return boost::none; 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::none;
return boost::make_optional<int>(date.year()); return date.year();
} }
std::vector<Wt::Dbo::ptr<Artist>> std::vector<Wt::Dbo::ptr<Artist>>
+2 -4
View File
@@ -21,9 +21,7 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include "SearchFilter.hpp"
namespace Database namespace Database
{ {
@@ -68,7 +66,7 @@ class Release : public Wt::Dbo::Dbo<Release>
// Accessors // Accessors
std::string getName() const { return _name; } std::string getName() const { return _name; }
std::string getMBID() const { return _MBID; } 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 // Get the artists of this release
std::vector<Wt::Dbo::ptr<Artist> > getArtists() const; 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"); return (res->_value == "true");
} }
boost::posix_time::time_duration Wt::WTime
Setting::getDuration(Wt::Dbo::Session& session, std::string setting, boost::posix_time::time_duration defaultValue) Setting::getTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime defaultValue)
{ {
Wt::Dbo::Transaction transaction(session); Wt::Dbo::Transaction transaction(session);
@@ -61,19 +61,7 @@ Setting::getDuration(Wt::Dbo::Session& session, std::string setting, boost::posi
if (!res) if (!res)
return defaultValue; return defaultValue;
return boost::posix_time::duration_from_string(res->_value); return Wt::WTime::fromString(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);
} }
int int
@@ -93,7 +81,7 @@ Setting::getInt(Wt::Dbo::Session& session, std::string setting, int defaultValue
Setting::pointer Setting::pointer
Setting::create(Wt::Dbo::Session& session, std::string name) 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 Setting::pointer
@@ -127,17 +115,10 @@ Setting::setBool(Wt::Dbo::Session& session, std::string setting, bool value)
} }
void 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); 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
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);
} }
void void
+7 -11
View File
@@ -19,9 +19,8 @@
#pragma once #pragma once
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/WTime.h>
#include <boost/date_time/posix_time/posix_time_types.hpp>
namespace Database { namespace Database {
@@ -29,7 +28,10 @@ namespace Database {
class Setting class Setting
{ {
public: public:
using pointer = Wt::Dbo::ptr<Setting>;
Setting() {} Setting() {}
Setting(std::string name) : _name(name) {}
// check if a setting exists or not // check if a setting exists or not
static bool exists(Wt::Dbo::Session& session, std::string setting); static bool exists(Wt::Dbo::Session& session, std::string setting);
@@ -38,16 +40,14 @@ class Setting
// Nested transactions // Nested transactions
static std::string getString(Wt::Dbo::Session& session, std::string setting, std::string defaultValue = ""); 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 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 Wt::WTime getTime(Wt::Dbo::Session& session, std::string setting, Wt::WTime defaultValue = Wt::WTime());
static boost::posix_time::ptime getTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime defaultTime = boost::posix_time::ptime());
static int getInt(Wt::Dbo::Session& session, std::string setting, int defaultValue = 0); static int getInt(Wt::Dbo::Session& session, std::string setting, int defaultValue = 0);
// Setters // Setters
// Nested transactions // Nested transactions
static void setString(Wt::Dbo::Session& session, std::string setting, std::string value); 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 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, Wt::WTime value);
static void setTime(Wt::Dbo::Session& session, std::string setting, boost::posix_time::ptime time);
static void setInt(Wt::Dbo::Session& session, std::string setting, int value); static void setInt(Wt::Dbo::Session& session, std::string setting, int value);
template<class Action> template<class Action>
@@ -58,10 +58,6 @@ class Setting
} }
private: 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 getByName(Wt::Dbo::Session& session, std::string name);
static pointer create(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); 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/>. * 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 "utils/Logger.hpp"
#include "SqlQuery.hpp" #include "SqlQuery.hpp"
@@ -75,7 +71,7 @@ Track::getByMBID(Wt::Dbo::Session& session, const std::string& mbid)
Track::pointer Track::pointer
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p) 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> std::vector<boost::filesystem::path>
@@ -111,7 +107,7 @@ Track::getClusters(void) const
static static
Wt::Dbo::Query< Track::pointer > Wt::Dbo::Query< Track::pointer >
getQuery(Wt::Dbo::Session& session, getQuery(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds, const std::set<Cluster::id_type>& clusterIds,
const std::vector<std::string> keywords) const std::vector<std::string> keywords)
{ {
WhereClause where; WhereClause where;
@@ -149,20 +145,6 @@ getQuery(Wt::Dbo::Session& session,
return query; 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> std::vector<Track::pointer>
Track::getByFilter(Wt::Dbo::Session& session, Track::getByFilter(Wt::Dbo::Session& session,
const std::set<id_type>& clusterIds, const std::set<id_type>& clusterIds,
@@ -233,7 +215,7 @@ Cluster::Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name)
Cluster::pointer Cluster::pointer
Cluster::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<ClusterType> type, std::string name) 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> std::vector<Cluster::pointer>
@@ -244,28 +226,6 @@ Cluster::getAll(Wt::Dbo::Session& session)
return std::vector<Cluster::pointer>(res.begin(), res.end()); 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) ClusterType::ClusterType(std::string name)
: _name(name) : _name(name)
{ {
@@ -297,7 +257,7 @@ ClusterType::getAll(Wt::Dbo::Session& session)
ClusterType::pointer ClusterType::pointer
ClusterType::create(Wt::Dbo::Session& session, std::string name) 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 Cluster::pointer
+19 -31
View File
@@ -21,17 +21,15 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <chrono>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits> #include <Wt/Dbo/WtSqlTraits.h>
#include <Wt/WDateTime> #include <Wt/WDateTime.h>
#include "SearchFilter.hpp"
namespace Database { namespace Database {
@@ -52,7 +50,6 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name); Cluster(Wt::Dbo::ptr<ClusterType> type, std::string name);
// Find utility // 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); static std::vector<pointer> getAll(Wt::Dbo::Session& session);
// Create utility // Create utility
@@ -75,7 +72,6 @@ class Cluster : public Wt::Dbo::Dbo<Cluster>
} }
private: private:
static Wt::Dbo::Query<pointer> getQuery(Wt::Dbo::Session& session, SearchFilter filter);
static const std::size_t _maxNameLength = 128; 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> getMBIDDuplicates(Wt::Dbo::Session& session);
static std::vector<pointer> getChecksumDuplicates(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 // Create utility
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p); 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 setDiscNumber(int num) { _discNumber = num; }
void setTotalDiscNumber(int num) { _totalDiscNumber = num; } void setTotalDiscNumber(int num) { _totalDiscNumber = num; }
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); } void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
void setDuration(boost::posix_time::time_duration duration) { _duration = duration; } void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
void setLastWriteTime(boost::posix_time::ptime time) { _fileLastWrite = time; } void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
void setAddedTime(boost::posix_time::ptime time) { _fileAdded = time; } void setAddedTime(Wt::WDateTime time) { _fileAdded = time; }
void setChecksum(const std::vector<unsigned char>& checksum) { _fileChecksum = checksum; } void setChecksum(const std::vector<unsigned char>& checksum) { _fileChecksum = checksum; }
void setDate(const boost::posix_time::ptime& date) { _date = date; } void setDate(Wt::WDate date) { _date = date; }
void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; } void setOriginalDate(Wt::WDate date) { _originalDate = date; }
void setGenres(const std::string& genreList) { _genreList = genreList; } void setGenres(const std::string& genreList) { _genreList = genreList; }
void setCoverType(CoverType coverType) { _coverType = coverType; } void setCoverType(CoverType coverType) { _coverType = coverType; }
void setMBID(const std::string& MBID) { _MBID = MBID; } void setMBID(const std::string& MBID) { _MBID = MBID; }
@@ -197,11 +185,11 @@ class Track
boost::optional<std::size_t> getTotalDiscNumber(void) const; boost::optional<std::size_t> getTotalDiscNumber(void) const;
std::string getName(void) const { return _name; } std::string getName(void) const { return _name; }
boost::filesystem::path getPath(void) const { return _filePath; } boost::filesystem::path getPath(void) const { return _filePath; }
boost::posix_time::time_duration getDuration(void) const { return _duration; } std::chrono::milliseconds getDuration(void) const { return _duration; }
boost::posix_time::ptime getDate(void) const { return _date; } Wt::WDate getDate(void) const { return _date; }
boost::posix_time::ptime getOriginalDate(void) const { return _originalDate; } Wt::WDate getOriginalDate(void) const { return _originalDate; }
boost::posix_time::ptime getLastWriteTime(void) const { return _fileLastWrite; } Wt::WDateTime getLastWriteTime(void) const { return _fileLastWrite; }
boost::posix_time::ptime getAddedTime(void) const { return _fileAdded; } Wt::WDateTime getAddedTime(void) const { return _fileAdded; }
const std::vector<unsigned char>& getChecksum(void) const { return _fileChecksum; } const std::vector<unsigned char>& getChecksum(void) const { return _fileChecksum; }
CoverType getCoverType(void) const { return _coverType; } CoverType getCoverType(void) const { return _coverType; }
const std::string& getMBID(void) const { return _MBID; } const std::string& getMBID(void) const { return _MBID; }
@@ -244,14 +232,14 @@ class Track
std::string _name; std::string _name;
std::string _artistName; std::string _artistName;
std::string _releaseName; std::string _releaseName;
boost::posix_time::time_duration _duration; std::chrono::duration<int, std::milli> _duration;
boost::posix_time::ptime _date; Wt::WDate _date;
boost::posix_time::ptime _originalDate; // original date time Wt::WDate _originalDate; // original date time
std::string _genreList; std::string _genreList;
std::string _filePath; std::string _filePath;
std::vector<unsigned char> _fileChecksum; std::vector<unsigned char> _fileChecksum;
boost::posix_time::ptime _fileLastWrite; Wt::WDateTime _fileLastWrite;
boost::posix_time::ptime _fileAdded; Wt::WDateTime _fileAdded;
CoverType _coverType; CoverType _coverType;
std::string _MBID; // Musicbrainz Identifier std::string _MBID; // Musicbrainz Identifier
+1 -1
View File
@@ -51,7 +51,7 @@ User::getAll(Wt::Dbo::Session& session)
User::pointer User::pointer
User::create(Wt::Dbo::Session& session) User::create(Wt::Dbo::Session& session)
{ {
return session.add(new User()); return session.add(std::make_unique<User>());
} }
User::pointer User::pointer
+2 -2
View File
@@ -22,8 +22,8 @@
#include <vector> #include <vector>
#include <Wt/Dbo/Dbo> #include <Wt/Dbo/Dbo.h>
#include <Wt/Auth/Dbo/AuthInfo> #include <Wt/Auth/Dbo/AuthInfo.h>
namespace Database { 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) 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; return false;
} }
} }
+7 -8
View File
@@ -20,14 +20,14 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/property_tree/xml_parser.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/Config.hpp"
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
#include "av/AvInfo.hpp" #include "av/AvInfo.hpp"
#include "av/AvTranscoder.hpp" #include "av/AvTranscoder.hpp"
#include "image/Image.hpp" #include "image/Image.hpp"
#include "feature/FeatureExtractor.hpp"
#include "scanner/MediaScanner.hpp" #include "scanner/MediaScanner.hpp"
@@ -117,17 +117,16 @@ int main(int argc, char* argv[])
Av::AvInit(); Av::AvInit();
Av::Transcoder::init(); Av::Transcoder::init();
Database::Handler::configureAuth(); Database::Handler::configureAuth();
Feature::Extractor::init();
// Initializing a connection pool to the database that will be shared along services // Initializing a connection pool to the database that will be shared along services
std::unique_ptr<Wt::Dbo::SqlConnectionPool> auto connectionPool = Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db");
connectionPool( Database::Handler::createConnectionPool(Config::instance().getPath("working-dir") / "lms.db"));
Scanner::MediaScanner scanner(*connectionPool); Scanner::MediaScanner scanner(*connectionPool);
// bind entry point // bind entry point
server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, server.addEntryPoint(Wt::EntryPointType::Application,
_1, boost::ref(*connectionPool), boost::ref(scanner))); std::bind(UserInterface::LmsApplication::create,
std::placeholders::_1, std::ref(*connectionPool), std::ref(scanner)));
// Start // Start
LMS_LOG(MAIN, INFO) << "Starting Media scanner..."; LMS_LOG(MAIN, INFO) << "Starting Media scanner...";
@@ -138,7 +137,7 @@ int main(int argc, char* argv[])
// Wait // Wait
LMS_LOG(MAIN, INFO) << "Now running..."; LMS_LOG(MAIN, INFO) << "Now running...";
Wt::WServer::waitForShutdown(argv[0]); Wt::WServer::waitForShutdown();
// Stop // Stop
LMS_LOG(MAIN, INFO) << "Stopping server..."; 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; std::cout << "TAG = " << tag << ", VAL = " << value << std::endl;
#endif #endif
if (tag == "ARTIST") 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") 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") 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") else if (tag == "TRACK")
{ {
// Expecting 'Number/Total' // Expecting 'Number/Total'
@@ -89,15 +89,15 @@ AvFormat::parse(const boost::filesystem::path& p)
if (strings.size() > 0) if (strings.size() > 0)
{ {
std::size_t number; auto number = readAs<std::size_t>(strings[0]);
if (readAs<std::size_t>(strings[0], number)) if (number)
items.insert( std::make_pair(MetaData::Type::TrackNumber, number )); items.insert( std::make_pair(MetaData::Type::TrackNumber, *number ));
if (strings.size() > 1) if (strings.size() > 1)
{ {
std::size_t totalNumber; auto totalNumber = readAs<std::size_t>(strings[1]);
if (readAs<std::size_t>(strings[1], totalNumber)) if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalTrack, 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) if (strings.size() > 0)
{ {
std::size_t number; auto number = readAs<std::size_t>(strings[0]);
if (readAs<std::size_t>(strings[0], number)) if (number)
items.insert( std::make_pair(MetaData::Type::DiscNumber, number )); items.insert( std::make_pair(MetaData::Type::DiscNumber, *number ));
if (strings.size() > 1) if (strings.size() > 1)
{ {
std::size_t totalNumber; auto totalNumber = readAs<std::size_t>(strings[1]);
if (readAs<std::size_t>(strings[1], totalNumber)) if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalDisc, totalNumber )); items.insert( std::make_pair(MetaData::Type::TotalDisc, *totalNumber ));
} }
} }
} }
@@ -124,36 +124,36 @@ AvFormat::parse(const boost::filesystem::path& p)
|| tag == "YEAR" || tag == "YEAR"
|| tag == "WM/Year") || tag == "WM/Year")
{ {
boost::posix_time::ptime p; auto date = readAs<Wt::WDate>(value);
if (readAsPosixTime(value, p)) if (date)
items.insert( std::make_pair(MetaData::Type::Date, p)); items.insert( std::make_pair(MetaData::Type::Date, *date));
} }
else if (tag == "TDOR" // Original release time (ID3v2 2.4) else if (tag == "TDOR" // Original release time (ID3v2 2.4)
|| tag == "TORY") // Original release year || tag == "TORY") // Original release year
{ {
boost::posix_time::ptime p; auto date = readAs<Wt::WDate>(value);
if (readAsPosixTime(value, p)) if (date)
items.insert( std::make_pair(MetaData::Type::OriginalDate, p)); items.insert( std::make_pair(MetaData::Type::OriginalDate, *date));
} }
else if (tag == "MUSICBRAINZ ARTIST ID" else if (tag == "MUSICBRAINZ ARTIST ID"
|| tag == "MUSICBRAINZ_ARTISTID") || 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" else if (tag == "MUSICBRAINZ ALBUM ID"
|| tag == "MUSICBRAINZ_ALBUMID") || 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" else if (tag == "MUSICBRAINZ RELEASE TRACK ID"
|| tag == "MUSICBRAINZ_RELEASETRACKID" || tag == "MUSICBRAINZ_RELEASETRACKID"
|| tag == "MUSICBRAINZ_TRACKID") || 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") 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()) else if (_clusterMap.find(tag) != _clusterMap.end())
{ {
+3 -3
View File
@@ -37,13 +37,13 @@ namespace MetaData
Title, // string Title, // string
Album, // string Album, // string
Clusters, // Clusters, ex: { "genre", {"death metal", "brutal death"} }, { "albumgrouping", {"metal"} } Clusters, // Clusters, ex: { "genre", {"death metal", "brutal death"} }, { "albumgrouping", {"metal"} }
Duration, // boost::posix_time::time_duration Duration, // std::chrono::milliseconds
TrackNumber, // size_t TrackNumber, // size_t
DiscNumber, // size_t DiscNumber, // size_t
TotalTrack, // size_t TotalTrack, // size_t
TotalDisc, // size_t TotalDisc, // size_t
Date, // boost::posix_time::ptime Date, // Wt::WDate
OriginalDate, // boost::posix_time::ptime OriginalDate, // Wt::WDate
HasCover, // bool HasCover, // bool
AudioStreams, // vector<AudioStream> AudioStreams, // vector<AudioStream>
MusicBrainzArtistID, // string MusicBrainzArtistID, // string
+22 -23
View File
@@ -55,8 +55,7 @@ TagLibParser::parse(const boost::filesystem::path& p)
{ {
TagLib::AudioProperties *properties = f.audioProperties(); 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) ); items.insert( std::make_pair(MetaData::Type::Duration, duration) );
MetaData::AudioStream audioStream = { .bitRate = static_cast<std::size_t>(properties->bitrate() * 1000) }; 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()) if (!strings.empty())
{ {
std::size_t number; auto number = readAs<std::size_t>(strings[0]);
if (readAs<std::size_t>(strings[0], number)) if (number)
items.insert( std::make_pair(MetaData::Type::TrackNumber, number )); items.insert( std::make_pair(MetaData::Type::TrackNumber, *number ));
if (strings.size() > 1) if (strings.size() > 1)
{ {
std::size_t totalNumber; auto totalNumber = readAs<std::size_t>(strings[1]);
if (readAs<std::size_t>(strings[1], totalNumber)) if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalTrack, totalNumber )); items.insert( std::make_pair(MetaData::Type::TotalTrack, *totalNumber ));
} }
} }
} }
@@ -143,32 +142,32 @@ TagLibParser::parse(const boost::filesystem::path& p)
if (!strings.empty()) if (!strings.empty())
{ {
std::size_t number; auto number = readAs<std::size_t>(strings[0]);
if (readAs<std::size_t>(strings[0], number)) if (number)
items.insert( std::make_pair(MetaData::Type::DiscNumber, number )); items.insert( std::make_pair(MetaData::Type::DiscNumber, *number));
if (strings.size() > 1) if (strings.size() > 1)
{ {
std::size_t totalNumber; auto totalNumber = readAs<std::size_t>(strings[1]);
if (readAs<std::size_t>(strings[1], totalNumber)) if (totalNumber)
items.insert( std::make_pair(MetaData::Type::TotalDisc, totalNumber )); items.insert( std::make_pair(MetaData::Type::TotalDisc, *totalNumber ));
} }
} }
} }
else if (tag == "DATE") else if (tag == "DATE")
{ {
boost::posix_time::ptime p; auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (readAsPosixTime(values.front().to8Bit(), p)) if (timePoint)
items.insert( std::make_pair(MetaData::Type::Date, p)); items.insert( std::make_pair(MetaData::Type::Date, *timePoint));
} }
else if (tag == "ORIGINALDATE") else if (tag == "ORIGINALDATE")
{ {
boost::posix_time::ptime p; auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (readAsPosixTime(values.front().to8Bit(), p)) if (timePoint)
{ {
// Take priority on original year // Take priority on original year
items.erase( MetaData::Type::OriginalDate ); 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") else if (tag == "ORIGINALYEAR")
@@ -176,9 +175,9 @@ TagLibParser::parse(const boost::filesystem::path& p)
// lower priority than original date // lower priority than original date
if (items.find(MetaData::Type::OriginalDate) == items.end()) if (items.find(MetaData::Type::OriginalDate) == items.end())
{ {
boost::posix_time::ptime p; auto timePoint = readAs<Wt::WDate>(values.front().to8Bit());
if (readAsPosixTime(values.front().to8Bit(), p)) if (timePoint)
items.insert( std::make_pair(MetaData::Type::OriginalDate, p)); items.insert( std::make_pair(MetaData::Type::OriginalDate, *timePoint));
} }
} }
else if (tag == "METADATA_BLOCK_PICTURE") else if (tag == "METADATA_BLOCK_PICTURE")
+61 -76
View File
@@ -20,9 +20,10 @@
#include <stdexcept> #include <stdexcept>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <boost/asio/placeholders.hpp> #include <boost/asio/placeholders.hpp>
#include <Wt/WLocalDateTime.h>
#include "cover/CoverArtGrabber.hpp" #include "cover/CoverArtGrabber.hpp"
#include "database/Setting.hpp" #include "database/Setting.hpp"
@@ -36,40 +37,28 @@
namespace { namespace {
boost::gregorian::date Wt::WDate
getNextDay(const boost::gregorian::date& current) getNextMonday(Wt::WDate current)
{ {
boost::gregorian::day_iterator it(current); do
return *(++it); {
current.addDays(1);
} while (current.dayOfWeek() != 1);
return current;
} }
boost::gregorian::date Wt::WDate
getNextMonday(const boost::gregorian::date& current) getNextFirstOfMonth(Wt::WDate current)
{ {
boost::gregorian::day_iterator it(current); do
{
current.addDays(1);
} while (current.day() != 1);
++it; return current;
// While it's not monday
while( it->day_of_week() != 1 )
++it;
return *(it);
} }
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 bool
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions) 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)); 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) MediaScanner::MediaScanner(Wt::Dbo::SqlConnectionPool& connectionPool)
@@ -176,7 +165,7 @@ MediaScanner::scheduleImmediateScan()
_ioService.post([=]() _ioService.post([=]()
{ {
LMS_LOG(DBUPDATER, INFO) << "Schedule immediate scan"; 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 void
MediaScanner::scheduleScan() MediaScanner::scheduleScan()
{ {
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); using namespace std::chrono_literals;
boost::posix_time::time_duration startTime = getUpdateStartTime(_db.getSession());
boost::gregorian::date nextScanDate; Wt::WTime startTime = getUpdateStartTime(_db.getSession());
Wt::WDateTime now = Wt::WLocalDateTime::currentServerDateTime().toUTC();
Wt::WDate nextScanDate;
switch ( getUpdatePeriod(_db.getSession()) ) switch ( getUpdatePeriod(_db.getSession()) )
{ {
case UpdatePeriod::Daily: case UpdatePeriod::Daily:
if (now.time_of_day() < startTime) if (now.time() < startTime)
nextScanDate = now.date(); nextScanDate = now.date();
else else
nextScanDate = getNextDay(now.date()); nextScanDate = now.date().addDays(1);
break; break;
case UpdatePeriod::Weekly: 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(); nextScanDate = now.date();
else else
nextScanDate = getNextMonday(now.date()); nextScanDate = getNextMonday(now.date());
break; break;
case UpdatePeriod::Monthly: case UpdatePeriod::Monthly:
if (now.time_of_day() < startTime && now.date().day() == 1) if (now.time() < startTime && now.date().day() == 1)
nextScanDate = now.date(); nextScanDate = now.date();
else else
nextScanDate = getNextFirstOfMonth(now.date()); nextScanDate = getNextFirstOfMonth(now.date());
@@ -226,24 +217,25 @@ MediaScanner::scheduleScan()
break; break;
} }
if (!nextScanDate.is_special()) if (nextScanDate.isValid())
scheduleScan( boost::posix_time::ptime (nextScanDate, startTime) ); scheduleScan( Wt::WDateTime(nextScanDate, startTime).toTimePoint() );
} }
void void
MediaScanner::scheduleScan( boost::posix_time::time_duration duration) MediaScanner::scheduleScan(std::chrono::seconds duration)
{ {
LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan in " << duration; LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan in " << duration.count() << " seconds";
_scheduleTimer.expires_from_now(duration); _scheduleTimer.expires_from_now(std::chrono::seconds(5)); //duration);
_scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) ); _scheduleTimer.async_wait( std::bind( &MediaScanner::scan, this, std::placeholders::_1) );
} }
void 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; std::time_t t = std::chrono::system_clock::to_time_t(timePoint);
_scheduleTimer.expires_at(time); LMS_LOG(DBUPDATER, INFO) << "Scheduling next scan at " << std::string(std::ctime(&t));
_scheduleTimer.async_wait( boost::bind( &MediaScanner::scan, this, boost::asio::placeholders::error) ); _scheduleTimer.expires_at(timePoint);
_scheduleTimer.async_wait(std::bind(&MediaScanner::scan, this, std::placeholders::_1));
} }
void void
@@ -263,9 +255,9 @@ MediaScanner::scan(boost::system::error_code err)
if (!_running) if (!_running)
break; break;
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "'..."; LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "'...";
scanRootDirectory(rootDirectory, stats); scanRootDirectory(rootDirectory, stats);
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory << "' DONE"; LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "' DONE";
} }
if (_running) 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 << ")"; 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 // Save the last scan only if it has been completed
if (_running) if (_running)
{ {
Setting::setTime(_db.getSession(), "last_scan", now);
scheduleScan(); scheduleScan();
scanComplete().emit(stats); scanComplete().emit(stats);
@@ -408,7 +393,7 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames)
void void
MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats) 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 // 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() if ((*items).find(MetaData::Type::AudioStreams) == (*items).end()
|| boost::any_cast<std::vector<MetaData::AudioStream>> ((*items)[MetaData::Type::AudioStreams]).empty()) || 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 exists here, delete it!
if (track) if (track)
@@ -457,9 +442,9 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
return; return;
} }
if ((*items).find(MetaData::Type::Duration) == (*items).end() 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 exists here, delete it!
if (track) if (track)
@@ -534,12 +519,12 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
{ {
// Create a new song // Create a new song
track = Track::create(_db.getSession(), file); track = Track::create(_db.getSession(), file);
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file << "'"; LMS_LOG(DBUPDATER, INFO) << "Adding '" << file.string() << "'";
stats.additions++; stats.additions++;
} }
else else
{ {
LMS_LOG(DBUPDATER, INFO) << "Updating '" << file << "'"; LMS_LOG(DBUPDATER, INFO) << "Updating '" << file.string() << "'";
// Remove the songs from its clusters // Remove the songs from its clusters
for (auto cluster : track->getClusters()) for (auto cluster : track->getClusters())
@@ -555,8 +540,8 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
track.modify()->setRelease(release); track.modify()->setRelease(release);
track.modify()->setLastWriteTime(lastWriteTime); track.modify()->setLastWriteTime(lastWriteTime);
track.modify()->setName(title); track.modify()->setName(title);
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>((*items)[MetaData::Type::Duration]) ); track.modify()->setDuration( boost::any_cast<std::chrono::milliseconds>((*items)[MetaData::Type::Duration]) );
track.modify()->setAddedTime( boost::posix_time::second_clock::local_time() ); track.modify()->setAddedTime( Wt::WLocalDateTime::currentServerDateTime().toUTC() );
{ {
std::string trackClusterList; 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]) ); track.modify()->setTotalDiscNumber( boost::any_cast<std::size_t>((*items)[MetaData::Type::TotalDisc]) );
if ((*items).find(MetaData::Type::Date) != (*items).end()) 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()) 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 a file has an OriginalDate but no date, set the date to ease filtering
if ((*items).find(MetaData::Type::Date) == (*items).end()) 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()) 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 ) if (!boost::filesystem::exists( p )
|| !boost::filesystem::is_regular( p ) ) || !boost::filesystem::is_regular( p ) )
{ {
LMS_LOG(DBUPDATER, INFO) << "Missing file '" << p << "'"; LMS_LOG(DBUPDATER, INFO) << "Missing file '" << p.string() << "'";
status = false; status = false;
} }
else else
@@ -670,12 +655,12 @@ checkFile(const boost::filesystem::path& p, const std::vector<boost::filesystem:
if (!foundRoot) if (!foundRoot)
{ {
LMS_LOG(DBUPDATER, INFO) << "Out of root file '" << p << "'"; LMS_LOG(DBUPDATER, INFO) << "Out of root file '" << p.string() << "'";
status = false; status = false;
} }
else if (!isFileSupported(p, extensions)) 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; status = false;
} }
} }
@@ -685,7 +670,7 @@ checkFile(const boost::filesystem::path& p, const std::vector<boost::filesystem:
} }
catch (boost::filesystem::filesystem_error& e) 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; return false;
} }
@@ -779,14 +764,14 @@ MediaScanner::checkDuplicatedAudioFiles(Stats& stats)
std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db.getSession()); std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db.getSession());
for (Track::pointer track : tracks) 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++; stats.duplicateMBID++;
} }
tracks = Database::Track::getChecksumDuplicates(_db.getSession()); tracks = Database::Track::getChecksumDuplicates(_db.getSession());
for (Track::pointer track : tracks) 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++; stats.duplicateHashes++;
} }
+10 -8
View File
@@ -19,10 +19,12 @@
#pragma once #pragma once
#include <boost/asio/deadline_timer.hpp> #include <chrono>
#include <Wt/WIOService> #include <Wt/WIOService.h>
#include <Wt/WSignal> #include <Wt/WSignal.h>
#include <boost/asio/system_timer.hpp>
#include "metadata/TagLibParser.hpp" #include "metadata/TagLibParser.hpp"
@@ -40,8 +42,8 @@ enum class UpdatePeriod {
UpdatePeriod getUpdatePeriod(Wt::Dbo::Session& session); UpdatePeriod getUpdatePeriod(Wt::Dbo::Session& session);
void setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod); void setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod);
boost::posix_time::time_duration getUpdateStartTime(Wt::Dbo::Session& session); Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session);
void setUpdateStartTime(Wt::Dbo::Session& session, boost::posix_time::time_duration); void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime time);
class MediaScanner class MediaScanner
{ {
@@ -92,8 +94,8 @@ class MediaScanner
// Job handling // Job handling
void scheduleScan(); void scheduleScan();
void scheduleScan(boost::posix_time::time_duration duration); void scheduleScan(std::chrono::seconds duration);
void scheduleScan(boost::posix_time::ptime time); void scheduleScan(std::chrono::system_clock::time_point time);
// Update database (scheduled callback) // Update database (scheduled callback)
void scan(boost::system::error_code ec); void scan(boost::system::error_code ec);
@@ -119,7 +121,7 @@ class MediaScanner
Wt::Signal<Database::Track::pointer> _sigAddedTrack; Wt::Signal<Database::Track::pointer> _sigAddedTrack;
Wt::Signal<Database::Track::pointer> _sigRemovedTrack; Wt::Signal<Database::Track::pointer> _sigRemovedTrack;
boost::asio::deadline_timer _scheduleTimer; boost::asio::system_timer _scheduleTimer;
Database::Handler _db; Database::Handler _db;
+22 -25
View File
@@ -17,10 +17,10 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WFormModel> #include <Wt/WFormModel.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WCheckBox> #include <Wt/WCheckBox.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -31,10 +31,10 @@
namespace UserInterface { namespace UserInterface {
Auth::Auth(Wt::WContainerWidget *parent) Auth::Auth()
: Wt::WTemplateFormView(parent) : 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()); _model->addPasswordAuth(&Database::Handler::getPasswordService());
@@ -43,50 +43,47 @@ Auth::Auth(Wt::WContainerWidget *parent)
addFunction("id", &WTemplate::Functions::id); addFunction("id", &WTemplate::Functions::id);
// LoginName // LoginName
auto loginName = new Wt::WLineEdit(); setFormWidget(Wt::Auth::AuthModel::LoginNameField, std::make_unique<Wt::WLineEdit>());
setFormWidget(Wt::Auth::AuthModel::LoginNameField, loginName);
// Password // Password
auto password = new Wt::WLineEdit(); auto password = std::make_unique<Wt::WLineEdit>();
setFormWidget(Wt::Auth::AuthModel::PasswordField, password); password->setEchoMode(Wt::EchoMode::Password);
password->setEchoMode(Wt::WLineEdit::Password); password->enterPressed().connect(this, &Auth::processAuth);
setFormWidget(Wt::Auth::AuthModel::PasswordField, std::move(password));
// Remember Me // Remember Me
auto rememberMe = new Wt::WCheckBox(); setFormWidget(Wt::Auth::AuthModel::RememberMeField, std::make_unique<Wt::WCheckBox>());
setFormWidget(Wt::Auth::AuthModel::RememberMeField, rememberMe);
auto loginBtn = new Wt::WPushButton(Wt::WString::tr("Lms.login")); Wt::WPushButton* loginBtn = bindNew<Wt::WPushButton>("login-btn", Wt::WString::tr("Lms.login"));
bindWidget("login-btn", loginBtn);
loginBtn->clicked().connect(this, &Auth::processAuth); 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); this->setHidden(true);
})); }));
Wt::Auth::User user = _model->processAuthToken(); 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 void
Auth::processAuth() Auth::processAuth()
{ {
updateModel(_model); updateModel(_model.get());
if (_model->validate()) if (_model->validate())
_model->login(DbHandler().getLogin()); _model->login(LmsApp->getDb().getLogin());
else else
updateView(_model); updateView(_model.get());
} }
void void
Auth::logout() Auth::logout()
{ {
_model->logout(DbHandler().getLogin()); _model->logout(LmsApp->getDb().getLogin());
} }
} // namespace UserInterface } // namespace UserInterface
+4 -6
View File
@@ -19,24 +19,22 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WTemplateFormView.h>
#include <Wt/WTemplateFormView> #include <Wt/Auth/AuthModel.h>
#include <Wt/Auth/Login>
#include <Wt/Auth/AuthModel>
namespace UserInterface { namespace UserInterface {
class Auth : public Wt::WTemplateFormView class Auth : public Wt::WTemplateFormView
{ {
public: public:
Auth(Wt::WContainerWidget *parent = 0); Auth();
void logout(); void logout();
private: private:
void processAuth(); 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -28,12 +28,13 @@
namespace UserInterface { namespace UserInterface {
Home::Home(Wt::WContainerWidget *parent) Home::Home()
: Wt::WContainerWidget(parent) : 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); t->addFunction("tr", &Wt::WTemplate::Functions::tr);
this->addWidget(std::move(t));
} }
} // namespace UserInterface } // namespace UserInterface
+2 -2
View File
@@ -19,14 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
namespace UserInterface { namespace UserInterface {
class Home : public Wt::WContainerWidget class Home : public Wt::WContainerWidget
{ {
public: public:
Home(Wt::WContainerWidget *parent = 0); Home();
private: private:
+60 -110
View File
@@ -17,15 +17,16 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WBootstrapTheme> #include <Wt/WBootstrapTheme.h>
#include <Wt/WEnvironment> #include <Wt/WEnvironment.h>
#include <Wt/WMenu> #include <Wt/WMenu.h>
#include <Wt/WNavigationBar> #include <Wt/WNavigationBar.h>
#include <Wt/WPopupMenu> #include <Wt/WPopupMenu.h>
#include <Wt/WStackedWidget> #include <Wt/WServer.h>
#include <Wt/WText> #include <Wt/WStackedWidget.h>
#include <Wt/Auth/Identity> #include <Wt/WText.h>
#include <Wt/Auth/Identity.h>
#include "config/config.h" #include "config/config.h"
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -49,14 +50,14 @@
namespace UserInterface { namespace UserInterface {
Wt::WApplication* std::unique_ptr<Wt::WApplication>
LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner) LmsApplication::create(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner)
{ {
/* /*
* You could read information from the environment to decide whether * You could read information from the environment to decide whether
* the user has permission to start a new application * the user has permission to start a new application
*/ */
return new LmsApplication(env, connectionPool, scanner); return std::make_unique<LmsApplication>(env, connectionPool, scanner);
} }
LmsApplication* LmsApplication*
@@ -78,10 +79,10 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
_imageResource(nullptr), _imageResource(nullptr),
_transcodeResource(nullptr) _transcodeResource(nullptr)
{ {
Wt::WBootstrapTheme *bootstrapTheme = new Wt::WBootstrapTheme(this); auto bootstrapTheme = std::make_unique<Wt::WBootstrapTheme>();
bootstrapTheme->setVersion(Wt::WBootstrapTheme::Version3); bootstrapTheme->setVersion(Wt::BootstrapVersion::v3);
bootstrapTheme->setResponsive(true); bootstrapTheme->setResponsive(true);
setTheme(bootstrapTheme); setTheme(std::move(bootstrapTheme));
useStyleSheet("css/lms.css"); useStyleSheet("css/lms.css");
useStyleSheet("resources/font-awesome/css/font-awesome.min.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-database");
messageResourceBundle().use(appRoot() + "admin-user"); messageResourceBundle().use(appRoot() + "admin-user");
messageResourceBundle().use(appRoot() + "admin-users"); messageResourceBundle().use(appRoot() + "admin-users");
messageResourceBundle().use(appRoot() + "admin-wizard"); messageResourceBundle().use(appRoot() + "admin-initwizard");
messageResourceBundle().use(appRoot() + "artist"); messageResourceBundle().use(appRoot() + "artist");
messageResourceBundle().use(appRoot() + "artists"); messageResourceBundle().use(appRoot() + "artists");
messageResourceBundle().use(appRoot() + "home"); 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 // If here is no account in the database, launch the first connection wizard
bool firstConnection; 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; LMS_LOG(UI, DEBUG) << "Creating root widget. First connection = " << std::boolalpha << firstConnection;
if (firstConnection) if (firstConnection)
{ {
root()->addWidget(new InitWizardView()); root()->addWidget(std::make_unique<InitWizardView>());
} }
else else
{ {
DbHandler().getLogin().changed().connect(std::bind([=] _auth = root()->addNew<Auth>();
LmsApp->getDb().getLogin().changed().connect(std::bind([=]
{ {
try try
{ {
@@ -138,68 +141,31 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
throw std::runtime_error("Internal error"); throw std::runtime_error("Internal error");
} }
})); }));
_auth = new Auth();
root()->addWidget(_auth);
} }
} }
Database::Handler& DbHandler() std::unique_ptr<Wt::WAnchor>
{
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*
LmsApplication::createArtistAnchor(Database::Artist::pointer artist, bool addText) 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) if (addText)
{ {
res->setTextFormat(Wt::PlainText); res->setTextFormat(Wt::TextFormat::Plain);
res->setText(Wt::WString::fromUTF8(artist->getName())); res->setText(Wt::WString::fromUTF8(artist->getName()));
} }
return res; return res;
} }
Wt::WAnchor* std::unique_ptr<Wt::WAnchor>
LmsApplication::createReleaseAnchor(Database::Release::pointer release, bool addText) 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) if (addText)
{ {
res->setTextFormat(Wt::PlainText); res->setTextFormat(Wt::TextFormat::Plain);
res->setText(Wt::WString::fromUTF8(release->getName())); res->setText(Wt::WString::fromUTF8(release->getName()));
} }
@@ -274,7 +240,7 @@ handlePathChange(Wt::WStackedWidget* stack, bool isAdmin)
void void
LmsApplication::handleAuthEvent(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(); LMS_LOG(UI, INFO) << "User logged out, session = " << Wt::WApplication::instance()->sessionId();
@@ -282,73 +248,69 @@ LmsApplication::handleAuthEvent(void)
return; 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()); Wt::Dbo::Transaction transaction (LmsApp->getDboSession());
_isAdmin = CurrentUser()->isAdmin(); _isAdmin = LmsApp->getCurrentUser()->isAdmin();
} }
_imageResource = new ImageResource(_db, root()); _imageResource = std::make_shared<ImageResource>(_db);
_transcodeResource = new TranscodeResource(_db, root()); _transcodeResource = std::make_shared<TranscodeResource>(_db);
setConfirmCloseMessage(Wt::WString::tr("Lms.quit-confirm")); 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 // Navbar
auto navbar = new Wt::WNavigationBar(); Wt::WNavigationBar* navbar = main->bindNew<Wt::WNavigationBar>("navbar-top");
navbar->setTitle("LMS", Wt::WLink(Wt::WLink::InternalPath, "/home")); navbar->setTitle("LMS", Wt::WLink(Wt::LinkType::InternalPath, "/home"));
navbar->setResponsive(true); navbar->setResponsive(true);
main->bindWidget("navbar-top", navbar); Wt::WMenu* menu = navbar->addMenu(std::make_unique<Wt::WMenu>());
auto menu = new Wt::WMenu();
{ {
auto menuItem = menu->insertItem(0, Wt::WString::tr("Lms.Explore.artists")); 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); menuItem->setSelectable(false);
} }
{ {
auto menuItem = menu->insertItem(1, Wt::WString::tr("Lms.Explore.releases")); 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); menuItem->setSelectable(false);
} }
{ {
auto menuItem = menu->insertItem(2, Wt::WString::tr("Lms.Explore.tracks")); 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); menuItem->setSelectable(false);
} }
{ {
auto menuItem = menu->insertItem(3, Wt::WString::tr("Lms.PlayQueue.playqueue")); 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); 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; std::size_t itemCounter = 0;
if (_isAdmin) if (_isAdmin)
{ {
auto menuItem = rightMenu->insertItem(itemCounter++, Wt::WString::tr("Lms.administration")); auto menuItem = rightMenu->insertItem(itemCounter++, Wt::WString::tr("Lms.administration"));
menuItem->setSelectable(false); 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")); 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); dbSettings->setSelectable(false);
auto usersSettings = admin->insertItem(1, Wt::WString::tr("Lms.Admin.Users.users")); 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); usersSettings->setSelectable(false);
menuItem->setMenu(admin); menuItem->setMenu(std::move(admin));
} }
{ {
auto menuItem = rightMenu->insertItem(itemCounter++, Wt::WString::tr("Lms.Settings.settings")); 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); menuItem->setSelectable(false);
} }
{ {
@@ -360,34 +322,22 @@ LmsApplication::handleAuthEvent(void)
_auth->logout(); _auth->logout();
})); }));
} }
navbar->addMenu(rightMenu, Wt::AlignRight);
// Contents // Contents
Wt::WStackedWidget* mainStack = new Wt::WStackedWidget(); // Order is important in mainStack, see IdxRoot!
main->bindWidget("contents", mainStack); Wt::WStackedWidget* mainStack = main->bindNew<Wt::WStackedWidget>("contents");
mainStack->addWidget(new Home()); mainStack->addNew<Home>();
Explore* explore = mainStack->addNew<Explore>();
auto explore = new Explore(); PlayQueue* playqueue = mainStack->addNew<PlayQueue>();
mainStack->addWidget(explore); mainStack->addNew<SettingsView>();
auto playqueue = new PlayQueue();
mainStack->addWidget(playqueue);
auto settings = new SettingsView();
mainStack->addWidget(settings);
// Admin stuff // Admin stuff
if (_isAdmin) if (_isAdmin)
{ {
auto databaseSettings = new DatabaseSettingsView(); mainStack->addNew<DatabaseSettingsView>();
mainStack->addWidget(databaseSettings); mainStack->addNew<UsersView>();
mainStack->addNew<UserView>();
auto users = new UsersView();
mainStack->addWidget(users);
auto user = new UserView();
mainStack->addWidget(user);
} }
explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks) explore->tracksAdd.connect(std::bind([=] (std::vector<Database::Track::pointer> tracks)
@@ -400,9 +350,9 @@ LmsApplication::handleAuthEvent(void)
playqueue->playTracks(tracks); playqueue->playTracks(tracks);
}, std::placeholders::_1)); }, std::placeholders::_1));
// MediaPlayer // MediaPlayer
auto player = new MediaPlayer(); MediaPlayer* player = main->bindNew<MediaPlayer>("player");
main->bindWidget("player", player);
// Events from MediaPlayer // Events from MediaPlayer
player->playNext.connect(std::bind([=] player->playNext.connect(std::bind([=]
@@ -468,7 +418,7 @@ void
LmsApplication::notifyMsg(const Wt::WString& message) LmsApplication::notifyMsg(const Wt::WString& message)
{ {
LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "'"; LMS_LOG(UI, INFO) << "Notifying message '" << message.toUTF8() << "'";
root()->addWidget(new Wt::WText(message)); root()->addNew<Wt::WText>(message);
} }
} // namespace UserInterface } // namespace UserInterface
+18 -19
View File
@@ -20,8 +20,8 @@
#ifndef LMS_APPLICATION_HPP #ifndef LMS_APPLICATION_HPP
#define LMS_APPLICATION_HPP #define LMS_APPLICATION_HPP
#include <Wt/WApplication> #include <Wt/WApplication.h>
#include <Wt/Dbo/SqlConnectionPool> #include <Wt/Dbo/SqlConnectionPool.h>
#include "database/DatabaseHandler.hpp" #include "database/DatabaseHandler.hpp"
#include "scanner/MediaScanner.hpp" #include "scanner/MediaScanner.hpp"
@@ -36,13 +36,20 @@ class ImageResource;
class LmsApplication : public Wt::WApplication class LmsApplication : public Wt::WApplication
{ {
public: 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(); static LmsApplication* instance();
// Session application data // Session application data
ImageResource* getImageResource() { return _imageResource; } std::shared_ptr<ImageResource> getImageResource() { return _imageResource; }
TranscodeResource* getTranscodeResource() { return _transcodeResource; } std::shared_ptr<TranscodeResource> getTranscodeResource() { return _transcodeResource; }
Database::Handler& getDbHandler() { return _db;} 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; } Scanner::MediaScanner& getMediaScanner() { return _scanner; }
@@ -51,33 +58,25 @@ class LmsApplication : public Wt::WApplication
void goHomeAndQuit(); void goHomeAndQuit();
void notifyMsg(const Wt::WString& message); void notifyMsg(const Wt::WString& message);
static Wt::WAnchor* createArtistAnchor(Database::Artist::pointer artist, bool addText = true); static std::unique_ptr<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> createReleaseAnchor(Database::Release::pointer release, bool addText = true);
private: private:
LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnectionPool& connectionPool, Scanner::MediaScanner& scanner);
void handleAuthEvent(void); void handleAuthEvent(void);
void notify(const Wt::WEvent& event) override; void notify(const Wt::WEvent& event) override;
Database::Handler _db; Database::Handler _db;
Auth* _auth; Auth* _auth;
Scanner::MediaScanner& _scanner; Scanner::MediaScanner& _scanner;
ImageResource* _imageResource; std::shared_ptr<ImageResource> _imageResource;
TranscodeResource* _transcodeResource; std::shared_ptr<TranscodeResource> _transcodeResource;
bool _isAdmin = false; bool _isAdmin = false;
}; };
// Helpers to get session data // Helper to get session data
#define LmsApp LmsApplication::instance() #define LmsApp LmsApplication::instance()
Database::Handler& DbHandler();
Wt::Dbo::Session& DboSession();
const Wt::Auth::User& CurrentAuthUser();
Database::User::pointer CurrentUser();
} // namespace UserInterface } // namespace UserInterface
#endif #endif
+5 -5
View File
@@ -30,8 +30,8 @@
namespace UserInterface { namespace UserInterface {
MediaPlayer::MediaPlayer(Wt::WContainerWidget* parent) MediaPlayer::MediaPlayer()
: Wt::WTemplate(Wt::WString::tr("template-mediaplayer"), parent), : Wt::WTemplate(Wt::WString::tr("template-mediaplayer")),
playbackEnded(this, "playbackEnded"), playbackEnded(this, "playbackEnded"),
playPrevious(this, "playPrevious"), playPrevious(this, "playPrevious"),
playNext(this, "playNext") playNext(this, "playNext")
@@ -49,8 +49,8 @@ MediaPlayer::playTrack(Database::Track::id_type trackId)
{ {
LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId; LMS_LOG(UI, DEBUG) << "Playing track ID = " << trackId;
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto track = Database::Track::getById(DboSession(), trackId); auto track = Database::Track::getById(LmsApp->getDboSession(), trackId);
try try
{ {
@@ -66,7 +66,7 @@ MediaPlayer::playTrack(Database::Track::id_type trackId)
<< " release: " << (track->getRelease() ? "\"" + escape(track->getRelease()->getName()) + "\"" : "undefined" ) << "," << " release: " << (track->getRelease() ? "\"" + escape(track->getRelease()->getName()) + "\"" : "undefined" ) << ","
<< " artist: " << (track->getArtist() ? "\"" + escape(track->getArtist()->getName()) + "\"" : "undefined" ) << "," << " artist: " << (track->getArtist() ? "\"" + escape(track->getArtist()->getName()) + "\"" : "undefined" ) << ","
<< " resource: \"" << resource << "\"," << " resource: \"" << resource << "\","
<< " duration: " << track->getDuration().total_seconds() << "," << " duration: " << std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count() << ","
<< " imgResource: \"" << imgResource << "\"," << " imgResource: \"" << imgResource << "\","
<< "};"; << "};";
oss << "LMS.mediaplayer.loadTrack(params, true)"; // true to autoplay oss << "LMS.mediaplayer.loadTrack(params, true)"; // true to autoplay
+6 -6
View File
@@ -19,8 +19,8 @@
#pragma once #pragma once
#include <Wt/WJavaScript> #include <Wt/WJavaScript.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -29,15 +29,15 @@ namespace UserInterface {
class MediaPlayer : public Wt::WTemplate class MediaPlayer : public Wt::WTemplate
{ {
public: public:
MediaPlayer(Wt::WContainerWidget* parent = 0); MediaPlayer();
void stop(); void stop();
void playTrack(Database::Track::id_type); void playTrack(Database::Track::id_type);
// Signals // Signals
Wt::JSignal<void> playbackEnded; Wt::JSignal<> playbackEnded;
Wt::JSignal<void> playPrevious; Wt::JSignal<> playPrevious;
Wt::JSignal<void> playNext; Wt::JSignal<> playNext;
private: private:
+47 -58
View File
@@ -17,9 +17,9 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WImage> #include <Wt/WImage.h>
#include <Wt/WText> #include <Wt/WText.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -30,26 +30,39 @@ namespace UserInterface {
static const std::string currentPlayQueueName = "__current__playqueue__"; static const std::string currentPlayQueueName = "__current__playqueue__";
PlayQueue::PlayQueue(Wt::WContainerWidget* parent) PlayQueue::PlayQueue()
: Wt::WContainerWidget(parent) : Wt::WTemplate(Wt::WString::tr("Lms.PlayQueue.template"))
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.PlayQueue.template"), this); addFunction("tr", &Wt::WTemplate::Functions::tr);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
auto saveBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.save-to-playlist"), Wt::XHTMLText); bindNew<Wt::WText>("save-btn", Wt::WString::tr("Lms.PlayQueue.save-to-playlist"), Wt::TextFormat::XHTML);
container->bindWidget("save-btn", saveBtn); 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); _entriesContainer = bindNew<Wt::WContainerWidget>("entries");
container->bindWidget("load-btn", loadBtn);
_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([=] 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(); playlist.modify()->clear();
_showMore->setHidden(true); _showMore->setHidden(true);
} }
@@ -58,27 +71,6 @@ PlayQueue::PlayQueue(Wt::WContainerWidget* parent)
updateInfo(); 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([=] _showMore->clicked().connect(std::bind([=]
{ {
addSome(); addSome();
@@ -103,9 +95,9 @@ PlayQueue::play(std::size_t pos)
Database::Track::id_type trackId; 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 out of range, stop playing
if (pos >= playlist->getCount()) if (pos >= playlist->getCount())
@@ -149,9 +141,9 @@ PlayQueue::playNext()
void void
PlayQueue::updateInfo() 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())); _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"; 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) for (auto track : tracks)
Database::PlaylistEntry::create(DboSession(), track, playlist); Database::PlaylistEntry::create(LmsApp->getDboSession(), track, playlist);
updateInfo(); updateInfo();
addSome(); 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"; 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(); playqueue.modify()->clear();
_entriesContainer->clear(); _entriesContainer->clear();
@@ -209,9 +201,9 @@ PlayQueue::playTracks(const std::vector<Database::Track::pointer>& tracks)
void void
PlayQueue::addSome() 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; bool moreResults;
auto playlistEntries = playlist->getEntries(_entriesContainer->count(), 50, moreResults); auto playlistEntries = playlist->getEntries(_entriesContainer->count(), 50, moreResults);
@@ -220,27 +212,24 @@ PlayQueue::addSome()
auto playlistEntryId = playlistEntry.id(); auto playlistEntryId = playlistEntry.id();
auto track = playlistEntry->getTrack(); 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(); auto artist = track->getArtist();
if (artist) if (artist)
{ {
entry->setCondition("if-has-artist", true); entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist()); entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
entry->bindWidget("artist-name", artistAnchor);
} }
auto release = track->getRelease(); auto release = track->getRelease();
if (release) if (release)
{ {
entry->setCondition("if-has-release", true); entry->setCondition("if-has-release", true);
Wt::WAnchor *releaseAnchor = LmsApplication::createReleaseAnchor(track->getRelease()); entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(track->getRelease()));
entry->bindWidget("release-name", releaseAnchor);
} }
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.play"), Wt::XHTMLText); Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.PlayQueue.play"), Wt::TextFormat::XHTML);
entry->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
auto pos = _entriesContainer->indexOf(entry); auto pos = _entriesContainer->indexOf(entry);
@@ -248,15 +237,14 @@ PlayQueue::addSome()
play(pos); play(pos);
})); }));
auto delBtn = new Wt::WText(Wt::WString::tr("Lms.PlayQueue.delete"), Wt::XHTMLText); Wt::WText* delBtn = entry->bindNew<Wt::WText>("del-btn", Wt::WString::tr("Lms.PlayQueue.delete"), Wt::TextFormat::XHTML);
entry->bindWidget("del-btn", delBtn);
delBtn->clicked().connect(std::bind([=] delBtn->clicked().connect(std::bind([=]
{ {
// Remove the entry n both the widget tree and the playqueue // 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(); entryToRemove.remove();
} }
@@ -271,6 +259,7 @@ PlayQueue::addSome()
updateInfo(); updateInfo();
})); }));
} }
_showMore->setHidden(!moreResults); _showMore->setHidden(!moreResults);
+7 -7
View File
@@ -19,10 +19,10 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WSignal> #include <Wt/WSignal.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include <Wt/WText> #include <Wt/WText.h>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@@ -30,10 +30,10 @@
namespace UserInterface { namespace UserInterface {
class PlayQueue : public Wt::WContainerWidget class PlayQueue : public Wt::WTemplate
{ {
public: public:
PlayQueue(Wt::WContainerWidget* parent = 0); PlayQueue();
void addTracks(const std::vector<Database::Track::pointer>& tracks); void addTracks(const std::vector<Database::Track::pointer>& tracks);
void playTracks(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; Wt::Signal<Database::Track::id_type> playTrack;
// Signal emitted when play has to be stopped // Signal emitted when play has to be stopped
Wt::Signal<void> playbackStop; Wt::Signal<> playbackStop;
private: private:
void addSome(); void addSome();
+61 -68
View File
@@ -17,13 +17,13 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WString> #include <Wt/WString.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include <Wt/WComboBox> #include <Wt/WComboBox.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WFormModel> #include <Wt/WFormModel.h>
#include <Wt/WStringListModel> #include <Wt/WStringListModel.h>
#include "common/Validators.hpp" #include "common/Validators.hpp"
@@ -45,8 +45,8 @@ class SettingsModel : public Wt::WFormModel
static const Field PasswordField; static const Field PasswordField;
static const Field PasswordConfirmField; static const Field PasswordConfirmField;
SettingsModel(Wt::WObject *parent = 0) SettingsModel()
: Wt::WFormModel(parent) : Wt::WFormModel()
{ {
initializeModels(); initializeModels();
@@ -61,18 +61,18 @@ class SettingsModel : public Wt::WFormModel
loadData(); loadData();
} }
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; } std::shared_ptr<Wt::WAbstractItemModel> bitrateModel() { return _bitrateModel; }
Wt::WAbstractItemModel *encodingModel() { return _encodingModel; } std::shared_ptr<Wt::WAbstractItemModel> encodingModel() { return _encodingModel; }
void loadData() 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) if (bitrate)
setValue(BitrateField, bitrateString(*bitrate)); setValue(BitrateField, bitrateString(*bitrate));
auto encodingRow = getEncodingRow(CurrentUser()->getAudioEncoding()); auto encodingRow = getEncodingRow(LmsApp->getCurrentUser()->getAudioEncoding());
if (encodingRow) if (encodingRow)
setValue(EncodingField, encodingString(*encodingRow)); setValue(EncodingField, encodingString(*encodingRow));
@@ -80,18 +80,18 @@ class SettingsModel : public Wt::WFormModel
void saveData() void saveData()
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField))); auto bitrateRow = getBitrateRow(Wt::asString(value(BitrateField)));
assert(bitrateRow); assert(bitrateRow);
CurrentUser().modify()->setAudioBitrate(bitrate(*bitrateRow)); LmsApp->getCurrentUser().modify()->setAudioBitrate(bitrate(*bitrateRow));
auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField))); auto encodingRow = getEncodingRow(Wt::asString(value(EncodingField)));
CurrentUser().modify()->setAudioEncoding(encoding(*encodingRow)); LmsApp->getCurrentUser().modify()->setAudioEncoding(encoding(*encodingRow));
if (!valueText(PasswordField).empty()) 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 // Evaluate the strength of the password
auto res = Database::Handler::getPasswordService().strengthValidator()->evaluateStrength(valueText(PasswordField), 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()) if (!res.isValid())
error = res.message(); error = res.message();
@@ -115,7 +115,7 @@ class SettingsModel : public Wt::WFormModel
} }
else if (field == PasswordConfirmField) else if (field == PasswordConfirmField)
{ {
if (validation(PasswordField).state() == Wt::WValidator::Valid) if (validation(PasswordField).state() == Wt::ValidationState::Valid)
{ {
if (valueText(PasswordField) != valueText(PasswordConfirmField)) if (valueText(PasswordField) != valueText(PasswordConfirmField))
error = Wt::WString::tr("Lms.passwords-dont-match"); error = Wt::WString::tr("Lms.passwords-dont-match");
@@ -126,9 +126,9 @@ class SettingsModel : public Wt::WFormModel
return Wt::WFormModel::validateField(field); 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) boost::optional<int> getBitrateRow(Wt::WString value)
@@ -155,14 +155,14 @@ class SettingsModel : public Wt::WFormModel
std::size_t bitrate(int row) std::size_t bitrate(int row)
{ {
return boost::any_cast<std::size_t> return Wt::cpp17::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole)); (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
} }
Wt::WString bitrateString(int row) Wt::WString bitrateString(int row)
{ {
return boost::any_cast<Wt::WString> return Wt::cpp17::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole)); (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
} }
boost::optional<int> getEncodingRow(Wt::WString value) boost::optional<int> getEncodingRow(Wt::WString value)
@@ -189,14 +189,14 @@ class SettingsModel : public Wt::WFormModel
Database::AudioEncoding encoding(int row) Database::AudioEncoding encoding(int row)
{ {
return boost::any_cast<Database::AudioEncoding> return Wt::cpp17::any_cast<Database::AudioEncoding>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::UserRole)); (_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::User));
} }
Wt::WString encodingString(int row) Wt::WString encodingString(int row)
{ {
return boost::any_cast<Wt::WString> return Wt::cpp17::any_cast<Wt::WString>
(_encodingModel->data(_encodingModel->index(row, 0), Wt::DisplayRole)); (_encodingModel->data(_encodingModel->index(row, 0), Wt::ItemDataRole::Display));
} }
@@ -204,42 +204,42 @@ class SettingsModel : public Wt::WFormModel
void initializeModels() void initializeModels()
{ {
_bitrateModel = std::make_shared<Wt::WStringListModel>();
_bitrateModel = new Wt::WStringListModel(this);
std::size_t maxAudioBitrate; std::size_t maxAudioBitrate;
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
maxAudioBitrate = CurrentUser()->getMaxAudioBitrate(); maxAudioBitrate = LmsApp->getCurrentUser()->getMaxAudioBitrate();
} }
std::size_t id = 0; std::size_t id = 0;
for (auto bitrate : Database::User::audioBitrates) for (std::size_t bitrate : Database::User::audioBitrates)
{ {
if (bitrate > maxAudioBitrate) if (bitrate > maxAudioBitrate)
break; break;
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) ); _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->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->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->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->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; std::shared_ptr<Wt::WStringListModel> _bitrateModel;
Wt::WStringListModel* _encodingModel; 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::PasswordField = "password";
const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm"; const Wt::WFormModel::Field SettingsModel::PasswordConfirmField = "password-confirm";
SettingsView::SettingsView(Wt::WContainerWidget *parent) SettingsView::SettingsView()
: Wt::WTemplateFormView(parent) : Wt::WTemplateFormView(Wt::WString::tr("Lms.Settings.template"))
{ {
auto model = new SettingsModel(this); auto model = std::make_shared<SettingsModel>();
setTemplateText(tr("Lms.Settings.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
// Password // Password
Wt::WLineEdit *password = new Wt::WLineEdit(); auto password = std::make_unique<Wt::WLineEdit>();
setFormWidget(SettingsModel::PasswordField, password); password->setEchoMode(Wt::EchoMode::Password);
password->setEchoMode(Wt::WLineEdit::Password); setFormWidget(SettingsModel::PasswordField, std::move(password));
// Password confirm // Password confirm
Wt::WLineEdit *passwordConfirm = new Wt::WLineEdit(); auto passwordConfirm = std::make_unique<Wt::WLineEdit>();
setFormWidget(SettingsModel::PasswordConfirmField, passwordConfirm); passwordConfirm->setEchoMode(Wt::EchoMode::Password);
passwordConfirm->setEchoMode(Wt::WLineEdit::Password); setFormWidget(SettingsModel::PasswordConfirmField, std::move(passwordConfirm));
// Bitrate // Bitrate
Wt::WComboBox *bitrate = new Wt::WComboBox(); auto bitrate = std::make_unique<Wt::WComboBox>();
setFormWidget(SettingsModel::BitrateField, bitrate);
bitrate->setModel(model->bitrateModel()); bitrate->setModel(model->bitrateModel());
setFormWidget(SettingsModel::BitrateField, std::move(bitrate));
// Encoding // Encoding
Wt::WComboBox *encoding = new Wt::WComboBox(); auto encoding = std::make_unique<Wt::WComboBox>();
setFormWidget(SettingsModel::EncodingField, encoding);
encoding->setModel(model->encodingModel()); encoding->setModel(model->encodingModel());
setFormWidget(SettingsModel::EncodingField, std::move(encoding));
// Buttons // Buttons
Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("Lms.apply")); Wt::WPushButton *saveBtn = bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
bindWidget("apply-btn", saveBtn); Wt::WPushButton *discardBtn = bindWidget("discard-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.discard")));
Wt::WPushButton *discardBtn = new Wt::WPushButton(Wt::WString::tr("Lms.discard"));
bindWidget("discard-btn", discardBtn);
saveBtn->clicked().connect(std::bind([=] () saveBtn->clicked().connect(std::bind([=] ()
{ {
updateModel(model); updateModel(model.get());
if (model->validate()) if (model->validate())
{ {
@@ -295,17 +288,17 @@ SettingsView::SettingsView(Wt::WContainerWidget *parent)
} }
// Udate the view: Delete any validation message in the view, etc. // Udate the view: Delete any validation message in the view, etc.
updateView(model); updateView(model.get());
})); }));
discardBtn->clicked().connect(std::bind([=] () discardBtn->clicked().connect(std::bind([=] ()
{ {
model->loadData(); model->loadData();
model->validate(); model->validate();
updateView(model); updateView(model.get());
})); }));
updateView(model); updateView(model.get());
} }
} // namespace UserInterface } // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WTemplateFormView.h>
#include <Wt/WTemplateFormView>
namespace UserInterface { namespace UserInterface {
class SettingsView : public Wt::WTemplateFormView class SettingsView : public Wt::WTemplateFormView
{ {
public: public:
SettingsView(Wt::WContainerWidget *parent = 0); SettingsView();
}; };
} // namespace UserInterface } // namespace UserInterface
+60 -78
View File
@@ -17,13 +17,13 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WString> #include <Wt/WString.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include <Wt/WComboBox> #include <Wt/WComboBox.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WFormModel> #include <Wt/WFormModel.h>
#include <Wt/WStringListModel> #include <Wt/WStringListModel.h>
#include "common/Validators.hpp" #include "common/Validators.hpp"
#include "database/MediaDirectory.hpp" #include "database/MediaDirectory.hpp"
@@ -46,8 +46,8 @@ class DatabaseSettingsModel : public Wt::WFormModel
static const Field UpdatePeriodField; static const Field UpdatePeriodField;
static const Field UpdateStartTimeField; static const Field UpdateStartTimeField;
DatabaseSettingsModel(Wt::WObject *parent = 0) DatabaseSettingsModel()
: Wt::WFormModel(parent) : Wt::WFormModel()
{ {
initializeModels(); initializeModels();
@@ -55,7 +55,7 @@ class DatabaseSettingsModel : public Wt::WFormModel
addField(UpdatePeriodField); addField(UpdatePeriodField);
addField(UpdateStartTimeField); addField(UpdateStartTimeField);
DirectoryValidator* dirValidator = new DirectoryValidator(); auto dirValidator = std::make_shared<DirectoryValidator>();
dirValidator->setMandatory(true); dirValidator->setMandatory(true);
setValidator(MediaDirectoryField, dirValidator); setValidator(MediaDirectoryField, dirValidator);
@@ -66,42 +66,42 @@ class DatabaseSettingsModel : public Wt::WFormModel
loadData(); loadData();
} }
Wt::WAbstractItemModel *updatePeriodModel() { return _updatePeriodModel; } std::shared_ptr<Wt::WAbstractItemModel> updatePeriodModel() { return _updatePeriodModel; }
Wt::WAbstractItemModel *updateStartTimeModel() { return _updateStartTimeModel; } std::shared_ptr<Wt::WAbstractItemModel> updateStartTimeModel() { return _updateStartTimeModel; }
void loadData() void loadData()
{ {
using namespace Database; 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()) if (!mediaDirectories.empty())
setValue(MediaDirectoryField, mediaDirectories.front()->getPath().string()); setValue(MediaDirectoryField, mediaDirectories.front()->getPath().string());
auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(DboSession()) ); auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(LmsApp->getDboSession()) );
if (periodRow) if (periodRow)
setValue(UpdatePeriodField, updatePeriodString(*periodRow)); setValue(UpdatePeriodField, updatePeriodString(*periodRow));
auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(DboSession()) ); auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(LmsApp->getDboSession()) );
if (startTimeRow) if (startTimeRow)
setValue(UpdateStartTimeField, updateStartTimeString(*startTimeRow) ); setValue(UpdateStartTimeField, updateStartTimeString(*startTimeRow) );
} }
void saveData() void saveData()
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
MediaDirectory::eraseAll(DboSession()); MediaDirectory::eraseAll(LmsApp->getDboSession());
MediaDirectory::create(DboSession(), boost::any_cast<Wt::WString>(value(MediaDirectoryField)).toUTF8()); 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); 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); assert(startTimeRow);
Scanner::setUpdateStartTime(DboSession(), updateStartTime(*startTimeRow)); Scanner::setUpdateStartTime(LmsApp->getDboSession(), updateStartTime(*startTimeRow));
} }
boost::optional<int> getUpdatePeriodModelRow(Wt::WString value) boost::optional<int> getUpdatePeriodModelRow(Wt::WString value)
@@ -128,14 +128,14 @@ class DatabaseSettingsModel : public Wt::WFormModel
Scanner::UpdatePeriod updatePeriod(int row) Scanner::UpdatePeriod updatePeriod(int row)
{ {
return boost::any_cast<Scanner::UpdatePeriod> return Wt::cpp17::any_cast<Scanner::UpdatePeriod>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::UserRole)); (_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::ItemDataRole::User));
} }
Wt::WString updatePeriodString(int row) Wt::WString updatePeriodString(int row)
{ {
return boost::any_cast<Wt::WString> return Wt::cpp17::any_cast<Wt::WString>
(_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::DisplayRole)); (_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::ItemDataRole::Display));
} }
@@ -150,7 +150,7 @@ class DatabaseSettingsModel : public Wt::WFormModel
return boost::none; 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) for (int i = 0; i < _updateStartTimeModel->rowCount(); ++i)
{ {
@@ -161,16 +161,16 @@ class DatabaseSettingsModel : public Wt::WFormModel
return boost::none; 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> return Wt::cpp17::any_cast<Wt::WTime>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::UserRole)); (_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::ItemDataRole::User));
} }
Wt::WString updateStartTimeString(int row) Wt::WString updateStartTimeString(int row)
{ {
return boost::any_cast<Wt::WString> return Wt::cpp17::any_cast<Wt::WString>
(_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::DisplayRole)); (_updateStartTimeModel->data(_updateStartTimeModel->index(row, 0), Wt::ItemDataRole::Display));
} }
@@ -179,43 +179,35 @@ class DatabaseSettingsModel : public Wt::WFormModel
void initializeModels() void initializeModels()
{ {
_updatePeriodModel = new Wt::WStringListModel(this); _updatePeriodModel = std::make_shared<Wt::WStringListModel>();
_updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.never")); _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->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->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->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) 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"); _updateStartTimeModel->addString( time.toString() );
facet->time_duration_format("%H:%M"); _updateStartTimeModel->setData(i, 0, time, Wt::ItemDataRole::User);
std::ostringstream oss;
oss.imbue(std::locale(oss.getloc(), facet));
oss << dur;
_updateStartTimeModel->addString( oss.str() );
_updateStartTimeModel->setData(i, 0, dur, Wt::UserRole);
} }
} }
Wt::WStringListModel* _updatePeriodModel; std::shared_ptr<Wt::WStringListModel> _updatePeriodModel;
Wt::WStringListModel* _updateStartTimeModel; 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"; const Wt::WFormModel::Field DatabaseSettingsModel::UpdateStartTimeField = "update-start-time";
DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent) DatabaseSettingsView::DatabaseSettingsView()
: Wt::WTemplateFormView(parent) : Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.Database.template"))
{ {
auto model = new DatabaseSettingsModel(this); auto model = std::make_shared<DatabaseSettingsModel>();
setTemplateText(tr("Lms.Admin.Database.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
// Media Directory // Media Directory
Wt::WLineEdit *mediaDirectoryEdit = new Wt::WLineEdit(); setFormWidget(DatabaseSettingsModel::MediaDirectoryField, std::make_unique<Wt::WLineEdit>());
setFormWidget(DatabaseSettingsModel::MediaDirectoryField, mediaDirectoryEdit);
// Update Period // Update Period
Wt::WComboBox *updatePeriodCB = new Wt::WComboBox(); auto updatePeriod = std::make_unique<Wt::WComboBox>();
setFormWidget(DatabaseSettingsModel::UpdatePeriodField, updatePeriodCB); updatePeriod->setModel(model->updatePeriodModel());
updatePeriodCB->setModel(model->updatePeriodModel()); setFormWidget(DatabaseSettingsModel::UpdatePeriodField, std::move(updatePeriod));
// Update Start Time // Update Start Time
Wt::WComboBox *updateStartTimeCB = new Wt::WComboBox(); auto updateStartTime = std::make_unique<Wt::WComboBox>();
setFormWidget(DatabaseSettingsModel::UpdateStartTimeField, updateStartTimeCB); updateStartTime->setModel(model->updateStartTimeModel());
updateStartTimeCB->setModel(model->updateStartTimeModel()); setFormWidget(DatabaseSettingsModel::UpdateStartTimeField, std::move(updateStartTime));
// Buttons // Buttons
Wt::WPushButton *saveBtn = new Wt::WPushButton(Wt::WString::tr("Lms.apply")); Wt::WPushButton *saveBtn = bindWidget("apply-btn", std::make_unique<Wt::WPushButton>(Wt::WString::tr("Lms.apply")));
bindWidget("apply-btn", saveBtn); 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")));
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);
saveBtn->clicked().connect(std::bind([=] () saveBtn->clicked().connect(std::bind([=] ()
{ {
updateModel(model); updateModel(model.get());
if (model->validate()) if (model->validate())
{ {
@@ -271,14 +253,14 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
} }
// Udate the view: Delete any validation message in the view, etc. // Udate the view: Delete any validation message in the view, etc.
updateView(model); updateView(model.get());
})); }));
discardBtn->clicked().connect(std::bind([=] () discardBtn->clicked().connect(std::bind([=] ()
{ {
model->loadData(); model->loadData();
model->validate(); model->validate();
updateView(model); updateView(model.get());
})); }));
immScanBtn->clicked().connect(std::bind([=] () immScanBtn->clicked().connect(std::bind([=] ()
@@ -287,7 +269,7 @@ DatabaseSettingsView::DatabaseSettingsView(Wt::WContainerWidget *parent)
LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.Database.scan-launched")); LmsApp->notifyMsg(Wt::WString::tr("Lms.Admin.Database.scan-launched"));
})); }));
updateView(model); updateView(model.get());
} }
} // namespace UserInterface } // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WTemplateFormView.h>
#include <Wt/WTemplateFormView>
namespace UserInterface { namespace UserInterface {
class DatabaseSettingsView : public Wt::WTemplateFormView class DatabaseSettingsView : public Wt::WTemplateFormView
{ {
public: 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WFormModel> #include <Wt/WFormModel.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include <Wt/Auth/Identity> #include <Wt/Auth/Identity.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -40,8 +40,7 @@ class InitWizardModel : public Wt::WFormModel
static const Field PasswordField; static const Field PasswordField;
static const Field PasswordConfirmField; static const Field PasswordConfirmField;
InitWizardModel(Wt::WObject *parent = 0) InitWizardModel() : Wt::WFormModel()
: Wt::WFormModel(parent)
{ {
addField(AdminLoginField); addField(AdminLoginField);
addField(PasswordField); addField(PasswordField);
@@ -54,16 +53,16 @@ class InitWizardModel : public Wt::WFormModel
void saveData() void saveData()
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
// Check if a user already exist // Check if a user already exist
// If it's the case, just do nothing // 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"); throw std::runtime_error("Admin user already created");
// Create user // Create user
Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew(); Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().registerNew();
Database::User::pointer user = DbHandler().getUser(authUser); Database::User::pointer user = LmsApp->getDb().createUser(authUser);
// Account // Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(AdminLoginField)); authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(AdminLoginField));
@@ -94,7 +93,7 @@ class InitWizardModel : public Wt::WFormModel
} }
else if (field == PasswordConfirmField) else if (field == PasswordConfirmField)
{ {
if (validation(PasswordField).state() == Wt::WValidator::Valid) if (validation(PasswordField).state() == Wt::ValidationState::Valid)
{ {
if (valueText(PasswordField) != valueText(PasswordConfirmField)) if (valueText(PasswordField) != valueText(PasswordConfirmField))
error = Wt::WString::tr("Lms.passwords-dont-match"); error = Wt::WString::tr("Lms.passwords-dont-match");
@@ -105,9 +104,9 @@ class InitWizardModel : public Wt::WFormModel
return Wt::WFormModel::validateField(field); 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::PasswordField = "password";
const Wt::WFormModel::Field InitWizardModel::PasswordConfirmField = "password-confirm"; const Wt::WFormModel::Field InitWizardModel::PasswordConfirmField = "password-confirm";
InitWizardView::InitWizardView(Wt::WContainerWidget *parent) InitWizardView::InitWizardView()
: Wt::WTemplateFormView(parent) : Wt::WTemplateFormView(Wt::WString::tr("Lms.Admin.InitWizard.template"))
{ {
auto model = new InitWizardModel(this); auto model = std::make_shared<InitWizardModel>();
setTemplateText(Wt::WString::tr("Lms.Admin.InitWizard.template"));
addFunction("tr", &WTemplate::Functions::tr);
addFunction("id", &WTemplate::Functions::id);
// AdminLogin // AdminLogin
Wt::WLineEdit* accountEdit = new Wt::WLineEdit(); setFormWidget(InitWizardModel::AdminLoginField, std::make_unique<Wt::WLineEdit>());
setFormWidget(InitWizardModel::AdminLoginField, accountEdit);
// Password // Password
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit(); auto passwordEdit = std::make_unique<Wt::WLineEdit>();
setFormWidget(InitWizardModel::PasswordField, passwordEdit ); passwordEdit->setEchoMode(Wt::EchoMode::Password);
passwordEdit->setEchoMode(Wt::WLineEdit::Password); setFormWidget(InitWizardModel::PasswordField, std::move(passwordEdit) );
// Password confirmation // Password confirmation
Wt::WLineEdit* passwordConfirmEdit = new Wt::WLineEdit(); auto passwordConfirmEdit = std::make_unique<Wt::WLineEdit>();
setFormWidget(InitWizardModel::PasswordConfirmField, passwordConfirmEdit); passwordConfirmEdit->setEchoMode(Wt::EchoMode::Password);
passwordConfirmEdit->setEchoMode(Wt::WLineEdit::Password); setFormWidget(InitWizardModel::PasswordConfirmField, std::move(passwordConfirmEdit));
auto saveButton = new Wt::WPushButton(Wt::WString::tr("Lms.create")); Wt::WPushButton* saveButton = bindNew<Wt::WPushButton>("create-btn", Wt::WString::tr("Lms.create"));
bindWidget("create-btn", saveButton);
saveButton->clicked().connect(std::bind([=] saveButton->clicked().connect(std::bind([=]
{ {
updateModel(model); updateModel(model.get());
if (model->validate()) if (model->validate())
{ {
@@ -152,10 +145,10 @@ InitWizardView::InitWizardView(Wt::WContainerWidget *parent)
saveButton->setEnabled(false); saveButton->setEnabled(false);
} }
updateView(model); updateView(model.get());
})); }));
updateView(model); updateView(model.get());
} }
} // namespace UserInterface } // namespace UserInterface
+2 -3
View File
@@ -19,15 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WTemplateFormView.h>
#include <Wt/WTemplateFormView>
namespace UserInterface { namespace UserInterface {
class InitWizardView : public Wt::WTemplateFormView class InitWizardView : public Wt::WTemplateFormView
{ {
public: 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WApplication> #include <Wt/WApplication.h>
#include <Wt/WCheckBox> #include <Wt/WCheckBox.h>
#include <Wt/WComboBox> #include <Wt/WComboBox.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include <Wt/WTemplateFormView> #include <Wt/WTemplateFormView.h>
#include <Wt/WFormModel> #include <Wt/WFormModel.h>
#include <Wt/WStringListModel> #include <Wt/WStringListModel.h>
#include "common/Validators.hpp" #include "common/Validators.hpp"
#include "utils/Utils.hpp" #include "utils/Utils.hpp"
@@ -44,8 +44,8 @@ class UserModel : public Wt::WFormModel
static const Field PasswordField; static const Field PasswordField;
static const Field BitrateLimitField; static const Field BitrateLimitField;
UserModel(boost::optional<Database::User::id_type> userId, Wt::WObject *parent = 0) UserModel(boost::optional<Database::User::id_type> userId)
: Wt::WFormModel(parent), : Wt::WFormModel(),
_userId(userId) _userId(userId)
{ {
if (!_userId) if (!_userId)
@@ -72,12 +72,12 @@ class UserModel : public Wt::WFormModel
if (!_userId) if (!_userId)
return; return;
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) ); Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = DbHandler().getUser(authUser); Database::User::pointer user = LmsApp->getDb().getUser(authUser);
if (user == CurrentUser()) if (user == LmsApp->getCurrentUser())
throw std::runtime_error("Cannot edit ourselves"); throw std::runtime_error("Cannot edit ourselves");
auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate()); auto bitrate = getBitrateLimitRow(user->getMaxAudioBitrate());
@@ -87,13 +87,13 @@ class UserModel : public Wt::WFormModel
void saveData() void saveData()
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
if (_userId) if (_userId)
{ {
// Update user // Update user
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId( std::to_string(*_userId) ); Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*_userId) );
Database::User::pointer user = DbHandler().getUser( authUser ); Database::User::pointer user = LmsApp->getDb().getUser( authUser );
// Account // Account
if (!valueText(PasswordField).empty()) if (!valueText(PasswordField).empty())
@@ -106,8 +106,8 @@ class UserModel : public Wt::WFormModel
else else
{ {
// Create user // Create user
Wt::Auth::User authUser = DbHandler().getUserDatabase().registerNew(); Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().registerNew();
Database::User::pointer user = DbHandler().createUser(authUser); Database::User::pointer user = LmsApp->getDb().createUser(authUser);
// Account // Account
authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField)); authUser.setIdentity(Wt::Auth::Identity::LoginName, valueText(LoginField));
@@ -142,35 +142,35 @@ class UserModel : public Wt::WFormModel
std::size_t bitrateLimit(int row) std::size_t bitrateLimit(int row)
{ {
return boost::any_cast<std::size_t> return Wt::cpp17::any_cast<std::size_t>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::UserRole)); (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::User));
} }
Wt::WString bitrateLimitString(int row) Wt::WString bitrateLimitString(int row)
{ {
return boost::any_cast<Wt::WString> return Wt::cpp17::any_cast<Wt::WString>
(_bitrateModel->data(_bitrateModel->index(row, 0), Wt::DisplayRole)); (_bitrateModel->data(_bitrateModel->index(row, 0), Wt::ItemDataRole::Display));
} }
Wt::WAbstractItemModel *bitrateModel() { return _bitrateModel; } std::shared_ptr<Wt::WAbstractItemModel> bitrateModel() { return _bitrateModel; }
private: private:
void initializeModels() void initializeModels()
{ {
_bitrateModel = new Wt::WStringListModel(this); _bitrateModel = std::make_shared<Wt::WStringListModel>();
std::size_t id = 0; std::size_t id = 0;
for (auto bitrate : Database::User::audioBitrates) for (auto bitrate : Database::User::audioBitrates)
{ {
_bitrateModel->addString( Wt::WString::fromUTF8(std::to_string(bitrate / 1000)) ); _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; 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::PasswordField = "password";
const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit"; const Wt::WFormModel::Field UserModel::BitrateLimitField = "audio-bitrate-limit";
UserView::UserView(Wt::WContainerWidget* parent) UserView::UserView()
: Wt::WContainerWidget(parent)
{ {
wApp->internalPathChanged().connect(std::bind([=] wApp->internalPathChanged().connect(std::bind([=]
{ {
@@ -195,47 +194,44 @@ UserView::refreshView()
if (!wApp->internalPathMatches("/admin/user")) if (!wApp->internalPathMatches("/admin/user"))
return; 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(); 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); auto model = std::make_shared<UserModel>(userId);
t->addFunction("id", &Wt::WTemplate::Functions::id);
auto model = new UserModel(userId ? boost::make_optional<Database::User::id_type>(*userId) : boost::none, this);
if (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); 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 else
{ {
// Login // Login
t->setCondition("if-has-login", true); 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")); t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-create"));
} }
// Password // Password
Wt::WLineEdit* passwordEdit = new Wt::WLineEdit(); auto passwordEdit = std::make_unique<Wt::WLineEdit>();
t->setFormWidget(UserModel::PasswordField, passwordEdit ); passwordEdit->setEchoMode(Wt::EchoMode::Password);
passwordEdit->setEchoMode(Wt::WLineEdit::Password); t->setFormWidget(UserModel::PasswordField, std::move(passwordEdit));
// AudioBitrate // Bitrate
Wt::WComboBox *bitrateCB = new Wt::WComboBox(); auto bitrate = std::make_unique<Wt::WComboBox>();
t->setFormWidget(UserModel::BitrateLimitField, bitrateCB); bitrate->setModel(model->bitrateModel());
bitrateCB->setModel(model->bitrateModel()); t->setFormWidget(UserModel::BitrateLimitField, std::move(bitrate));
auto saveBtn = new Wt::WPushButton(Wt::WString::tr(userId ? "Lms.save" : "Lms.create")); Wt::WPushButton* saveBtn = t->bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr(userId ? "Lms.save" : "Lms.create"));
t->bindWidget("save-btn", saveBtn);
saveBtn->clicked().connect(std::bind([=] saveBtn->clicked().connect(std::bind([=]
{ {
t->updateModel(model); t->updateModel(model.get());
if (model->validate()) if (model->validate())
{ {
@@ -245,12 +241,11 @@ UserView::refreshView()
} }
else else
{ {
t->updateView(model); t->updateView(model.get());
} }
})); }));
t->updateView(model); t->updateView(model.get());
} }
} // namespace UserInterface } // namespace UserInterface
+2 -2
View File
@@ -19,14 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
namespace UserInterface { namespace UserInterface {
class UserView : public Wt::WContainerWidget class UserView : public Wt::WContainerWidget
{ {
public: public:
UserView(Wt::WContainerWidget *parent = 0); UserView();
private: private:
void refreshView(); void refreshView();
+19 -26
View File
@@ -17,8 +17,8 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include "database/Types.hpp" #include "database/Types.hpp"
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -28,18 +28,14 @@
namespace UserInterface { namespace UserInterface {
UsersView::UsersView(Wt::WContainerWidget *parent) UsersView::UsersView()
: Wt::WContainerWidget(parent) : Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template"))
{ {
auto t = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template"), this); addFunction("tr", &Wt::WTemplate::Functions::tr);
t->addFunction("tr", &Wt::WTemplate::Functions::tr);
_container = new Wt::WContainerWidget(); _container = bindNew<Wt::WContainerWidget>("users");
t->bindWidget("users", _container);
auto addBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.add"));
t->bindWidget("add-btn", addBtn);
Wt::WPushButton* addBtn = bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.Admin.Users.add"));
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
LmsApp->setInternalPath("/admin/user", true); LmsApp->setInternalPath("/admin/user", true);
@@ -61,44 +57,41 @@ UsersView::refreshView()
_container->clear(); _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) for (auto user : users)
{ {
auto userId = std::to_string(user.id()); auto userId = std::to_string(user.id());
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Admin.Users.template.entry"), _container); Wt::WTemplate* entry = _container->addNew<Wt::WTemplate>(Wt::WString::tr("Lms.Admin.Users.template.entry"));
Wt::Auth::User authUser = DbHandler().getUserDatabase().findWithId(userId);
Wt::Auth::User authUser = LmsApp->getDb().getUserDatabase().findWithId(userId);
if (!authUser.isValid()) { if (!authUser.isValid()) {
LMS_LOG(UI, ERROR) << "Skipping invalid userId = " << user.id(); LMS_LOG(UI, ERROR) << "Skipping invalid userId = " << user.id();
continue; 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 // Don't edit ourself this way
if (CurrentUser() == user) if (LmsApp->getCurrentUser() == user)
continue; continue;
entry->setCondition("if-edit", true); entry->setCondition("if-edit", true);
auto editBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.edit")); Wt::WPushButton* editBtn = entry->bindNew<Wt::WPushButton>("edit-btn", Wt::WString::tr("Lms.Admin.Users.edit"));
entry->bindWidget("edit-btn", editBtn);
editBtn->clicked().connect(std::bind([=] editBtn->clicked().connect(std::bind([=]
{ {
LmsApp->setInternalPath("/admin/user/" + std::to_string(user.id()), true); LmsApp->setInternalPath("/admin/user/" + std::to_string(user.id()), true);
})); }));
auto delBtn = new Wt::WPushButton(Wt::WString::tr("Lms.Admin.Users.del")); Wt::WPushButton* delBtn = entry->bindNew<Wt::WPushButton>("del-btn", Wt::WString::tr("Lms.Admin.Users.del"));
entry->bindWidget("del-btn", delBtn);
delBtn->clicked().connect(std::bind([=] delBtn->clicked().connect(std::bind([=]
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto authUser = DbHandler().getUserDatabase().findWithId(userId); auto authUser = LmsApp->getDb().getUserDatabase().findWithId(userId);
auto user = DbHandler().getUser(authUser); auto user = LmsApp->getDb().getUser(authUser);
DbHandler().getUserDatabase().deleteUser( authUser ); LmsApp->getDb().getUserDatabase().deleteUser( authUser );
user.remove(); user.remove();
_container->removeWidget(entry); _container->removeWidget(entry);
})); }));
+3 -3
View File
@@ -19,14 +19,14 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
namespace UserInterface { namespace UserInterface {
class UsersView : public Wt::WContainerWidget class UsersView : public Wt::WTemplate
{ {
public: public:
UsersView(Wt::WContainerWidget *parent = 0); UsersView();
private: private:
void refreshView(); 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 <boost/filesystem.hpp>
#include <Wt/WLengthValidator.h>
#include "Validators.hpp" #include "Validators.hpp"
namespace UserInterface { 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->setMandatory(true);
v->setMinimumLength(::Database::User::MinNameLength); v->setMinimumLength(::Database::User::MinNameLength);
v->setMaximumLength(::Database::User::MaxNameLength); v->setMaximumLength(::Database::User::MaxNameLength);
return v; 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); v->setMandatory(true);
return v; return v;
} }
DirectoryValidator::DirectoryValidator(Wt::WObject *parent) DirectoryValidator::DirectoryValidator() : Wt::WValidator()
: Wt::WValidator(parent)
{ {
} }
@@ -56,11 +59,11 @@ DirectoryValidator::validate(const Wt::WString& input) const
// TODO check rights // TODO check rights
bool res = boost::filesystem::is_directory(p, ec); bool res = boost::filesystem::is_directory(p, ec);
if (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) else if (res)
return Wt::WValidator::Result(Wt::WValidator::Valid); return Wt::WValidator::Result(Wt::ValidationState::Valid);
else 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 #pragma once
#include <Wt/WLengthValidator> #include <Wt/WValidator.h>
#include "database/User.hpp" #include "database/User.hpp"
namespace UserInterface { namespace UserInterface {
Wt::WValidator* createNameValidator(); std::shared_ptr<Wt::WValidator> createNameValidator();
Wt::WValidator* createMandatoryValidator(); std::shared_ptr<Wt::WValidator> createMandatoryValidator();
class DirectoryValidator : public Wt::WValidator class DirectoryValidator : public Wt::WValidator
{ {
public: public:
DirectoryValidator(Wt::WObject *parent = 0); DirectoryValidator();
Wt::WValidator::Result validate(const Wt::WString& input) const override; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WApplication> #include <Wt/WAnchor.h>
#include <Wt/WAnchor> #include <Wt/WImage.h>
#include <Wt/WImage> #include <Wt/WTemplate.h>
#include <Wt/WTemplate> #include <Wt/WText.h>
#include <Wt/WText>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -36,9 +35,8 @@
namespace UserInterface { namespace UserInterface {
Artist::Artist(Filters* filters, Wt::WContainerWidget* parent) Artist::Artist(Filters* filters)
: Wt::WContainerWidget(parent), : _filters(filters)
_filters(filters)
{ {
wApp->internalPathChanged().connect(std::bind([=] wApp->internalPathChanged().connect(std::bind([=]
{ {
@@ -60,81 +58,73 @@ Artist::refresh()
clear(); clear();
Database::Artist::id_type artistId; auto artistId = readAs<Database::Artist::id_type>(wApp->internalPathNextPart("/artist/"));
if (!readAs(wApp->internalPathNextPart("/artist/"), artistId)) if (!artistId)
return; return;
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
auto artist = Database::Artist::getById(DboSession(), artistId); auto artist = Database::Artist::getById(LmsApp->getDboSession(), *artistId);
if (!artist) if (!artist)
{ {
LmsApp->goHome(); LmsApp->goHome();
return; 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); t->addFunction("tr", &Wt::WTemplate::Functions::tr);
auto clusterContainers = new Wt::WContainerWidget(); Wt::WContainerWidget* clusterContainers = t->bindNew<Wt::WContainerWidget>("clusters");
t->bindWidget("clusters", clusterContainers);
{ {
auto clusters = artist->getClusters(3); auto clusters = artist->getClusters(3);
for (auto cluster : clusters) for (auto cluster : clusters)
{ {
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artist.template.cluster-entry"), clusterContainers); 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);
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::PlainText);
} }
} }
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); Wt::WText* playBtn = t->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Artist.play"), Wt::TextFormat::XHTML);
t->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] 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); Wt::WText* addBtn = t->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Artist.add"), Wt::TextFormat::XHTML);
t->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
artistAdd.emit(artistId); artistAdd.emit(*artistId);
})); }));
} }
auto releasesContainer = new Wt::WContainerWidget(); Wt::WContainerWidget* releasesContainer = t->bindNew<Wt::WContainerWidget>("releases");
t->bindWidget("releases", releasesContainer);
auto releases = artist->getReleases(_filters->getClusterIds()); auto releases = artist->getReleases(_filters->getClusterIds());
for (auto release : releases) for (auto release : releases)
{ {
auto releaseId = release.id(); 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); entry->addFunction("tr", Wt::WTemplate::Functions::tr);
{ {
Wt::WAnchor* coverAnchor = LmsApplication::createReleaseAnchor(release, false); Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
Wt::WImage* cover = new Wt::WImage(coverAnchor);
auto cover = std::make_unique<Wt::WImage>();
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 128)); cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 128));
// Some images may not be square // Some images may not be square
cover->setWidth(128); cover->setWidth(128);
entry->bindWidget("cover", coverAnchor); anchor->setImage(std::move(cover));
} }
{ entry->bindWidget("name", LmsApplication::createReleaseAnchor(release));
Wt::WAnchor* releaseAnchor = LmsApplication::createReleaseAnchor(release);
entry->bindWidget("name", releaseAnchor);
}
if (release->hasVariousArtists()) if (release->hasVariousArtists())
entry->setCondition("if-has-various-artists", true); 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); Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Artist.play"), Wt::TextFormat::XHTML);
entry->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
releasePlay.emit(releaseId); releasePlay.emit(releaseId);
})); }));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Artist.add"), Wt::XHTMLText); Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Artist.add"), Wt::TextFormat::XHTML);
entry->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
releaseAdd.emit(releaseId); releaseAdd.emit(releaseId);
+3 -3
View File
@@ -19,8 +19,8 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WSignal> #include <Wt/WSignal.h>
namespace UserInterface { namespace UserInterface {
@@ -29,7 +29,7 @@ class Filters;
class Artist : public Wt::WContainerWidget class Artist : public Wt::WContainerWidget
{ {
public: public:
Artist(Filters* filters, Wt::WContainerWidget* parent = 0); Artist(Filters* filters);
Wt::Signal<Database::id_type> artistAdd; Wt::Signal<Database::id_type> artistAdd;
Wt::Signal<Database::id_type> artistPlay; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -34,24 +34,20 @@ namespace UserInterface {
using namespace Database; using namespace Database;
Artists::Artists(Filters* filters, Wt::WContainerWidget* parent) Artists::Artists(Filters* filters)
: Wt::WContainerWidget(parent), : Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artists.template")),
_filters(filters) _filters(filters)
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Artists.template"), this); addFunction("tr", &Wt::WTemplate::Functions::tr);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit(); _search = bindNew<Wt::WLineEdit>("search");
container->bindWidget("search", _search);
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder")); _search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Artists::refresh); _search->textInput().connect(this, &Artists::refresh);
_artistsContainer = new Wt::WContainerWidget(); _artistsContainer = bindNew<Wt::WContainerWidget>("artists");
container->bindWidget("artists", _artistsContainer);
_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); _showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=] _showMore->clicked().connect(std::bind([=]
{ {
@@ -77,24 +73,20 @@ Artists::addSome()
auto clusterIds = _filters->getClusterIds(); auto clusterIds = _filters->getClusterIds();
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults; bool moreResults;
auto artists = Artist::getByFilter(DboSession(), auto artists = Artist::getByFilter(LmsApp->getDboSession(),
clusterIds, clusterIds,
searchKeywords, searchKeywords,
_artistsContainer->count(), 20, moreResults); _artistsContainer->count(), 20, moreResults);
for (auto artist : artists) 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()); entry->bindInt("nb-release", artist->getReleases(clusterIds).size());
entry->bindWidget("name", LmsApplication::createArtistAnchor(artist));
{
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(artist);
entry->bindWidget("name", artistAnchor);
}
} }
_showMore->setHidden(!moreResults); _showMore->setHidden(!moreResults);
+6 -5
View File
@@ -19,18 +19,19 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit> #include <Wt/WTemplate.h>
#include <Wt/WSignal> #include <Wt/WLineEdit.h>
#include <Wt/WSignal.h>
namespace UserInterface { namespace UserInterface {
class Filters; class Filters;
class Artists : public Wt::WContainerWidget class Artists : public Wt::WTemplate
{ {
public: public:
Artists(Filters* filters, Wt::WContainerWidget* parent = 0); Artists(Filters* filters);
Wt::Signal<Database::id_type> artistAdd; Wt::Signal<Database::id_type> artistAdd;
Wt::Signal<Database::id_type> artistPlay; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WStackedWidget> #include <Wt/WStackedWidget.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include <Wt/WText> #include <Wt/WText.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -70,54 +70,44 @@ handlePathChange(Wt::WStackedWidget* stack)
} }
} }
Explore::Explore(Wt::WContainerWidget* parent) Explore::Explore()
: Wt::WContainerWidget(parent) : Wt::WTemplate(Wt::WString::tr("Lms.Explore.template"))
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template"), this); _filters = bindNew<Filters>("filters");
_filters = new Filters();
container->bindWidget("filters", _filters);
// Contents // Contents
Wt::WStackedWidget* stack = new Wt::WStackedWidget(); Wt::WStackedWidget* stack = bindNew<Wt::WStackedWidget>("contents");
container->bindWidget("contents", stack);
auto artists = new Artists(_filters);
stack->addWidget(artists);
auto artists = std::make_unique<Artists>(_filters);
artists->artistAdd.connect(this, &Explore::handleArtistAdd); artists->artistAdd.connect(this, &Explore::handleArtistAdd);
artists->artistPlay.connect(this, &Explore::handleArtistPlay); artists->artistPlay.connect(this, &Explore::handleArtistPlay);
stack->addWidget(std::move(artists));
auto artist = new Artist(_filters); auto artist = std::make_unique<Artist>(_filters);
stack->addWidget(artist);
artist->artistAdd.connect(this, &Explore::handleArtistAdd); artist->artistAdd.connect(this, &Explore::handleArtistAdd);
artist->artistPlay.connect(this, &Explore::handleArtistPlay); artist->artistPlay.connect(this, &Explore::handleArtistPlay);
artist->releaseAdd.connect(this, &Explore::handleReleaseAdd); artist->releaseAdd.connect(this, &Explore::handleReleaseAdd);
artist->releasePlay.connect(this, &Explore::handleReleasePlay); artist->releasePlay.connect(this, &Explore::handleReleasePlay);
stack->addWidget(std::move(artist));
auto releases = new Releases(_filters); auto releases = std::make_unique<Releases>(_filters);
stack->addWidget(releases);
releases->releaseAdd.connect(this, &Explore::handleReleaseAdd); releases->releaseAdd.connect(this, &Explore::handleReleaseAdd);
releases->releasePlay.connect(this, &Explore::handleReleasePlay); releases->releasePlay.connect(this, &Explore::handleReleasePlay);
stack->addWidget(std::move(releases));
auto release = new Release(_filters); auto release = std::make_unique<Release>(_filters);
stack->addWidget(release);
release->releaseAdd.connect(this, &Explore::handleReleaseAdd); release->releaseAdd.connect(this, &Explore::handleReleaseAdd);
release->releasePlay.connect(this, &Explore::handleReleasePlay); release->releasePlay.connect(this, &Explore::handleReleasePlay);
release->trackAdd.connect(this, &Explore::handleTrackAdd); release->trackAdd.connect(this, &Explore::handleTrackAdd);
release->trackPlay.connect(this, &Explore::handleTrackPlay); release->trackPlay.connect(this, &Explore::handleTrackPlay);
stack->addWidget(std::move(release));
auto tracks = new Tracks(_filters); auto tracks = std::make_unique<Tracks>(_filters);
stack->addWidget(tracks);
tracks->trackAdd.connect(this, &Explore::handleTrackAdd); tracks->trackAdd.connect(this, &Explore::handleTrackAdd);
tracks->trackPlay.connect(this, &Explore::handleTrackPlay); tracks->trackPlay.connect(this, &Explore::handleTrackPlay);
tracks->tracksAdd.connect(this, &Explore::handleTracksAdd); tracks->tracksAdd.connect(this, &Explore::handleTracksAdd);
tracks->tracksPlay.connect(this, &Explore::handleTracksPlay); tracks->tracksPlay.connect(this, &Explore::handleTracksPlay);
stack->addWidget(std::move(tracks));
wApp->internalPathChanged().connect(std::bind([=] wApp->internalPathChanged().connect(std::bind([=]
{ {
@@ -175,49 +165,49 @@ static std::vector<Database::Track::pointer> getTrack(Wt::Dbo::Session& session,
void void
Explore::handleArtistAdd(Database::id_type id) 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 void
Explore::handleArtistPlay(Database::id_type id) 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 void
Explore::handleReleaseAdd(Database::id_type id) 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 void
Explore::handleReleasePlay(Database::id_type id) 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 void
Explore::handleTrackAdd(Database::id_type id) 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 void
Explore::handleTrackPlay(Database::id_type id) 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 void
+9 -9
View File
@@ -19,7 +19,7 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WTemplate.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -27,22 +27,22 @@ namespace UserInterface {
class Filters; class Filters;
class Explore : public Wt::WContainerWidget class Explore : public Wt::WTemplate
{ {
public: public:
Explore(Wt::WContainerWidget *parent = 0); Explore();
Wt::Signal<std::vector<Database::Track::pointer>> tracksAdd; Wt::Signal<std::vector<Database::Track::pointer>> tracksAdd;
Wt::Signal<std::vector<Database::Track::pointer>> tracksPlay; Wt::Signal<std::vector<Database::Track::pointer>> tracksPlay;
private: private:
void handleArtistAdd(Database::id_type id); void handleArtistAdd(Database::Artist::id_type id);
void handleArtistPlay(Database::id_type id); void handleArtistPlay(Database::Artist::id_type id);
void handleReleaseAdd(Database::id_type id); void handleReleaseAdd(Database::Release::id_type id);
void handleReleasePlay(Database::id_type id); void handleReleasePlay(Database::Release::id_type id);
void handleTrackAdd(Database::id_type id); void handleTrackAdd(Database::Track::id_type id);
void handleTrackPlay(Database::id_type id); void handleTrackPlay(Database::Track::id_type id);
void handleTracksAdd(std::vector<Database::Track::pointer> tracks); void handleTracksAdd(std::vector<Database::Track::pointer> tracks);
void handleTracksPlay(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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WComboBox> #include <Wt/WComboBox.h>
#include <Wt/WDialog> #include <Wt/WDialog.h>
#include <Wt/WPushButton> #include <Wt/WPushButton.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include "Filters.hpp" #include "Filters.hpp"
@@ -31,32 +31,25 @@ namespace UserInterface {
void void
Filters::showDialog() 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); 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(); Wt::WPushButton* addBtn = container->bindNew<Wt::WPushButton>("add-btn", Wt::WString::tr("Lms.add"));
container->bindWidget("type", typeCombo); addBtn->clicked().connect(dialog.get(), &Wt::WDialog::accept);
auto valueCombo = new Wt::WComboBox(); Wt::WPushButton* cancelBtn = container->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"));
container->bindWidget("value", valueCombo); cancelBtn->clicked().connect(dialog.get(), &Wt::WDialog::reject);
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);
// Populate data // 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) for (auto type : types)
typeCombo->addItem(Wt::WString::fromUTF8(type->getName())); typeCombo->addItem(Wt::WString::fromUTF8(type->getName()));
@@ -81,9 +74,9 @@ Filters::showDialog()
valueCombo->clear(); 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(); auto values = clusterType->getClusters();
for (auto value : values) for (auto value : values)
@@ -94,24 +87,22 @@ Filters::showDialog()
})); }));
dialog->setModal(true); dialog->setModal(true);
#if WT_VERSION >= 0x03030700
dialog->setMovable(false); dialog->setMovable(false);
#endif
dialog->setResizable(false); dialog->setResizable(false);
dialog->setClosable(false); dialog->setClosable(false);
dialog->finished().connect(std::bind([=] dialog->finished().connect(std::bind([=]
{ {
if (dialog->result() != Wt::WDialog::Accepted) if (dialog->result() != Wt::DialogCode::Accepted)
return; return;
auto type = typeCombo->valueText().toUTF8(); auto type = typeCombo->valueText().toUTF8();
auto value = valueCombo->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) if (!clusterType)
return; return;
@@ -123,8 +114,7 @@ Filters::showDialog()
_filterIds.insert(clusterId); _filterIds.insert(clusterId);
_sigUpdated.emit(); _sigUpdated.emit();
auto filterBtn = new Wt::WPushButton(Wt::WString::fromUTF8(value)); Wt::WPushButton* filterBtn = _filters->addNew<Wt::WPushButton>(Wt::WString::fromUTF8(value), Wt::TextFormat::Plain);
_filters->addWidget(filterBtn);
filterBtn->clicked().connect(std::bind([=] filterBtn->clicked().connect(std::bind([=]
{ {
@@ -137,23 +127,14 @@ Filters::showDialog()
dialog->show(); dialog->show();
} }
Filters::Filters(Wt::WContainerWidget *parent) Filters::Filters()
: Wt::WContainerWidget(parent) : Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.filters"))
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.template.filters"), this);
// Filters // Filters
Wt::WPushButton *addFilterBtn = new Wt::WPushButton(Wt::WText::tr("Lms.Explore.add-filter")); Wt::WPushButton *addFilterBtn = bindNew<Wt::WPushButton>("add-filter", Wt::WText::tr("Lms.Explore.add-filter"));
container->bindWidget("add-filter", addFilterBtn); addFilterBtn->clicked().connect(this, &Filters::showDialog);
_filters = new Wt::WContainerWidget();
container->bindWidget("filters", _filters);
addFilterBtn->clicked().connect(std::bind([this]
{
showDialog();
}));
_filters = bindNew<Wt::WContainerWidget>("filters");
} }
+7 -6
View File
@@ -19,8 +19,9 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WSignal> #include <Wt/WSignal.h>
#include <Wt/WTemplate.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -28,21 +29,21 @@
namespace UserInterface { namespace UserInterface {
class Filters : public Wt::WContainerWidget class Filters : public Wt::WTemplate
{ {
public: public:
Filters(Wt::WContainerWidget *parent = 0); Filters();
std::set<Database::Cluster::id_type> getClusterIds() const { return _filterIds; } std::set<Database::Cluster::id_type> getClusterIds() const { return _filterIds; }
Wt::Signal<void>& updated() { return _sigUpdated; } Wt::Signal<>& updated() { return _sigUpdated; }
private: private:
void showDialog(); void showDialog();
Wt::WContainerWidget *_filters; Wt::WContainerWidget *_filters;
Wt::Signal<void> _sigUpdated; Wt::Signal<> _sigUpdated;
std::set<Database::Cluster::id_type> _filterIds; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WApplication> #include <Wt/WApplication.h>
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WImage> #include <Wt/WImage.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include <Wt/WText> #include <Wt/WText.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -36,9 +36,8 @@
namespace UserInterface { namespace UserInterface {
Release::Release(Filters* filters, Wt::WContainerWidget* parent) Release::Release(Filters* filters)
: Wt::WContainerWidget(parent), : _filters(filters)
_filters(filters)
{ {
wApp->internalPathChanged().connect(std::bind([=] wApp->internalPathChanged().connect(std::bind([=]
{ {
@@ -60,24 +59,23 @@ Release::refresh()
clear(); clear();
Database::Release::id_type releaseId; auto releaseId = readAs<Database::Release::id_type>(wApp->internalPathNextPart("/release/"));
if (!releaseId)
if (!readAs(wApp->internalPathNextPart("/release/"), releaseId))
return; 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) if (!release)
{ {
LmsApp->goHome(); LmsApp->goHome();
return; 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->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(); boost::optional<int> year = release->getReleaseYear();
if (year) if (year)
@@ -103,49 +101,40 @@ Release::refresh()
else if (artists.size() == 1) else if (artists.size() == 1)
{ {
t->setCondition("if-has-artist", true); t->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(artists.front()); t->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
t->bindWidget("artist-name", artistAnchor);
} }
} }
Wt::WImage *cover = new Wt::WImage(); t->bindNew<Wt::WImage>("cover", Wt::WLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 512)));
cover->setImageLink(Wt::WLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 512)));
t->bindWidget("cover", cover);
auto clusterContainers = new Wt::WContainerWidget();
t->bindWidget("clusters", clusterContainers);
Wt::WContainerWidget* clusterContainers = t->bindNew<Wt::WContainerWidget>("clusters");
{ {
auto clusters = release->getClusters(3); auto clusters = release->getClusters(3);
for (auto cluster : clusters) for (auto cluster : clusters)
{ {
auto entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Release.template.cluster-entry"), clusterContainers); 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);
entry->bindString("name", Wt::WString::fromUTF8(cluster->getName()), Wt::PlainText);
} }
} }
{ {
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.play"), Wt::XHTMLText); Wt::WText* playBtn = t->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Release.play"), Wt::TextFormat::XHTML);
t->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] 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); Wt::WText* addBtn = t->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Release.add"), Wt::TextFormat::XHTML);
t->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
releaseAdd.emit(releaseId); releaseAdd.emit(*releaseId);
})); }));
} }
auto tracksContainer = new Wt::WContainerWidget(); Wt::WContainerWidget* tracksContainer = t->bindNew<Wt::WContainerWidget>("tracks");
t->bindWidget("tracks", tracksContainer);
auto clusterIds = _filters->getClusterIds(); auto clusterIds = _filters->getClusterIds();
auto tracks = release->getTracks(clusterIds); auto tracks = release->getTracks(clusterIds);
@@ -156,15 +145,13 @@ Release::refresh()
{ {
auto trackId = track.id(); auto trackId = track.id();
Wt::WTemplate* entry = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Release.template.entry"), tracksContainer); 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);
entry->bindString("name", Wt::WString::fromUTF8(track->getName()), Wt::PlainText);
if (variousArtists && track->getArtist()) if (variousArtists && track->getArtist())
{ {
entry->setCondition("if-has-artist", true); entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist()); entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
entry->bindWidget("artist-name", artistAnchor);
} }
auto trackNumber = track->getTrackNumber(); auto trackNumber = track->getTrackNumber();
@@ -182,15 +169,13 @@ Release::refresh()
entry->bindInt("disc-number", *discNumber); entry->bindInt("disc-number", *discNumber);
} }
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.play"), Wt::XHTMLText); Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Release.play"), Wt::TextFormat::XHTML);
entry->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
trackPlay.emit(trackId); trackPlay.emit(trackId);
})); }));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Release.add"), Wt::XHTMLText); Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Release.add"), Wt::TextFormat::XHTML);
entry->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
trackAdd.emit(trackId); trackAdd.emit(trackId);
+2 -2
View File
@@ -19,7 +19,7 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WSignal.h>
namespace UserInterface { namespace UserInterface {
@@ -27,7 +27,7 @@ class Filters;
class Release : public Wt::WContainerWidget class Release : public Wt::WContainerWidget
{ {
public: public:
Release(Filters* filters, Wt::WContainerWidget* parent = 0); Release(Filters* filters);
Wt::Signal<Database::id_type> releaseAdd; Wt::Signal<Database::id_type> releaseAdd;
Wt::Signal<Database::id_type> releasePlay; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WApplication> #include <Wt/WApplication.h>
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WImage> #include <Wt/WImage.h>
#include <Wt/WText> #include <Wt/WText.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -38,25 +38,20 @@ namespace UserInterface {
using namespace Database; using namespace Database;
Releases::Releases(Filters* filters, Wt::WContainerWidget* parent) Releases::Releases(Filters* filters)
: Wt::WContainerWidget(parent), : Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template")),
_filters(filters) _filters(filters)
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Releases.template"), this); addFunction("tr", &Wt::WTemplate::Functions::tr);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit(); _search = bindNew<Wt::WLineEdit>("search");
container->bindWidget("search", _search);
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder")); _search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Releases::refresh); _search->textInput().connect(this, &Releases::refresh);
_releasesContainer = new Wt::WContainerWidget(); _releasesContainer = bindNew<Wt::WContainerWidget>("releases");
container->bindWidget("releases", _releasesContainer);
_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); _showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=] _showMore->clicked().connect(std::bind([=]
{ {
addSome(); addSome();
@@ -81,29 +76,24 @@ Releases::addSome()
auto clusterIds = _filters->getClusterIds(); auto clusterIds = _filters->getClusterIds();
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults; 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) for (auto release : releases)
{ {
auto releaseId = release.id(); 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); entry->addFunction("tr", Wt::WTemplate::Functions::tr);
Wt::WAnchor* coverAnchor = LmsApplication::createReleaseAnchor(release, false); Wt::WAnchor* anchor = entry->bindWidget("cover", LmsApplication::createReleaseAnchor(release, false));
Wt::WImage* cover = new Wt::WImage(coverAnchor); auto cover = std::make_unique<Wt::WImage>();
cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(releaseId, 128)); cover->setImageLink(LmsApp->getImageResource()->getReleaseUrl(release.id(), 128));
// Some images may not be square anchor->setImage(std::move(cover));
cover->setWidth(128);
entry->bindWidget("cover", coverAnchor);
{ entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(release));
Wt::WAnchor* releaseAnchor = LmsApplication::createReleaseAnchor(release);
entry->bindWidget("release-name", releaseAnchor);
}
auto artists = release->getArtists(); auto artists = release->getArtists();
if (artists.size() > 1) if (artists.size() > 1)
@@ -114,19 +104,16 @@ Releases::addSome()
else if (artists.size() == 1) else if (artists.size() == 1)
{ {
entry->setCondition("if-has-artist", true); entry->setCondition("if-has-artist", true);
Wt::WAnchor* artistAnchor = LmsApplication::createArtistAnchor(artists.front()); entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
entry->bindWidget("artist-name", artistAnchor);
} }
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Releases.play"), Wt::XHTMLText); Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Releases.play"), Wt::TextFormat::XHTML);
entry->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
releasePlay.emit(releaseId); releasePlay.emit(releaseId);
})); }));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Releases.add"), Wt::XHTMLText); Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Releases.add"), Wt::TextFormat::XHTML);
entry->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
releaseAdd.emit(releaseId); releaseAdd.emit(releaseId);
+6 -5
View File
@@ -19,18 +19,19 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WText> #include <Wt/WText.h>
#include <Wt/WTemplate.h>
namespace UserInterface { namespace UserInterface {
class Filters; class Filters;
class Releases : public Wt::WContainerWidget class Releases : public Wt::WTemplate
{ {
public: public:
Releases(Filters* filters, Wt::WContainerWidget* parent = 0); Releases(Filters* filters);
Wt::Signal<Database::id_type> releaseAdd; Wt::Signal<Database::id_type> releaseAdd;
Wt::Signal<Database::id_type> releasePlay; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WAnchor> #include <Wt/WAnchor.h>
#include <Wt/WImage> #include <Wt/WImage.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WText> #include <Wt/WText.h>
#include "database/Types.hpp" #include "database/Types.hpp"
@@ -37,41 +37,34 @@ namespace UserInterface {
using namespace Database; using namespace Database;
Tracks::Tracks(Filters* filters, Wt::WContainerWidget* parent) Tracks::Tracks(Filters* filters)
: Wt::WContainerWidget(parent), : Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template")),
_filters(filters) _filters(filters)
{ {
auto container = new Wt::WTemplate(Wt::WString::tr("Lms.Explore.Tracks.template"), this); addFunction("tr", &Wt::WTemplate::Functions::tr);
container->addFunction("tr", &Wt::WTemplate::Functions::tr);
_search = new Wt::WLineEdit(); _search = bindNew<Wt::WLineEdit>("search");
container->bindWidget("search", _search);
_search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder")); _search->setPlaceholderText(Wt::WString::tr("Lms.Explore.search-placeholder"));
_search->textInput().connect(this, &Tracks::refresh); _search->textInput().connect(this, &Tracks::refresh);
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::XHTMLText); Wt::WText* playBtn = bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::TextFormat::XHTML);
container->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksPlay.emit(getTracks()); tracksPlay.emit(getTracks());
})); }));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::XHTMLText); Wt::WText* addBtn = bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::TextFormat::XHTML);
container->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
tracksAdd.emit(getTracks()); tracksAdd.emit(getTracks());
})); }));
_tracksContainer = new Wt::WContainerWidget(); _tracksContainer = bindNew<Wt::WContainerWidget>("tracks");
container->bindWidget("tracks", _tracksContainer);
_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); _showMore->addFunction("tr", &Wt::WTemplate::Functions::tr);
container->bindWidget("show-more", _showMore);
_showMore->clicked().connect(std::bind([=] _showMore->clicked().connect(std::bind([=]
{ {
addSome(); addSome();
@@ -88,9 +81,9 @@ Tracks::getTracks(int offset, int size, bool& moreResults)
auto searchKeywords = splitString(_search->text().toUTF8(), " "); auto searchKeywords = splitString(_search->text().toUTF8(), " ");
auto clusterIds = _filters->getClusterIds(); 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> std::vector<Database::Track::pointer>
@@ -110,7 +103,7 @@ Tracks::refresh()
void void
Tracks::addSome() Tracks::addSome()
{ {
Wt::Dbo::Transaction transaction(DboSession()); Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
bool moreResults; bool moreResults;
auto tracks = getTracks(_tracksContainer->count(), 20, moreResults); auto tracks = getTracks(_tracksContainer->count(), 20, moreResults);
@@ -118,41 +111,35 @@ Tracks::addSome()
for (auto track : tracks) for (auto track : tracks)
{ {
auto trackId = track.id(); 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(); auto artist = track->getArtist();
if (artist) if (artist)
{ {
entry->setCondition("if-has-artist", true); entry->setCondition("if-has-artist", true);
Wt::WAnchor *artistAnchor = LmsApplication::createArtistAnchor(track->getArtist()); entry->bindWidget("artist-name", LmsApplication::createArtistAnchor(track->getArtist()));
entry->bindWidget("artist-name", artistAnchor);
} }
auto release = track->getRelease(); auto release = track->getRelease();
if (release) if (release)
{ {
entry->setCondition("if-has-release", true); entry->setCondition("if-has-release", true);
Wt::WAnchor *releaseAnchor = LmsApplication::createReleaseAnchor(track->getRelease()); entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(track->getRelease()));
entry->bindWidget("release-name", releaseAnchor);
} }
Wt::WImage *cover = new Wt::WImage(); Wt::WImage* cover = entry->bindNew<Wt::WImage>("cover", LmsApp->getImageResource()->getTrackUrl(track.id(), 64));
cover->setImageLink(LmsApp->getImageResource()->getTrackUrl(track.id(), 64));
// Some images may not be square // Some images may not be square
cover->setWidth(64); cover->setWidth(64);
entry->bindWidget("cover", cover);
auto playBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::XHTMLText); Wt::WText* playBtn = entry->bindNew<Wt::WText>("play-btn", Wt::WString::tr("Lms.Explore.Tracks.play"), Wt::TextFormat::XHTML);
entry->bindWidget("play-btn", playBtn);
playBtn->clicked().connect(std::bind([=] playBtn->clicked().connect(std::bind([=]
{ {
trackPlay.emit(trackId); trackPlay.emit(trackId);
})); }));
auto addBtn = new Wt::WText(Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::XHTMLText); Wt::WText* addBtn = entry->bindNew<Wt::WText>("add-btn", Wt::WString::tr("Lms.Explore.Tracks.add"), Wt::TextFormat::XHTML);
entry->bindWidget("add-btn", addBtn);
addBtn->clicked().connect(std::bind([=] addBtn->clicked().connect(std::bind([=]
{ {
trackAdd.emit(trackId); trackAdd.emit(trackId);
+5 -5
View File
@@ -19,17 +19,17 @@
#pragma once #pragma once
#include <Wt/WContainerWidget> #include <Wt/WContainerWidget.h>
#include <Wt/WLineEdit> #include <Wt/WLineEdit.h>
#include <Wt/WTemplate> #include <Wt/WTemplate.h>
namespace UserInterface { namespace UserInterface {
class Filters; class Filters;
class Tracks : public Wt::WContainerWidget class Tracks : public Wt::WTemplate
{ {
public: public:
Tracks(Filters* filters, Wt::WContainerWidget* parent = 0); Tracks(Filters* filters);
Wt::Signal<Database::id_type> trackAdd; Wt::Signal<Database::id_type> trackAdd;
Wt::Signal<Database::id_type> trackPlay; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/WApplication> #include <Wt/WApplication.h>
#include <Wt/Http/Response> #include <Wt/Http/Response.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
#include "utils/Utils.hpp" #include "utils/Utils.hpp"
@@ -34,9 +34,8 @@ namespace UserInterface {
static const std::string unknownCoverPath = "/images/unknown-cover.jpg"; static const std::string unknownCoverPath = "/images/unknown-cover.jpg";
static const std::string unknownArtistImagePath = "/images/unknown-artist.jpg"; static const std::string unknownArtistImagePath = "/images/unknown-artist.jpg";
ImageResource::ImageResource(Database::Handler& db, Wt::WObject *parent) ImageResource::ImageResource(Database::Handler& db)
: Wt::WResource(parent), : _db(db)
_db(db)
{ {
} }
@@ -154,14 +153,14 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
if (!sizeStr) if (!sizeStr)
return; return;
std::size_t size; auto size = readAs<std::size_t>(*sizeStr);
if (!readAs(*sizeStr, size) || size > maxSize) if (!size || *size > maxSize)
return; return;
if (trackIdStr) if (trackIdStr)
{ {
Database::Track::id_type trackId; auto trackId = readAs<Database::Track::id_type>(*trackIdStr);
if (!readAs(*trackIdStr, trackId)) if (!trackId)
return; return;
boost::filesystem::path path; boost::filesystem::path path;
@@ -172,7 +171,7 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
Wt::Dbo::Transaction transaction(_db.getSession()); 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) if (track)
{ {
coverType = track->getCoverType(); coverType = track->getCoverType();
@@ -195,17 +194,16 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
break; break;
} }
putCover(response, covers, size); putCover(response, covers, *size);
return; return;
} }
putImage(response, getDefaultCover(size)); putImage(response, getDefaultCover(*size));
return;
} }
else if (releaseIdStr) else if (releaseIdStr)
{ {
Database::Release::id_type releaseId; auto releaseId = readAs<Database::Release::id_type>(*releaseIdStr);
if (!readAs(*releaseIdStr, releaseId)) if (!releaseId)
return; return;
std::vector<Image::Image> covers; std::vector<Image::Image> covers;
@@ -213,21 +211,18 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
// transactions are not thread safe // transactions are not thread safe
{ {
Wt::WApplication::UpdateLock lock(LmsApplication::instance()); 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); putCover(response, covers, *size);
return;
} }
else if (artistIdStr) else if (artistIdStr)
{ {
putImage(response, getDefaultArtistImage(size)); putImage(response, getDefaultArtistImage(*size));
return;
} }
else else
{ {
putImage(response, getDefaultCover(size)); putImage(response, getDefaultCover(*size));
return;
} }
} }
+2 -2
View File
@@ -22,7 +22,7 @@
#include <mutex> #include <mutex>
#include <Wt/WResource> #include <Wt/WResource.h>
#include "database/DatabaseHandler.hpp" #include "database/DatabaseHandler.hpp"
#include "image/Image.hpp" #include "image/Image.hpp"
@@ -35,7 +35,7 @@ class ImageResource : public Wt::WResource
public: public:
static const std::size_t maxSize = 512; static const std::size_t maxSize = 512;
ImageResource(Database::Handler& db, Wt::WObject *parent = 0); ImageResource(Database::Handler& db);
~ImageResource(); ~ImageResource();
std::string getReleaseUrl(Database::Release::id_type releaseId, size_t size) const; 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/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <Wt/Http/Response> #include <Wt/Http/Response.h>
#include "utils/Logger.hpp" #include "utils/Logger.hpp"
@@ -27,9 +27,8 @@
namespace UserInterface { namespace UserInterface {
TranscodeResource::TranscodeResource(Database::Handler& db, Wt::WObject *parent) TranscodeResource::TranscodeResource(Database::Handler& db)
: Wt::WResource(parent), : _db(db)
_db(db)
{ {
LMS_LOG(UI, DEBUG) << "CONSTRUCTING RESOURCE"; LMS_LOG(UI, DEBUG) << "CONSTRUCTING RESOURCE";
} }
+2 -2
View File
@@ -21,7 +21,7 @@
#include <mutex> #include <mutex>
#include <Wt/WResource> #include <Wt/WResource.h>
#include "av/AvTranscoder.hpp" #include "av/AvTranscoder.hpp"
@@ -32,7 +32,7 @@ namespace UserInterface {
class TranscodeResource : public Wt::WResource class TranscodeResource : public Wt::WResource
{ {
public: public:
TranscodeResource(Database::Handler& db, Wt::WObject *parent); TranscodeResource(Database::Handler& db);
~TranscodeResource(); ~TranscodeResource();
std::string getUrl(Database::Track::id_type trackId, Av::Encoding encoding, boost::posix_time::time_duration offset) const; 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 #pragma once
#include <Wt/WServer>
#include <Wt/WApplication>
#include <Wt/WLogger>
#include <string> #include <string>
#include <Wt/WApplication.h>
#include <Wt/WLogger.h>
enum class Severity enum class Severity
{ {
+1 -1
View File
@@ -73,7 +73,7 @@ void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& cr
} }
else 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() + "'" ); throw std::runtime_error("Failed to open file '" + p.string() + "'" );
} }
+20 -41
View File
@@ -19,6 +19,7 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <iomanip>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
@@ -26,42 +27,28 @@
#include "Utils.hpp" #include "Utils.hpp"
template<>
boost::optional<long> boost::optional<Wt::WDate> readAs(const std::string& str)
readLong(const std::string& str)
{ {
try const std::vector<std::string> formats = {
{ "yyyy-MM-dd",
return std::stol(str); "yyyy/MM/dd",
} "yyyy-MM",
catch (std::exception& e) "yyyy/MM",
{ "yyyy"
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")),
}; };
for(size_t i=0; i < sizeof(formats)/sizeof(formats[0]); ++i)
for (auto format : formats)
{ {
std::istringstream iss(str); auto date = Wt::WDate::fromString(str, format);
iss.imbue(formats[i]);
if (iss >> time) if (!date.isValid())
return true; continue;
return date;
} }
return false; return boost::none;
} }
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results) 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 (separators.find(c) != std::string::npos) {
if (!curStr.empty()) { if (!curStr.empty()) {
results.push_back(stringToUTF8(curStr)); results.push_back(curStr);
curStr.clear(); curStr.clear();
} }
} }
@@ -85,7 +72,7 @@ bool readList(const std::string& str, const std::string& separators, std::list<s
} }
if (!curStr.empty()) if (!curStr.empty())
results.push_back(stringToUTF8(curStr)); results.push_back(curStr);
return !str.empty(); 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); 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 std::string
bufferToString(const std::vector<unsigned char>& data) bufferToString(const std::vector<unsigned char>& data)
{ {
+19 -18
View File
@@ -19,26 +19,21 @@
#pragma once #pragma once
#include <string> #include <chrono>
#include <vector>
#include <list> #include <list>
#include <string>
#include <sstream>
#include <vector>
#include <boost/locale.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <Wt/WDate.h>
boost::optional<long>
readLong(const std::string& str);
bool
readAsPosixTime(const std::string& str, boost::posix_time::ptime& time);
bool bool
readList(const std::string& str, const std::string& separators, std::list<std::string>& results); readList(const std::string& str, const std::string& separators, std::list<std::string>& results);
std::string //std::string
durationToString(boost::posix_time::time_duration duration, std::string format); //durationToString(boost::posix_time::time_duration duration, std::string format);
std::vector<std::string> std::vector<std::string>
splitString(std::string string, std::string separators); splitString(std::string string, std::string separators);
@@ -49,17 +44,23 @@ stringTrim(const std::string& str, const std::string& whitespaces = " \t");
std::string std::string
stringTrimEnd(const std::string& str, const std::string& whitespaces = " \t"); stringTrimEnd(const std::string& str, const std::string& whitespaces = " \t");
std::string
stringToUTF8(const std::string& str);
std::string std::string
bufferToString(const std::vector<unsigned char>& data); bufferToString(const std::vector<unsigned char>& data);
template<typename T> 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 ); std::istringstream iss ( str );
iss >> data; iss >> res;
return !iss.fail(); if (iss.fail())
return boost::none;
return res;
} }
template<>
boost::optional<Wt::WDate> readAs(const std::string& str);
+7 -7
View File
@@ -1,11 +1,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <chrono>
#include <boost/date_time/time_duration.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include <Wt/WDate.h>
#include "av/AvInfo.hpp" #include "av/AvInfo.hpp"
#include "metadata/AvFormat.hpp" #include "metadata/AvFormat.hpp"
#include "metadata/TagLibParser.hpp" #include "metadata/TagLibParser.hpp"
@@ -67,7 +67,7 @@ int main(int argc, char *argv[])
break; break;
case MetaData::Type::Duration: case MetaData::Type::Duration:
std::cout << "Duration: " << boost::posix_time::to_simple_string(boost::any_cast<boost::posix_time::time_duration>(item.second)) << std::endl; std::cout << "Duration: " << boost::any_cast<std::chrono::milliseconds>(item.second).count() / 1000 << "s" << std::endl;
break; break;
case MetaData::Type::TrackNumber: case MetaData::Type::TrackNumber:
@@ -87,11 +87,11 @@ int main(int argc, char *argv[])
break; break;
case MetaData::Type::Date: case MetaData::Type::Date:
std::cout << "Date: " << boost::posix_time::to_simple_string(boost::any_cast<boost::posix_time::ptime>(item.second)) << std::endl; std::cout << "Date: " << boost::any_cast<Wt::WDate>(item.second).toString() << std::endl;
break; break;
case MetaData::Type::OriginalDate: case MetaData::Type::OriginalDate:
std::cout << "Original date: " << boost::posix_time::to_simple_string(boost::any_cast<boost::posix_time::ptime>(item.second)) << std::endl; std::cout << "Original date: " << boost::any_cast<Wt::WDate>(item.second).toString() << std::endl;
break; break;
case MetaData::Type::HasCover: case MetaData::Type::HasCover:
@@ -100,7 +100,7 @@ int main(int argc, char *argv[])
case MetaData::Type::AudioStreams: case MetaData::Type::AudioStreams:
for (auto& audioStream : boost::any_cast<std::vector<MetaData::AudioStream> >(item.second)) for (auto& audioStream : boost::any_cast<std::vector<MetaData::AudioStream> >(item.second))
std::cout << "Audio stream '" << audioStream.desc << "' - " << audioStream.bitRate << " bps" << std::endl; std::cout << "Audio stream: " << audioStream.bitRate << " bps" << std::endl;
break; break;
case MetaData::Type::MusicBrainzArtistID: case MetaData::Type::MusicBrainzArtistID: