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