/[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 1781 - (show annotations) (download)
Mon Sep 29 18:21:21 2008 UTC (15 years, 6 months ago) by iliev
File size: 20901 byte(s)
* Implemented option for adding instruments in separate directories
  in the instruments database
  (patch by Chris Cherrett & Andrew Williams, a bit adjusted)

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

  ViewVC Help
Powered by ViewVC