1 /* DatabaseLibrary.cpp */
2 
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4  *
5  * This file is part of sayonara player
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "Database/Query.h"
22 #include "Database/Library.h"
23 #include "Database/Albums.h"
24 #include "Database/Artists.h"
25 #include "Database/Tracks.h"
26 
27 #include "Utils/Utils.h"
28 #include "Utils/Algorithm.h"
29 #include "Utils/MetaData/MetaData.h"
30 #include "Utils/MetaData/MetaDataList.h"
31 #include "Utils/MetaData/Album.h"
32 #include "Utils/MetaData/Artist.h"
33 #include "Utils/Logger/Logger.h"
34 #include "Utils/Library/LibraryInfo.h"
35 
36 #include <QList>
37 
38 using DB::Query;
39 
40 namespace Algorithm=Util::Algorithm;
41 
42 struct DB::Library::Private {};
43 
Library(const QString & connection_name,DbId databaseId)44 DB::Library::Library(const QString& connection_name, DbId databaseId) :
45 	Module(connection_name, databaseId)
46 {}
47 
48 DB::Library::~Library() = default;
49 
50 
51 template<typename T>
52 struct Order
53 {
54 	int index;
55 	T value;
56 };
57 
58 using InfoOrder=Order<::Library::Info>;
59 
getAllLibraries()60 QList<::Library::Info> DB::Library::getAllLibraries()
61 {
62 	QString query = "SELECT libraryID, libraryName, libraryPath, libraryIndex FROM Libraries;";
63 
64 	QList<::Library::Info> infos;
65 	QList<InfoOrder> orders;
66 
67 	Query q(this);
68 	q.prepare(query);
69 
70 	bool success = q.exec();
71 
72 	if(!success)
73 	{
74 		q.showError("Cannot fetch all libraries");
75 	}
76 
77 	while(q.next())
78 	{
79 		LibraryId id = q.value(0).toInt();
80 		QString name = q.value(1).toString();
81 		QString path = q.value(2).toString();
82 
83 		InfoOrder order;
84 		order.value = ::Library::Info(name, path, id);
85 		order.index = q.value(3).toInt();
86 
87 		orders << order;
88 	}
89 
90 	if(orders.size() == 0){
91 		return QList<::Library::Info>();
92 	}
93 
94 	else if(orders.size() == 1){
95 		infos << orders.first().value;
96 	}
97 
98 	else {
99 
100 		std::sort(orders.begin(), orders.end(), [](const InfoOrder& order1, const InfoOrder& order2){
101 			return (order1.index < order2.index);
102 		});
103 
104 
105 		for(const InfoOrder& order : Algorithm::AsConst(orders)){
106 			infos << order.value;
107 		}
108 	}
109 
110 	return infos;
111 }
112 
insertLibrary(LibraryId id,const QString & library_name,const QString & library_path,int index)113 bool DB::Library::insertLibrary(LibraryId id, const QString& library_name, const QString& library_path, int index)
114 {
115 	if(library_name.isEmpty() || library_path.isEmpty())
116 	{
117 		spLog(Log::Warning, this) << "Cannot insert library: Invalid parameters";
118 		return false;
119 	}
120 
121 	QString query = "INSERT INTO Libraries "
122 					"(libraryID, libraryName, libraryPath, libraryIndex) "
123 					"VALUES "
124 					"(:libraryId, :library_name, :library_path, :library_index);";
125 
126 	Query q(this);
127 
128 	q.prepare(query);
129 	q.bindValue(":libraryId",		id);
130 	q.bindValue(":library_name",	Util::convertNotNull(library_name));
131 	q.bindValue(":library_path",	Util::convertNotNull(library_path));
132 	q.bindValue(":library_index",	index);
133 
134 	bool success = q.exec();
135 
136 	if(!success)
137 	{
138 		q.showError
139 		(
140 			QString("Cannot insert library (name: %1, path: %2)")
141 					.arg(library_name, library_path)
142 		);
143 	}
144 
145 	return success;
146 }
147 
editLibrary(LibraryId libraryId,const QString & new_name,const QString & new_path)148 bool DB::Library::editLibrary(LibraryId libraryId, const QString& new_name, const QString& new_path)
149 {
150 	if(new_name.isEmpty() || new_path.isEmpty())
151 	{
152 		spLog(Log::Warning, this) << "Cannot update library: Invalid parameters";
153 		return false;
154 	}
155 
156 	QString query = "UPDATE Libraries "
157 					"SET "
158 					"libraryName=:library_name, "
159 					"libraryPath=:library_path "
160 					"WHERE "
161 					"libraryID=:libraryId;";
162 
163 	Query q(this);
164 
165 	q.prepare(query);
166 	q.bindValue(":library_name",	Util::convertNotNull(new_name));
167 	q.bindValue(":library_path",	Util::convertNotNull(new_path));
168 	q.bindValue(":libraryId",		libraryId);
169 
170 	bool success = q.exec();
171 
172 	if(!success)
173 	{
174 		q.showError(
175 			QString("Cannot update library (name: %1, path: %2)")
176 					.arg(new_name, new_path)
177 		);
178 	}
179 
180 	return success;
181 }
182 
removeLibrary(LibraryId libraryId)183 bool DB::Library::removeLibrary(LibraryId libraryId)
184 {
185 	QString query = "DELETE FROM Libraries WHERE libraryID=:libraryId;";
186 
187 	Query q(this);
188 
189 	q.prepare(query);
190 	q.bindValue(":libraryId", libraryId);
191 
192 	bool success = q.exec();
193 
194 	if(!success)
195 	{
196 		q.showError(
197 			QString("Cannot remove library %1").arg(libraryId)
198 		);
199 	}
200 
201 	return success;
202 }
203 
reorderLibraries(const QMap<LibraryId,int> & order)204 bool DB::Library::reorderLibraries(const QMap<LibraryId, int>& order)
205 {
206 	if(order.isEmpty())
207 	{
208 		spLog(Log::Warning, this) << "Cannot reorder library: Invalid parameters";
209 		return false;
210 	}
211 
212 	bool success = true;
213 	for(auto it=order.cbegin(); it != order.cend(); it++)
214 	{
215 		QString query = "UPDATE Libraries "
216 						"SET "
217 						"libraryIndex=:index "
218 						"WHERE "
219 						"libraryID=:libraryId;";
220 
221 		Query q(this);
222 		q.prepare(query);
223 		q.bindValue(":index",		it.value());
224 		q.bindValue(":libraryId",	it.key());
225 
226 		success = (success && q.exec());
227 
228 		if(!success)
229 		{
230 			q.showError("Cannot reorder libraries");
231 
232 		}
233 	}
234 
235 	return success;
236 }
237 
addAlbumArtists()238 void DB::Library::addAlbumArtists()
239 {
240 	Query q(this);
241 	QString	querytext = "UPDATE tracks SET albumArtistID = artistID WHERE albumArtistID = -1;";
242 	q.prepare(querytext);
243 	if(!q.exec()){
244 		q.showError("Cannot add album artists");
245 	}
246 }
247 
dropIndexes()248 void DB::Library::dropIndexes()
249 {
250 	QStringList indexes;
251 	indexes << "album_search";
252 	indexes << "artist_search";
253 	indexes << "track_search";
254 	//indexes << "track_file_search";
255 
256 	for(const QString& idx : Algorithm::AsConst(indexes))
257 	{
258 		Query q(this);
259 		QString text = "DROP INDEX IF EXISTS " + idx + ";";
260 		q.prepare(text);
261 		if(!q.exec()){
262 			q.showError("Cannot drop index " + idx);
263 		}
264 	}
265 }
266 
267 using IndexDescription=std::tuple<QString, QString, QString>;
createIndexes()268 void DB::Library::createIndexes()
269 {
270 	dropIndexes();
271 
272 	QList<IndexDescription> indexes;
273 
274 	indexes << std::make_tuple("album_search", "albums", "albumID");
275 	indexes << std::make_tuple("artist_search", "artists", "artistID");
276 	indexes << std::make_tuple("track_search", "tracks", "trackID");
277 	//indexes << std::make_tuple("track_file_search", "tracks", "trackID");
278 
279 	for(const IndexDescription& idx : Algorithm::AsConst(indexes))
280 	{
281 		Query q(this);
282 		QString name = std::get<0>(idx);
283 		QString table = std::get<1>(idx);
284 		QString column = std::get<2>(idx);
285 		QString text = "CREATE INDEX " + name + " ON " + table + " (cissearch, " + column + ");";
286 		q.prepare(text);
287 		if(!q.exec()){
288 			q.showError("Cannot create index " + name);
289 		}
290 	}
291 }
292 
293