/[svn]/linuxsampler/trunk/src/db/InstrumentsDbUtilities.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/db/InstrumentsDbUtilities.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3091 - (show annotations) (download)
Mon Jan 16 15:01:21 2017 UTC (7 years, 3 months ago) by schoenebeck
File size: 21491 byte(s)
* Cleanup of instruments DB file creation and opening code.
* The instrument DB path of linuxsampler's --create-instruments-db argument
  is now optional, if it is missing, then a default location is used.
* Bumped version (2.0.0.svn39).

1 /***************************************************************************
2 * *
3 * Copyright (C) 2007 - 2009 Grigor Iliev *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
18 * MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "InstrumentsDbUtilities.h"
22
23 #include "../common/File.h"
24 #include "../common/global_private.h"
25
26 #include <algorithm>
27 #include <errno.h>
28
29 #include "../common/Exception.h"
30 #include "InstrumentsDb.h"
31
32 namespace LinuxSampler {
33
34 void DbInstrument::Copy(const DbInstrument& Instr) {
35 if (this == &Instr) return;
36
37 InstrFile = Instr.InstrFile;
38 InstrNr = Instr.InstrNr;
39 FormatFamily = Instr.FormatFamily;
40 FormatVersion = Instr.FormatVersion;
41 Size = Instr.Size;
42 Created = Instr.Created;
43 Modified = Instr.Modified;
44 Description = Instr.Description;
45 IsDrum = Instr.IsDrum;
46 Product = Instr.Product;
47 Artists = Instr.Artists;
48 Keywords = Instr.Keywords;
49 }
50
51
52 void DbDirectory::Copy(const DbDirectory& Dir) {
53 if (this == &Dir) return;
54
55 Created = Dir.Created;
56 Modified = Dir.Modified;
57 Description = Dir.Description;
58 }
59
60 SearchQuery::SearchQuery() {
61 MinSize = -1;
62 MaxSize = -1;
63 InstrType = BOTH;
64 }
65
66 void SearchQuery::SetFormatFamilies(String s) {
67 if (s.length() == 0) return;
68 int i = 0;
69 int j = s.find(',', 0);
70
71 while (j != std::string::npos) {
72 FormatFamilies.push_back(s.substr(i, j - i));
73 i = j + 1;
74 j = s.find(',', i);
75 }
76
77 if (i < s.length()) FormatFamilies.push_back(s.substr(i));
78 }
79
80 void SearchQuery::SetSize(String s) {
81 String s2 = GetMin(s);
82 if (s2.length() > 0) MinSize = atoll(s2.c_str());
83 else MinSize = -1;
84
85 s2 = GetMax(s);
86 if (s2.length() > 0) MaxSize = atoll(s2.c_str());
87 else MaxSize = -1;
88 }
89
90 void SearchQuery::SetCreated(String s) {
91 CreatedAfter = GetMin(s);
92 CreatedBefore = GetMax(s);
93 }
94
95 void SearchQuery::SetModified(String s) {
96 ModifiedAfter = GetMin(s);
97 ModifiedBefore = GetMax(s);
98 }
99
100 String SearchQuery::GetMin(String s) {
101 if (s.length() < 3) return "";
102 if (s.at(0) == '.' && s.at(1) == '.') return "";
103 int i = s.find("..");
104 if (i == std::string::npos) return "";
105 return s.substr(0, i);
106 }
107
108 String SearchQuery::GetMax(String s) {
109 if (s.length() < 3) return "";
110 if (s.find("..", s.length() - 2) != std::string::npos) return "";
111 int i = s.find("..");
112 if (i == std::string::npos) return "";
113 return s.substr(i + 2);
114 }
115
116 void ScanJob::Copy(const ScanJob& Job) {
117 if (this == &Job) return;
118
119 JobId = Job.JobId;
120 FilesTotal = Job.FilesTotal;
121 FilesScanned = Job.FilesScanned;
122 Scanning = Job.Scanning;
123 Status = Job.Status;
124 }
125
126 int JobList::AddJob(ScanJob Job) {
127 if (Counter + 1 < Counter) Counter = 0;
128 else Counter++;
129 Job.JobId = Counter;
130 Jobs.push_back(Job);
131 if (Jobs.size() > 3) {
132 std::vector<ScanJob>::iterator iter = Jobs.begin();
133 Jobs.erase(iter);
134 }
135 return Job.JobId;
136 }
137
138 ScanJob& JobList::GetJobById(int JobId) {
139 for (int i = 0; i < Jobs.size(); i++) {
140 if (Jobs.at(i).JobId == JobId) return Jobs.at(i);
141 }
142
143 throw Exception("Invalid job ID: " + ToString(JobId));
144 }
145
146 bool AbstractFinder::IsRegex(String Pattern) {
147 if(Pattern.find('?') != String::npos) return true;
148 if(Pattern.find('*') != String::npos) return true;
149 return false;
150 }
151
152 void AbstractFinder::AddSql(String Col, String Pattern, std::stringstream& Sql) {
153 if (Pattern.length() == 0) return;
154
155 if (IsRegex(Pattern)) {
156 #ifndef WIN32
157 Sql << " AND " << Col << " regexp ?";
158 #else
159 for (int i = 0; i < Pattern.length(); i++) {
160 if (Pattern.at(i) == '?') Pattern.at(i) = '_';
161 else if (Pattern.at(i) == '*') Pattern.at(i) = '%';
162 }
163 Sql << " AND " << Col << " LIKE ?";
164 #endif
165 Params.push_back(Pattern);
166 return;
167 }
168
169 String buf;
170 std::vector<String> tokens;
171 std::vector<String> tokens2;
172 std::stringstream ss(Pattern);
173 while (ss >> buf) tokens.push_back(buf);
174
175 if (tokens.size() == 0) {
176 Sql << " AND " << Col << " LIKE ?";
177 Params.push_back("%" + Pattern + "%");
178 return;
179 }
180
181 bool b = false;
182 for (int i = 0; i < tokens.size(); i++) {
183 Sql << (i == 0 ? " AND (" : "");
184
185 for (int j = 0; j < tokens.at(i).length(); j++) {
186 if (tokens.at(i).at(j) == '+') tokens.at(i).at(j) = ' ';
187 }
188
189 ss.clear();
190 ss.str("");
191 ss << tokens.at(i);
192
193 tokens2.clear();
194 while (ss >> buf) tokens2.push_back(buf);
195
196 if (b && tokens2.size() > 0) Sql << " OR ";
197 if (tokens2.size() > 1) Sql << "(";
198 for (int j = 0; j < tokens2.size(); j++) {
199 if (j != 0) Sql << " AND ";
200 Sql << Col << " LIKE ?";
201 Params.push_back("%" + tokens2.at(j) + "%");
202 b = true;
203 }
204 if (tokens2.size() > 1) Sql << ")";
205 }
206 if (!b) Sql << "0)";
207 else Sql << ")";
208 }
209
210 DirectoryFinder::DirectoryFinder(SearchQuery* pQuery) : pDirectories(new std::vector<String>) {
211 pStmt = NULL;
212 this->pQuery = pQuery;
213 std::stringstream sql;
214 sql << "SELECT dir_name from instr_dirs WHERE dir_id!=0 AND parent_dir_id=?";
215
216 if (pQuery->CreatedAfter.length() != 0) {
217 sql << " AND created > ?";
218 Params.push_back(pQuery->CreatedAfter);
219 }
220 if (pQuery->CreatedBefore.length() != 0) {
221 sql << " AND created < ?";
222 Params.push_back(pQuery->CreatedBefore);
223 }
224 if (pQuery->ModifiedAfter.length() != 0) {
225 sql << " AND modified > ?";
226 Params.push_back(pQuery->ModifiedAfter);
227 }
228 if (pQuery->ModifiedBefore.length() != 0) {
229 sql << " AND modified < ?";
230 Params.push_back(pQuery->ModifiedBefore);
231 }
232
233 AddSql("dir_name", pQuery->Name, sql);
234 AddSql("description", pQuery->Description, sql);
235 SqlQuery = sql.str();
236
237 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
238
239 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
240 if (res != SQLITE_OK) {
241 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
242 }
243
244 for(int i = 0; i < Params.size(); i++) {
245 idb->BindTextParam(pStmt, i + 2, Params.at(i));
246 }
247 }
248
249 DirectoryFinder::~DirectoryFinder() {
250 if (pStmt != NULL) sqlite3_finalize(pStmt);
251 }
252
253 StringListPtr DirectoryFinder::GetDirectories() {
254 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
255 return std::move(pDirectories);
256 #else
257 return pDirectories;
258 #endif
259 }
260
261 void DirectoryFinder::ProcessDirectory(String Path, int DirId) {
262 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
263 idb->BindIntParam(pStmt, 1, DirId);
264
265 String s = Path;
266 if(Path.compare("/") != 0) s += "/";
267 int res = sqlite3_step(pStmt);
268 while(res == SQLITE_ROW) {
269 pDirectories->push_back(s + idb->toAbstractName(ToString(sqlite3_column_text(pStmt, 0))));
270 res = sqlite3_step(pStmt);
271 }
272
273 if (res != SQLITE_DONE) {
274 sqlite3_finalize(pStmt);
275 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
276 }
277
278 res = sqlite3_reset(pStmt);
279 if (res != SQLITE_OK) {
280 sqlite3_finalize(pStmt);
281 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
282 }
283 }
284
285 InstrumentFinder::InstrumentFinder(SearchQuery* pQuery) : pInstruments(new std::vector<String>) {
286 pStmt = NULL;
287 this->pQuery = pQuery;
288 std::stringstream sql;
289 sql << "SELECT instr_name from instruments WHERE dir_id=?";
290
291 if (pQuery->CreatedAfter.length() != 0) {
292 sql << " AND created > ?";
293 Params.push_back(pQuery->CreatedAfter);
294 }
295 if (pQuery->CreatedBefore.length() != 0) {
296 sql << " AND created < ?";
297 Params.push_back(pQuery->CreatedBefore);
298 }
299 if (pQuery->ModifiedAfter.length() != 0) {
300 sql << " AND modified > ?";
301 Params.push_back(pQuery->ModifiedAfter);
302 }
303 if (pQuery->ModifiedBefore.length() != 0) {
304 sql << " AND modified < ?";
305 Params.push_back(pQuery->ModifiedBefore);
306 }
307 if (pQuery->MinSize != -1) sql << " AND instr_size > " << pQuery->MinSize;
308 if (pQuery->MaxSize != -1) sql << " AND instr_size < " << pQuery->MaxSize;
309
310 if (pQuery->InstrType == SearchQuery::CHROMATIC) sql << " AND is_drum = 0";
311 else if (pQuery->InstrType == SearchQuery::DRUM) sql << " AND is_drum != 0";
312
313 if (pQuery->FormatFamilies.size() > 0) {
314 sql << " AND (format_family=?";
315 Params.push_back(pQuery->FormatFamilies.at(0));
316 for (int i = 1; i < pQuery->FormatFamilies.size(); i++) {
317 sql << "OR format_family=?";
318 Params.push_back(pQuery->FormatFamilies.at(i));
319 }
320 sql << ")";
321 }
322
323 AddSql("instr_name", pQuery->Name, sql);
324 AddSql("description", pQuery->Description, sql);
325 AddSql("product", pQuery->Product, sql);
326 AddSql("artists", pQuery->Artists, sql);
327 AddSql("keywords", pQuery->Keywords, sql);
328 SqlQuery = sql.str();
329
330 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
331
332 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
333 if (res != SQLITE_OK) {
334 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
335 }
336
337 for(int i = 0; i < Params.size(); i++) {
338 idb->BindTextParam(pStmt, i + 2, Params.at(i));
339 }
340 }
341
342 InstrumentFinder::~InstrumentFinder() {
343 if (pStmt != NULL) sqlite3_finalize(pStmt);
344 }
345
346 void InstrumentFinder::ProcessDirectory(String Path, int DirId) {
347 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
348 idb->BindIntParam(pStmt, 1, DirId);
349
350 String s = Path;
351 if(Path.compare("/") != 0) s += "/";
352 int res = sqlite3_step(pStmt);
353 while(res == SQLITE_ROW) {
354 pInstruments->push_back(s + idb->toAbstractName(ToString(sqlite3_column_text(pStmt, 0))));
355 res = sqlite3_step(pStmt);
356 }
357
358 if (res != SQLITE_DONE) {
359 sqlite3_finalize(pStmt);
360 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
361 }
362
363 res = sqlite3_reset(pStmt);
364 if (res != SQLITE_OK) {
365 sqlite3_finalize(pStmt);
366 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
367 }
368 }
369
370 StringListPtr InstrumentFinder::GetInstruments() {
371 #if __cplusplus >= 201103L && !CONFIG_NO_CPP11STL
372 return std::move(pInstruments);
373 #else
374 return pInstruments;
375 #endif
376 }
377
378 void DirectoryCounter::ProcessDirectory(String Path, int DirId) {
379 count += InstrumentsDb::GetInstrumentsDb()->GetDirectoryCount(DirId);
380 }
381
382 void InstrumentCounter::ProcessDirectory(String Path, int DirId) {
383 count += InstrumentsDb::GetInstrumentsDb()->GetInstrumentCount(DirId);
384 }
385
386 DirectoryCopier::DirectoryCopier(String SrcParentDir, String DestDir) {
387 this->SrcParentDir = SrcParentDir;
388 this->DestDir = DestDir;
389
390 if (DestDir.at(DestDir.length() - 1) != '/') {
391 this->DestDir.append("/");
392 }
393 if (SrcParentDir.at(SrcParentDir.length() - 1) != '/') {
394 this->SrcParentDir.append("/");
395 }
396 }
397
398 void DirectoryCopier::ProcessDirectory(String Path, int DirId) {
399 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
400
401 String dir = DestDir;
402 String subdir = Path;
403 if(subdir.length() > SrcParentDir.length()) {
404 subdir = subdir.substr(SrcParentDir.length());
405 dir += subdir;
406 db->AddDirectory(dir);
407 }
408
409 int dstDirId = db->GetDirectoryId(dir);
410 if(dstDirId == -1) {
411 throw Exception("Unkown DB directory: " + InstrumentsDb::toEscapedPath(dir));
412 }
413 IntListPtr ids = db->GetInstrumentIDs(DirId);
414 for (int i = 0; i < ids->size(); i++) {
415 String name = db->GetInstrumentName(ids->at(i));
416 db->CopyInstrument(ids->at(i), name, dstDirId, dir);
417 }
418 }
419
420 ScanProgress::ScanProgress() {
421 TotalFileCount = ScannedFileCount = Status = 0;
422 CurrentFile = "";
423 GigFileProgress.custom = this;
424 GigFileProgress.callback = GigFileProgressCallback;
425 }
426
427 void ScanProgress::StatusChanged() {
428 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
429 db->Jobs.GetJobById(JobId).FilesTotal = GetTotalFileCount();
430 db->Jobs.GetJobById(JobId).FilesScanned = GetScannedFileCount();
431 db->Jobs.GetJobById(JobId).Scanning = CurrentFile;
432 db->Jobs.GetJobById(JobId).Status = GetStatus();
433
434 InstrumentsDb::GetInstrumentsDb()->FireJobStatusChanged(JobId);
435 }
436
437 int ScanProgress::GetTotalFileCount() {
438 return TotalFileCount;
439 }
440
441 void ScanProgress::SetTotalFileCount(int Count) {
442 if (TotalFileCount == Count) return;
443 TotalFileCount = Count;
444 StatusChanged();
445 }
446
447 int ScanProgress::GetScannedFileCount() {
448 return ScannedFileCount;
449 }
450
451 void ScanProgress::SetScannedFileCount(int Count) {
452 if (ScannedFileCount == Count) return;
453 ScannedFileCount = Count;
454 if (Count > TotalFileCount) TotalFileCount = Count;
455 StatusChanged();
456 }
457
458 int ScanProgress::GetStatus() {
459 return Status;
460 }
461
462 void ScanProgress::SetStatus(int Status) {
463 if (this->Status == Status) return;
464 if (Status < 0) this->Status = 0;
465 else if (Status > 100) this->Status = 100;
466 else this->Status = Status;
467 StatusChanged();
468 }
469
470 void ScanProgress::SetErrorStatus(int Err) {
471 if (Err > 0) Err *= -1;
472 Status = Err;
473 StatusChanged();
474 }
475
476 void ScanProgress::GigFileProgressCallback(gig::progress_t* pProgress) {
477 if (pProgress == NULL) return;
478 ScanProgress* sp = static_cast<ScanProgress*> (pProgress->custom);
479
480 sp->SetStatus((int)(pProgress->factor * 100));
481 }
482
483 AddInstrumentsJob::AddInstrumentsJob(int JobId, ScanMode Mode, String DbDir, String FsDir, bool insDir) {
484 this->JobId = JobId;
485 Progress.JobId = JobId;
486 this->Mode = Mode;
487 this->DbDir = DbDir;
488 this->FsDir = FsDir;
489 this->insDir = insDir;
490 }
491
492 void AddInstrumentsJob::Run() {
493 try {
494 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
495
496 switch (Mode) {
497 case NON_RECURSIVE:
498 Progress.SetTotalFileCount(GetFileCount());
499 db->AddInstrumentsNonrecursive(DbDir, FsDir, insDir, &Progress);
500 break;
501 case RECURSIVE:
502 db->AddInstrumentsRecursive(DbDir, FsDir, false, insDir, &Progress);
503 break;
504 case FLAT:
505 db->AddInstrumentsRecursive(DbDir, FsDir, true, insDir, &Progress);
506 break;
507 default:
508 throw Exception("Unknown scan mode");
509 }
510
511 // Just to be sure that the frontends will be notified about the job completion
512 if (Progress.GetTotalFileCount() != Progress.GetScannedFileCount()) {
513 Progress.SetTotalFileCount(Progress.GetScannedFileCount());
514 }
515 if (Progress.GetStatus() != 100) Progress.SetStatus(100);
516 } catch(Exception e) {
517 Progress.SetErrorStatus(-1);
518 throw e;
519 }
520 }
521
522 int AddInstrumentsJob::GetFileCount() {
523 int count = 0;
524
525 try {
526 FileListPtr fileList = File::GetFiles(FsDir);
527
528 for (int i = 0; i < fileList->size(); i++) {
529 String s = fileList->at(i);
530 if (s.length() < 4) continue;
531 if(!strcasecmp(".gig", s.substr(s.length() - 4).c_str())) count++;
532 }
533 } catch(Exception e) {
534 e.PrintMessage();
535 return 0;
536 }
537
538 return count;
539 }
540
541 AddInstrumentsFromFileJob::AddInstrumentsFromFileJob(int JobId, String DbDir, String FilePath, int Index, bool insDir) {
542 this->JobId = JobId;
543 Progress.JobId = JobId;
544 Progress.SetTotalFileCount(1);
545
546 this->DbDir = DbDir;
547 this->FilePath = FilePath;
548 this->Index = Index;
549 this->insDir = insDir;
550 }
551
552 void AddInstrumentsFromFileJob::Run() {
553 try {
554 InstrumentsDb::GetInstrumentsDb()->AddInstruments(DbDir, insDir, FilePath, Index, &Progress);
555
556 // Just to be sure that the frontends will be notified about the job completion
557 if (Progress.GetTotalFileCount() != Progress.GetScannedFileCount()) {
558 Progress.SetTotalFileCount(Progress.GetScannedFileCount());
559 }
560 if (Progress.GetStatus() != 100) Progress.SetStatus(100);
561 } catch(Exception e) {
562 Progress.SetErrorStatus(-1);
563 throw e;
564 }
565 }
566
567
568 void DirectoryScanner::Scan(String DbDir, String FsDir, bool Flat, bool insDir, ScanProgress* pProgress) {
569 dmsg(2,("DirectoryScanner: Scan(DbDir=%s,FsDir=%s,Flat=%d,insDir=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat, insDir));
570 if (DbDir.empty() || FsDir.empty()) throw Exception("Directory expected");
571
572 this->DbDir = DbDir;
573 this->FsDir = FsDir;
574 this->insDir = insDir;
575 if (DbDir.at(DbDir.length() - 1) != '/') {
576 this->DbDir.append("/");
577 }
578 if (FsDir.at(FsDir.length() - 1) != File::DirSeparator) {
579 this->FsDir.push_back(File::DirSeparator);
580 }
581 this->Flat = Flat;
582 this->pProgress = pProgress;
583
584 File::WalkDirectoryTree(FsDir, this);
585 }
586
587 void DirectoryScanner::DirectoryEntry(std::string Path) {
588 dmsg(2,("DirectoryScanner: DirectoryEntry(Path=%s)\n", Path.c_str()));
589
590 String dir = DbDir;
591 if (!Flat) {
592 String subdir = Path;
593 if(subdir.length() > FsDir.length()) {
594 subdir = subdir.substr(FsDir.length());
595 #ifdef WIN32
596 replace(subdir.begin(), subdir.end(), '\\', '/');
597 #endif
598 dir += subdir;
599 }
600 }
601
602 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
603
604 if (HasInstrumentFiles(Path)) {
605 if (!db->DirectoryExist(dir)) db->AddDirectory(dir);
606 db->AddInstrumentsNonrecursive(dir, Path, insDir, pProgress);
607 }
608 };
609
610 bool DirectoryScanner::HasInstrumentFiles(String Dir) {
611 InstrumentFileCounter c;
612 return c.Count(Dir) > 0;
613 }
614
615 int InstrumentFileCounter::Count(String FsDir) {
616 dmsg(2,("InstrumentFileCounter: Count(FsDir=%s)\n", FsDir.c_str()));
617 if (FsDir.empty()) throw Exception("Directory expected");
618 FileCount = 0;
619
620 File::WalkDirectoryTree(FsDir, this);
621 return FileCount;
622 }
623
624 void InstrumentFileCounter::FileEntry(std::string Path) {
625 dmsg(2,("InstrumentFileCounter: FileEntry(Path=%s)\n", Path.c_str()));
626 if(Path.length() < 4) return;
627 if(!strcasecmp(".gig", Path.substr(Path.length() - 4).c_str())) FileCount++;
628 };
629
630 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC