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