Merge branch 'develop'
This commit is contained in:
@@ -1,19 +1,12 @@
|
|||||||
|
|
||||||
[ServiceManager]
|
[Book]
|
||||||
- Rework the whole start/stop/try/cach/thread/interrupts things
|
- Make the feature
|
||||||
- Use our own WIOService
|
|
||||||
|
|
||||||
[Services]
|
|
||||||
- consider adding support for external web servers (new FCGI service?)
|
|
||||||
|
|
||||||
[Users]
|
|
||||||
- Handle login lifetime?
|
|
||||||
- Limit multi login from UI and remote interfaces (limit per interface is acceptable)
|
|
||||||
|
|
||||||
[Cover]
|
[Cover]
|
||||||
- Scaling: find something more "reliable" than GIL and its custom extensions (adobe work, io_new)?
|
- Scaling: find something more "reliable" than GIL and its custom extensions (adobe work, io_new)?
|
||||||
- Handle several file formats (not only jpg)
|
- Handle several file formats (not only jpg)
|
||||||
- Handle preferred cover file names
|
- Handle preferred cover file names
|
||||||
|
- Implement a cache and a grabber from some web service (mandatory for artists)
|
||||||
|
|
||||||
[Database]
|
[Database]
|
||||||
- Optim, use SQL query to get the "genre" orphans
|
- Optim, use SQL query to get the "genre" orphans
|
||||||
@@ -24,8 +17,22 @@
|
|||||||
[Metadata]
|
[Metadata]
|
||||||
- OGG metadata -> properly handle metadata nested in the audio stream
|
- OGG metadata -> properly handle metadata nested in the audio stream
|
||||||
|
|
||||||
|
[Playlist]
|
||||||
|
- Make public playlists so that users can see what other people are listening to
|
||||||
|
|
||||||
|
[Users]
|
||||||
|
- Handle login lifetime?
|
||||||
|
- Limit multi login from UI and remote interfaces (limit per interface is acceptable)
|
||||||
|
|
||||||
|
[ServiceManager]
|
||||||
|
- Rework the whole start/stop/try/cach/thread/interrupts things
|
||||||
|
- Use our own WIOService
|
||||||
|
|
||||||
|
[Services]
|
||||||
|
- consider adding support for external web servers (new FCGI service?)
|
||||||
|
|
||||||
[Transcode]
|
[Transcode]
|
||||||
- some early end of playback spotted on flac files. Spotted on old firefox versions?
|
- some early end of playback spotted on files. Maybe this is because the stderr of the forked process is not properly closed?
|
||||||
|
|
||||||
[UI]
|
[UI]
|
||||||
- handle internationalization
|
- handle internationalization
|
||||||
@@ -44,9 +51,10 @@
|
|||||||
|
|
||||||
[Audio]
|
[Audio]
|
||||||
- Style eveything nicely...
|
- Style eveything nicely...
|
||||||
- MediaPlayer: move the slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478)
|
- MediaPlayer: move the slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478, http://redmine.emweb.be/boards/2/topics/10994)
|
||||||
- TrackView : handle durations > 1 hour
|
- TrackView: handle durations > 1 hour
|
||||||
- TrackView : Reselect the current selected item when displaying the updated search results
|
- TrackView: Reselect the current selected item when displaying the updated search results
|
||||||
|
- TrackView: display the total duration of the track query
|
||||||
- Add covers in the release filter?
|
- Add covers in the release filter?
|
||||||
- Add a download button to get the current playlist in a streamed zip file
|
- Add a download button to get the current playlist in a streamed zip file
|
||||||
- Add a upload button to upload media files in a dedicated directory
|
- Add a upload button to upload media files in a dedicated directory
|
||||||
@@ -65,7 +73,7 @@
|
|||||||
- Implement partial text search options in the GetXXX messages
|
- Implement partial text search options in the GetXXX messages
|
||||||
|
|
||||||
[REST API]
|
[REST API]
|
||||||
- Make another dedicated REST API. Maybe use the SubSonic API?
|
- Make another dedicated REST API. Maybe use the SubSonic API or Ampache API?
|
||||||
|
|
||||||
[Logs]
|
[Logs]
|
||||||
- Capture transcoder log output to get more information on errors
|
- Capture transcoder log output to get more information on errors
|
||||||
|
|||||||
@@ -2,9 +2,14 @@
|
|||||||
main = {
|
main = {
|
||||||
|
|
||||||
logger = {
|
logger = {
|
||||||
file = "/var/lms/lms.log" # comment to disable file logging
|
level = 7; # level common for all loggers
|
||||||
console = true;
|
file = {
|
||||||
level = 7;
|
enable = true;
|
||||||
|
path = "/var/lms/lms.log"; # comment to disable file logging
|
||||||
|
}
|
||||||
|
console = {
|
||||||
|
enable = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
database = {
|
database = {
|
||||||
@@ -16,15 +21,13 @@ main = {
|
|||||||
|
|
||||||
cover = {
|
cover = {
|
||||||
file_extensions = "jpg jpeg";
|
file_extensions = "jpg jpeg";
|
||||||
file_max_size = 500000;
|
file_max_size = 5000000;
|
||||||
file_preferred_names = "cover front";
|
file_preferred_names = "cover front";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ui = {
|
ui = {
|
||||||
enable = true;
|
|
||||||
|
|
||||||
resources = {
|
resources = {
|
||||||
docroot = "/var/lms/docroot"
|
docroot = "/var/lms/docroot"
|
||||||
approot = "/var/lms/approot"
|
approot = "/var/lms/approot"
|
||||||
@@ -44,8 +47,6 @@ ui = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remote = {
|
remote = {
|
||||||
enable = true;
|
|
||||||
|
|
||||||
nb-threads = 1;
|
nb-threads = 1;
|
||||||
|
|
||||||
listen-endpoint = {
|
listen-endpoint = {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "common.proto";
|
|
||||||
|
|
||||||
package LmsAPI;
|
package LmsAPI;
|
||||||
|
|
||||||
message AuthRequest
|
message AuthRequest
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "common.proto";
|
|
||||||
|
|
||||||
package LmsAPI;
|
package LmsAPI;
|
||||||
|
|
||||||
message AudioCollectionRequest
|
message AudioCollectionRequest
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "common.proto";
|
|
||||||
|
|
||||||
package LmsAPI;
|
package LmsAPI;
|
||||||
|
|
||||||
message MediaRequest
|
message MediaRequest
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "common.proto";
|
|
||||||
|
|
||||||
import "auth.proto";
|
import "auth.proto";
|
||||||
import "collection.proto";
|
import "collection.proto";
|
||||||
import "media.proto";
|
import "media.proto";
|
||||||
+2
-8
@@ -81,8 +81,6 @@ nodist_lms_SOURCES = \
|
|||||||
$(builddir)/auth.pb.h \
|
$(builddir)/auth.pb.h \
|
||||||
$(builddir)/collection.pb.cc \
|
$(builddir)/collection.pb.cc \
|
||||||
$(builddir)/collection.pb.h \
|
$(builddir)/collection.pb.h \
|
||||||
$(builddir)/common.pb.cc \
|
|
||||||
$(builddir)/common.pb.h \
|
|
||||||
$(builddir)/media.pb.cc \
|
$(builddir)/media.pb.cc \
|
||||||
$(builddir)/media.pb.h \
|
$(builddir)/media.pb.h \
|
||||||
$(builddir)/messages.pb.cc \
|
$(builddir)/messages.pb.cc \
|
||||||
@@ -93,8 +91,6 @@ BUILT_SOURCES = \
|
|||||||
$(builddir)/auth.pb.h \
|
$(builddir)/auth.pb.h \
|
||||||
$(builddir)/collection.pb.cc \
|
$(builddir)/collection.pb.cc \
|
||||||
$(builddir)/collection.pb.h \
|
$(builddir)/collection.pb.h \
|
||||||
$(builddir)/common.pb.cc \
|
|
||||||
$(builddir)/common.pb.h \
|
|
||||||
$(builddir)/media.pb.cc \
|
$(builddir)/media.pb.cc \
|
||||||
$(builddir)/media.pb.h \
|
$(builddir)/media.pb.h \
|
||||||
$(builddir)/messages.pb.cc \
|
$(builddir)/messages.pb.cc \
|
||||||
@@ -105,15 +101,13 @@ MOSTLYCLEANFILES = \
|
|||||||
$(builddir)/auth.pb.h \
|
$(builddir)/auth.pb.h \
|
||||||
$(builddir)/collection.pb.cc \
|
$(builddir)/collection.pb.cc \
|
||||||
$(builddir)/collection.pb.h \
|
$(builddir)/collection.pb.h \
|
||||||
$(builddir)/common.pb.cc \
|
|
||||||
$(builddir)/common.pb.h \
|
|
||||||
$(builddir)/media.pb.cc \
|
$(builddir)/media.pb.cc \
|
||||||
$(builddir)/media.pb.h \
|
$(builddir)/media.pb.h \
|
||||||
$(builddir)/messages.pb.cc \
|
$(builddir)/messages.pb.cc \
|
||||||
$(builddir)/messages.pb.h
|
$(builddir)/messages.pb.h
|
||||||
|
|
||||||
%.pb.cc %.pb.h: $(srcdir)/lms-api/proto/%.proto
|
%.pb.cc %.pb.h: $(top_srcdir)/lms-api/proto/%.proto
|
||||||
$(PROTOC) --proto_path=$(srcdir)/lms-api/proto/ --cpp_out=$(builddir)/ $^
|
$(PROTOC) --proto_path=$(top_srcdir)/lms-api/proto/ --cpp_out=$(builddir)/ $^
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
+31
-68
@@ -23,89 +23,52 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void splitStrings(const std::string& source, std::vector<std::string>& res)
|
|
||||||
{
|
|
||||||
std::istringstream oss(source);
|
|
||||||
|
|
||||||
std::string str;
|
|
||||||
while(oss >> str)
|
|
||||||
res.push_back(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigReader::ConfigReader(boost::filesystem::path p)
|
ConfigReader::ConfigReader()
|
||||||
|
: _config (nullptr)
|
||||||
{
|
{
|
||||||
_config.readFile(p.string().c_str());
|
}
|
||||||
|
|
||||||
|
ConfigReader&
|
||||||
|
ConfigReader::instance()
|
||||||
|
{
|
||||||
|
static ConfigReader instance;
|
||||||
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ConfigReader::getLoggerConfig(Logger::Config& config)
|
ConfigReader::setFile(boost::filesystem::path p)
|
||||||
{
|
{
|
||||||
config.enableFileLogging = _config.lookupValue("main.logger.file", config.logPath);
|
if (_config != nullptr)
|
||||||
config.enableConsoleLogging = _config.lookup("main.logger.console");
|
delete _config;
|
||||||
config.minSeverity = static_cast<Severity>((int)_config.lookup("main.logger.level"));
|
|
||||||
|
_config = new libconfig::Config();
|
||||||
|
|
||||||
|
_config->readFile(p.string().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
std::string
|
||||||
ConfigReader::getCoverGrabberConfig(CoverArt::Grabber::Config& config)
|
ConfigReader::getString(std::string setting)
|
||||||
{
|
{
|
||||||
std::string extensions = _config.lookup("main.cover.file_extensions");
|
return _config->lookup(setting);
|
||||||
config.maxFileSize = static_cast<unsigned int>(_config.lookup("main.cover.file_max_size"));
|
|
||||||
std::string filenames = _config.lookup("main.cover.file_preferred_names");
|
|
||||||
|
|
||||||
splitStrings(extensions, config.fileExtensions);
|
|
||||||
splitStrings(filenames, config.preferredFileNames);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
unsigned long
|
||||||
ConfigReader::getUserInterfaceConfig(Service::UserInterfaceService::Config& config)
|
ConfigReader::getULong(std::string setting)
|
||||||
{
|
{
|
||||||
|
return static_cast<unsigned int>(_config->lookup(setting));
|
||||||
config.enable = _config.lookup("ui.enable");
|
|
||||||
if (!config.enable)
|
|
||||||
return;
|
|
||||||
|
|
||||||
config.docRootPath = _config.lookup("ui.resources.docroot");
|
|
||||||
config.appRootPath = _config.lookup("ui.resources.approot");
|
|
||||||
config.httpsPort = static_cast<unsigned int>(_config.lookup("ui.listen-endpoint.port"));
|
|
||||||
config.httpsAddress = boost::asio::ip::address::from_string((const char*)_config.lookup("ui.listen-endpoint.addr"));
|
|
||||||
config.sslCertificatePath = _config.lookup("ui.ssl-crypto.cert");
|
|
||||||
config.sslPrivateKeyPath = _config.lookup("ui.ssl-crypto.key");
|
|
||||||
config.sslTempDhPath = _config.lookup("ui.ssl-crypto.dh");
|
|
||||||
|
|
||||||
config.dbPath = _config.lookup("main.database.path");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined HAVE_LMSAPI
|
long
|
||||||
void
|
ConfigReader::getLong(std::string setting)
|
||||||
ConfigReader::getLmsAPIConfig(Service::LmsAPIService::Config& config)
|
|
||||||
{
|
{
|
||||||
config.enable = _config.lookup("remote.enable");
|
return _config->lookup(setting);
|
||||||
if (!config.enable)
|
}
|
||||||
return;
|
|
||||||
|
bool
|
||||||
config.port = static_cast<unsigned int>(_config.lookup("remote.listen-endpoint.port"));
|
ConfigReader::getBool(std::string setting)
|
||||||
config.address = boost::asio::ip::address::from_string((const char*)_config.lookup("remote.listen-endpoint.addr"));
|
{
|
||||||
config.sslCertificatePath = _config.lookup("remote.ssl-crypto.cert");
|
return _config->lookup(setting);
|
||||||
config.sslPrivateKeyPath = _config.lookup("remote.ssl-crypto.key");
|
|
||||||
config.sslTempDhPath = _config.lookup("remote.ssl-crypto.dh");
|
|
||||||
|
|
||||||
config.dbPath = _config.lookup("main.database.path");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void
|
|
||||||
ConfigReader::getDatabaseUpdateConfig(Service::DatabaseUpdateService::Config& config)
|
|
||||||
{
|
|
||||||
config.enable = true;
|
|
||||||
|
|
||||||
config.dbPath = _config.lookup("main.database.path");
|
|
||||||
|
|
||||||
std::string audioExtensions = _config.lookup("main.database.audio_extensions");
|
|
||||||
std::string videoExtensions = _config.lookup("main.database.video_extensions");
|
|
||||||
|
|
||||||
splitStrings(audioExtensions, config.audioExtensions);
|
|
||||||
splitStrings(videoExtensions, config.videoExtensions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-24
@@ -23,40 +23,27 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <libconfig.h++>
|
#include <libconfig.h++>
|
||||||
|
|
||||||
#include "config/config.h"
|
|
||||||
|
|
||||||
#include "cover/CoverArtGrabber.hpp"
|
|
||||||
#include "logger/Logger.hpp"
|
|
||||||
|
|
||||||
#include "service/UserInterfaceService.hpp"
|
|
||||||
#include "service/DatabaseUpdateService.hpp"
|
|
||||||
#if defined HAVE_LMSAPI
|
|
||||||
#include "service/LmsAPIServerService.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class ConfigReader
|
class ConfigReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ConfigReader(boost::filesystem::path p);
|
ConfigReader(const ConfigReader&) = delete;
|
||||||
|
ConfigReader& operator=(const ConfigReader&) = delete;
|
||||||
|
|
||||||
// Logger configuration
|
static ConfigReader& instance();
|
||||||
void getLoggerConfig(Logger::Config& config);
|
|
||||||
|
|
||||||
// Covers
|
void setFile(boost::filesystem::path p);
|
||||||
void getCoverGrabberConfig(CoverArt::Grabber::Config& config);
|
|
||||||
|
|
||||||
// Service configurations
|
std::string getString(std::string setting);
|
||||||
void getUserInterfaceConfig(Service::UserInterfaceService::Config& config);
|
unsigned long getULong(std::string setting);
|
||||||
void getDatabaseUpdateConfig(Service::DatabaseUpdateService::Config& config);
|
long getLong(std::string setting);
|
||||||
|
bool getBool(std::string setting);
|
||||||
#if defined HAVE_LMSAPI
|
|
||||||
void getLmsAPIConfig(Service::LmsAPIService::Config& config);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
libconfig::Config _config;
|
ConfigReader();
|
||||||
|
|
||||||
|
libconfig::Config *_config;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -17,10 +17,8 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
#include "config/ConfigReader.hpp"
|
||||||
#include "av/InputFormatContext.hpp"
|
#include "av/InputFormatContext.hpp"
|
||||||
|
|
||||||
#include "CoverArtGrabber.hpp"
|
#include "CoverArtGrabber.hpp"
|
||||||
@@ -28,6 +26,18 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
std::vector<std::string> splitStrings(const std::string& source)
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
std::istringstream oss(source);
|
||||||
|
|
||||||
|
std::string str;
|
||||||
|
while(oss >> str)
|
||||||
|
res.push_back(str);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions)
|
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions)
|
||||||
{
|
{
|
||||||
@@ -58,12 +68,12 @@ Grabber::instance()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Grabber::init(const Config& config)
|
Grabber::init()
|
||||||
{
|
{
|
||||||
for (auto extension : config.fileExtensions)
|
for (const std::string& extension : splitStrings( ConfigReader::instance().getString("main.cover.file_extensions")))
|
||||||
_fileExtensions.push_back("." + extension);
|
_fileExtensions.push_back("." + extension);
|
||||||
|
|
||||||
_maxFileSize = config.maxFileSize;
|
_maxFileSize = ConfigReader::instance().getULong("main.cover.file_max_size");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CoverArt>
|
std::vector<CoverArt>
|
||||||
@@ -75,7 +85,7 @@ Grabber::getFromInputFormatContext(const Av::InputFormatContext& input, std::siz
|
|||||||
{
|
{
|
||||||
std::vector<Av::Picture> pictures = input.getPictures(nbMaxCovers);
|
std::vector<Av::Picture> pictures = input.getPictures(nbMaxCovers);
|
||||||
|
|
||||||
BOOST_FOREACH(const Av::Picture& picture, pictures)
|
for (Av::Picture& picture : pictures)
|
||||||
res.push_back( CoverArt(picture.mimeType, picture.data) );
|
res.push_back( CoverArt(picture.mimeType, picture.data) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,14 +38,7 @@ class Grabber
|
|||||||
|
|
||||||
static Grabber& instance();
|
static Grabber& instance();
|
||||||
|
|
||||||
struct Config
|
void init();
|
||||||
{
|
|
||||||
std::vector<std::string> fileExtensions;
|
|
||||||
std::size_t maxFileSize;
|
|
||||||
std::vector<std::string> preferredFileNames;
|
|
||||||
};
|
|
||||||
|
|
||||||
void init(const Config& config);
|
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers = 1) const;
|
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers = 1) const;
|
||||||
std::vector<CoverArt> getFromDirectory(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
|
std::vector<CoverArt> getFromDirectory(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
|
||||||
|
|||||||
@@ -493,6 +493,7 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
|||||||
track.modify()->setLastWriteTime(lastWriteTime);
|
track.modify()->setLastWriteTime(lastWriteTime);
|
||||||
track.modify()->setName(title);
|
track.modify()->setName(title);
|
||||||
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]) );
|
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]) );
|
||||||
|
track.modify()->setAddedTime( boost::posix_time::second_clock::local_time() );
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string trackGenreList;
|
std::string trackGenreList;
|
||||||
@@ -630,19 +631,15 @@ void
|
|||||||
Updater::checkAudioFiles( Stats& stats )
|
Updater::checkAudioFiles( Stats& stats )
|
||||||
{
|
{
|
||||||
|
|
||||||
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking audio files...";
|
LMS_LOG(MOD_DBUPDATER, SEV_INFO) << "Checking audio files...";
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> rootDirs = getRootDirectoriesByType(_db.getSession(), Database::MediaDirectory::Audio);
|
std::vector<boost::filesystem::path> rootDirs = getRootDirectoriesByType(_db.getSession(), Database::MediaDirectory::Audio);
|
||||||
|
|
||||||
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking tracks...";
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking tracks...";
|
||||||
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Track> > Tracks;
|
auto tracks = Track::getAll(_db.getSession());
|
||||||
Tracks tracks = Track::getAll(_db.getSession());
|
for (auto track : tracks)
|
||||||
|
|
||||||
for (Tracks::iterator it = tracks.begin(); it != tracks.end(); ++it)
|
|
||||||
{
|
{
|
||||||
Track::pointer track = (*it);
|
|
||||||
|
|
||||||
if (!checkFile(track->getPath(), rootDirs, _audioExtensions))
|
if (!checkFile(track->getPath(), rootDirs, _audioExtensions))
|
||||||
{
|
{
|
||||||
track.remove();
|
track.remove();
|
||||||
@@ -651,20 +648,34 @@ Updater::checkAudioFiles( Stats& stats )
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now process orphan Genre (no track)
|
// Now process orphan Genre (no track)
|
||||||
/* LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking Genres...";
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking Genres...";
|
||||||
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Genre> > Genres;
|
auto genres = Genre::getAll(_db.getSession());
|
||||||
Genres genres = Genre::getAll(_db.getSession());
|
for (auto genre : genres)
|
||||||
|
|
||||||
for (Genres::iterator it = genres.begin(); it != genres.end(); ++it)
|
|
||||||
{
|
{
|
||||||
Genre::pointer genre = (*it);
|
|
||||||
|
|
||||||
if (genre->getTracks().size() == 0)
|
if (genre->getTracks().size() == 0)
|
||||||
|
{
|
||||||
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Removing orphan genre '" << genre->getName() << "'";
|
||||||
genre.remove();
|
genre.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Check audio files done!";
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking artists...";
|
||||||
|
auto artists = Artist::getAllOrphans(_db.getSession());
|
||||||
|
for (auto artist : artists)
|
||||||
|
{
|
||||||
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Removing orphan artist '" << artist->getName() << "'";
|
||||||
|
artist.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking releases...";
|
||||||
|
auto releases = Release::getAllOrphans(_db.getSession());
|
||||||
|
for (auto release : releases)
|
||||||
|
{
|
||||||
|
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Removing orphan release '" << release->getName() << "'";
|
||||||
|
release.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
LMS_LOG(MOD_DBUPDATER, SEV_INFO) << "Check audio files done!";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ Artist::getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset,
|
|||||||
return std::vector<pointer>(res.begin(), res.end());
|
return std::vector<pointer>(res.begin(), res.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Wt::Dbo::ptr<Release> >
|
||||||
|
Artist::getReleases() const
|
||||||
|
{
|
||||||
|
assert(self());
|
||||||
|
assert(self()->id() != Wt::Dbo::dbo_traits<Artist>::invalidId() );
|
||||||
|
assert(session());
|
||||||
|
|
||||||
|
Wt::Dbo::collection< Wt::Dbo::ptr<Release> > res = session()->query<Wt::Dbo::ptr<Release> >("SELECT r FROM release r INNER JOIN artist a ON t.artist_id = a.id INNER JOIN track t ON t.release_id = r.id").where("a.id = ?").bind(id());
|
||||||
|
|
||||||
|
return std::vector< Wt::Dbo::ptr<Release> > (res.begin(), res.end());
|
||||||
|
}
|
||||||
|
|
||||||
Wt::Dbo::Query<Artist::pointer>
|
Wt::Dbo::Query<Artist::pointer>
|
||||||
Artist::getQuery(Wt::Dbo::Session& session, SearchFilter filter)
|
Artist::getQuery(Wt::Dbo::Session& session, SearchFilter filter)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ namespace Database
|
|||||||
|
|
||||||
class Track;
|
class Track;
|
||||||
class Genre;
|
class Genre;
|
||||||
|
class Release;
|
||||||
|
|
||||||
class Artist
|
class Artist : public Wt::Dbo::Dbo<Artist>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -58,6 +59,9 @@ class Artist
|
|||||||
std::string getName(void) const { return _name; }
|
std::string getName(void) const { return _name; }
|
||||||
std::string getMBID(void) const { return _MBID; }
|
std::string getMBID(void) const { return _MBID; }
|
||||||
|
|
||||||
|
// Get the releases that have at least one track for this artist
|
||||||
|
std::vector<Wt::Dbo::ptr<Release> > getReleases() const;
|
||||||
|
|
||||||
void setMBID(std::string mbid) { _MBID = mbid; }
|
void setMBID(std::string mbid) { _MBID = mbid; }
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Release::getAll(Wt::Dbo::Session& session, int offset, int size)
|
|||||||
std::vector<Release::pointer>
|
std::vector<Release::pointer>
|
||||||
Release::getAllOrphans(Wt::Dbo::Session& session)
|
Release::getAllOrphans(Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
Wt::Dbo::collection<Release::pointer> res = session.query< Wt::Dbo::ptr<Release> >("select a from artist a LEFT OUTER JOIN Track t ON a.id = t.artist_id WHERE t.id IS NULL");
|
Wt::Dbo::collection<Release::pointer> res = session.query< Wt::Dbo::ptr<Release> >("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
|
||||||
|
|
||||||
return std::vector<pointer>(res.begin(), res.end());
|
return std::vector<pointer>(res.begin(), res.end());
|
||||||
}
|
}
|
||||||
@@ -129,4 +129,16 @@ Release::getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset,
|
|||||||
return std::vector<pointer>(res.begin(), res.end());
|
return std::vector<pointer>(res.begin(), res.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector< Wt::Dbo::ptr<Artist> >
|
||||||
|
Release::getArtists() const
|
||||||
|
{
|
||||||
|
assert(self());
|
||||||
|
assert(self()->id() != Wt::Dbo::dbo_traits<Release>::invalidId() );
|
||||||
|
assert(session());
|
||||||
|
|
||||||
|
Wt::Dbo::collection< Wt::Dbo::ptr<Artist> > res = session()->query<Wt::Dbo::ptr<Artist> >("SELECT a FROM artist a INNER JOIN release r ON r.id = t.release_id INNER JOIN track t ON t.release_id = r.id").where("r.id = ?").bind(id());
|
||||||
|
|
||||||
|
return std::vector< Wt::Dbo::ptr<Artist> > (res.begin(), res.end());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Database
|
} // namespace Database
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ namespace Database
|
|||||||
{
|
{
|
||||||
|
|
||||||
class Track;
|
class Track;
|
||||||
|
class Release;
|
||||||
|
|
||||||
class Release
|
class Release : public Wt::Dbo::Dbo<Release>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -64,6 +65,8 @@ class Release
|
|||||||
std::string getMBID() const { return _MBID; }
|
std::string getMBID() const { return _MBID; }
|
||||||
bool isNone(void) const;
|
bool isNone(void) const;
|
||||||
boost::posix_time::time_duration getDuration(void) const;
|
boost::posix_time::time_duration getDuration(void) const;
|
||||||
|
std::vector<Wt::Dbo::ptr<Artist> > getArtists() const; // Get the artists of this release
|
||||||
|
std::vector<Wt::Dbo::ptr<Track> > getTracks() const; // Get the tracks of this release
|
||||||
|
|
||||||
void setMBID(std::string mbid) { _MBID = mbid; }
|
void setMBID(std::string mbid) { _MBID = mbid; }
|
||||||
|
|
||||||
|
|||||||
+17
-4
@@ -113,6 +113,20 @@ Track::getUIQuery(Wt::Dbo::Session& session, SearchFilter filter)
|
|||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Track::StatsQueryResult
|
||||||
|
Track::getStats(Wt::Dbo::Session& session, SearchFilter filter)
|
||||||
|
{
|
||||||
|
SqlQuery sqlQuery = generatePartialQuery(filter);
|
||||||
|
|
||||||
|
Wt::Dbo::Query<StatsQueryResult> query = session.query<StatsQueryResult>( "SELECT COUNT(DISTINCT t.id), SUM(t.duration) FROM track t INNER JOIN artist a ON t.artist_id = a.id INNER JOIN genre g ON g.id = t_g.genre_id INNER JOIN track_genre t_g ON t_g.track_id = t.id INNER JOIN release r ON r.id = t.release_id " + sqlQuery.where().get());
|
||||||
|
|
||||||
|
for (const std::string& bindArg : sqlQuery.where().getBindArgs())
|
||||||
|
query.bind(bindArg);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<Track::pointer>
|
std::vector<Track::pointer>
|
||||||
Track::getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size)
|
Track::getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset, int size)
|
||||||
{
|
{
|
||||||
@@ -152,11 +166,10 @@ Genre::Genre(const std::string& name)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Genre::pointer>
|
Wt::Dbo::collection<Genre::pointer>
|
||||||
Genre::getAll(Wt::Dbo::Session& session, int offset, int size)
|
Genre::getAll(Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
Wt::Dbo::collection<pointer> res = session.find<Genre>().offset(offset).limit(size);
|
return session.find<Genre>();
|
||||||
return std::vector<Genre::pointer>(res.begin(), res.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Genre::pointer
|
Genre::pointer
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Genre
|
|||||||
static pointer getByName(Wt::Dbo::Session& session, const std::string& name);
|
static pointer getByName(Wt::Dbo::Session& session, const std::string& name);
|
||||||
static pointer getNone(Wt::Dbo::Session& session);
|
static pointer getNone(Wt::Dbo::Session& session);
|
||||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
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, int offset = -1, int size = -1);
|
static Wt::Dbo::collection<pointer> getAll(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
// MVC models for the user interface
|
// MVC models for the user interface
|
||||||
// Genre ID, name, track count
|
// Genre ID, name, track count
|
||||||
@@ -105,10 +105,8 @@ class Track
|
|||||||
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
||||||
static pointer getById(Wt::Dbo::Session& session, id_type id);
|
static pointer getById(Wt::Dbo::Session& session, id_type id);
|
||||||
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
|
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
|
||||||
static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session);
|
|
||||||
|
|
||||||
// Used for remote
|
|
||||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
||||||
|
static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
// Utility fonctions
|
// Utility fonctions
|
||||||
// MVC models for the user interface
|
// MVC models for the user interface
|
||||||
@@ -127,6 +125,13 @@ class Track
|
|||||||
static Wt::Dbo::Query< UIQueryResult > getUIQuery(Wt::Dbo::Session& session, SearchFilter filter);
|
static Wt::Dbo::Query< UIQueryResult > getUIQuery(Wt::Dbo::Session& session, SearchFilter filter);
|
||||||
static void updateUIQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< UIQueryResult >& model, SearchFilter filter, const std::vector<Wt::WString>& columnNames = std::vector<Wt::WString>());
|
static void updateUIQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< UIQueryResult >& model, SearchFilter filter, const std::vector<Wt::WString>& columnNames = std::vector<Wt::WString>());
|
||||||
|
|
||||||
|
// Stats for a given search filter
|
||||||
|
typedef boost::tuple<
|
||||||
|
int, // Total tracks
|
||||||
|
boost::posix_time::time_duration // Total duration
|
||||||
|
> StatsQueryResult;
|
||||||
|
static StatsQueryResult getStats(Wt::Dbo::Session& session, SearchFilter filter);
|
||||||
|
|
||||||
// Create utility
|
// Create utility
|
||||||
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package LmsAPI;
|
|
||||||
|
|
||||||
message Error
|
|
||||||
{
|
|
||||||
required bool error = 1;
|
|
||||||
optional string message = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
#include <boost/log/core.hpp>
|
#include <boost/log/core.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/log/expressions.hpp>
|
#include <boost/log/expressions.hpp>
|
||||||
@@ -37,6 +35,8 @@
|
|||||||
|
|
||||||
#include <boost/date_time/posix_time/ptime.hpp>
|
#include <boost/date_time/posix_time/ptime.hpp>
|
||||||
|
|
||||||
|
#include "config/ConfigReader.hpp"
|
||||||
|
|
||||||
|
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ Logger::Logger()
|
|||||||
MOD_UI,
|
MOD_UI,
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_FOREACH(Module module, modules)
|
for(Module module : modules)
|
||||||
_loggers[module].add_attribute("Module", boost::log::attributes::constant< Module >(module));
|
_loggers[module].add_attribute("Module", boost::log::attributes::constant< Module >(module));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,19 +74,18 @@ Logger::get(Module module)
|
|||||||
return _loggers[module];
|
return _loggers[module];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Logger::init(const Config& config)
|
Logger::init()
|
||||||
{
|
{
|
||||||
boost::log::add_common_attributes();
|
boost::log::add_common_attributes();
|
||||||
|
|
||||||
boost::log::register_simple_formatter_factory< Severity, char >("Severity");
|
boost::log::register_simple_formatter_factory< Severity, char >("Severity");
|
||||||
|
|
||||||
if (config.enableFileLogging)
|
if (ConfigReader::instance().getBool("main.logger.file.enable"))
|
||||||
{
|
{
|
||||||
boost::log::add_file_log
|
boost::log::add_file_log
|
||||||
(
|
(
|
||||||
boost::log::keywords::file_name = config.logPath + std::string(".%N"),
|
boost::log::keywords::file_name = ConfigReader::instance().getString("main.logger.file.path") + std::string(".%N"),
|
||||||
boost::log::keywords::rotation_size = 10 * 1024 * 1024,
|
boost::log::keywords::rotation_size = 10 * 1024 * 1024,
|
||||||
boost::log::keywords::open_mode = std::ios_base::app,
|
boost::log::keywords::open_mode = std::ios_base::app,
|
||||||
boost::log::keywords::auto_flush = true,
|
boost::log::keywords::auto_flush = true,
|
||||||
@@ -100,7 +99,7 @@ Logger::init(const Config& config)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.enableConsoleLogging)
|
if (ConfigReader::instance().getBool("main.logger.console.enable"))
|
||||||
{
|
{
|
||||||
boost::log::add_console_log(std::cout,
|
boost::log::add_console_log(std::cout,
|
||||||
boost::log::keywords::format = (
|
boost::log::keywords::format = (
|
||||||
@@ -115,7 +114,7 @@ Logger::init(const Config& config)
|
|||||||
|
|
||||||
boost::log::core::get()->set_filter
|
boost::log::core::get()->set_filter
|
||||||
(
|
(
|
||||||
boost::log::expressions::attr<Severity>("Severity") <= config.minSeverity
|
boost::log::expressions::attr<Severity>("Severity") <= ConfigReader::instance().getULong("main.logger.level")
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,15 +65,8 @@ class Logger
|
|||||||
|
|
||||||
static Logger& instance();
|
static Logger& instance();
|
||||||
|
|
||||||
struct Config {
|
|
||||||
bool enableFileLogging;
|
|
||||||
bool enableConsoleLogging;
|
|
||||||
std::string logPath;
|
|
||||||
Severity minSeverity;
|
|
||||||
};
|
|
||||||
|
|
||||||
//[ example_tutorial_file_advanced
|
//[ example_tutorial_file_advanced
|
||||||
void init(const Config& config);
|
void init();
|
||||||
|
|
||||||
boost::log::sources::severity_logger< Severity >&
|
boost::log::sources::severity_logger< Severity >&
|
||||||
get(Module module);
|
get(Module module);
|
||||||
|
|||||||
+8
-34
@@ -20,11 +20,11 @@
|
|||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "logger/Logger.hpp"
|
|
||||||
|
|
||||||
#include "config/ConfigReader.hpp"
|
#include "config/ConfigReader.hpp"
|
||||||
#include "transcode/AvConvTranscoder.hpp"
|
#include "transcode/AvConvTranscoder.hpp"
|
||||||
#include "av/Common.hpp"
|
#include "av/Common.hpp"
|
||||||
|
#include "logger/Logger.hpp"
|
||||||
|
#include "cover/CoverArtGrabber.hpp"
|
||||||
|
|
||||||
#include "service/ServiceManager.hpp"
|
#include "service/ServiceManager.hpp"
|
||||||
#include "service/DatabaseUpdateService.hpp"
|
#include "service/DatabaseUpdateService.hpp"
|
||||||
@@ -61,28 +61,10 @@ int main(int argc, char* argv[])
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigReader configReader(configFile);
|
ConfigReader::instance().setFile(configFile);
|
||||||
|
|
||||||
// Initializa logging facility
|
Logger::instance().init();
|
||||||
{
|
CoverArt::Grabber::instance().init();
|
||||||
Logger::Config loggerConfig;
|
|
||||||
configReader.getLoggerConfig(loggerConfig);
|
|
||||||
Logger::instance().init(loggerConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
CoverArt::Grabber::Config config;
|
|
||||||
configReader.getCoverGrabberConfig(config);
|
|
||||||
CoverArt::Grabber::instance().init(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
LMS_LOG(MOD_MAIN, SEV_INFO) << "Reading service configurations...";
|
|
||||||
|
|
||||||
Service::DatabaseUpdateService::Config dbUpdateConfig;
|
|
||||||
configReader.getDatabaseUpdateConfig(dbUpdateConfig);
|
|
||||||
|
|
||||||
Service::UserInterfaceService::Config uiConfig;
|
|
||||||
configReader.getUserInterfaceConfig(uiConfig);
|
|
||||||
|
|
||||||
Service::ServiceManager& serviceManager = Service::ServiceManager::instance();
|
Service::ServiceManager& serviceManager = Service::ServiceManager::instance();
|
||||||
|
|
||||||
@@ -93,20 +75,12 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
LMS_LOG(MOD_MAIN, SEV_INFO) << "Starting services...";
|
LMS_LOG(MOD_MAIN, SEV_INFO) << "Starting services...";
|
||||||
|
|
||||||
if (dbUpdateConfig.enable)
|
serviceManager.startService( std::make_shared<Service::DatabaseUpdateService>() );
|
||||||
serviceManager.startService( std::make_shared<Service::DatabaseUpdateService>( dbUpdateConfig ) );
|
serviceManager.startService( std::make_shared<Service::UserInterfaceService>(boost::filesystem::path(argv[0])));
|
||||||
|
|
||||||
#if defined HAVE_LMSAPI
|
#if defined HAVE_LMSAPI
|
||||||
Service::LmsAPIService::Config lmsAPIConfig;
|
serviceManager.startService( std::make_shared<Service::LmsAPIService>( ));
|
||||||
configReader.getLmsAPIConfig(lmsAPIConfig);
|
|
||||||
|
|
||||||
if (lmsAPIConfig.enable)
|
|
||||||
serviceManager.startService( std::make_shared<Service::LmsAPIService>( lmsAPIConfig ));
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (uiConfig.enable)
|
|
||||||
serviceManager.startService( std::make_shared<Service::UserInterfaceService>(boost::filesystem::path(argv[0]), uiConfig));
|
|
||||||
|
|
||||||
LMS_LOG(MOD_MAIN, SEV_NOTICE) << "Now running...";
|
LMS_LOG(MOD_MAIN, SEV_NOTICE) << "Now running...";
|
||||||
|
|
||||||
serviceManager.run();
|
serviceManager.run();
|
||||||
|
|||||||
@@ -19,18 +19,32 @@
|
|||||||
|
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
|
#include "config/ConfigReader.hpp"
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
|
||||||
#include "DatabaseUpdateService.hpp"
|
#include "DatabaseUpdateService.hpp"
|
||||||
|
|
||||||
|
static std::vector<std::string> splitStrings(const std::string& source)
|
||||||
|
{
|
||||||
|
std::vector<std::string> res;
|
||||||
|
std::istringstream oss(source);
|
||||||
|
|
||||||
|
std::string str;
|
||||||
|
while(oss >> str)
|
||||||
|
res.push_back(str);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
DatabaseUpdateService::DatabaseUpdateService(const Config& config)
|
DatabaseUpdateService::DatabaseUpdateService()
|
||||||
: _metadataParser(),
|
: _metadataParser(),
|
||||||
_databaseUpdater( config.dbPath, _metadataParser)
|
_databaseUpdater( ConfigReader::instance().getString("main.database.path"),
|
||||||
|
_metadataParser)
|
||||||
{
|
{
|
||||||
_databaseUpdater.setAudioExtensions(config.audioExtensions);
|
_databaseUpdater.setAudioExtensions(splitStrings(ConfigReader::instance().getString("main.database.audio_extensions")));
|
||||||
_databaseUpdater.setVideoExtensions(config.videoExtensions);
|
_databaseUpdater.setVideoExtensions(splitStrings(ConfigReader::instance().getString("main.database.video_extensions")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -36,14 +36,7 @@ class DatabaseUpdateService : public Service
|
|||||||
|
|
||||||
typedef std::shared_ptr<DatabaseUpdateService> pointer;
|
typedef std::shared_ptr<DatabaseUpdateService> pointer;
|
||||||
|
|
||||||
struct Config {
|
DatabaseUpdateService();
|
||||||
bool enable;
|
|
||||||
boost::filesystem::path dbPath;
|
|
||||||
std::vector<std::string> audioExtensions;
|
|
||||||
std::vector<std::string> videoExtensions;
|
|
||||||
};
|
|
||||||
|
|
||||||
DatabaseUpdateService(const Config& config);
|
|
||||||
|
|
||||||
// Service interface
|
// Service interface
|
||||||
void start(void);
|
void start(void);
|
||||||
|
|||||||
@@ -17,18 +17,24 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
|
#include "config/ConfigReader.hpp"
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
|
||||||
#include "LmsAPIServerService.hpp"
|
#include "LmsAPIServerService.hpp"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
LmsAPIService::LmsAPIService(const Config& config)
|
LmsAPIService::LmsAPIService()
|
||||||
: _server(boost::asio::ip::tcp::endpoint(config.address, config.port),
|
: _server(
|
||||||
config.sslCertificatePath,
|
boost::asio::ip::tcp::endpoint(
|
||||||
config.sslPrivateKeyPath,
|
boost::asio::ip::address::from_string(ConfigReader::instance().getString("remote.listen-endpoint.addr")),
|
||||||
config.sslTempDhPath,
|
ConfigReader::instance().getULong("remote.listen-endpoint.port")),
|
||||||
config.dbPath)
|
ConfigReader::instance().getString("remote.ssl-crypto.cert"),
|
||||||
|
ConfigReader::instance().getString("remote.ssl-crypto.key"),
|
||||||
|
ConfigReader::instance().getString("remote.ssl-crypto.dh"),
|
||||||
|
ConfigReader::instance().getString("main.database.path"))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#define REMOTE_SERVER_SERVICE_HPP
|
#define REMOTE_SERVER_SERVICE_HPP
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/asio/ip/address.hpp>
|
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
|
|
||||||
@@ -35,17 +34,7 @@ class LmsAPIService : public Service
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Config {
|
LmsAPIService();
|
||||||
bool enable;
|
|
||||||
boost::asio::ip::address address;
|
|
||||||
unsigned short port;
|
|
||||||
boost::filesystem::path sslCertificatePath;
|
|
||||||
boost::filesystem::path sslPrivateKeyPath;
|
|
||||||
boost::filesystem::path sslTempDhPath;
|
|
||||||
boost::filesystem::path dbPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
LmsAPIService(const Config& config);
|
|
||||||
|
|
||||||
void start(void);
|
void start(void);
|
||||||
void stop(void);
|
void stop(void);
|
||||||
|
|||||||
@@ -22,24 +22,23 @@
|
|||||||
#include "UserInterfaceService.hpp"
|
#include "UserInterfaceService.hpp"
|
||||||
#include "ui/LmsApplication.hpp"
|
#include "ui/LmsApplication.hpp"
|
||||||
|
|
||||||
|
#include "config/ConfigReader.hpp"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
UserInterfaceService::UserInterfaceService( boost::filesystem::path runAppPath, const Config& config)
|
UserInterfaceService::UserInterfaceService( boost::filesystem::path runAppPath)
|
||||||
: _server(runAppPath.string())
|
: _server(runAppPath.string())
|
||||||
{
|
{
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
args.push_back(runAppPath.string());
|
args.push_back(runAppPath.string());
|
||||||
args.push_back("--docroot=" + config.docRootPath.string());
|
args.push_back("--docroot=" + ConfigReader::instance().getString("ui.resources.docroot"));
|
||||||
args.push_back("--approot=" + config.appRootPath.string());
|
args.push_back("--approot=" + ConfigReader::instance().getString("ui.resources.approot"));
|
||||||
{
|
args.push_back("--https-port=" + std::to_string( ConfigReader::instance().getULong("ui.listen-endpoint.port")));
|
||||||
std::ostringstream oss; oss << config.httpsPort;
|
args.push_back("--https-address=" + ConfigReader::instance().getString("ui.listen-endpoint.addr"));
|
||||||
args.push_back("--https-port=" + oss.str());
|
args.push_back("--ssl-certificate=" + ConfigReader::instance().getString("ui.ssl-crypto.cert"));
|
||||||
}
|
args.push_back("--ssl-private-key=" + ConfigReader::instance().getString("ui.ssl-crypto.key"));
|
||||||
args.push_back("--https-address=" + config.httpsAddress.to_string());
|
args.push_back("--ssl-tmp-dh=" + ConfigReader::instance().getString("ui.ssl-crypto.dh"));
|
||||||
args.push_back("--ssl-certificate=" + config.sslCertificatePath.string());
|
|
||||||
args.push_back("--ssl-private-key=" + config.sslPrivateKeyPath.string());
|
|
||||||
args.push_back("--ssl-tmp-dh=" + config.sslTempDhPath.string());
|
|
||||||
|
|
||||||
// Construct argc/argv
|
// Construct argc/argv
|
||||||
int argc = args.size();
|
int argc = args.size();
|
||||||
@@ -55,7 +54,7 @@ UserInterfaceService::UserInterfaceService( boost::filesystem::path runAppPath,
|
|||||||
_server.setServerConfiguration (argc, const_cast<char**>(argv));
|
_server.setServerConfiguration (argc, const_cast<char**>(argv));
|
||||||
|
|
||||||
// bind entry point
|
// bind entry point
|
||||||
_server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, _1, config.dbPath));
|
_server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, _1, ConfigReader::instance().getString("main.database.path")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#define WEB_SERVER_SERVICE_HPP
|
#define WEB_SERVER_SERVICE_HPP
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/asio/ip/address.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WServer>
|
#include <Wt/WServer>
|
||||||
|
|
||||||
@@ -33,20 +32,7 @@ class UserInterfaceService : public Service
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Config {
|
UserInterfaceService(boost::filesystem::path runAppPath);
|
||||||
bool enable;
|
|
||||||
boost::filesystem::path docRootPath;
|
|
||||||
boost::filesystem::path appRootPath;
|
|
||||||
unsigned short httpsPort;
|
|
||||||
boost::asio::ip::address httpsAddress;
|
|
||||||
boost::filesystem::path sslCertificatePath;
|
|
||||||
boost::filesystem::path sslPrivateKeyPath;
|
|
||||||
boost::filesystem::path sslTempDhPath;
|
|
||||||
boost::filesystem::path dbPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
UserInterfaceService(boost::filesystem::path runAppPath,
|
|
||||||
const Config& config);
|
|
||||||
|
|
||||||
void start(void);
|
void start(void);
|
||||||
void stop(void);
|
void stop(void);
|
||||||
|
|||||||
@@ -157,14 +157,12 @@ AvConvTranscoder::AvConvTranscoder(const Parameters& parameters)
|
|||||||
// See boost process FAQ
|
// See boost process FAQ
|
||||||
{
|
{
|
||||||
boost::lock_guard<boost::mutex> lock(_mutex);
|
boost::lock_guard<boost::mutex> lock(_mutex);
|
||||||
std::vector<int> ranges = { 3, 1024 }; // fd range to be closed
|
|
||||||
|
|
||||||
_child = std::make_shared<boost::process::child>( boost::process::execute(
|
_child = std::make_shared<boost::process::child>( boost::process::execute(
|
||||||
boost::process::initializers::run_exe(_avConvPath),
|
boost::process::initializers::run_exe(_avConvPath),
|
||||||
boost::process::initializers::set_cmd_line(oss.str()),
|
boost::process::initializers::set_cmd_line(oss.str()),
|
||||||
boost::process::initializers::bind_stdout(sink),
|
boost::process::initializers::bind_stdout(sink),
|
||||||
boost::process::initializers::close_fd(STDIN_FILENO),
|
boost::process::initializers::close_fds_if([](int fd) { return fd != STDOUT_FILENO;})
|
||||||
boost::process::initializers::close_fds(ranges)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,8 +102,13 @@ _playQueue(nullptr)
|
|||||||
Wt::WPushButton* addBtn = new Wt::WPushButton("Add");
|
Wt::WPushButton* addBtn = new Wt::WPushButton("Add");
|
||||||
addBtn->setStyleClass("btn-sm");
|
addBtn->setStyleClass("btn-sm");
|
||||||
trackControls->addWidget(addBtn);
|
trackControls->addWidget(addBtn);
|
||||||
trackControls->addWidget(new Wt::WText("Total duration: "), 1);
|
|
||||||
|
|
||||||
|
Wt::WText *statsText = new Wt::WText();
|
||||||
|
statsText->setStyleClass("vertical-align");
|
||||||
|
trackControls->addWidget(statsText, 1);
|
||||||
|
_trackView->statsUpdated().connect(std::bind([=] (Wt::WString stats) {
|
||||||
|
statsText->setText(stats);
|
||||||
|
}, std::placeholders::_1));
|
||||||
|
|
||||||
trackLayout->addLayout(trackControls);
|
trackLayout->addLayout(trackControls);
|
||||||
|
|
||||||
@@ -111,6 +116,7 @@ _playQueue(nullptr)
|
|||||||
|
|
||||||
_filterChain.addFilter(_trackView);
|
_filterChain.addFilter(_trackView);
|
||||||
|
|
||||||
|
|
||||||
_playQueue = new PlayQueue();
|
_playQueue = new PlayQueue();
|
||||||
|
|
||||||
// Playlist/PlayQueue
|
// Playlist/PlayQueue
|
||||||
@@ -216,6 +222,9 @@ _playQueue(nullptr)
|
|||||||
|
|
||||||
|
|
||||||
playlistRefreshMenus();
|
playlistRefreshMenus();
|
||||||
|
|
||||||
|
// Initially, search for everything
|
||||||
|
_filterChain.searchKeyword("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -30,14 +30,6 @@ namespace Desktop {
|
|||||||
class Filter
|
class Filter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct Constraint {
|
|
||||||
std::vector<std::string> search;
|
|
||||||
|
|
||||||
typedef std::map<std::string, std::vector<std::string> > ColumnValues;
|
|
||||||
ColumnValues columnValues;
|
|
||||||
};
|
|
||||||
|
|
||||||
Filter() {}
|
Filter() {}
|
||||||
virtual ~Filter() {}
|
virtual ~Filter() {}
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,8 @@ _trackSelector(new TrackSelector())
|
|||||||
this->setColumnWidth(COLUMN_ID_NAME, 240);
|
this->setColumnWidth(COLUMN_ID_NAME, 240);
|
||||||
|
|
||||||
this->setLayoutSizeAware(true);
|
this->setLayoutSizeAware(true);
|
||||||
|
this->setOverflow(Wt::WContainerWidget::OverflowHidden, Wt::Horizontal);
|
||||||
|
this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||||
|
|
||||||
this->setColumnHidden(COLUMN_ID_TRACK_ID, true);
|
this->setColumnHidden(COLUMN_ID_TRACK_ID, true);
|
||||||
|
|
||||||
@@ -501,12 +503,17 @@ PlayQueue::delSelected(void)
|
|||||||
int minId = _model->rowCount();
|
int minId = _model->rowCount();
|
||||||
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||||
|
|
||||||
BOOST_REVERSE_FOREACH(Wt::WModelIndex index, indexSet)
|
// Make sure to delete entries in the reverse order
|
||||||
{
|
std::vector<int> rowIds;
|
||||||
_model->removeRow(index.row());
|
for (Wt::WModelIndex index : indexSet)
|
||||||
if (index.row() < minId)
|
rowIds.push_back(index.row());
|
||||||
minId = index.row();
|
|
||||||
}
|
std::sort(rowIds.begin(), rowIds.end(), std::greater<int>());
|
||||||
|
if (!rowIds.empty())
|
||||||
|
minId = rowIds.back();
|
||||||
|
|
||||||
|
for (int rowId : rowIds)
|
||||||
|
_model->removeRow(rowId);
|
||||||
|
|
||||||
// If the current played track is removed, make sure to unselect it
|
// If the current played track is removed, make sure to unselect it
|
||||||
_trackSelector->setSize(_model->rowCount());
|
_trackSelector->setSize(_model->rowCount());
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WItemDelegate>
|
#include <Wt/WItemDelegate>
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
@@ -50,7 +48,10 @@ TableFilterGenre::TableFilterGenre(Wt::WContainerWidget* parent)
|
|||||||
|
|
||||||
this->selectionChanged().connect(this, &TableFilterGenre::emitUpdate);
|
this->selectionChanged().connect(this, &TableFilterGenre::emitUpdate);
|
||||||
|
|
||||||
setLayoutSizeAware(true);
|
this->setOverflow(Wt::WContainerWidget::OverflowHidden, Wt::Horizontal);
|
||||||
|
this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||||
|
|
||||||
|
this->setLayoutSizeAware(true);
|
||||||
|
|
||||||
_queryModel.setBatchSize(100);
|
_queryModel.setBatchSize(100);
|
||||||
|
|
||||||
@@ -122,7 +123,10 @@ TableFilterArtist::TableFilterArtist(Wt::WContainerWidget* parent)
|
|||||||
|
|
||||||
this->selectionChanged().connect(this, &TableFilterArtist::emitUpdate);
|
this->selectionChanged().connect(this, &TableFilterArtist::emitUpdate);
|
||||||
|
|
||||||
setLayoutSizeAware(true);
|
this->setOverflow(Wt::WContainerWidget::OverflowHidden, Wt::Horizontal);
|
||||||
|
this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||||
|
|
||||||
|
this->setLayoutSizeAware(true);
|
||||||
|
|
||||||
_queryModel.setBatchSize(100);
|
_queryModel.setBatchSize(100);
|
||||||
|
|
||||||
@@ -163,8 +167,8 @@ TableFilterArtist::getConstraint(SearchFilter& filter)
|
|||||||
{
|
{
|
||||||
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
Wt::WModelIndexSet indexSet = this->selectedIndexes();
|
||||||
|
|
||||||
BOOST_FOREACH(Wt::WModelIndex index, indexSet) {
|
for (Wt::WModelIndex index : indexSet)
|
||||||
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -200,7 +204,10 @@ TableFilterRelease::TableFilterRelease(Wt::WContainerWidget* parent)
|
|||||||
|
|
||||||
this->selectionChanged().connect(this, &TableFilterRelease::emitUpdate);
|
this->selectionChanged().connect(this, &TableFilterRelease::emitUpdate);
|
||||||
|
|
||||||
setLayoutSizeAware(true);
|
this->setOverflow(Wt::WContainerWidget::OverflowHidden, Wt::Horizontal);
|
||||||
|
this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||||
|
|
||||||
|
this->setLayoutSizeAware(true);
|
||||||
|
|
||||||
_queryModel.setBatchSize(100);
|
_queryModel.setBatchSize(100);
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
#include <Wt/WItemDelegate>
|
#include <Wt/WItemDelegate>
|
||||||
#include <Wt/WBreak>
|
#include <Wt/WBreak>
|
||||||
|
|
||||||
@@ -31,6 +28,8 @@
|
|||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
namespace Desktop {
|
namespace Desktop {
|
||||||
|
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
TrackView::TrackView(Wt::WContainerWidget* parent)
|
TrackView::TrackView(Wt::WContainerWidget* parent)
|
||||||
: Wt::WTableView( parent )
|
: Wt::WTableView( parent )
|
||||||
{
|
{
|
||||||
@@ -48,9 +47,9 @@ TrackView::TrackView(Wt::WContainerWidget* parent)
|
|||||||
"Genres",
|
"Genres",
|
||||||
};
|
};
|
||||||
|
|
||||||
Database::SearchFilter filter;
|
SearchFilter filter;
|
||||||
|
|
||||||
Database::Track::updateUIQueryModel(DboSession(), _queryModel, filter, columnNames);
|
Track::updateUIQueryModel(DboSession(), _queryModel, filter, columnNames);
|
||||||
|
|
||||||
_queryModel.setBatchSize(300);
|
_queryModel.setBatchSize(300);
|
||||||
|
|
||||||
@@ -68,7 +67,7 @@ TrackView::TrackView(Wt::WContainerWidget* parent)
|
|||||||
this->setColumnWidth(6, 70); // Date
|
this->setColumnWidth(6, 70); // Date
|
||||||
this->setColumnWidth(7, 70); // Original Date
|
this->setColumnWidth(7, 70); // Original Date
|
||||||
this->setColumnWidth(8, 180); // Genres
|
this->setColumnWidth(8, 180); // Genres
|
||||||
// this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
this->setOverflow(Wt::WContainerWidget::OverflowScroll, Wt::Vertical);
|
||||||
|
|
||||||
// Duration display
|
// Duration display
|
||||||
{
|
{
|
||||||
@@ -107,15 +106,47 @@ TrackView::TrackView(Wt::WContainerWidget* parent)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TrackView::emitStats(const SearchFilter& filter)
|
||||||
|
{
|
||||||
|
Wt::Dbo::Transaction transaction (DboSession());
|
||||||
|
|
||||||
|
// Update stats on the view
|
||||||
|
Track::StatsQueryResult stats = Track::getStats(DboSession(), filter);
|
||||||
|
|
||||||
|
transaction.commit();
|
||||||
|
|
||||||
|
int nbTracks = stats.get<0>();
|
||||||
|
boost::posix_time::time_duration totalDuration = stats.get<1>();
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
oss << nbTracks << " track" << (nbTracks > 1 ? "s" : "") << ", ";
|
||||||
|
|
||||||
|
if (totalDuration.hours() >= 24)
|
||||||
|
{
|
||||||
|
auto days = totalDuration.hours() / 24;
|
||||||
|
oss << days << " day" << (days > 1 ? "s " : " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << std::setw(2) << std::setfill('0') << totalDuration.hours() % 24
|
||||||
|
<< ":" << std::setw(2) << std::setfill('0') << totalDuration.minutes()
|
||||||
|
<< ":" << std::setw(2) << std::setfill('0') << totalDuration.seconds();
|
||||||
|
|
||||||
|
_sigStatsUpdated.emit(Wt::WString(oss.str()));
|
||||||
|
}
|
||||||
|
|
||||||
// Set constraints created by parent filters
|
// Set constraints created by parent filters
|
||||||
void
|
void
|
||||||
TrackView::refresh(Database::SearchFilter& filter)
|
TrackView::refresh(SearchFilter& filter)
|
||||||
{
|
{
|
||||||
Database::Track::updateUIQueryModel(DboSession(), _queryModel, filter);
|
Track::updateUIQueryModel(DboSession(), _queryModel, filter);
|
||||||
|
|
||||||
|
emitStats(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TrackView::getSelectedTracks(std::vector<Database::Track::id_type>& track_ids)
|
TrackView::getSelectedTracks(std::vector<Track::id_type>& track_ids)
|
||||||
{
|
{
|
||||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting selected tracks...";
|
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting selected tracks...";
|
||||||
|
|
||||||
@@ -126,7 +157,7 @@ TrackView::getSelectedTracks(std::vector<Database::Track::id_type>& track_ids)
|
|||||||
if (!index.isValid())
|
if (!index.isValid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Database::Track::id_type id = _queryModel.resultRow( index.row() ).get<0>();
|
Track::id_type id = _queryModel.resultRow( index.row() ).get<0>();
|
||||||
|
|
||||||
track_ids.push_back(id);
|
track_ids.push_back(id);
|
||||||
}
|
}
|
||||||
@@ -157,16 +188,16 @@ TrackView::getFirstSelectedTrackPosition(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TrackView::getTracks(std::vector<Database::Track::id_type>& trackIds)
|
TrackView::getTracks(std::vector<Track::id_type>& trackIds)
|
||||||
{
|
{
|
||||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting all tracks...";
|
LMS_LOG(MOD_UI, SEV_DEBUG) << "Getting all tracks...";
|
||||||
|
|
||||||
Wt::Dbo::Transaction transaction(DboSession());
|
Wt::Dbo::Transaction transaction(DboSession());
|
||||||
Wt::Dbo::collection<Database::Track::UIQueryResult> results = _queryModel.query();
|
Wt::Dbo::collection<Track::UIQueryResult> results = _queryModel.query();
|
||||||
|
|
||||||
for (auto it = results.begin(); it != results.end(); ++it)
|
for (auto it = results.begin(); it != results.end(); ++it)
|
||||||
{
|
{
|
||||||
Database::Track::id_type id = it->get<0>();
|
Track::id_type id = it->get<0>();
|
||||||
trackIds.push_back(id);
|
trackIds.push_back(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,12 +55,17 @@ class TrackView : public Wt::WTableView, public Filter
|
|||||||
void getTracks(std::vector<Database::Track::id_type>& track_ids);
|
void getTracks(std::vector<Database::Track::id_type>& track_ids);
|
||||||
|
|
||||||
typedef Wt::Signal<void> SigTrackDoubleClicked;
|
typedef Wt::Signal<void> SigTrackDoubleClicked;
|
||||||
|
typedef Wt::Signal<Wt::WString> SigStatsUpdated;
|
||||||
|
|
||||||
SigTrackDoubleClicked& trackDoubleClicked() { return _sigTrackDoubleClicked; }
|
SigTrackDoubleClicked& trackDoubleClicked() { return _sigTrackDoubleClicked; }
|
||||||
|
SigStatsUpdated& statsUpdated() { return _sigStatsUpdated; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
SigTrackDoubleClicked _sigTrackDoubleClicked;
|
SigTrackDoubleClicked _sigTrackDoubleClicked;
|
||||||
|
SigStatsUpdated _sigStatsUpdated;
|
||||||
|
|
||||||
|
void emitStats(const Database::SearchFilter& filter);
|
||||||
|
|
||||||
typedef Database::Track::UIQueryResult ResultType;
|
typedef Database::Track::UIQueryResult ResultType;
|
||||||
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
Wt::Dbo::QueryModel< ResultType > _queryModel;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ AvConvTranscodeStreamResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
// see if this request is for a continuation:
|
// see if this request is for a continuation:
|
||||||
Wt::Http::ResponseContinuation *continuation = request.continuation();
|
Wt::Http::ResponseContinuation *continuation = request.continuation();
|
||||||
|
|
||||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Handling new request. Continuation = " << std::boolalpha << continuation;
|
LMS_LOG(MOD_UI, SEV_DEBUG) << "Handling new request. Continuation = " << continuation;
|
||||||
|
|
||||||
std::shared_ptr<Transcode::AvConvTranscoder> transcoder;
|
std::shared_ptr<Transcode::AvConvTranscoder> transcoder;
|
||||||
if (continuation)
|
if (continuation)
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ int main(void)
|
|||||||
std::vector<Artist::pointer> res = Artist::getByFilter(db.getSession(), filter, -1, -1);
|
std::vector<Artist::pointer> res = Artist::getByFilter(db.getSession(), filter, -1, -1);
|
||||||
assert(res.size() == 1);
|
assert(res.size() == 1);
|
||||||
assert(res.front().id() == 1);
|
assert(res.front().id() == 1);
|
||||||
|
|
||||||
|
// Make sure artist has a release
|
||||||
|
assert(res.front()->getReleases().size() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select Release by name
|
// Select Release by name
|
||||||
@@ -179,6 +182,9 @@ int main(void)
|
|||||||
assert(res.size() == 1);
|
assert(res.size() == 1);
|
||||||
assert(res.front().id() == 1);
|
assert(res.front().id() == 1);
|
||||||
assert(res.front()->getName() == "release01");
|
assert(res.front()->getName() == "release01");
|
||||||
|
|
||||||
|
// Make sure release has an artist
|
||||||
|
assert(res.front()->getArtists().size() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select genre by name
|
// Select genre by name
|
||||||
|
|||||||
+2
-4
@@ -13,21 +13,19 @@ remote_client_SOURCES = \
|
|||||||
nodist_remote_client_SOURCES = \
|
nodist_remote_client_SOURCES = \
|
||||||
$(builddir)/auth.pb.cc \
|
$(builddir)/auth.pb.cc \
|
||||||
$(builddir)/collection.pb.cc \
|
$(builddir)/collection.pb.cc \
|
||||||
$(builddir)/common.pb.cc \
|
|
||||||
$(builddir)/media.pb.cc \
|
$(builddir)/media.pb.cc \
|
||||||
$(builddir)/messages.pb.cc
|
$(builddir)/messages.pb.cc
|
||||||
|
|
||||||
BUILT_SOURCES = \
|
BUILT_SOURCES = \
|
||||||
$(builddir)/auth.pb.h \
|
$(builddir)/auth.pb.h \
|
||||||
$(builddir)/collection.pb.h \
|
$(builddir)/collection.pb.h \
|
||||||
$(builddir)/common.pb.h \
|
|
||||||
$(builddir)/media.pb.h \
|
$(builddir)/media.pb.h \
|
||||||
$(builddir)/messages.pb.h
|
$(builddir)/messages.pb.h
|
||||||
|
|
||||||
remote_client_CXXFLAGS=-std=c++11 -Wall -Wextra -DBOOST_LOG_DYN_LINK -I$(top_srcdir)/src
|
remote_client_CXXFLAGS=-std=c++11 -Wall -Wextra -DBOOST_LOG_DYN_LINK -I$(top_srcdir)/src
|
||||||
|
|
||||||
%.pb.cc %.pb.h: $(top_srcdir)/src/lms-api/proto/%.proto
|
%.pb.cc %.pb.h: $(top_srcdir)/lms-api/proto/%.proto
|
||||||
$(PROTOC) --proto_path=$(top_srcdir)/src/lms-api/proto/ --cpp_out=$(builddir)/ $^
|
$(PROTOC) --proto_path=$(top_srcdir)/lms-api/proto/ --cpp_out=$(builddir)/ $^
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
+68
-68
@@ -27,7 +27,7 @@
|
|||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
#include "remote/messages/Header.hpp"
|
#include "lms-api/messages/Header.hpp"
|
||||||
#include "messages.pb.h"
|
#include "messages.pb.h"
|
||||||
|
|
||||||
#include "TestDatabase.hpp"
|
#include "TestDatabase.hpp"
|
||||||
@@ -123,7 +123,7 @@ struct SearchFilter
|
|||||||
std::vector<uint64_t> trackIds;
|
std::vector<uint64_t> trackIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SearchFilterToRequest(const SearchFilter& filter, Remote::AudioCollectionRequest_SearchFilter& request)
|
void SearchFilterToRequest(const SearchFilter& filter, LmsAPI::AudioCollectionRequest_SearchFilter& request)
|
||||||
{
|
{
|
||||||
for (uint64_t id : filter.artistIds)
|
for (uint64_t id : filter.artistIds)
|
||||||
request.add_artist_id(id);
|
request.add_artist_id(id);
|
||||||
@@ -174,11 +174,11 @@ class TestClient
|
|||||||
std::size_t nbArtists = 0;
|
std::size_t nbArtists = 0;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage_Type_AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage_Type_AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest_Type_TypeGetArtistList);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest_Type_TypeGetArtistList);
|
||||||
request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_size(size);
|
request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_size(size);
|
||||||
request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_offset(offset);
|
request.mutable_audio_collection_request()->mutable_get_artists()->mutable_batch_parameter()->set_offset(offset);
|
||||||
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_artists()->mutable_search_filter());
|
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_artists()->mutable_search_filter());
|
||||||
@@ -186,7 +186,7 @@ class TestClient
|
|||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -198,7 +198,7 @@ class TestClient
|
|||||||
|
|
||||||
for (int i = 0; i < response.audio_collection_response().artist_list().artists_size(); ++i)
|
for (int i = 0; i < response.audio_collection_response().artist_list().artists_size(); ++i)
|
||||||
{
|
{
|
||||||
const Remote::AudioCollectionResponse_Artist& respArtist = response.audio_collection_response().artist_list().artists(i);
|
const LmsAPI::AudioCollectionResponse_Artist& respArtist = response.audio_collection_response().artist_list().artists(i);
|
||||||
|
|
||||||
if (!respArtist.has_id())
|
if (!respArtist.has_id())
|
||||||
throw std::runtime_error("no id!");
|
throw std::runtime_error("no id!");
|
||||||
@@ -235,11 +235,11 @@ class TestClient
|
|||||||
std::size_t nbAdded = 0;
|
std::size_t nbAdded = 0;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage_Type_AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage_Type_AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest_Type_TypeGetGenreList);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest_Type_TypeGetGenreList);
|
||||||
request.mutable_audio_collection_request()->mutable_get_genres()->mutable_batch_parameter()->set_size(size);
|
request.mutable_audio_collection_request()->mutable_get_genres()->mutable_batch_parameter()->set_size(size);
|
||||||
request.mutable_audio_collection_request()->mutable_get_genres()->mutable_batch_parameter()->set_offset(offset);
|
request.mutable_audio_collection_request()->mutable_get_genres()->mutable_batch_parameter()->set_offset(offset);
|
||||||
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_genres()->mutable_search_filter());
|
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_genres()->mutable_search_filter());
|
||||||
@@ -247,7 +247,7 @@ class TestClient
|
|||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -259,7 +259,7 @@ class TestClient
|
|||||||
|
|
||||||
for (int i = 0; i < response.audio_collection_response().genre_list().genres_size(); ++i)
|
for (int i = 0; i < response.audio_collection_response().genre_list().genres_size(); ++i)
|
||||||
{
|
{
|
||||||
const Remote::AudioCollectionResponse_Genre& respGenre = response.audio_collection_response().genre_list().genres(i);
|
const LmsAPI::AudioCollectionResponse_Genre& respGenre = response.audio_collection_response().genre_list().genres(i);
|
||||||
|
|
||||||
if (!respGenre.has_id())
|
if (!respGenre.has_id())
|
||||||
throw std::runtime_error("no genre id!");
|
throw std::runtime_error("no genre id!");
|
||||||
@@ -293,11 +293,11 @@ class TestClient
|
|||||||
std::size_t nbAdded = 0;
|
std::size_t nbAdded = 0;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage_Type_AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage_Type_AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest_Type_TypeGetReleaseList);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest_Type_TypeGetReleaseList);
|
||||||
request.mutable_audio_collection_request()->mutable_get_releases()->mutable_batch_parameter()->set_size(size);
|
request.mutable_audio_collection_request()->mutable_get_releases()->mutable_batch_parameter()->set_size(size);
|
||||||
request.mutable_audio_collection_request()->mutable_get_releases()->mutable_batch_parameter()->set_offset(offset);
|
request.mutable_audio_collection_request()->mutable_get_releases()->mutable_batch_parameter()->set_offset(offset);
|
||||||
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_releases()->mutable_search_filter());
|
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_releases()->mutable_search_filter());
|
||||||
@@ -305,14 +305,14 @@ class TestClient
|
|||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
if (!response.audio_collection_response().has_type())
|
if (!response.audio_collection_response().has_type())
|
||||||
throw std::runtime_error("Missing type!");
|
throw std::runtime_error("Missing type!");
|
||||||
|
|
||||||
// if (!response.audio_collection_response().type() != Remote::ServerMessage::AudioCollectionResponse::TypeReleaseList)
|
// if (!response.audio_collection_response().type() != LmsAPI::ServerMessage::AudioCollectionResponse::TypeReleaseList)
|
||||||
// throw std::runtime_error("Bad type!");
|
// throw std::runtime_error("Bad type!");
|
||||||
|
|
||||||
if (!response.has_audio_collection_response())
|
if (!response.has_audio_collection_response())
|
||||||
@@ -323,7 +323,7 @@ class TestClient
|
|||||||
|
|
||||||
for (int i = 0; i < response.audio_collection_response().release_list().releases_size(); ++i)
|
for (int i = 0; i < response.audio_collection_response().release_list().releases_size(); ++i)
|
||||||
{
|
{
|
||||||
const Remote::AudioCollectionResponse_Release& respRelease = response.audio_collection_response().release_list().releases(i);
|
const LmsAPI::AudioCollectionResponse_Release& respRelease = response.audio_collection_response().release_list().releases(i);
|
||||||
|
|
||||||
if (!respRelease.has_id())
|
if (!respRelease.has_id())
|
||||||
throw std::runtime_error("no id!");
|
throw std::runtime_error("no id!");
|
||||||
@@ -355,11 +355,11 @@ class TestClient
|
|||||||
std::size_t nbAdded = 0;
|
std::size_t nbAdded = 0;
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage_Type_AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage_Type_AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest_Type_TypeGetTrackList);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest_Type_TypeGetTrackList);
|
||||||
request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_batch_parameter()->set_size(size);
|
request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_batch_parameter()->set_size(size);
|
||||||
request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_batch_parameter()->set_offset(offset);
|
request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_batch_parameter()->set_offset(offset);
|
||||||
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_search_filter());
|
SearchFilterToRequest(filter, *request.mutable_audio_collection_request()->mutable_get_tracks()->mutable_search_filter());
|
||||||
@@ -367,7 +367,7 @@ class TestClient
|
|||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -379,7 +379,7 @@ class TestClient
|
|||||||
|
|
||||||
for (int i = 0; i < response.audio_collection_response().track_list().tracks_size(); ++i)
|
for (int i = 0; i < response.audio_collection_response().track_list().tracks_size(); ++i)
|
||||||
{
|
{
|
||||||
const Remote::AudioCollectionResponse_Track& respTrack = response.audio_collection_response().track_list().tracks(i);;
|
const LmsAPI::AudioCollectionResponse_Track& respTrack = response.audio_collection_response().track_list().tracks(i);;
|
||||||
|
|
||||||
TrackInfo track;
|
TrackInfo track;
|
||||||
track.id = respTrack.id();
|
track.id = respTrack.id();
|
||||||
@@ -426,18 +426,18 @@ class TestClient
|
|||||||
void getCoverTrack(std::vector<Cover>& coverArt, uint64_t trackId)
|
void getCoverTrack(std::vector<Cover>& coverArt, uint64_t trackId)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage::AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest::TypeGetCoverArt);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest::TypeGetCoverArt);
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_type( Remote::AudioCollectionRequest::GetCoverArt::TypeGetCoverArtTrack);
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_type( LmsAPI::AudioCollectionRequest::GetCoverArt::TypeGetCoverArtTrack);
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_track_id( trackId );
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_track_id( trackId );
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_size( 256 );
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_size( 256 );
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -457,19 +457,19 @@ class TestClient
|
|||||||
void getCoverRelease(std::vector<Cover>& coverArt, uint64_t releaseId)
|
void getCoverRelease(std::vector<Cover>& coverArt, uint64_t releaseId)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage::AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest::TypeGetCoverArt);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest::TypeGetCoverArt);
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_type( Remote::AudioCollectionRequest::GetCoverArt::TypeGetCoverArtRelease);
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_type( LmsAPI::AudioCollectionRequest::GetCoverArt::TypeGetCoverArtRelease);
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_release_id( releaseId );
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_release_id( releaseId );
|
||||||
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_size( 256 );
|
request.mutable_audio_collection_request()->mutable_get_cover_art()->set_size( 256 );
|
||||||
|
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -490,16 +490,16 @@ class TestClient
|
|||||||
std::string getRevision(void)
|
std::string getRevision(void)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::AudioCollectionRequest );
|
request.set_type( LmsAPI::ClientMessage::AudioCollectionRequest );
|
||||||
|
|
||||||
request.mutable_audio_collection_request()->set_type( Remote::AudioCollectionRequest::TypeGetRevision);
|
request.mutable_audio_collection_request()->set_type( LmsAPI::AudioCollectionRequest::TypeGetRevision);
|
||||||
|
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -515,18 +515,18 @@ class TestClient
|
|||||||
bool login(const std::string& username, const std::string& password)
|
bool login(const std::string& username, const std::string& password)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::AuthRequest );
|
request.set_type( LmsAPI::ClientMessage::AuthRequest );
|
||||||
|
|
||||||
request.mutable_auth_request()->set_type( Remote::AuthRequest::TypePassword);
|
request.mutable_auth_request()->set_type( LmsAPI::AuthRequest::TypePassword);
|
||||||
request.mutable_auth_request()->mutable_password()->set_user_login( username );
|
request.mutable_auth_request()->mutable_password()->set_user_login( username );
|
||||||
request.mutable_auth_request()->mutable_password()->set_user_password( password );
|
request.mutable_auth_request()->mutable_password()->set_user_password( password );
|
||||||
|
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive responses
|
// Receive responses
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
@@ -538,11 +538,11 @@ class TestClient
|
|||||||
|
|
||||||
switch( response.auth_response().password_result().type())
|
switch( response.auth_response().password_result().type())
|
||||||
{
|
{
|
||||||
case Remote::AuthResponse::PasswordResult::TypePasswordValid:
|
case LmsAPI::AuthResponse::PasswordResult::TypePasswordValid:
|
||||||
return true;
|
return true;
|
||||||
case Remote::AuthResponse::PasswordResult::TypePasswordInvalid:
|
case LmsAPI::AuthResponse::PasswordResult::TypePasswordInvalid:
|
||||||
return false;
|
return false;
|
||||||
case Remote::AuthResponse::PasswordResult::TypeLoginThrottling:
|
case LmsAPI::AuthResponse::PasswordResult::TypeLoginThrottling:
|
||||||
if (response.auth_response().password_result().has_delay())
|
if (response.auth_response().password_result().has_delay())
|
||||||
std::cerr << "Has to wait for " << response.auth_response().password_result().delay() << " seconds" << std::endl;
|
std::cerr << "Has to wait for " << response.auth_response().password_result().delay() << " seconds" << std::endl;
|
||||||
|
|
||||||
@@ -571,24 +571,24 @@ class TestClient
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage_Type_MediaRequest);
|
request.set_type( LmsAPI::ClientMessage_Type_MediaRequest);
|
||||||
|
|
||||||
request.mutable_media_request()->set_type( Remote::MediaRequest_Type_TypeMediaPrepare);
|
request.mutable_media_request()->set_type( LmsAPI::MediaRequest_Type_TypeMediaPrepare);
|
||||||
request.mutable_media_request()->mutable_prepare()->set_type(Remote::MediaRequest_Prepare_Type_AudioRequest);
|
request.mutable_media_request()->mutable_prepare()->set_type(LmsAPI::MediaRequest_Prepare_Type_AudioRequest);
|
||||||
|
|
||||||
// Set fields
|
// Set fields
|
||||||
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_track_id( audioId );
|
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_track_id( audioId );
|
||||||
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_codec_type( Remote::MediaRequest::Prepare::AudioCodecTypeOGA );
|
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_codec_type( LmsAPI::MediaRequest::Prepare::AudioCodecTypeOGA );
|
||||||
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_bitrate( Remote::MediaRequest::Prepare::AudioBitrate_64_kbps );
|
request.mutable_media_request()->mutable_prepare()->mutable_audio()->set_bitrate( LmsAPI::MediaRequest::Prepare::AudioBitrate_64_kbps );
|
||||||
|
|
||||||
std::cout << "Sending prepare request" << std::endl;
|
std::cout << "Sending prepare request" << std::endl;
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
std::cout << "Waiting for response" << std::endl;
|
std::cout << "Waiting for response" << std::endl;
|
||||||
// Receive response
|
// Receive response
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
std::cout << "Got a response" << std::endl;
|
std::cout << "Got a response" << std::endl;
|
||||||
@@ -616,11 +616,11 @@ class TestClient
|
|||||||
std::size_t mediaGetPart(uint32_t handle, std::vector<unsigned char>& data)
|
std::size_t mediaGetPart(uint32_t handle, std::vector<unsigned char>& data)
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::MediaRequest);
|
request.set_type( LmsAPI::ClientMessage::MediaRequest);
|
||||||
|
|
||||||
request.mutable_media_request()->set_type( Remote::MediaRequest::TypeMediaGetPart);
|
request.mutable_media_request()->set_type( LmsAPI::MediaRequest::TypeMediaGetPart);
|
||||||
request.mutable_media_request()->mutable_get_part()->set_handle(handle);
|
request.mutable_media_request()->mutable_get_part()->set_handle(handle);
|
||||||
request.mutable_media_request()->mutable_get_part()->set_requested_data_size(65536);
|
request.mutable_media_request()->mutable_get_part()->set_requested_data_size(65536);
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ class TestClient
|
|||||||
|
|
||||||
std::cout << "Waiting for response" << std::endl;
|
std::cout << "Waiting for response" << std::endl;
|
||||||
// Receive response
|
// Receive response
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
std::cout << "Got a response" << std::endl;
|
std::cout << "Got a response" << std::endl;
|
||||||
@@ -638,7 +638,7 @@ class TestClient
|
|||||||
throw std::runtime_error("not an media response");
|
throw std::runtime_error("not an media response");
|
||||||
|
|
||||||
// Process message
|
// Process message
|
||||||
if (response.media_response().type() != Remote::MediaResponse::TypePartResult)
|
if (response.media_response().type() != LmsAPI::MediaResponse::TypePartResult)
|
||||||
throw std::runtime_error("GetPart: not a Part response!");
|
throw std::runtime_error("GetPart: not a Part response!");
|
||||||
|
|
||||||
if (!response.media_response().has_part_result())
|
if (!response.media_response().has_part_result())
|
||||||
@@ -652,17 +652,17 @@ class TestClient
|
|||||||
void mediaTerminate( uint32_t handle )
|
void mediaTerminate( uint32_t handle )
|
||||||
{
|
{
|
||||||
// Send request
|
// Send request
|
||||||
Remote::ClientMessage request;
|
LmsAPI::ClientMessage request;
|
||||||
|
|
||||||
request.set_type( Remote::ClientMessage::MediaRequest);
|
request.set_type( LmsAPI::ClientMessage::MediaRequest);
|
||||||
|
|
||||||
request.mutable_media_request()->set_type( Remote::MediaRequest::TypeMediaTerminate);
|
request.mutable_media_request()->set_type( LmsAPI::MediaRequest::TypeMediaTerminate);
|
||||||
request.mutable_media_request()->mutable_terminate()->set_handle(handle);
|
request.mutable_media_request()->mutable_terminate()->set_handle(handle);
|
||||||
|
|
||||||
sendMsg(request);
|
sendMsg(request);
|
||||||
|
|
||||||
// Receive response
|
// Receive response
|
||||||
Remote::ServerMessage response;
|
LmsAPI::ServerMessage response;
|
||||||
recvMsg(response);
|
recvMsg(response);
|
||||||
|
|
||||||
|
|
||||||
@@ -684,18 +684,18 @@ class TestClient
|
|||||||
if (message.SerializeToOstream(&os))
|
if (message.SerializeToOstream(&os))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (_outputStreamBuf.size() > Remote::Header::max_data_size)
|
if (_outputStreamBuf.size() > LmsAPI::Header::max_data_size)
|
||||||
{
|
{
|
||||||
std::ostringstream oss; oss << "Message too big = " << _outputStreamBuf.size() << " bytes! (max is " << Remote::Header::max_data_size << ")" << std::endl;
|
std::ostringstream oss; oss << "Message too big = " << _outputStreamBuf.size() << " bytes! (max is " << LmsAPI::Header::max_data_size << ")" << std::endl;
|
||||||
throw std::runtime_error("Message to big!");
|
throw std::runtime_error("Message to big!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send message header
|
// Send message header
|
||||||
std::array<unsigned char, Remote::Header::size> headerBuffer;
|
std::array<unsigned char, LmsAPI::Header::size> headerBuffer;
|
||||||
|
|
||||||
// Generate header content
|
// Generate header content
|
||||||
{
|
{
|
||||||
Remote::Header header;
|
LmsAPI::Header header;
|
||||||
header.setDataSize(_outputStreamBuf.size());
|
header.setDataSize(_outputStreamBuf.size());
|
||||||
header.to_buffer(headerBuffer);
|
header.to_buffer(headerBuffer);
|
||||||
}
|
}
|
||||||
@@ -725,18 +725,18 @@ class TestClient
|
|||||||
|
|
||||||
{
|
{
|
||||||
// reserve bytes in output sequence
|
// reserve bytes in output sequence
|
||||||
boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(Remote::Header::size);
|
boost::asio::streambuf::mutable_buffers_type bufs = _inputStreamBuf.prepare(LmsAPI::Header::size);
|
||||||
|
|
||||||
std::size_t n = boost::asio::read(_socket,
|
std::size_t n = boost::asio::read(_socket,
|
||||||
bufs,
|
bufs,
|
||||||
boost::asio::transfer_exactly(Remote::Header::size));
|
boost::asio::transfer_exactly(LmsAPI::Header::size));
|
||||||
|
|
||||||
assert(n == Remote::Header::size);
|
assert(n == LmsAPI::Header::size);
|
||||||
_inputStreamBuf.commit(n);
|
_inputStreamBuf.commit(n);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Remote::Header header;
|
LmsAPI::Header header;
|
||||||
if (!header.from_istream(is))
|
if (!header.from_istream(is))
|
||||||
throw std::runtime_error("Cannot read header from buffer!");
|
throw std::runtime_error("Cannot read header from buffer!");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user