[DB] Some code refactoring

This commit is contained in:
emeric
2016-04-15 23:43:59 +02:00
parent 0dace5cbdf
commit 7723d461a5
17 changed files with 164 additions and 454 deletions
+3
View File
@@ -2,6 +2,9 @@
[Book] [Book]
- Make the feature? - Make the feature?
[Build]
- Makefile per directory. Use the include flags only where needed?
[Cover] [Cover]
- Handle preferred cover file names ("front.xxx", "cover.xxx", ...) - Handle preferred cover file names ("front.xxx", "cover.xxx", ...)
- Implement a cache and a grabber from some web service (mandatory for artists?) - Implement a cache and a grabber from some web service (mandatory for artists?)
+2 -4
View File
@@ -7,6 +7,7 @@ lms_SOURCES = \
$(srcdir)/cover/CoverArtGrabber.cpp \ $(srcdir)/cover/CoverArtGrabber.cpp \
$(srcdir)/database/Artist.cpp \ $(srcdir)/database/Artist.cpp \
$(srcdir)/database/DatabaseHandler.cpp \ $(srcdir)/database/DatabaseHandler.cpp \
$(srcdir)/database/DatabaseUpdater.cpp \
$(srcdir)/database/MediaDirectory.cpp \ $(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Playlist.cpp \ $(srcdir)/database/Playlist.cpp \
$(srcdir)/database/Release.cpp \ $(srcdir)/database/Release.cpp \
@@ -15,13 +16,9 @@ lms_SOURCES = \
$(srcdir)/database/Track.cpp \ $(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \ $(srcdir)/database/User.cpp \
$(srcdir)/database/Video.cpp \ $(srcdir)/database/Video.cpp \
$(srcdir)/database-updater/DatabaseUpdater.cpp \
$(srcdir)/database-updater/Checksum.cpp \
$(srcdir)/image/Image.cpp \ $(srcdir)/image/Image.cpp \
$(srcdir)/logger/Logger.cpp \ $(srcdir)/logger/Logger.cpp \
$(srcdir)/metadata/AvFormat.cpp \ $(srcdir)/metadata/AvFormat.cpp \
$(srcdir)/service/ServiceManager.cpp \
$(srcdir)/service/DatabaseUpdateService.cpp \
$(srcdir)/ui/LmsApplication.cpp \ $(srcdir)/ui/LmsApplication.cpp \
$(srcdir)/ui/auth/LmsAuth.cpp \ $(srcdir)/ui/auth/LmsAuth.cpp \
$(srcdir)/ui/audio/AudioPlayer.cpp \ $(srcdir)/ui/audio/AudioPlayer.cpp \
@@ -57,6 +54,7 @@ lms_SOURCES = \
$(srcdir)/ui/settings/SettingsMediaDirectoryFormView.cpp \ $(srcdir)/ui/settings/SettingsMediaDirectoryFormView.cpp \
$(srcdir)/ui/settings/SettingsUserFormView.cpp \ $(srcdir)/ui/settings/SettingsUserFormView.cpp \
$(srcdir)/ui/settings/SettingsUsers.cpp \ $(srcdir)/ui/settings/SettingsUsers.cpp \
$(srcdir)/utils/Checksum.cpp \
$(srcdir)/utils/Utils.cpp $(srcdir)/utils/Utils.cpp
if VIDEO if VIDEO
+2
View File
@@ -83,6 +83,8 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
_session.mapClass<Database::Artist>("artist"); _session.mapClass<Database::Artist>("artist");
_session.mapClass<Database::Genre>("genre"); _session.mapClass<Database::Genre>("genre");
_session.mapClass<Database::Track>("track"); _session.mapClass<Database::Track>("track");
_session.mapClass<Database::Classification>("classification");
_session.mapClass<Database::ClassificationData>("classification_data");
_session.mapClass<Database::Playlist>("playlist"); _session.mapClass<Database::Playlist>("playlist");
_session.mapClass<Database::PlaylistEntry>("playlist_entry"); _session.mapClass<Database::PlaylistEntry>("playlist_entry");
_session.mapClass<Database::Release>("release"); _session.mapClass<Database::Release>("release");
@@ -17,19 +17,19 @@
* along with LMS. If not, see <http://www.gnu.org/licenses/>. * along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stdexcept>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/asio/placeholders.hpp> #include <boost/asio/placeholders.hpp>
#include "logger/Logger.hpp" #include "logger/Logger.hpp"
#include "utils/Utils.hpp"
#include "database/Types.hpp"
#include "Checksum.hpp"
#include "DatabaseUpdater.hpp"
#include "cover/CoverArtGrabber.hpp" #include "cover/CoverArtGrabber.hpp"
#include "utils/Utils.hpp"
#include "utils/Checksum.hpp"
#include "Types.hpp"
#include "DatabaseUpdater.hpp"
namespace { namespace {
@@ -115,23 +115,40 @@ isPathInParentPath(const boost::filesystem::path& path, const boost::filesystem:
} // namespace } // namespace
namespace DatabaseUpdater { namespace Database {
using namespace Database; Updater& Updater::instance(void)
{
static Updater updater;
return updater;
}
Updater::Updater()
Updater::Updater(Wt::Dbo::SqlConnectionPool &connectionPool, MetaData::Parser& parser)
: _running(false), : _running(false),
_scheduleTimer(_ioService), _scheduleTimer(_ioService)
_db(connectionPool),
_metadataParser(parser)
{ {
_ioService.setThreadCount(1); _ioService.setThreadCount(1);
} }
void
Updater::setConnectionPool(Wt::Dbo::SqlConnectionPool& connectionPool)
{
_db = new Database::Handler(connectionPool);
}
void
Updater::restart(void)
{
stop();
start();
}
void void
Updater::start(void) Updater::start(void)
{ {
if (_db == nullptr)
throw std::logic_error("uninitialized db!");
_running = true; _running = true;
// post some jobs in the io_service // post some jobs in the io_service
@@ -154,9 +171,9 @@ Updater::stop(void)
void void
Updater::processNextJob(void) Updater::processNextJob(void)
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
MediaDirectorySettings::pointer settings = MediaDirectorySettings::get(_db.getSession()); MediaDirectorySettings::pointer settings = MediaDirectorySettings::get(_db->getSession());
if (settings->getManualScanRequested()) { if (settings->getManualScanRequested()) {
LMS_LOG(DBUPDATER, INFO) << "Manual scan requested!"; LMS_LOG(DBUPDATER, INFO) << "Manual scan requested!";
@@ -229,9 +246,9 @@ Updater::process(boost::system::error_code err)
std::vector<RootDirectory> rootDirectories; std::vector<RootDirectory> rootDirectories;
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
for (MediaDirectory::pointer directory : MediaDirectory::getAll(_db.getSession())) for (MediaDirectory::pointer directory : MediaDirectory::getAll(_db->getSession()))
rootDirectories.push_back( RootDirectory( directory->getType(), directory->getPath() )); rootDirectories.push_back( RootDirectory( directory->getType(), directory->getPath() ));
} }
@@ -253,9 +270,9 @@ Updater::process(boost::system::error_code err)
// Update database stats // Update database stats
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
Database::MediaDirectorySettings::pointer settings = Database::MediaDirectorySettings::get(_db.getSession()); Database::MediaDirectorySettings::pointer settings = Database::MediaDirectorySettings::get(_db->getSession());
if (stats.nbChanges() > 0) if (stats.nbChanges() > 0)
settings.modify()->setLastUpdate(now); settings.modify()->setLastUpdate(now);
@@ -279,10 +296,10 @@ Updater::process(boost::system::error_code err)
void void
Updater::updateFileExtensions() Updater::updateFileExtensions()
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
_audioFileExtensions = MediaDirectorySettings::get(_db.getSession())->getAudioFileExtensions(); _audioFileExtensions = MediaDirectorySettings::get(_db->getSession())->getAudioFileExtensions();
_videoFileExtensions = MediaDirectorySettings::get(_db.getSession())->getVideoFileExtensions(); _videoFileExtensions = MediaDirectorySettings::get(_db->getSession())->getVideoFileExtensions();
} }
Artist::pointer Artist::pointer
@@ -293,9 +310,9 @@ Updater::getArtist( const boost::filesystem::path& file, const std::string& name
// First try to get by MBID // First try to get by MBID
if (!mbid.empty()) if (!mbid.empty())
{ {
artist = Artist::getByMBID( _db.getSession(), mbid ); artist = Artist::getByMBID( _db->getSession(), mbid );
if (!artist) if (!artist)
artist = Artist::create( _db.getSession(), name, mbid); artist = Artist::create( _db->getSession(), name, mbid);
return artist; return artist;
} }
@@ -303,7 +320,7 @@ Updater::getArtist( const boost::filesystem::path& file, const std::string& name
// Fall back on artist name (collisions may occur) // Fall back on artist name (collisions may occur)
if (!name.empty()) if (!name.empty())
{ {
for (Artist::pointer sameNamedArtist : Artist::getByName( _db.getSession(), name )) for (Artist::pointer sameNamedArtist : Artist::getByName( _db->getSession(), name ))
{ {
if (sameNamedArtist->getMBID().empty()) if (sameNamedArtist->getMBID().empty())
{ {
@@ -314,12 +331,12 @@ Updater::getArtist( const boost::filesystem::path& file, const std::string& name
// No Artist found with the same name and without MBID -> creating // No Artist found with the same name and without MBID -> creating
if (!artist) if (!artist)
artist = Artist::create( _db.getSession(), name); artist = Artist::create( _db->getSession(), name);
return artist; return artist;
} }
return Artist::getNone( _db.getSession() ); return Artist::getNone( _db->getSession() );
} }
Release::pointer Release::pointer
@@ -330,9 +347,9 @@ Updater::getRelease( const boost::filesystem::path& file, const std::string& nam
// First try to get by MBID // First try to get by MBID
if (!mbid.empty()) if (!mbid.empty())
{ {
release = Release::getByMBID( _db.getSession(), mbid ); release = Release::getByMBID( _db->getSession(), mbid );
if (!release) if (!release)
release = Release::create( _db.getSession(), name, mbid); release = Release::create( _db->getSession(), name, mbid);
return release; return release;
} }
@@ -340,7 +357,7 @@ Updater::getRelease( const boost::filesystem::path& file, const std::string& nam
// Fall back on release name (collisions may occur) // Fall back on release name (collisions may occur)
if (!name.empty()) if (!name.empty())
{ {
for (Release::pointer sameNamedRelease : Release::getByName( _db.getSession(), name )) for (Release::pointer sameNamedRelease : Release::getByName( _db->getSession(), name ))
{ {
if (sameNamedRelease->getMBID().empty()) if (sameNamedRelease->getMBID().empty())
{ {
@@ -351,12 +368,12 @@ Updater::getRelease( const boost::filesystem::path& file, const std::string& nam
// No release found with the same name and without MBID -> creating // No release found with the same name and without MBID -> creating
if (!release) if (!release)
release = Release::create( _db.getSession(), name); release = Release::create( _db->getSession(), name);
return release; return release;
} }
return Release::getNone( _db.getSession() ); return Release::getNone( _db->getSession() );
} }
std::vector<Genre::pointer> std::vector<Genre::pointer>
@@ -366,15 +383,15 @@ Updater::getGenres( const std::list<std::string>& names)
for (const std::string& name : names) for (const std::string& name : names)
{ {
Genre::pointer genre ( Genre::getByName(_db.getSession(), name) ); Genre::pointer genre ( Genre::getByName(_db->getSession(), name) );
if (!genre) if (!genre)
genre = Genre::create(_db.getSession(), name); genre = Genre::create(_db->getSession(), name);
genres.push_back( genre ); genres.push_back( genre );
} }
if (genres.empty()) if (genres.empty())
genres.push_back( Genre::getNone( _db.getSession() )); genres.push_back( Genre::getNone( _db->getSession() ));
return genres; return genres;
} }
@@ -386,9 +403,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
// Skip file if last write is the same // Skip file if last write is the same
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file); Wt::Dbo::ptr<Track> track = Track::getByPath(_db->getSession(), file);
if (track && track->getLastWriteTime() == lastWriteTime) if (track && track->getLastWriteTime() == lastWriteTime)
{ {
@@ -412,9 +429,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
std::vector<unsigned char> checksum ; std::vector<unsigned char> checksum ;
computeCrc(file, checksum); computeCrc(file, checksum);
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file); Wt::Dbo::ptr<Track> track = Track::getByPath(_db->getSession(), file);
// We estimate this is a audio file if: // We estimate this is a audio file if:
// - we found a least one audio stream // - we found a least one audio stream
@@ -510,7 +527,7 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
if (!track) if (!track)
{ {
// Create a new song // Create a new song
track = Track::create(_db.getSession(), file); track = Track::create(_db->getSession(), file);
LMS_LOG(DBUPDATER, INFO) << "Adding '" << file << "'"; LMS_LOG(DBUPDATER, INFO) << "Adding '" << file << "'";
stats.nbAdded++; stats.nbAdded++;
} }
@@ -675,8 +692,8 @@ Updater::checkAudioFiles( Stats& stats )
{ {
LMS_LOG(DBUPDATER, INFO) << "Checking audio files..."; LMS_LOG(DBUPDATER, INFO) << "Checking audio files...";
std::vector<boost::filesystem::path> trackPaths = Track::getAllPaths(_db.getSession());; std::vector<boost::filesystem::path> trackPaths = Track::getAllPaths(_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(DBUPDATER, DEBUG) << "Checking tracks..."; LMS_LOG(DBUPDATER, DEBUG) << "Checking tracks...";
for (auto& trackPath : trackPaths) for (auto& trackPath : trackPaths)
@@ -686,9 +703,9 @@ Updater::checkAudioFiles( Stats& stats )
if (!checkFile(trackPath, rootDirs, _audioFileExtensions)) if (!checkFile(trackPath, rootDirs, _audioFileExtensions))
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
Track::pointer track = Track::getByPath(_db.getSession(), trackPath); Track::pointer track = Track::getByPath(_db->getSession(), trackPath);
if (track) if (track)
{ {
track.remove(); track.remove();
@@ -699,10 +716,10 @@ Updater::checkAudioFiles( Stats& stats )
LMS_LOG(DBUPDATER, DEBUG) << "Checking Genres..."; LMS_LOG(DBUPDATER, DEBUG) << "Checking Genres...";
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
// Now process orphan Genre (no track) // Now process orphan Genre (no track)
auto genres = Genre::getAll(_db.getSession()); auto genres = Genre::getAll(_db->getSession());
for (auto genre : genres) for (auto genre : genres)
{ {
if (genre->getTracks().size() == 0) if (genre->getTracks().size() == 0)
@@ -715,9 +732,9 @@ Updater::checkAudioFiles( Stats& stats )
LMS_LOG(DBUPDATER, DEBUG) << "Checking artists..."; LMS_LOG(DBUPDATER, DEBUG) << "Checking artists...";
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
auto artists = Artist::getAllOrphans(_db.getSession()); auto artists = Artist::getAllOrphans(_db->getSession());
for (auto artist : artists) for (auto artist : artists)
{ {
LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan artist '" << artist->getName() << "'"; LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan artist '" << artist->getName() << "'";
@@ -727,9 +744,9 @@ Updater::checkAudioFiles( Stats& stats )
LMS_LOG(DBUPDATER, DEBUG) << "Checking releases..."; LMS_LOG(DBUPDATER, DEBUG) << "Checking releases...";
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
auto releases = Release::getAllOrphans(_db.getSession()); auto releases = Release::getAllOrphans(_db->getSession());
for (auto release : releases) for (auto release : releases)
{ {
LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan release '" << release->getName() << "'"; LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan release '" << release->getName() << "'";
@@ -745,15 +762,15 @@ Updater::checkDuplicatedAudioFiles(Stats& stats)
{ {
LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files"; LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files";
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db.getSession()); std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db->getSession());
for (Track::pointer track : tracks) for (Track::pointer track : tracks)
{ {
LMS_LOG(DBUPDATER, INFO) << "Found duplicated MBID [" << track->getMBID() << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName(); LMS_LOG(DBUPDATER, INFO) << "Found duplicated MBID [" << track->getMBID() << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName();
} }
tracks = Database::Track::getChecksumDuplicates(_db.getSession()); tracks = Database::Track::getChecksumDuplicates(_db->getSession());
for (Track::pointer track : tracks) for (Track::pointer track : tracks)
{ {
LMS_LOG(DBUPDATER, INFO) << "Found duplicated checksum [" << bufferToString(track->getChecksum()) << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName(); LMS_LOG(DBUPDATER, INFO) << "Found duplicated checksum [" << bufferToString(track->getChecksum()) << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName();
@@ -766,8 +783,8 @@ Updater::checkDuplicatedAudioFiles(Stats& stats)
void void
Updater::checkVideoFiles( Stats& stats ) Updater::checkVideoFiles( Stats& stats )
{ {
std::vector<boost::filesystem::path> rootDirs = getRootDirectoriesByType(_db.getSession(), Database::MediaDirectory::Video); std::vector<boost::filesystem::path> rootDirs = getRootDirectoriesByType(_db->getSession(), Database::MediaDirectory::Video);
std::vector<boost::filesystem::path> videoPaths = Video::getAllPaths(_db.getSession()); std::vector<boost::filesystem::path> videoPaths = Video::getAllPaths(_db->getSession());
LMS_LOG(DBUPDATER, DEBUG) << "Checking videos..."; LMS_LOG(DBUPDATER, DEBUG) << "Checking videos...";
for (auto& videoPath : videoPaths) for (auto& videoPath : videoPaths)
@@ -777,9 +794,9 @@ Updater::checkVideoFiles( Stats& stats )
if (!checkFile(videoPath, rootDirs, _videoFileExtensions)) if (!checkFile(videoPath, rootDirs, _videoFileExtensions))
{ {
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
Video::pointer video = Video::getByPath(_db.getSession(), videoPath); Video::pointer video = Video::getByPath(_db->getSession(), videoPath);
if (video) if (video)
{ {
video.remove(); video.remove();
@@ -797,10 +814,10 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
// Check last update time // Check last update time
boost::posix_time::ptime lastWriteTime (boost::posix_time::from_time_t( boost::filesystem::last_write_time( file ) ) ); boost::posix_time::ptime lastWriteTime (boost::posix_time::from_time_t( boost::filesystem::last_write_time( file ) ) );
Wt::Dbo::Transaction transaction(_db.getSession()); Wt::Dbo::Transaction transaction(_db->getSession());
// Skip file if last write is the same // Skip file if last write is the same
Wt::Dbo::ptr<Video> video = Video::getByPath(_db.getSession(), file); Wt::Dbo::ptr<Video> video = Video::getByPath(_db->getSession(), file);
if (video && video->getLastWriteTime() == lastWriteTime) if (video && video->getLastWriteTime() == lastWriteTime)
return; return;
@@ -841,7 +858,7 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
// Today we are very aggressive, but we could also guess names from path, etc. // Today we are very aggressive, but we could also guess names from path, etc.
if (!video) if (!video)
{ {
video = Video::create(_db.getSession(), file); video = Video::create(_db->getSession(), file);
LMS_LOG(DBUPDATER, DEBUG) << "Adding '" << file << "'"; LMS_LOG(DBUPDATER, DEBUG) << "Adding '" << file << "'";
stats.nbAdded++; stats.nbAdded++;
} }
@@ -860,4 +877,4 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
transaction.commit(); transaction.commit();
} }
} // namespace DatabaseUpdater } // namespace Database
@@ -20,28 +20,22 @@
#ifndef DB_UPDATER_HPP #ifndef DB_UPDATER_HPP
#define DB_UPDATER_HPP #define DB_UPDATER_HPP
#include <boost/asio/deadline_timer.hpp>
#include <Wt/WIOService> #include <Wt/WIOService>
#include <Wt/WSignal>
#include "metadata/MetaData.hpp" #include <mutex>
#include <boost/asio/deadline_timer.hpp>
#include "metadata/AvFormat.hpp"
#include "database/DatabaseHandler.hpp" #include "database/DatabaseHandler.hpp"
namespace DatabaseUpdater { namespace Database {
class Updater class Updater
{ {
public: public:
Updater(Wt::Dbo::SqlConnectionPool& connectionPool, MetaData::Parser& parser);
void setAudioExtensions(const std::vector<std::string>& extensions);
void setVideoExtensions(const std::vector<std::string>& extensions);
void start();
void stop();
private:
struct Stats struct Stats
{ {
std::size_t nbSkipped = 0; // no change since last scan std::size_t nbSkipped = 0; // no change since last scan
@@ -55,6 +49,25 @@ class Updater
std::size_t nbChanges() const { return nbAdded + nbRemoved + nbModified;} std::size_t nbChanges() const { return nbAdded + nbRemoved + nbModified;}
}; };
static Updater& instance();
void setConnectionPool(Wt::Dbo::SqlConnectionPool& connectionPool);
void setAudioExtensions(const std::vector<std::string>& extensions);
void setVideoExtensions(const std::vector<std::string>& extensions);
void start();
void stop();
void restart();
Wt::Signal<Stats>& changed() { return _sigChanged; }
std::mutex& getMutex(void) { return _mutex; }
private:
Updater();
struct RootDirectory struct RootDirectory
{ {
Database::MediaDirectory::Type type; Database::MediaDirectory::Type type;
@@ -96,19 +109,21 @@ class Updater
bool _running; bool _running;
Wt::WIOService _ioService; Wt::WIOService _ioService;
Wt::Signal<Stats> _sigChanged;
std::mutex _mutex;
boost::asio::deadline_timer _scheduleTimer; boost::asio::deadline_timer _scheduleTimer;
Database::Handler _db; Database::Handler* _db = nullptr;
std::vector<boost::filesystem::path> _audioFileExtensions; std::vector<boost::filesystem::path> _audioFileExtensions;
std::vector<boost::filesystem::path> _videoFileExtensions; std::vector<boost::filesystem::path> _videoFileExtensions;
MetaData::Parser& _metadataParser; MetaData::AvFormat _metadataParser;
}; // class Updater }; // class Updater
} // DatabaseUpdater } // Database
#endif #endif
+10 -1
View File
@@ -37,6 +37,8 @@ class Artist;
class Release; class Release;
class Track; class Track;
class PlaylistEntry; class PlaylistEntry;
class Classification;
class ClassificationData;
class Genre class Genre
{ {
@@ -156,6 +158,8 @@ class Track
void setArtist(Wt::Dbo::ptr<Artist> artist) { _artist = artist; } void setArtist(Wt::Dbo::ptr<Artist> artist) { _artist = artist; }
void setRelease(Wt::Dbo::ptr<Release> release) { _release = release; } void setRelease(Wt::Dbo::ptr<Release> release) { _release = release; }
void setGenres(std::vector<Genre::pointer> genres); void setGenres(std::vector<Genre::pointer> genres);
void addClassifification(Wt::Dbo::ptr<Classification> classification);
void addClassifificationData(Wt::Dbo::ptr<ClassificationData> classificationData);
int getTrackNumber(void) const { return _trackNumber; } int getTrackNumber(void) const { return _trackNumber; }
int getTotalTrackNumber(void) const { return _totalTrackNumber; } int getTotalTrackNumber(void) const { return _totalTrackNumber; }
@@ -175,6 +179,8 @@ class Track
Wt::Dbo::ptr<Release> getRelease(void) const { return _release; } Wt::Dbo::ptr<Release> getRelease(void) const { return _release; }
std::vector< Genre::pointer > getGenres(void) const; std::vector< Genre::pointer > getGenres(void) const;
bool hasGenre(Genre::pointer genre) const { return _genres.count(genre); } bool hasGenre(Genre::pointer genre) const { return _genres.count(genre); }
std::vector< Wt::Dbo::ptr<Classification> > getClassifications(void) const;
std::vector< Wt::Dbo::ptr<ClassificationData> > getClassificationData(void) const;
template<class Action> template<class Action>
void persist(Action& a) void persist(Action& a)
@@ -198,6 +204,8 @@ class Track
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade); Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _genres, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade); Wt::Dbo::hasMany(a, _genres, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track"); Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasMany(a, _classifications, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasMany(a, _classificationData, Wt::Dbo::ManyToOne, "track");
} }
private: private:
@@ -228,7 +236,8 @@ class Track
Wt::Dbo::ptr<Release> _release; Wt::Dbo::ptr<Release> _release;
Wt::Dbo::collection< Genre::pointer > _genres; // Genres that are related to this track Wt::Dbo::collection< Genre::pointer > _genres; // Genres that are related to this track
Wt::Dbo::collection< Wt::Dbo::ptr<PlaylistEntry> > _playlistEntries; Wt::Dbo::collection< Wt::Dbo::ptr<PlaylistEntry> > _playlistEntries;
Wt::Dbo::collection< Wt::Dbo::ptr<Classification> > _classifications;
Wt::Dbo::collection< Wt::Dbo::ptr<ClassificationData> > _classificationData;
}; };
+1
View File
@@ -22,6 +22,7 @@
#include "Artist.hpp" #include "Artist.hpp"
#include "Track.hpp" #include "Track.hpp"
#include "Playlist.hpp" #include "Playlist.hpp"
#include "Classification.hpp"
#include "Release.hpp" #include "Release.hpp"
#include "Video.hpp" #include "Video.hpp"
#include "MediaDirectory.hpp" #include "MediaDirectory.hpp"
+15 -19
View File
@@ -19,18 +19,17 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <Wt/WServer>
#include "config/config.h" #include "config/config.h"
#include "av/AvInfo.hpp" #include "av/AvInfo.hpp"
#include "av/AvTranscoder.hpp" #include "av/AvTranscoder.hpp"
#include "logger/Logger.hpp" #include "logger/Logger.hpp"
#include "image/Image.hpp" #include "image/Image.hpp"
#include "database/DatabaseUpdater.hpp"
#include "ui/LmsApplication.hpp" #include "ui/LmsApplication.hpp"
#include "service/ServiceManager.hpp"
#include "service/DatabaseUpdateService.hpp"
#include <Wt/WServer>
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
@@ -49,8 +48,6 @@ int main(int argc, char* argv[])
Wt::WServer::instance()->logger().configure("*"); // log everything Wt::WServer::instance()->logger().configure("*"); // log everything
Service::ServiceManager& serviceManager = Service::ServiceManager::instance();
// lib init // lib init
Image::init(argv[0]); Image::init(argv[0]);
Av::AvInit(); Av::AvInit();
@@ -60,30 +57,29 @@ int main(int argc, char* argv[])
// Initializing a connection pool to the database that will be shared along services // Initializing a connection pool to the database that will be shared along services
std::unique_ptr<Wt::Dbo::SqlConnectionPool> connectionPool( Database::Handler::createConnectionPool("/var/lms/lms.db")); // TODO use $datadir from autotools std::unique_ptr<Wt::Dbo::SqlConnectionPool> connectionPool( Database::Handler::createConnectionPool("/var/lms/lms.db")); // TODO use $datadir from autotools
serviceManager.add( std::make_shared<Service::DatabaseUpdateService>(*connectionPool)); Database::Updater::instance().setConnectionPool(*connectionPool);
// bind entry point // bind entry point
server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, _1, boost::ref(*connectionPool))); server.addEntryPoint(Wt::Application, boost::bind(UserInterface::LmsApplication::create, _1, boost::ref(*connectionPool)));
// Starting the main server // Start
LMS_LOG(MAIN, INFO) << "Starting database updater...";
Database::Updater::instance().start();
LMS_LOG(MAIN, INFO) << "Starting server..."; LMS_LOG(MAIN, INFO) << "Starting server...";
server.start(); server.start();
// Start underlying services // Wait
LMS_LOG(MAIN, INFO) << "Starting services...";
serviceManager.start();
LMS_LOG(MAIN, INFO) << "Now running..."; LMS_LOG(MAIN, INFO) << "Now running...";
// Waiting for shutdown command
Wt::WServer::waitForShutdown(argv[0]); Wt::WServer::waitForShutdown(argv[0]);
LMS_LOG(MAIN, INFO) << "Stopping services..."; // Stop
serviceManager.stop();
serviceManager.clear();
LMS_LOG(MAIN, INFO) << "Stopping server..."; LMS_LOG(MAIN, INFO) << "Stopping server...";
server.stop(); Database::Updater::instance().stop();
LMS_LOG(MAIN, INFO) << "Stopping database updater...";
Database::Updater::instance().stop();
res = EXIT_SUCCESS; res = EXIT_SUCCESS;
} }
-52
View File
@@ -1,52 +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/>.
*/
#include <boost/thread.hpp>
#include "DatabaseUpdateService.hpp"
namespace Service {
DatabaseUpdateService::DatabaseUpdateService(Wt::Dbo::SqlConnectionPool &connectionPool)
: _metadataParser(),
_databaseUpdater( connectionPool, _metadataParser)
{
}
void
DatabaseUpdateService::start(void)
{
_databaseUpdater.start();
}
void
DatabaseUpdateService::stop(void)
{
_databaseUpdater.stop();
}
void
DatabaseUpdateService::restart(void)
{
stop();
start();
}
} // namespace Service
-55
View File
@@ -1,55 +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/>.
*/
#ifndef DB_UPDATE_SERVICE_HPP
#define DB_UPDATE_SERVICE_HPP
#include <boost/thread.hpp>
#include <boost/asio/io_service.hpp>
#include "metadata/AvFormat.hpp"
#include "database-updater/DatabaseUpdater.hpp"
#include "Service.hpp"
namespace Service {
class DatabaseUpdateService : public Service
{
public:
typedef std::shared_ptr<DatabaseUpdateService> pointer;
DatabaseUpdateService(Wt::Dbo::SqlConnectionPool &connectionPool);
// Service interface
void start(void);
void stop(void);
void restart(void);
private:
MetaData::AvFormat _metadataParser;
DatabaseUpdater::Updater _databaseUpdater; // Todo use handler
};
} // namespace Service
#endif
-51
View File
@@ -1,51 +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/>.
*/
#ifndef SERVICE_HPP
#define SERVICE_HPP
#include <boost/thread.hpp>
#include <memory>
#include <set>
namespace Service {
// Interface class wrapper for running services
class Service
{
public:
typedef std::shared_ptr<Service> pointer;
Service(const Service&) = delete;
Service& operator=(const Service&) = delete;
Service() {}
virtual ~Service() {}
virtual void start(void) = 0;
virtual void stop(void) = 0;
virtual void restart(void) = 0;
};
} // namespace Service
#endif
-84
View File
@@ -1,84 +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/>.
*/
#include "ServiceManager.hpp"
namespace Service {
ServiceManager&
ServiceManager::instance()
{
static ServiceManager instance;
return instance;
}
ServiceManager::ServiceManager()
{
}
ServiceManager::~ServiceManager()
{
stop();
}
void
ServiceManager::add(Service::pointer service)
{
_services.insert(service);
service->start();
}
void
ServiceManager::del(Service::pointer service)
{
service->stop();
_services.erase(service);
}
void
ServiceManager::clear(void)
{
stop();
_services.clear();
}
void
ServiceManager::start(void)
{
for (Service::pointer service : _services)
service->start();
}
void
ServiceManager::stop(void)
{
for (Service::pointer service : _services)
service->stop();
}
void
ServiceManager::restart(void)
{
for (Service::pointer service : _services)
service->restart();
}
} // namespace Service
-77
View File
@@ -1,77 +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/>.
*/
#ifndef SERVICE_CONTROLER_HPP
#define SERVICE_CONTROLER_HPP
#include <set>
#include "Service.hpp"
namespace Service {
// Start/Stop/Reload Services
class ServiceManager
{
public:
static ServiceManager& instance();
~ServiceManager();
void add(Service::pointer service);
void del(Service::pointer service);
void clear();
void start();
void stop();
void restart();
template <class T> typename T::pointer get();
boost::mutex& mutex() { return _mutex;}
private:
ServiceManager();
ServiceManager(ServiceManager const&); // Don't Implement
void operator=(ServiceManager const&); // Don't implement
boost::mutex _mutex;
std::set<Service::pointer> _services;
};
template <class T> typename T::pointer
ServiceManager::get()
{
std::set<Service::pointer>::iterator it;
for (Service::pointer service : _services)
{
if (typeid(*(service)) == typeid(T)) {
return std::dynamic_pointer_cast<T>(service);
}
}
return std::shared_ptr<T>();
}
} // namespace Service
#endif
+21 -28
View File
@@ -30,8 +30,7 @@
#include "SettingsUsers.hpp" #include "SettingsUsers.hpp"
#include "logger/Logger.hpp" #include "logger/Logger.hpp"
#include "service/ServiceManager.hpp" #include "database/DatabaseUpdater.hpp"
#include "service/DatabaseUpdateService.hpp"
#include "LmsApplication.hpp" #include "LmsApplication.hpp"
@@ -74,11 +73,29 @@ Settings::Settings(Wt::WContainerWidget* parent)
if (userIsAdmin) if (userIsAdmin)
{ {
MediaDirectories* mediaDirectories = new MediaDirectories(); MediaDirectories* mediaDirectories = new MediaDirectories();
mediaDirectories->changed().connect(this, &Settings::handleDatabaseDirectoriesChanged); mediaDirectories->changed().connect(std::bind([=]
{
LMS_LOG(UI, INFO) << "Media directories have changed: requesting imediate scan";
// On directory add or delete, request an immediate scan
//
{
Wt::Dbo::Transaction transaction(DboSession());
Database::MediaDirectorySettings::get(DboSession()).modify()->setManualScanRequested(true);
}
{
std::lock_guard<std::mutex> lock(Database::Updater::instance().getMutex());
Database::Updater::instance().restart();
}
}));
menu->addItem("Media Folders", mediaDirectories)->setPathComponent("mediadirectories"); menu->addItem("Media Folders", mediaDirectories)->setPathComponent("mediadirectories");
DatabaseFormView* databaseFormView = new DatabaseFormView(); DatabaseFormView* databaseFormView = new DatabaseFormView();
databaseFormView->changed().connect(this, &Settings::restartDatabaseUpdateService); databaseFormView->changed().connect(std::bind([=]
{
std::lock_guard<std::mutex> lock(Database::Updater::instance().getMutex());
Database::Updater::instance().restart();
}));
menu->addItem("Database", databaseFormView)->setPathComponent("database"); menu->addItem("Database", databaseFormView)->setPathComponent("database");
menu->addItem("Users", new Users())->setPathComponent("users"); menu->addItem("Users", new Users())->setPathComponent("users");
@@ -90,29 +107,5 @@ Settings::Settings(Wt::WContainerWidget* parent)
} }
void
Settings::handleDatabaseDirectoriesChanged()
{
LMS_LOG(UI, INFO) << "Media directories have changed: requesting imediate scan";
// On directory add or delete, request an immediate scan
{
Wt::Dbo::Transaction transaction(DboSession());
Database::MediaDirectorySettings::get(DboSession()).modify()->setManualScanRequested(true);
}
restartDatabaseUpdateService();
}
void
Settings::restartDatabaseUpdateService()
{
// Restarting the update service
boost::lock_guard<boost::mutex> serviceLock (Service::ServiceManager::instance().mutex());
Service::DatabaseUpdateService::pointer service = Service::ServiceManager::instance().get<Service::DatabaseUpdateService>();
if (service)
service->restart();
}
} // namespace Settings } // namespace Settings
} // namespace UserInterface } // namespace UserInterface
-5
View File
@@ -28,11 +28,6 @@ class Settings : public Wt::WContainerWidget
Settings(Wt::WContainerWidget* parent = 0); Settings(Wt::WContainerWidget* parent = 0);
private: private:
void handleDatabaseDirectoriesChanged();
void restartDatabaseUpdateService(void);
}; };
} // namespace Settings } // namespace Settings
@@ -20,6 +20,6 @@
#include <vector> #include <vector>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
// TODO move to utils
void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& checksum); void computeCrc(const boost::filesystem::path& p, std::vector<unsigned char>& checksum);