1 /*
2  * Copyright (C) 2018 Emeric Poupon
3  *
4  * This file is part of LMS.
5  *
6  * LMS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * LMS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with LMS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "database/ScanSettings.hpp"
21 
22 #include <Wt/Dbo/WtSqlTraits.h>
23 
24 #include "utils/Path.hpp"
25 #include "utils/Logger.hpp"
26 #include "utils/String.hpp"
27 
28 #include "database/Cluster.hpp"
29 #include "database/Session.hpp"
30 
31 namespace {
32 
33 const std::set<std::string> defaultClusterTypeNames =
34 {
35 	"GENRE",
36 	"ALBUMGROUPING",
37 	"MOOD",
38 	"ALBUMMOOD",
39 };
40 
41 }
42 
43 namespace Database {
44 
45 void
init(Session & session)46 ScanSettings::init(Session& session)
47 {
48 	session.checkUniqueLocked();
49 
50 	pointer settings {get(session)};
51 	if (settings)
52 		return;
53 
54 	settings = session.getDboSession().add(std::make_unique<ScanSettings>());
55 	settings.modify()->setClusterTypes(session, defaultClusterTypeNames );
56 }
57 
58 ScanSettings::pointer
get(Session & session)59 ScanSettings::get(Session& session)
60 {
61 	session.checkSharedLocked();
62 
63 	return session.getDboSession().find<ScanSettings>();
64 }
65 
66 std::unordered_set<std::filesystem::path>
getAudioFileExtensions() const67 ScanSettings::getAudioFileExtensions() const
68 {
69 	auto extensions = StringUtils::splitString(_audioFileExtensions, " ");
70 	return std::unordered_set<std::filesystem::path>(std::cbegin(extensions), std::cend(extensions));
71 }
72 
73 void
addAudioFileExtension(const std::filesystem::path & ext)74 ScanSettings::addAudioFileExtension(const std::filesystem::path& ext)
75 {
76 	_audioFileExtensions += " " + ext.string();
77 }
78 
79 std::vector<ClusterType::pointer>
getClusterTypes() const80 ScanSettings::getClusterTypes() const
81 {
82 	return std::vector<ClusterType::pointer>(std::cbegin(_clusterTypes), std::cend(_clusterTypes));
83 }
84 
85 void
setMediaDirectory(const std::filesystem::path & p)86 ScanSettings::setMediaDirectory(const std::filesystem::path& p)
87 {
88 	_mediaDirectory = StringUtils::stringTrimEnd(p.string(), "/\\");
89 }
90 
91 template <typename It>
getNames(It begin,It end)92 std::set<std::string> getNames(It begin, It end)
93 {
94 	std::set<std::string> names;
95 	std::transform(begin, end, std::inserter(names, std::cbegin(names)),
96 		[](const ClusterType::pointer& clusterType)
97 		{
98 			return clusterType->getName();
99 		});
100 
101 	return names;
102 }
103 
104 void
setClusterTypes(Session & session,const std::set<std::string> & clusterTypeNames)105 ScanSettings::setClusterTypes(Session& session, const std::set<std::string>& clusterTypeNames)
106 {
107 	session.checkUniqueLocked();
108 
109 	bool needRescan {};
110 
111 	// Create any missing cluster type
112 	for (const std::string& clusterTypeName : clusterTypeNames)
113 	{
114 		auto clusterType {ClusterType::getByName(session, clusterTypeName)};
115 		if (!clusterType)
116 		{
117 			LMS_LOG(DB, INFO) << "Creating cluster type " << clusterTypeName;
118 			clusterType = ClusterType::create(session, clusterTypeName);
119 			_clusterTypes.insert(clusterType);
120 
121 			needRescan = true;
122 		}
123 	}
124 
125 	// Delete no longer existing cluster types
126 	for (ClusterType::pointer& clusterType : _clusterTypes)
127 	{
128 		if (std::none_of(clusterTypeNames.begin(), clusterTypeNames.end(),
129 			[clusterType](const std::string& name) { return name == clusterType->getName(); }))
130 		{
131 			LMS_LOG(DB, INFO) << "Deleting cluster type " << clusterType->getName();
132 			clusterType.remove();
133 		}
134 	}
135 
136 	if (needRescan)
137 		_scanVersion += 1;
138 }
139 
140 void
incScanVersion()141 ScanSettings::incScanVersion()
142 {
143 	_scanVersion += 1;
144 }
145 
146 } // namespace Database
147 
148