Prepare the customization of the cluster types
This commit is contained in:
+109
-37
@@ -37,6 +37,35 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
const std::string updatePeriodSetting = "update_period";
|
||||||
|
const std::string updateStartTimeSetting = "update_start_time";
|
||||||
|
const std::string clustersSetting = "clusters";
|
||||||
|
const std::string fileExtensionsSetting = "file_extensions";
|
||||||
|
|
||||||
|
const std::vector<std::string> defaultFileExtensions =
|
||||||
|
{
|
||||||
|
".mp3",
|
||||||
|
".ogg",
|
||||||
|
".oga",
|
||||||
|
".aac",
|
||||||
|
".m4a",
|
||||||
|
".flac",
|
||||||
|
".wav",
|
||||||
|
".wma",
|
||||||
|
".aif",
|
||||||
|
".aiff",
|
||||||
|
".ape",
|
||||||
|
".mpc",
|
||||||
|
".shn",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Caution current implementation prevents spaces with the names
|
||||||
|
const std::vector<std::string> defaultClusters =
|
||||||
|
{
|
||||||
|
"Genre",
|
||||||
|
"Group",
|
||||||
|
};
|
||||||
|
|
||||||
Wt::WDate
|
Wt::WDate
|
||||||
getNextMonday(Wt::WDate current)
|
getNextMonday(Wt::WDate current)
|
||||||
{
|
{
|
||||||
@@ -99,23 +128,23 @@ using namespace Database;
|
|||||||
UpdatePeriod
|
UpdatePeriod
|
||||||
getUpdatePeriod(Wt::Dbo::Session& session)
|
getUpdatePeriod(Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
return static_cast<UpdatePeriod>(Setting::getInt(session, "update_period", static_cast<int>(UpdatePeriod::Never)));
|
return static_cast<UpdatePeriod>(Setting::getInt(session, updatePeriodSetting, static_cast<int>(UpdatePeriod::Never)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod)
|
setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod)
|
||||||
{
|
{
|
||||||
Setting::setInt(session, "update_period", static_cast<int>(updatePeriod));
|
Setting::setInt(session, updatePeriodSetting, static_cast<int>(updatePeriod));
|
||||||
}
|
}
|
||||||
|
|
||||||
Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session)
|
Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session)
|
||||||
{
|
{
|
||||||
return Setting::getTime(session, "update_start_time");
|
return Setting::getTime(session, updateStartTimeSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime startTime)
|
void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime startTime)
|
||||||
{
|
{
|
||||||
Setting::setTime(session, "update_start_time", startTime);
|
Setting::setTime(session, updateStartTimeSetting, startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaScanner::MediaScanner(Wt::Dbo::SqlConnectionPool& connectionPool)
|
MediaScanner::MediaScanner(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||||
@@ -127,8 +156,11 @@ _db(connectionPool)
|
|||||||
|
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
if (!Setting::exists(_db.getSession(), "file_extensions"))
|
if (!Setting::exists(_db.getSession(), fileExtensionsSetting))
|
||||||
Setting::setString(_db.getSession(), "file_extensions", ".mp3 .ogg .oga .aac .m4a .flac .wav .wma .aif .aiff .ape .mpc .shn" );
|
Setting::setString(_db.getSession(), fileExtensionsSetting, joinStrings(defaultFileExtensions, " "));
|
||||||
|
|
||||||
|
if (!Setting::exists(_db.getSession(), clustersSetting))
|
||||||
|
Setting::setString(_db.getSession(), clustersSetting, joinStrings(defaultClusters, " "));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -244,11 +276,17 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
if (err)
|
if (err)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
LMS_LOG(UI, INFO) << "New scan started!";
|
||||||
|
|
||||||
refreshScanSettings();
|
refreshScanSettings();
|
||||||
|
|
||||||
|
bool forceScan = false;
|
||||||
Stats stats;
|
Stats stats;
|
||||||
|
|
||||||
checkAudioFiles(stats);
|
checkAudioFiles(stats);
|
||||||
|
forceScan = checkClusters();
|
||||||
|
|
||||||
|
LMS_LOG(UI, INFO) << "Checks complete, force scan = " << forceScan;
|
||||||
|
|
||||||
for (auto rootDirectory : _rootDirectories)
|
for (auto rootDirectory : _rootDirectories)
|
||||||
{
|
{
|
||||||
@@ -256,7 +294,7 @@ MediaScanner::scan(boost::system::error_code err)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "'...";
|
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "'...";
|
||||||
scanRootDirectory(rootDirectory, stats);
|
scanRootDirectory(rootDirectory, forceScan, stats);
|
||||||
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "' DONE";
|
LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "' DONE";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,12 +318,16 @@ MediaScanner::refreshScanSettings()
|
|||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
_fileExtensions.clear();
|
_fileExtensions.clear();
|
||||||
for (auto extension : splitString(Setting::getString(_db.getSession(), "file_extensions"), " "))
|
for (auto extension : splitString(Setting::getString(_db.getSession(), fileExtensionsSetting), " "))
|
||||||
_fileExtensions.push_back( extension );
|
_fileExtensions.push_back( extension );
|
||||||
|
|
||||||
_rootDirectories.clear();
|
_rootDirectories.clear();
|
||||||
for (auto rootDir : Database::MediaDirectory::getAll(_db.getSession()))
|
for (auto rootDir : Database::MediaDirectory::getAll(_db.getSession()))
|
||||||
_rootDirectories.push_back(rootDir->getPath());
|
_rootDirectories.push_back(rootDir->getPath());
|
||||||
|
|
||||||
|
_clusterTypes.clear();
|
||||||
|
for (auto cluster : splitString(Setting::getString(_db.getSession(), clustersSetting), " "))
|
||||||
|
_clusterTypes.push_back(cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
Artist::pointer
|
Artist::pointer
|
||||||
@@ -369,17 +411,13 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames)
|
|||||||
|
|
||||||
for (auto clusterNames : clustersNames)
|
for (auto clusterNames : clustersNames)
|
||||||
{
|
{
|
||||||
ClusterType::pointer clusterType = ClusterType::getByName(_db.getSession(), clusterNames.first);
|
auto clusterType = ClusterType::getByName(_db.getSession(), clusterNames.first);
|
||||||
if (!clusterType)
|
if (!clusterType)
|
||||||
{
|
continue;
|
||||||
clusterType = ClusterType::create(_db.getSession(), clusterNames.first);
|
|
||||||
_db.getSession().flush(); // Make sure the newly createdType has an id
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto clusterName : clusterNames.second)
|
for (auto clusterName : clusterNames.second)
|
||||||
{
|
{
|
||||||
Cluster::pointer cluster = clusterType->getCluster(clusterName);
|
auto cluster = clusterType->getCluster(clusterName);
|
||||||
|
|
||||||
if (!cluster)
|
if (!cluster)
|
||||||
cluster = Cluster::create(_db.getSession(), clusterType, clusterName);
|
cluster = Cluster::create(_db.getSession(), clusterType, clusterName);
|
||||||
|
|
||||||
@@ -391,20 +429,23 @@ MediaScanner::getClusters( const MetaData::Clusters& clustersNames)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
|
MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan, Stats& stats)
|
||||||
{
|
{
|
||||||
auto lastWriteTime = Wt::WDateTime::fromTime_t(boost::filesystem::last_write_time(file));
|
auto lastWriteTime = Wt::WDateTime::fromTime_t(boost::filesystem::last_write_time(file));
|
||||||
|
|
||||||
// Skip file if last write is the same
|
if (!forceScan)
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
// Skip file if last write is the same
|
||||||
|
|
||||||
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file);
|
|
||||||
|
|
||||||
if (track && track->getLastWriteTime() == lastWriteTime)
|
|
||||||
{
|
{
|
||||||
stats.skips++;
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
return;
|
|
||||||
|
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file);
|
||||||
|
|
||||||
|
if (track && track->getLastWriteTime() == lastWriteTime)
|
||||||
|
{
|
||||||
|
stats.skips++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,7 +642,7 @@ MediaScanner::scanAudioFile( const boost::filesystem::path& file, Stats& stats)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, Stats& stats)
|
MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, bool forceScan, Stats& stats)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
|
|
||||||
@@ -619,7 +660,7 @@ MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, Stats& st
|
|||||||
if (boost::filesystem::is_regular(path))
|
if (boost::filesystem::is_regular(path))
|
||||||
{
|
{
|
||||||
if (isFileSupported(path, _fileExtensions))
|
if (isFileSupported(path, _fileExtensions))
|
||||||
scanAudioFile(path, stats );
|
scanAudioFile(path, forceScan, stats );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -702,7 +743,7 @@ MediaScanner::checkAudioFiles( Stats& stats )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, DEBUG) << "Checking Clusters...";
|
LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan clusters...";
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
@@ -717,17 +758,9 @@ MediaScanner::checkAudioFiles( Stats& stats )
|
|||||||
cluster.remove();
|
cluster.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now process orphan cluster types
|
|
||||||
auto clusterTypes = ClusterType::getAllOrphans(_db.getSession());
|
|
||||||
for (auto clusterType : clusterTypes)
|
|
||||||
{
|
|
||||||
LMS_LOG(DBUPDATER, DEBUG) << "Removing orphan cluster type '" << clusterType->getName() << "'";
|
|
||||||
clusterType.remove();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, DEBUG) << "Checking artists...";
|
LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan artists...";
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
@@ -739,7 +772,7 @@ MediaScanner::checkAudioFiles( Stats& stats )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, DEBUG) << "Checking releases...";
|
LMS_LOG(DBUPDATER, DEBUG) << "Checking orphan releases...";
|
||||||
{
|
{
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
@@ -754,6 +787,45 @@ MediaScanner::checkAudioFiles( Stats& stats )
|
|||||||
LMS_LOG(DBUPDATER, INFO) << "Check audio files done!";
|
LMS_LOG(DBUPDATER, INFO) << "Check audio files done!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MediaScanner::checkClusters()
|
||||||
|
{
|
||||||
|
bool hasChanges = false;
|
||||||
|
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Checking clusters";
|
||||||
|
|
||||||
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
auto clusterTypes = ClusterType::getAll(_db.getSession());
|
||||||
|
|
||||||
|
// Remove no longer desired clusters
|
||||||
|
for (auto clusterType : clusterTypes)
|
||||||
|
{
|
||||||
|
if (std::none_of(_clusterTypes.begin(), _clusterTypes.end(),
|
||||||
|
[&clusterType](std::string clusterTypeName) { return clusterTypeName == clusterType->getName(); }))
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Removing cluster type " << clusterType->getName();
|
||||||
|
clusterType.remove();
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any missing clusters
|
||||||
|
for (auto clusterTypeName : _clusterTypes)
|
||||||
|
{
|
||||||
|
if (std::none_of(clusterTypes.begin(), clusterTypes.end(),
|
||||||
|
[&clusterTypeName](ClusterType::pointer clusterType) { return (clusterType->getName() == clusterTypeName); }))
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Creating cluster type " << clusterTypeName;
|
||||||
|
ClusterType::create(_db.getSession(), clusterTypeName);
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Checking clusters done!";
|
||||||
|
|
||||||
|
return hasChanges;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaScanner::checkDuplicatedAudioFiles(Stats& stats)
|
MediaScanner::checkDuplicatedAudioFiles(Stats& stats)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class MediaScanner
|
|||||||
// Update database (scheduled callback)
|
// Update database (scheduled callback)
|
||||||
void scan(boost::system::error_code ec);
|
void scan(boost::system::error_code ec);
|
||||||
|
|
||||||
void scanRootDirectory( boost::filesystem::path rootDirectory, Stats& stats);
|
void scanRootDirectory( boost::filesystem::path rootDirectory, bool forceScan, Stats& stats);
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
Database::Artist::pointer getArtist( const boost::filesystem::path& file, const std::string& name, const std::string& MBID);
|
Database::Artist::pointer getArtist( const boost::filesystem::path& file, const std::string& name, const std::string& MBID);
|
||||||
@@ -108,10 +108,10 @@ class MediaScanner
|
|||||||
std::vector<Database::Cluster::pointer> getClusters( const MetaData::Clusters& names);
|
std::vector<Database::Cluster::pointer> getClusters( const MetaData::Clusters& names);
|
||||||
void refreshScanSettings();
|
void refreshScanSettings();
|
||||||
|
|
||||||
// Audio
|
|
||||||
void checkAudioFiles( Stats& stats );
|
void checkAudioFiles( Stats& stats );
|
||||||
|
bool checkClusters();
|
||||||
void checkDuplicatedAudioFiles( Stats& stats );
|
void checkDuplicatedAudioFiles( Stats& stats );
|
||||||
void scanAudioFile( const boost::filesystem::path& file, Stats& stats);
|
void scanAudioFile( const boost::filesystem::path& file, bool forceScan, Stats& stats);
|
||||||
|
|
||||||
bool _running;
|
bool _running;
|
||||||
Wt::WIOService _ioService;
|
Wt::WIOService _ioService;
|
||||||
@@ -126,6 +126,7 @@ class MediaScanner
|
|||||||
Database::Handler _db;
|
Database::Handler _db;
|
||||||
|
|
||||||
// Scan settings
|
// Scan settings
|
||||||
|
std::vector<std::string> _clusterTypes;
|
||||||
std::vector<boost::filesystem::path> _fileExtensions;
|
std::vector<boost::filesystem::path> _fileExtensions;
|
||||||
std::vector<boost::filesystem::path> _rootDirectories;
|
std::vector<boost::filesystem::path> _rootDirectories;
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -20,10 +20,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <iostream>
|
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
|
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
|
||||||
@@ -62,6 +64,12 @@ splitString(std::string string, std::string separators)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
joinStrings(std::vector<std::string> strings, std::string delimiter)
|
||||||
|
{
|
||||||
|
return boost::algorithm::join(strings, delimiter);
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
stringTrim(const std::string& str, const std::string& whitespace)
|
stringTrim(const std::string& str, const std::string& whitespace)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ readList(const std::string& str, const std::string& separators, std::list<std::s
|
|||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
splitString(std::string string, std::string separators);
|
splitString(std::string string, std::string separators);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
joinStrings(std::vector<std::string> strings, std::string delimiter);
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
stringTrim(const std::string& str, const std::string& whitespaces = " \t");
|
stringTrim(const std::string& str, const std::string& whitespaces = " \t");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user