/[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 1200 - (show annotations) (download)
Thu May 24 14:04:18 2007 UTC (16 years, 11 months ago) by iliev
File size: 21108 byte(s)
* Implemented instrument scanning in background
  and commands for monitoring the scan progress

1 /***************************************************************************
2 * *
3 * Copyright (C) 2007 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 #if HAVE_SQLITE3
24
25 #include <dirent.h>
26 #include <errno.h>
27 #include <ftw.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 Sql << " AND " << Col << " regexp ?";
157 Params.push_back(Pattern);
158 return;
159 }
160
161 String buf;
162 std::vector<String> tokens;
163 std::vector<String> tokens2;
164 std::stringstream ss(Pattern);
165 while (ss >> buf) tokens.push_back(buf);
166
167 if (tokens.size() == 0) {
168 Sql << " AND " << Col << " LIKE ?";
169 Params.push_back("%" + Pattern + "%");
170 return;
171 }
172
173 bool b = false;
174 for (int i = 0; i < tokens.size(); i++) {
175 Sql << (i == 0 ? " AND (" : "");
176
177 for (int j = 0; j < tokens.at(i).length(); j++) {
178 if (tokens.at(i).at(j) == '+') tokens.at(i).at(j) = ' ';
179 }
180
181 ss.clear();
182 ss.str("");
183 ss << tokens.at(i);
184
185 tokens2.clear();
186 while (ss >> buf) tokens2.push_back(buf);
187
188 if (b && tokens2.size() > 0) Sql << " OR ";
189 if (tokens2.size() > 1) Sql << "(";
190 for (int j = 0; j < tokens2.size(); j++) {
191 if (j != 0) Sql << " AND ";
192 Sql << Col << " LIKE ?";
193 Params.push_back("%" + tokens2.at(j) + "%");
194 b = true;
195 }
196 if (tokens2.size() > 1) Sql << ")";
197 }
198 if (!b) Sql << "0)";
199 else Sql << ")";
200 }
201
202 DirectoryFinder::DirectoryFinder(SearchQuery* pQuery) : pDirectories(new std::vector<String>) {
203 pStmt = NULL;
204 this->pQuery = pQuery;
205 std::stringstream sql;
206 sql << "SELECT dir_name from instr_dirs WHERE dir_id!=0 AND parent_dir_id=?";
207
208 if (pQuery->CreatedAfter.length() != 0) {
209 sql << " AND created > ?";
210 Params.push_back(pQuery->CreatedAfter);
211 }
212 if (pQuery->CreatedBefore.length() != 0) {
213 sql << " AND created < ?";
214 Params.push_back(pQuery->CreatedBefore);
215 }
216 if (pQuery->ModifiedAfter.length() != 0) {
217 sql << " AND modified > ?";
218 Params.push_back(pQuery->ModifiedAfter);
219 }
220 if (pQuery->ModifiedBefore.length() != 0) {
221 sql << " AND modified < ?";
222 Params.push_back(pQuery->ModifiedBefore);
223 }
224
225 AddSql("dir_name", pQuery->Name, sql);
226 AddSql("description", pQuery->Description, sql);
227 SqlQuery = sql.str();
228
229 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
230
231 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
232 if (res != SQLITE_OK) {
233 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
234 }
235
236 for(int i = 0; i < Params.size(); i++) {
237 idb->BindTextParam(pStmt, i + 2, Params.at(i));
238 }
239 }
240
241 DirectoryFinder::~DirectoryFinder() {
242 if (pStmt != NULL) sqlite3_finalize(pStmt);
243 }
244
245 StringListPtr DirectoryFinder::GetDirectories() {
246 return pDirectories;
247 }
248
249 void DirectoryFinder::ProcessDirectory(String Path, int DirId) {
250 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
251 idb->BindIntParam(pStmt, 1, DirId);
252
253 String s = Path;
254 if(Path.compare("/") != 0) s += "/";
255 int res = sqlite3_step(pStmt);
256 while(res == SQLITE_ROW) {
257 pDirectories->push_back(s + ToString(sqlite3_column_text(pStmt, 0)));
258 res = sqlite3_step(pStmt);
259 }
260
261 if (res != SQLITE_DONE) {
262 sqlite3_finalize(pStmt);
263 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
264 }
265
266 res = sqlite3_reset(pStmt);
267 if (res != SQLITE_OK) {
268 sqlite3_finalize(pStmt);
269 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
270 }
271 }
272
273 InstrumentFinder::InstrumentFinder(SearchQuery* pQuery) : pInstruments(new std::vector<String>) {
274 pStmt = NULL;
275 this->pQuery = pQuery;
276 std::stringstream sql;
277 sql << "SELECT instr_name from instruments WHERE dir_id=?";
278
279 if (pQuery->CreatedAfter.length() != 0) {
280 sql << " AND created > ?";
281 Params.push_back(pQuery->CreatedAfter);
282 }
283 if (pQuery->CreatedBefore.length() != 0) {
284 sql << " AND created < ?";
285 Params.push_back(pQuery->CreatedBefore);
286 }
287 if (pQuery->ModifiedAfter.length() != 0) {
288 sql << " AND modified > ?";
289 Params.push_back(pQuery->ModifiedAfter);
290 }
291 if (pQuery->ModifiedBefore.length() != 0) {
292 sql << " AND modified < ?";
293 Params.push_back(pQuery->ModifiedBefore);
294 }
295 if (pQuery->MinSize != -1) sql << " AND instr_size > " << pQuery->MinSize;
296 if (pQuery->MaxSize != -1) sql << " AND instr_size < " << pQuery->MaxSize;
297
298 if (pQuery->InstrType == SearchQuery::CHROMATIC) sql << " AND is_drum = 0";
299 else if (pQuery->InstrType == SearchQuery::DRUM) sql << " AND is_drum != 0";
300
301 if (pQuery->FormatFamilies.size() > 0) {
302 sql << " AND (format_family=?";
303 Params.push_back(pQuery->FormatFamilies.at(0));
304 for (int i = 1; i < pQuery->FormatFamilies.size(); i++) {
305 sql << "OR format_family=?";
306 Params.push_back(pQuery->FormatFamilies.at(i));
307 }
308 sql << ")";
309 }
310
311 AddSql("instr_name", pQuery->Name, sql);
312 AddSql("description", pQuery->Description, sql);
313 AddSql("product", pQuery->Product, sql);
314 AddSql("artists", pQuery->Artists, sql);
315 AddSql("keywords", pQuery->Keywords, sql);
316 SqlQuery = sql.str();
317
318 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
319
320 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
321 if (res != SQLITE_OK) {
322 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
323 }
324
325 for(int i = 0; i < Params.size(); i++) {
326 idb->BindTextParam(pStmt, i + 2, Params.at(i));
327 }
328 }
329
330 InstrumentFinder::~InstrumentFinder() {
331 if (pStmt != NULL) sqlite3_finalize(pStmt);
332 }
333
334 void InstrumentFinder::ProcessDirectory(String Path, int DirId) {
335 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
336 idb->BindIntParam(pStmt, 1, DirId);
337
338 String s = Path;
339 if(Path.compare("/") != 0) s += "/";
340 int res = sqlite3_step(pStmt);
341 while(res == SQLITE_ROW) {
342 pInstruments->push_back(s + ToString(sqlite3_column_text(pStmt, 0)));
343 res = sqlite3_step(pStmt);
344 }
345
346 if (res != SQLITE_DONE) {
347 sqlite3_finalize(pStmt);
348 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
349 }
350
351 res = sqlite3_reset(pStmt);
352 if (res != SQLITE_OK) {
353 sqlite3_finalize(pStmt);
354 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
355 }
356 }
357
358 StringListPtr InstrumentFinder::GetInstruments() {
359 return pInstruments;
360 }
361
362 void DirectoryCounter::ProcessDirectory(String Path, int DirId) {
363 count += InstrumentsDb::GetInstrumentsDb()->GetDirectoryCount(DirId);
364 }
365
366 void InstrumentCounter::ProcessDirectory(String Path, int DirId) {
367 count += InstrumentsDb::GetInstrumentsDb()->GetInstrumentCount(DirId);
368 }
369
370 DirectoryCopier::DirectoryCopier(String SrcParentDir, String DestDir) {
371 this->SrcParentDir = SrcParentDir;
372 this->DestDir = DestDir;
373
374 if (DestDir.at(DestDir.length() - 1) != '/') {
375 this->DestDir.append("/");
376 }
377 if (SrcParentDir.at(SrcParentDir.length() - 1) != '/') {
378 this->SrcParentDir.append("/");
379 }
380 }
381
382 void DirectoryCopier::ProcessDirectory(String Path, int DirId) {
383 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
384
385 String dir = DestDir;
386 String subdir = Path;
387 if(subdir.length() > SrcParentDir.length()) {
388 subdir = subdir.substr(SrcParentDir.length());
389 dir += subdir;
390 db->AddDirectory(dir);
391 }
392
393 int dstDirId = db->GetDirectoryId(dir);
394 if(dstDirId == -1) throw Exception("Unkown DB directory: " + dir);
395 IntListPtr ids = db->GetInstrumentIDs(DirId);
396 for (int i = 0; i < ids->size(); i++) {
397 String name = db->GetInstrumentName(ids->at(i));
398 db->CopyInstrument(ids->at(i), name, dstDirId, dir);
399 }
400 }
401
402 ScanProgress::ScanProgress() {
403 TotalFileCount = ScannedFileCount = Status = 0;
404 CurrentFile = "";
405 GigFileProgress.custom = this;
406 GigFileProgress.callback = GigFileProgressCallback;
407 }
408
409 void ScanProgress::StatusChanged() {
410 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
411 db->Jobs.GetJobById(JobId).FilesTotal = GetTotalFileCount();
412 db->Jobs.GetJobById(JobId).FilesScanned = GetScannedFileCount();
413 db->Jobs.GetJobById(JobId).Scanning = CurrentFile;
414 db->Jobs.GetJobById(JobId).Status = GetStatus();
415
416 InstrumentsDb::GetInstrumentsDb()->FireJobStatusChanged(JobId);
417 }
418
419 int ScanProgress::GetTotalFileCount() {
420 return TotalFileCount;
421 }
422
423 void ScanProgress::SetTotalFileCount(int Count) {
424 if (TotalFileCount == Count) return;
425 TotalFileCount = Count;
426 StatusChanged();
427 }
428
429 int ScanProgress::GetScannedFileCount() {
430 return ScannedFileCount;
431 }
432
433 void ScanProgress::SetScannedFileCount(int Count) {
434 if (ScannedFileCount == Count) return;
435 ScannedFileCount = Count;
436 if (Count > TotalFileCount) TotalFileCount = Count;
437 StatusChanged();
438 }
439
440 int ScanProgress::GetStatus() {
441 return Status;
442 }
443
444 void ScanProgress::SetStatus(int Status) {
445 if (this->Status == Status) return;
446 if (Status < 0) this->Status = 0;
447 else if (Status > 100) this->Status = 100;
448 else this->Status = Status;
449 StatusChanged();
450 }
451
452 void ScanProgress::SetErrorStatus(int Err) {
453 if (Err > 0) Err *= -1;
454 Status = Err;
455 StatusChanged();
456 }
457
458 void ScanProgress::GigFileProgressCallback(gig::progress_t* pProgress) {
459 if (pProgress == NULL) return;
460 ScanProgress* sp = static_cast<ScanProgress*> (pProgress->custom);
461
462 sp->SetStatus((int)(pProgress->factor * 100));
463 }
464
465 AddInstrumentsJob::AddInstrumentsJob(int JobId, ScanMode Mode, String DbDir, String FsDir) {
466 this->JobId = JobId;
467 Progress.JobId = JobId;
468 this->Mode = Mode;
469 this->DbDir = DbDir;
470 this->FsDir = FsDir;
471 }
472
473 void AddInstrumentsJob::Run() {
474 try {
475 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
476
477 switch (Mode) {
478 case NON_RECURSIVE:
479 Progress.SetTotalFileCount(GetFileCount());
480 db->AddInstrumentsNonrecursive(DbDir, FsDir, &Progress);
481 break;
482 case RECURSIVE:
483 db->AddInstrumentsRecursive(DbDir, FsDir, false, &Progress);
484 break;
485 case FLAT:
486 db->AddInstrumentsRecursive(DbDir, FsDir, true, &Progress);
487 break;
488 default:
489 throw Exception("Unknown scan mode");
490 }
491 } catch(Exception e) {
492 Progress.SetErrorStatus(-1);
493 throw e;
494 }
495 }
496
497 int AddInstrumentsJob::GetFileCount() {
498 int count = 0;
499
500 DIR* pDir = opendir(FsDir.c_str());
501 if (pDir == NULL) {
502 std::stringstream ss;
503 ss << "The scanning of directory `" << FsDir << "` failed: ";
504 ss << strerror(errno);
505 std::cerr << ss.str();
506 return 0;
507 }
508
509 struct dirent* pEnt = readdir(pDir);
510 while (pEnt != NULL) {
511 if (pEnt->d_type != DT_REG) {
512 pEnt = readdir(pDir);
513 continue;
514 }
515
516 count++;
517 pEnt = readdir(pDir);
518 }
519
520 if (closedir(pDir)) {
521 std::stringstream ss;
522 ss << "Failed to close directory `" << FsDir << "`: ";
523 ss << strerror(errno);
524 std::cerr << ss.str();
525 }
526
527 return count;
528 }
529
530 AddInstrumentsFromFileJob::AddInstrumentsFromFileJob(int JobId, String DbDir, String FilePath, int Index) {
531 this->JobId = JobId;
532 Progress.JobId = JobId;
533 Progress.SetTotalFileCount(1);
534
535 this->DbDir = DbDir;
536 this->FilePath = FilePath;
537 this->Index = Index;
538 }
539
540 void AddInstrumentsFromFileJob::Run() {
541 InstrumentsDb::GetInstrumentsDb()->AddInstruments(DbDir, FilePath, Index, &Progress);
542 }
543
544
545 String DirectoryScanner::DbDir;
546 String DirectoryScanner::FsDir;
547 bool DirectoryScanner::Flat;
548 ScanProgress* DirectoryScanner::pProgress;
549
550 void DirectoryScanner::Scan(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {
551 dmsg(2,("DirectoryScanner: Scan(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));
552 if (DbDir.empty() || FsDir.empty()) throw Exception("Directory expected");
553
554 struct stat statBuf;
555 int res = stat(FsDir.c_str(), &statBuf);
556 if (res) {
557 std::stringstream ss;
558 ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);
559 throw Exception(ss.str());
560 }
561
562 if (!S_ISDIR(statBuf.st_mode)) {
563 throw Exception("Directory expected");
564 }
565
566 DirectoryScanner::DbDir = DbDir;
567 DirectoryScanner::FsDir = FsDir;
568 if (DbDir.at(DbDir.length() - 1) != '/') {
569 DirectoryScanner::DbDir.append("/");
570 }
571 if (FsDir.at(FsDir.length() - 1) != '/') {
572 DirectoryScanner::FsDir.append("/");
573 }
574 DirectoryScanner::Flat = Flat;
575 DirectoryScanner::pProgress = pProgress;
576
577 ftw(FsDir.c_str(), FtwCallback, 10);
578 }
579
580 int DirectoryScanner::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {
581 dmsg(2,("DirectoryScanner: FtwCallback(fpath=%s)\n", fpath));
582 if (typeflag != FTW_D) return 0;
583
584 String dir = DbDir;
585 if (!Flat) {
586 String subdir = fpath;
587 if(subdir.length() > FsDir.length()) {
588 subdir = subdir.substr(FsDir.length());
589 dir += subdir;
590 }
591 }
592
593 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
594 if (!db->DirectoryExist(dir)) db->AddDirectory(dir);
595
596 db->AddInstrumentsNonrecursive(dir, String(fpath), pProgress);
597
598 return 0;
599 };
600
601 int InstrumentFileCounter::FileCount;
602
603 int InstrumentFileCounter::Count(String FsDir) {
604 dmsg(2,("InstrumentFileCounter: Count(FsDir=%s)\n", FsDir.c_str()));
605 if (FsDir.empty()) throw Exception("Directory expected");
606 FileCount = 0;
607
608 struct stat statBuf;
609 int res = stat(FsDir.c_str(), &statBuf);
610 if (res) {
611 std::stringstream ss;
612 ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);
613 throw Exception(ss.str());
614 }
615
616 if (!S_ISDIR(statBuf.st_mode)) {
617 throw Exception("Directory expected");
618 }
619
620 ftw(FsDir.c_str(), FtwCallback, 10);
621 return FileCount;
622 }
623
624 int InstrumentFileCounter::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {
625 if (typeflag != FTW_F) return 0;
626 String s = fpath;
627 if(s.length() < 4) return 0;
628 if(!strcasecmp(".gig", s.substr(s.length() - 4).c_str())) FileCount++;
629 return 0;
630 };
631
632 } // namespace LinuxSampler
633
634 #endif // HAVE_SQLITE3

  ViewVC Help
Powered by ViewVC