From f1c12ebb4d5e008afbfea9069250a341122a32c5 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 20 May 2018 14:38:04 +0200 Subject: [PATCH] Centralized scan settings into a dedicated ScanSettings table --- src/Makefile.am | 3 +- src/database/Cluster.cpp | 1 + src/database/Cluster.hpp | 3 + src/database/DatabaseHandler.cpp | 29 ++-- src/database/MediaDirectory.cpp | 58 ------- src/database/MediaDirectory.hpp | 59 -------- src/database/ScanSettings.cpp | 111 ++++++++++++++ src/database/ScanSettings.hpp | 82 ++++++++++ src/metadata/AvFormat.cpp | 7 +- src/metadata/AvFormat.hpp | 5 +- src/metadata/MetaData.cpp | 33 ---- src/metadata/MetaData.hpp | 12 +- src/metadata/TagLibParser.cpp | 7 +- src/metadata/TagLibParser.hpp | 5 +- src/scanner/MediaScanner.cpp | 209 +++++--------------------- src/scanner/MediaScanner.hpp | 28 +--- src/ui/admin/DatabaseSettingsView.cpp | 45 +++--- tools/metadata/Makefile.am | 3 +- 18 files changed, 290 insertions(+), 410 deletions(-) delete mode 100644 src/database/MediaDirectory.cpp delete mode 100644 src/database/MediaDirectory.hpp create mode 100644 src/database/ScanSettings.cpp create mode 100644 src/database/ScanSettings.hpp delete mode 100644 src/metadata/MetaData.cpp diff --git a/src/Makefile.am b/src/Makefile.am index b364c52d..7c5889d0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,16 +8,15 @@ lms_SOURCES = \ $(srcdir)/database/Artist.cpp \ $(srcdir)/database/Cluster.cpp \ $(srcdir)/database/DatabaseHandler.cpp \ - $(srcdir)/database/MediaDirectory.cpp \ $(srcdir)/database/Playlist.cpp \ $(srcdir)/database/Release.cpp \ + $(srcdir)/database/ScanSettings.cpp \ $(srcdir)/database/Setting.cpp \ $(srcdir)/database/SqlQuery.cpp \ $(srcdir)/database/Track.cpp \ $(srcdir)/database/User.cpp \ $(srcdir)/image/Image.cpp \ $(srcdir)/metadata/AvFormat.cpp \ - $(srcdir)/metadata/MetaData.cpp \ $(srcdir)/metadata/TagLibParser.cpp \ $(srcdir)/scanner/MediaScanner.cpp \ $(srcdir)/ui/Auth.cpp \ diff --git a/src/database/Cluster.cpp b/src/database/Cluster.cpp index 12c7e947..5468cd46 100644 --- a/src/database/Cluster.cpp +++ b/src/database/Cluster.cpp @@ -21,6 +21,7 @@ #include "Artist.hpp" #include "Release.hpp" +#include "ScanSettings.hpp" #include "SqlQuery.hpp" #include "Track.hpp" diff --git a/src/database/Cluster.hpp b/src/database/Cluster.hpp index 65abf978..65c1d68f 100644 --- a/src/database/Cluster.hpp +++ b/src/database/Cluster.hpp @@ -32,6 +32,7 @@ namespace Database { class Track; class ClusterType; +class ScanSettings; class Cluster : public Wt::Dbo::Dbo { @@ -101,6 +102,7 @@ class ClusterType : public Wt::Dbo::Dbo { Wt::Dbo::field(a, _name, "name"); Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToOne, "cluster_type"); + Wt::Dbo::belongsTo(a, _scanSettings, "scan_settings", Wt::Dbo::OnDeleteCascade); } private: @@ -109,6 +111,7 @@ class ClusterType : public Wt::Dbo::Dbo std::string _name; Wt::Dbo::collection< Wt::Dbo::ptr > _clusters; + Wt::Dbo::ptr _scanSettings; }; } // namespace Database diff --git a/src/database/DatabaseHandler.cpp b/src/database/DatabaseHandler.cpp index 4978703b..7d3eb8d5 100644 --- a/src/database/DatabaseHandler.cpp +++ b/src/database/DatabaseHandler.cpp @@ -37,9 +37,9 @@ #include "Artist.hpp" #include "Cluster.hpp" -#include "MediaDirectory.hpp" #include "Playlist.hpp" #include "Release.hpp" +#include "ScanSettings.hpp" #include "Track.hpp" namespace Database { @@ -98,20 +98,21 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool) { _session.setConnectionPool(connectionPool); - _session.mapClass("artist"); - _session.mapClass("cluster"); - _session.mapClass("cluster_type"); - _session.mapClass("media_directory"); - _session.mapClass("playlist"); - _session.mapClass("playlist_entry"); - _session.mapClass("release"); - _session.mapClass("setting"); - _session.mapClass("track"); + _session.mapClass("artist"); + _session.mapClass("cluster"); + _session.mapClass("cluster_type"); + _session.mapClass("playlist"); + _session.mapClass("playlist_entry"); + _session.mapClass("release"); + _session.mapClass("setting"); + _session.mapClass("track"); - _session.mapClass("auth_info"); - _session.mapClass("auth_identity"); - _session.mapClass("auth_token"); - _session.mapClass("user"); + _session.mapClass("scan_settings"); + + _session.mapClass("auth_info"); + _session.mapClass("auth_identity"); + _session.mapClass("auth_token"); + _session.mapClass("user"); try { Wt::Dbo::Transaction transaction(_session); diff --git a/src/database/MediaDirectory.cpp b/src/database/MediaDirectory.cpp deleted file mode 100644 index 72dea4e7..00000000 --- a/src/database/MediaDirectory.cpp +++ /dev/null @@ -1,58 +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 . - */ - -#include "MediaDirectory.hpp" - -#include "utils/Utils.hpp" - -namespace Database { - -MediaDirectory::MediaDirectory(boost::filesystem::path p) -: _path(stringTrimEnd(p.string(), "/\\")) -{ -} - -MediaDirectory::pointer -MediaDirectory::create(Wt::Dbo::Session& session, boost::filesystem::path p) -{ - return session.add( std::make_unique(p) ); -} - -void -MediaDirectory::eraseAll(Wt::Dbo::Session& session) -{ - for (auto dir : getAll(session)) - dir.remove(); -} - -std::vector -MediaDirectory::getAll(Wt::Dbo::Session& session) -{ - Wt::Dbo::collection< MediaDirectory::pointer > res = session.find(); - - return std::vector(res.begin(), res.end()); -} - -boost::filesystem::path -MediaDirectory::getPath(void) const -{ - return boost::filesystem::path(stringTrimEnd(_path, "/\\")); -} - -} // namespace Database diff --git a/src/database/MediaDirectory.hpp b/src/database/MediaDirectory.hpp deleted file mode 100644 index 84201b1d..00000000 --- a/src/database/MediaDirectory.hpp +++ /dev/null @@ -1,59 +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 . - */ - -#pragma once - -#include - -#include - -#include - -namespace Database { - -class MediaDirectory -{ - public: - typedef Wt::Dbo::ptr pointer; - - MediaDirectory() {} - MediaDirectory(boost::filesystem::path p); - - // Accessors - static pointer create(Wt::Dbo::Session& session, boost::filesystem::path p); - static std::vector getAll(Wt::Dbo::Session& session); - - static void eraseAll(Wt::Dbo::Session& session); - static void eraseByPath(Wt::Dbo::Session& session, boost::filesystem::path p); - - boost::filesystem::path getPath(void) const; - - template - void persist(Action& a) - { - Wt::Dbo::field(a, _path, "path"); - } - - private: - - std::string _path; -}; - -} // namespace Database - diff --git a/src/database/ScanSettings.cpp b/src/database/ScanSettings.cpp new file mode 100644 index 00000000..2e05e377 --- /dev/null +++ b/src/database/ScanSettings.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 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 . + */ + +#include "ScanSettings.hpp" + +#include + +#include "utils/Logger.hpp" +#include "utils/Utils.hpp" + +#include "Cluster.hpp" + +namespace { + +std::set defaultClusterTypeNames = +{ + "GENRE", + "ALBUMGROUPING", + "MOOD", + "ALBUMMOOD", + "COMMENT:SONGS-DB_OCCASION", +}; + +} + +namespace Database { + + +ScanSettings::pointer +ScanSettings::get(Wt::Dbo::Session& session) +{ + ScanSettings::pointer settings = session.find(); + if (!settings) + { + settings = session.add(std::make_unique()); + settings.modify()->setClusterTypes(defaultClusterTypeNames); + } + + return settings; +} + +std::set +ScanSettings::getAudioFileExtensions() const +{ + auto extensions = splitString(_audioFileExtensions, " "); + return std::set(extensions.begin(), extensions.end()); +} + +std::vector +ScanSettings::getClusterTypes() const +{ + return std::vector(_clusterTypes.begin(), _clusterTypes.end()); +} + +void +ScanSettings::setMediaDirectory(boost::filesystem::path p) +{ + _mediaDirectory = stringTrimEnd(p.string(), "/\\"); +} + +void +ScanSettings::setClusterTypes(const std::set& clusterTypeNames) +{ + assert(session()); + + // Backup the old list + std::vector oldClusterTypes(_clusterTypes.begin(), _clusterTypes.end()); + + _clusterTypes.clear(); + // Create any missing cluster type + for (auto clusterTypeName : clusterTypeNames) + { + auto clusterType = ClusterType::getByName(*session(), clusterTypeName); + if (!clusterType) + { + LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName; + clusterType = ClusterType::create(*session(), clusterTypeName); + } + _clusterTypes.insert(clusterType); + } + + // Delete no longer existing cluster types + for (auto oldClusterType : oldClusterTypes) + { + if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(), + [oldClusterType](const std::string& name) { return name == oldClusterType->getName(); })) + { + LMS_LOG(DB, INFO) << "Deleting cluster type " << oldClusterType->getName(); + oldClusterType.remove(); + } + } +} + +} // namespace Database + diff --git a/src/database/ScanSettings.hpp b/src/database/ScanSettings.hpp new file mode 100644 index 00000000..c478fa3f --- /dev/null +++ b/src/database/ScanSettings.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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 . + */ + +#pragma once + +#include + +#include +#include + +namespace Database { + +class ClusterType; +// class meant to store general settings +class ScanSettings : public Wt::Dbo::Dbo +{ + public: + using pointer = Wt::Dbo::ptr; + + enum class UpdatePeriod { + Never = 0, + Daily, + Weekly, + Monthly + }; + + ScanSettings() {} + + static pointer get(Wt::Dbo::Session& session); + + // Getters + boost::filesystem::path getMediaDirectory() const { return _mediaDirectory; } + Wt::WTime getUpdateStartTime() const { return _startTime; } + UpdatePeriod getUpdatePeriod() const { return _updatePeriod; } + std::vector> getClusterTypes() const; + std::set getAudioFileExtensions() const; + + // Setters + void setMediaDirectory(boost::filesystem::path p); + void setUpdateStartTime(Wt::WTime t) { _startTime = t; } + void setUpdatePeriod(UpdatePeriod p) { _updatePeriod = p; } + void setClusterTypes(const std::set& clusterTypeNames); + void setAudioFileExtensions(std::set fileExtensions); + + template + void persist(Action& a) + { + Wt::Dbo::field(a, _mediaDirectory, "media_directory"); + Wt::Dbo::field(a, _startTime, "start_time"); + Wt::Dbo::field(a, _updatePeriod, "update_period"); + Wt::Dbo::field(a, _audioFileExtensions, "audio_file_extensions"); + Wt::Dbo::hasMany(a, _clusterTypes, Wt::Dbo::ManyToOne, "scan_settings"); + } + + private: + + std::string _mediaDirectory = ""; + Wt::WTime _startTime = Wt::WTime(0,0,0); + UpdatePeriod _updatePeriod = UpdatePeriod::Never; + std::string _audioFileExtensions = ".mp3 .ogg .oga .aac .m4a .flac .wav .wma .aif .aiff .ape .mpc .shn"; + Wt::Dbo::collection> _clusterTypes; +}; + + +} // namespace Database + diff --git a/src/metadata/AvFormat.cpp b/src/metadata/AvFormat.cpp index a4a2943d..e23c8682 100644 --- a/src/metadata/AvFormat.cpp +++ b/src/metadata/AvFormat.cpp @@ -29,11 +29,6 @@ namespace MetaData { -AvFormat::AvFormat(const ClusterTypes& clusterTypes) -: Parser(clusterTypes) -{ -} - boost::optional AvFormat::parse(const boost::filesystem::path& p, bool debug) { @@ -155,7 +150,7 @@ AvFormat::parse(const boost::filesystem::path& p, bool debug) { items.insert( std::make_pair(MetaData::Type::AcoustID, stringTrim(value)) ); } - else if (_clusterTypes.find(tag) != _clusterTypes.end()) + else if (_clusterTypeNames.find(tag) != _clusterTypeNames.end()) { std::vector clusterNames = splitString(value, ";,\\"); diff --git a/src/metadata/AvFormat.hpp b/src/metadata/AvFormat.hpp index 9039c90a..9c29e3f6 100644 --- a/src/metadata/AvFormat.hpp +++ b/src/metadata/AvFormat.hpp @@ -31,10 +31,7 @@ namespace MetaData class AvFormat : public Parser { public: - - AvFormat(const ClusterTypes& clusterTypes = defaultClusterTypes); - - boost::optional parse(const boost::filesystem::path& p, bool debug = false); + boost::optional parse(const boost::filesystem::path& p, bool debug = false) override; }; } // namespace MetaData diff --git a/src/metadata/MetaData.cpp b/src/metadata/MetaData.cpp deleted file mode 100644 index d949ef93..00000000 --- a/src/metadata/MetaData.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 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 . - */ - -#include "MetaData.hpp" - -namespace MetaData { - -const ClusterTypes Parser::defaultClusterTypes = -{ - "GENRE", - "ALBUMGROUPING", - "MOOD", - "ALBUMMOOD", - "COMMENT:SONGS-DB_OCCASION", -}; - -} // namespace MetaData diff --git a/src/metadata/MetaData.hpp b/src/metadata/MetaData.hpp index f09b1504..9cef1e80 100644 --- a/src/metadata/MetaData.hpp +++ b/src/metadata/MetaData.hpp @@ -63,24 +63,16 @@ namespace MetaData // See enum Type's comments using Items = std::map; using Clusters = std::map /* names */>; - using ClusterTypes = std::set; class Parser { public: - static const ClusterTypes defaultClusterTypes; - - // Provide a map for tag name -> Cluster name - Parser(const ClusterTypes& clusterTypes) : _clusterTypes(clusterTypes) {} - virtual boost::optional parse(const boost::filesystem::path& p, bool debug = false) = 0; - void updateClusterTypes(const ClusterTypes& clusterTypes) { _clusterTypes = clusterTypes; } - const ClusterTypes& getClusterTypes() const { return _clusterTypes; } - bool isClusterTypeSupported(const std::string& clusterType) const { return _clusterTypes.find(clusterType) != _clusterTypes.end(); } + void setClusterTypeNames(const std::set& clusterTypeNames) { _clusterTypeNames = clusterTypeNames; } protected: - ClusterTypes _clusterTypes; + std::set _clusterTypeNames; }; } // namespace MetaData diff --git a/src/metadata/TagLibParser.cpp b/src/metadata/TagLibParser.cpp index 21800b35..47440e47 100644 --- a/src/metadata/TagLibParser.cpp +++ b/src/metadata/TagLibParser.cpp @@ -31,11 +31,6 @@ namespace MetaData { -TagLibParser::TagLibParser(const ClusterTypes& clusterTypes) -: Parser(clusterTypes) -{ -} - boost::optional TagLibParser::parse(const boost::filesystem::path& p, bool debug) { @@ -199,7 +194,7 @@ TagLibParser::parse(const boost::filesystem::path& p, bool debug) if (items.find(MetaData::Type::HasCover) == items.end()) items.insert( std::make_pair(MetaData::Type::HasCover, true)); } - else if (_clusterTypes.find(tag) != _clusterTypes.end()) + else if (_clusterTypeNames.find(tag) != _clusterTypeNames.end()) { std::set clusterNames; for (const auto& value : values) diff --git a/src/metadata/TagLibParser.hpp b/src/metadata/TagLibParser.hpp index 2b6c7425..0ecadbe5 100644 --- a/src/metadata/TagLibParser.hpp +++ b/src/metadata/TagLibParser.hpp @@ -31,10 +31,7 @@ namespace MetaData class TagLibParser : public Parser { public: - - TagLibParser(const ClusterTypes& clusterTypes = defaultClusterTypes); - - boost::optional parse(const boost::filesystem::path& p, bool debug = false); + boost::optional parse(const boost::filesystem::path& p, bool debug = false) override; }; } // namespace MetaData diff --git a/src/scanner/MediaScanner.cpp b/src/scanner/MediaScanner.cpp index 42709b6a..d652f6af 100644 --- a/src/scanner/MediaScanner.cpp +++ b/src/scanner/MediaScanner.cpp @@ -31,9 +31,8 @@ #include "database/Artist.hpp" #include "database/Cluster.hpp" #include "database/Release.hpp" -#include "database/MediaDirectory.hpp" +#include "database/ScanSettings.hpp" #include "database/Track.hpp" -#include "database/Setting.hpp" #include "utils/Logger.hpp" #include "utils/Path.hpp" @@ -43,28 +42,6 @@ using namespace Database; namespace { -const std::string updatePeriodSetting = "update_period"; -const std::string updateStartTimeSetting = "update_start_time"; -const std::string clusterTypesSetting = "cluster_types"; -const std::string fileExtensionsSetting = "file_extensions"; - -const std::vector defaultFileExtensions = -{ - ".mp3", - ".ogg", - ".oga", - ".aac", - ".m4a", - ".flac", - ".wav", - ".wma", - ".aif", - ".aiff", - ".ape", - ".mpc", - ".shn", -}; - Wt::WDate getNextMonday(Wt::WDate current) { @@ -88,17 +65,9 @@ getNextFirstOfMonth(Wt::WDate current) } bool -isFileSupported(const boost::filesystem::path& file, const std::vector extensions) +isFileSupported(const boost::filesystem::path& file, const std::set& extensions) { - boost::filesystem::path fileExtension = file.extension(); - - for (auto& extension : extensions) - { - if (extension == fileExtension) - return true; - } - - return false; + return (extensions.find(file.extension()) != extensions.end()); } bool @@ -219,47 +188,6 @@ getClusters(Wt::Dbo::Session& session, const MetaData::Clusters& clustersNames) namespace Scanner { -UpdatePeriod -getUpdatePeriod(Wt::Dbo::Session& session) -{ - return static_cast(Setting::getInt(session, updatePeriodSetting, static_cast(UpdatePeriod::Never))); -} - -void -setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod) -{ - Setting::setInt(session, updatePeriodSetting, static_cast(updatePeriod)); -} - -Wt::WTime -getUpdateStartTime(Wt::Dbo::Session& session) -{ - return Setting::getTime(session, updateStartTimeSetting); -} - -void -setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime startTime) -{ - Setting::setTime(session, updateStartTimeSetting, startTime); -} - -std::set -getClusterTypes(Wt::Dbo::Session& session) -{ - MetaData::ClusterTypes clusterTypes; - - for (auto cluster : splitString(Setting::getString(session, clusterTypesSetting), " ")) - clusterTypes.insert(cluster); - - return clusterTypes; -} - -void setClusterTypes(Wt::Dbo::Session& session, const std::set clusterTypes) -{ - std::vector vecClusterTypes(clusterTypes.begin(), clusterTypes.end()); - Setting::setString(session, clusterTypesSetting, joinStrings(vecClusterTypes, " ")); -} - MediaScanner::MediaScanner(Wt::Dbo::SqlConnectionPool& connectionPool) : _running(false), _scheduleTimer(_ioService), @@ -267,14 +195,6 @@ _db(connectionPool) { _ioService.setThreadCount(1); - Wt::Dbo::Transaction transaction(_db.getSession()); - - if (!Setting::exists(_db.getSession(), fileExtensionsSetting)) - Setting::setString(_db.getSession(), fileExtensionsSetting, joinStrings(defaultFileExtensions, " ")); - - if (!Setting::exists(_db.getSession(), clusterTypesSetting)) - setClusterTypes(_db.getSession(), MetaData::Parser::defaultClusterTypes); - refreshScanSettings(); } @@ -329,43 +249,41 @@ MediaScanner::reschedule() void MediaScanner::scheduleScan() { - using namespace std::chrono_literals; + refreshScanSettings(); - Wt::WTime startTime = getUpdateStartTime(_db.getSession()); Wt::WDateTime now = Wt::WLocalDateTime::currentServerDateTime().toUTC(); Wt::WDate nextScanDate; - - switch ( getUpdatePeriod(_db.getSession()) ) + switch (_updatePeriod) { - case UpdatePeriod::Daily: - if (now.time() < startTime) + case ScanSettings::UpdatePeriod::Daily: + if (now.time() < _startTime) nextScanDate = now.date(); else nextScanDate = now.date().addDays(1); break; - case UpdatePeriod::Weekly: - if (now.time() < startTime && now.date().dayOfWeek() == 1) + case ScanSettings::UpdatePeriod::Weekly: + if (now.time() < _startTime && now.date().dayOfWeek() == 1) nextScanDate = now.date(); else nextScanDate = getNextMonday(now.date()); break; - case UpdatePeriod::Monthly: - if (now.time() < startTime && now.date().day() == 1) + case ScanSettings::UpdatePeriod::Monthly: + if (now.time() < _startTime && now.date().day() == 1) nextScanDate = now.date(); else nextScanDate = getNextFirstOfMonth(now.date()); break; - case UpdatePeriod::Never: + case ScanSettings::UpdatePeriod::Never: LMS_LOG(DBUPDATER, INFO) << "Auto scan disabled!"; break; } if (nextScanDate.isValid()) - scheduleScan( Wt::WDateTime(nextScanDate, startTime).toTimePoint() ); + scheduleScan( Wt::WDateTime(nextScanDate, _startTime).toTimePoint() ); } void @@ -399,19 +317,12 @@ MediaScanner::scan(boost::system::error_code err) Stats stats; checkAudioFiles(stats); - forceScan = checkClusters(); LMS_LOG(UI, INFO) << "Checks complete, force scan = " << forceScan; - for (auto rootDirectory : _rootDirectories) - { - if (!_running) - break; - - LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "'..."; - scanRootDirectory(rootDirectory, forceScan, stats); - LMS_LOG(DBUPDATER, INFO) << "scaning root directory '" << rootDirectory.string() << "' DONE"; - } + LMS_LOG(DBUPDATER, INFO) << "scaning media directory '" << _mediaDirectory.string() << "'..."; + scanMediaDirectory(_mediaDirectory, forceScan, stats); + LMS_LOG(DBUPDATER, INFO) << "scaning media directory '" << _mediaDirectory.string() << "' DONE"; if (_running) checkDuplicatedAudioFiles(stats); @@ -432,15 +343,22 @@ MediaScanner::refreshScanSettings() { Wt::Dbo::Transaction transaction(_db.getSession()); - _fileExtensions.clear(); - for (auto extension : splitString(Setting::getString(_db.getSession(), fileExtensionsSetting), " ")) - _fileExtensions.push_back( extension ); + auto scanSettings = ScanSettings::get(_db.getSession()); - _rootDirectories.clear(); - for (auto rootDir : Database::MediaDirectory::getAll(_db.getSession())) - _rootDirectories.push_back(rootDir->getPath()); + _startTime = scanSettings->getUpdateStartTime(); + _updatePeriod = scanSettings->getUpdatePeriod(); - _metadataParser.updateClusterTypes(getClusterTypes(_db.getSession())); + _fileExtensions = scanSettings->getAudioFileExtensions(); + _mediaDirectory = scanSettings->getMediaDirectory(); + + auto clusterTypes = scanSettings->getClusterTypes(); + std::set clusterTypeNames; + + std::transform(clusterTypes.begin(), clusterTypes.end(), + std::inserter(clusterTypeNames, clusterTypeNames.begin()), + [](ClusterType::pointer clusterType) -> std::string { return clusterType->getName(); }); + + _metadataParser.setClusterTypeNames(clusterTypeNames); } void @@ -653,11 +571,11 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan, } void -MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, bool forceScan, Stats& stats) +MediaScanner::scanMediaDirectory(boost::filesystem::path mediaDirectory, bool forceScan, Stats& stats) { boost::system::error_code ec; - boost::filesystem::recursive_directory_iterator itPath(rootDirectory, ec); + boost::filesystem::recursive_directory_iterator itPath(mediaDirectory, ec); boost::filesystem::recursive_directory_iterator itEnd; while (!ec && itPath != itEnd) @@ -676,38 +594,27 @@ MediaScanner::scanRootDirectory(boost::filesystem::path rootDirectory, bool forc } } -// Check if a file exists and is still in a root directory +// Check if a file exists and is still in a media directory static bool -checkFile(const boost::filesystem::path& p, const std::vector& rootDirs, const std::vector& extensions) +checkFile(const boost::filesystem::path& p, boost::filesystem::path mediaDirectory, const std::set& extensions) { try { bool status = true; // For each track, make sure the the file still exists - // and still belongs to a root directory + // and still belongs to a media directory if (!boost::filesystem::exists( p ) - || !boost::filesystem::is_regular( p ) ) + || !boost::filesystem::is_regular( p ) ) { LMS_LOG(DBUPDATER, INFO) << "Missing file '" << p.string() << "'"; status = false; } else { - - bool foundRoot = false; - for (auto& rootDir : rootDirs) + if (!isPathInParentPath(p, mediaDirectory)) { - if (isPathInParentPath(p, rootDir)) - { - foundRoot = true; - break; - } - } - - if (!foundRoot) - { - LMS_LOG(DBUPDATER, INFO) << "Out of root file '" << p.string() << "'"; + LMS_LOG(DBUPDATER, INFO) << "File '" << p.string() << "' is out of media directory '"; status = false; } else if (!isFileSupported(p, extensions)) @@ -741,7 +648,7 @@ MediaScanner::checkAudioFiles( Stats& stats ) if (!_running) return; - if (!checkFile(trackPath, _rootDirectories, _fileExtensions)) + if (!checkFile(trackPath, _mediaDirectory, _fileExtensions)) { Wt::Dbo::Transaction transaction(_db.getSession()); @@ -798,44 +705,6 @@ MediaScanner::checkAudioFiles( Stats& stats ) 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 (!_metadataParser.isClusterTypeSupported(clusterType->getName())) - { - LMS_LOG(DBUPDATER, INFO) << "Removing cluster type " << clusterType->getName(); - clusterType.remove(); - hasChanges = true; - } - } - - // Add any missing clusters - for (auto clusterTypeName : _metadataParser.getClusterTypes()) - { - 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 MediaScanner::checkDuplicatedAudioFiles(Stats& stats) { diff --git a/src/scanner/MediaScanner.hpp b/src/scanner/MediaScanner.hpp index a18d0153..9102574b 100644 --- a/src/scanner/MediaScanner.hpp +++ b/src/scanner/MediaScanner.hpp @@ -28,26 +28,11 @@ #include "metadata/TagLibParser.hpp" +#include "database/ScanSettings.hpp" #include "database/DatabaseHandler.hpp" namespace Scanner { -enum class UpdatePeriod { - Never = 0, - Daily, - Weekly, - Monthly -}; - -UpdatePeriod getUpdatePeriod(Wt::Dbo::Session& session); -void setUpdatePeriod(Wt::Dbo::Session& session, UpdatePeriod updatePeriod); - -Wt::WTime getUpdateStartTime(Wt::Dbo::Session& session); -void setUpdateStartTime(Wt::Dbo::Session& session, Wt::WTime time); - -std::set getClusterTypes(Wt::Dbo::Session& session); -void setClusterTypes(Wt::Dbo::Session& session, const std::set clusterTypes); - class MediaScanner { public: @@ -93,13 +78,12 @@ class MediaScanner // Update database (scheduled callback) void scan(boost::system::error_code ec); - void scanRootDirectory( boost::filesystem::path rootDirectory, bool forceScan, Stats& stats); + void scanMediaDirectory( boost::filesystem::path mediaDirectory, bool forceScan, Stats& stats); // Helpers void refreshScanSettings(); void checkAudioFiles( Stats& stats ); - bool checkClusters(); void checkDuplicatedAudioFiles( Stats& stats ); void scanAudioFile( const boost::filesystem::path& file, bool forceScan, Stats& stats); @@ -112,9 +96,11 @@ class MediaScanner Database::Handler _db; - // Scan settings - std::vector _fileExtensions; - std::vector _rootDirectories; + // Current scan settings + Wt::WTime _startTime; + Database::ScanSettings::UpdatePeriod _updatePeriod; + std::set _fileExtensions; + boost::filesystem::path _mediaDirectory; MetaData::TagLibParser _metadataParser; diff --git a/src/ui/admin/DatabaseSettingsView.cpp b/src/ui/admin/DatabaseSettingsView.cpp index 98a1e1c7..90890588 100644 --- a/src/ui/admin/DatabaseSettingsView.cpp +++ b/src/ui/admin/DatabaseSettingsView.cpp @@ -29,8 +29,6 @@ #include #include "common/Validators.hpp" -#include "database/MediaDirectory.hpp" -#include "scanner/MediaScanner.hpp" #include "utils/Logger.hpp" #include "utils/Utils.hpp" @@ -80,40 +78,45 @@ class DatabaseSettingsModel : public Wt::WFormModel Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); - std::vector mediaDirectories = MediaDirectory::getAll(LmsApp->getDboSession()); - if (!mediaDirectories.empty()) - setValue(MediaDirectoryField, mediaDirectories.front()->getPath().string()); + auto scanSettings = ScanSettings::get(LmsApp->getDboSession()); - auto periodRow = getUpdatePeriodModelRow( Scanner::getUpdatePeriod(LmsApp->getDboSession()) ); + setValue(MediaDirectoryField, scanSettings->getMediaDirectory()); + + auto periodRow = getUpdatePeriodModelRow( scanSettings->getUpdatePeriod() ); if (periodRow) setValue(UpdatePeriodField, updatePeriodString(*periodRow)); - auto startTimeRow = getUpdateStartTimeModelRow( Scanner::getUpdateStartTime(LmsApp->getDboSession()) ); + auto startTimeRow = getUpdateStartTimeModelRow( scanSettings->getUpdateStartTime() ); if (startTimeRow) setValue(UpdateStartTimeField, updateStartTimeString(*startTimeRow) ); - auto clusterTypes = Scanner::getClusterTypes(LmsApp->getDboSession()); + auto clusterTypes = scanSettings->getClusterTypes(); if (!clusterTypes.empty()) - setValue(TagsField, joinStrings(std::vector(clusterTypes.begin(), clusterTypes.end()), " ")); + { + std::vector names; + std::transform(clusterTypes.begin(), clusterTypes.end(),std::back_inserter(names), [](auto clusterType) { return clusterType->getName(); }); + setValue(TagsField, joinStrings(names, " ")); + } } void saveData() { Wt::Dbo::Transaction transaction(LmsApp->getDboSession()); - MediaDirectory::eraseAll(LmsApp->getDboSession()); - MediaDirectory::create(LmsApp->getDboSession(), valueText(MediaDirectoryField).toUTF8()); + auto scanSettings = ScanSettings::get(LmsApp->getDboSession()); + + scanSettings.modify()->setMediaDirectory(valueText(MediaDirectoryField).toUTF8()); auto updatePeriodRow = getUpdatePeriodModelRow( valueText(UpdatePeriodField)); assert(updatePeriodRow); - Scanner::setUpdatePeriod(LmsApp->getDboSession(), updatePeriod(*updatePeriodRow)); + scanSettings.modify()->setUpdatePeriod(updatePeriod(*updatePeriodRow)); auto startTimeRow = getUpdateStartTimeModelRow( valueText(UpdateStartTimeField)); assert(startTimeRow); - Scanner::setUpdateStartTime(LmsApp->getDboSession(), updateStartTime(*startTimeRow)); + scanSettings.modify()->setUpdateStartTime(updateStartTime(*startTimeRow)); auto clusterTypes = splitString(valueText(TagsField).toUTF8(), " "); - Scanner::setClusterTypes(LmsApp->getDboSession(), std::set(clusterTypes.begin(), clusterTypes.end())); + scanSettings.modify()->setClusterTypes(std::set(clusterTypes.begin(), clusterTypes.end())); } boost::optional getUpdatePeriodModelRow(Wt::WString value) @@ -127,7 +130,7 @@ class DatabaseSettingsModel : public Wt::WFormModel return boost::none; } - boost::optional getUpdatePeriodModelRow(Scanner::UpdatePeriod period) + boost::optional getUpdatePeriodModelRow(ScanSettings::UpdatePeriod period) { for (int i = 0; i < _updatePeriodModel->rowCount(); ++i) { @@ -138,9 +141,9 @@ class DatabaseSettingsModel : public Wt::WFormModel return boost::none; } - Scanner::UpdatePeriod updatePeriod(int row) + ScanSettings::UpdatePeriod updatePeriod(int row) { - return Wt::cpp17::any_cast + return Wt::cpp17::any_cast (_updatePeriodModel->data(_updatePeriodModel->index(row, 0), Wt::ItemDataRole::User)); } @@ -200,16 +203,16 @@ class DatabaseSettingsModel : public Wt::WFormModel _updatePeriodModel = std::make_shared(); _updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.never")); - _updatePeriodModel->setData(0, 0, Scanner::UpdatePeriod::Never, Wt::ItemDataRole::User); + _updatePeriodModel->setData(0, 0, ScanSettings::UpdatePeriod::Never, Wt::ItemDataRole::User); _updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.daily")); - _updatePeriodModel->setData(1, 0, Scanner::UpdatePeriod::Daily, Wt::ItemDataRole::User); + _updatePeriodModel->setData(1, 0, ScanSettings::UpdatePeriod::Daily, Wt::ItemDataRole::User); _updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.weekly")); - _updatePeriodModel->setData(2, 0, Scanner::UpdatePeriod::Weekly, Wt::ItemDataRole::User); + _updatePeriodModel->setData(2, 0, ScanSettings::UpdatePeriod::Weekly, Wt::ItemDataRole::User); _updatePeriodModel->addString(Wt::WString::tr("Lms.Admin.Database.monthly")); - _updatePeriodModel->setData(3, 0, Scanner::UpdatePeriod::Monthly, Wt::ItemDataRole::User); + _updatePeriodModel->setData(3, 0, ScanSettings::UpdatePeriod::Monthly, Wt::ItemDataRole::User); _updateStartTimeModel = std::make_shared(); diff --git a/tools/metadata/Makefile.am b/tools/metadata/Makefile.am index cded72b1..f61b517d 100644 --- a/tools/metadata/Makefile.am +++ b/tools/metadata/Makefile.am @@ -4,8 +4,7 @@ lms_metadata_SOURCES = \ $(srcdir)/LmsMetadata.cpp \ $(top_srcdir)/src/utils/Logger.cpp \ $(top_srcdir)/src/utils/Utils.cpp \ - $(top_srcdir)/src/metadata/TagLibParser.cpp \ - $(top_srcdir)/src/metadata/MetaData.cpp + $(top_srcdir)/src/metadata/TagLibParser.cpp lms_metadata_CXXFLAGS=-std=c++14 -Wall -I$(top_srcdir)/src -D_REENTRANT