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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1717 - (show annotations) (download)
Sun Mar 16 17:43:20 2008 UTC (16 years, 1 month ago) by iliev
File size: 65520 byte(s)
* moved all OS dependent file operation to File class

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 "InstrumentsDb.h"
22
23 #include "../common/File.h"
24 #include "../common/global_private.h"
25
26 #include <iostream>
27 #include <sstream>
28 #include <vector>
29 #include <errno.h>
30 #include <fnmatch.h>
31
32 #include "../common/Exception.h"
33
34 namespace LinuxSampler {
35
36 InstrumentsDb InstrumentsDb::instance;
37
38 void InstrumentsDb::CreateInstrumentsDb(String FilePath) {
39 File f = File(FilePath);
40 if (f.Exist()) {
41 throw Exception("File exists: " + FilePath);
42 }
43
44 GetInstrumentsDb()->SetDbFile(FilePath);
45
46 String sql =
47 " CREATE TABLE instr_dirs ( "
48 " dir_id INTEGER PRIMARY KEY AUTOINCREMENT, "
49 " parent_dir_id INTEGER DEFAULT 0, "
50 " dir_name TEXT, "
51 " created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
52 " modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
53 " description TEXT, "
54 " FOREIGN KEY(parent_dir_id) REFERENCES instr_dirs(dir_id), "
55 " UNIQUE (parent_dir_id,dir_name) "
56 " ); ";
57
58 GetInstrumentsDb()->ExecSql(sql);
59
60 sql = "INSERT INTO instr_dirs (dir_id, parent_dir_id, dir_name) VALUES (0, -2, '/');";
61 GetInstrumentsDb()->ExecSql(sql);
62
63 sql =
64 " CREATE TABLE instruments ( "
65 " instr_id INTEGER PRIMARY KEY AUTOINCREMENT, "
66 " dir_id INTEGER DEFAULT 0, "
67 " instr_name TEXT, "
68 " instr_file TEXT, "
69 " instr_nr INTEGER, "
70 " format_family TEXT, "
71 " format_version TEXT, "
72 " instr_size INTEGER, "
73 " created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
74 " modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
75 " description TEXT, "
76 " is_drum INTEGER(1), "
77 " product TEXT, "
78 " artists TEXT, "
79 " keywords TEXT, "
80 " FOREIGN KEY(dir_id) REFERENCES instr_dirs(dir_id), "
81 " UNIQUE (dir_id,instr_name) "
82 " ); ";
83
84 GetInstrumentsDb()->ExecSql(sql);
85 }
86
87 InstrumentsDb::InstrumentsDb() {
88 db = NULL;
89 DbInstrumentsMutex = Mutex();
90 InTransaction = false;
91 }
92
93 InstrumentsDb::~InstrumentsDb() {
94 if (db != NULL) sqlite3_close(db);
95 }
96
97 void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {
98 llInstrumentsDbListeners.AddListener(l);
99 }
100
101 void InstrumentsDb::RemoveInstrumentsDbListener(InstrumentsDb::Listener* l) {
102 llInstrumentsDbListeners.RemoveListener(l);
103 }
104
105 InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {
106 return &instance;
107 }
108
109 void InstrumentsDb::SetDbFile(String File) {
110 DbInstrumentsMutex.Lock();
111 if (File.empty() || DbFile.length() > 0) {
112 DbInstrumentsMutex.Unlock();
113 throw Exception("Failed to set the database file");
114 }
115 DbFile = File;
116 DbInstrumentsMutex.Unlock();
117 }
118
119 sqlite3* InstrumentsDb::GetDb() {
120 if ( db != NULL) return db;
121
122 if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
123 #if defined(__APPLE__) /* 20071224 Toshi Nagata */
124 if (DbFile.find("~") == 0)
125 DbFile.replace(0, 1, getenv("HOME"));
126 #endif
127 int rc = sqlite3_open(DbFile.c_str(), &db);
128 if (rc) {
129 sqlite3_close(db);
130 db = NULL;
131 throw Exception("Cannot open instruments database: " + DbFile);
132 }
133 rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, NULL, Regexp, NULL, NULL);
134 if (rc) { throw Exception("Failed to add user function for handling regular expressions."); }
135
136 // TODO: remove this in the next version
137 try {
138 int i = ExecSqlInt("SELECT parent_dir_id FROM instr_dirs WHERE dir_id=0");
139 // The parent ID of the root directory should be -2 now.
140 if(i != -2) ExecSql("UPDATE instr_dirs SET parent_dir_id=-2 WHERE dir_id=0");
141 } catch(Exception e) { }
142 ////////////////////////////////////////
143
144 return db;
145 }
146
147 int InstrumentsDb::GetDirectoryCount(int DirId) {
148 dmsg(2,("InstrumentsDb: GetDirectoryCount(DirId=%d)\n", DirId));
149 if(DirId == -1) return -1;
150
151 std::stringstream sql;
152 sql << "SELECT COUNT(*) FROM instr_dirs WHERE parent_dir_id=" << DirId;
153
154 int count = ExecSqlInt(sql.str());
155
156 return count;
157 }
158
159 int InstrumentsDb::GetDirectoryCount(String Dir, bool Recursive) {
160 dmsg(2,("InstrumentsDb: GetDirectoryCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
161 int i;
162
163 BeginTransaction();
164 try {
165 if (Recursive) {
166 DirectoryCounter directoryCounter;
167 DirectoryTreeWalk(Dir, &directoryCounter);
168 i = directoryCounter.GetDirectoryCount();
169 } else {
170 i = GetDirectoryCount(GetDirectoryId(Dir));
171 }
172 } catch (Exception e) {
173 EndTransaction();
174 throw e;
175 }
176 EndTransaction();
177 if (i == -1) throw Exception("Unkown DB directory: " + toEscapedPath(Dir));
178
179 return i;
180 }
181
182 IntListPtr InstrumentsDb::GetDirectoryIDs(int DirId) {
183 std::stringstream sql;
184 sql << "SELECT dir_id FROM instr_dirs ";
185 sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
186
187 return ExecSqlIntList(sql.str());
188 }
189
190 StringListPtr InstrumentsDb::GetDirectories(String Dir, bool Recursive) {
191 dmsg(2,("InstrumentsDb: GetDirectories(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
192
193 BeginTransaction();
194 try {
195 int dirId = GetDirectoryId(Dir);
196 if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
197
198 StringListPtr pDirs;
199 if (Recursive) {
200 SearchQuery q;
201 DirectoryFinder directoryFinder(&q);
202 DirectoryTreeWalk(Dir, &directoryFinder);
203 pDirs = directoryFinder.GetDirectories();
204 } else {
205 pDirs = GetDirectories(dirId);
206 }
207 EndTransaction();
208 return pDirs;
209 } catch (Exception e) {
210 EndTransaction();
211 throw e;
212 }
213 }
214
215 StringListPtr InstrumentsDb::GetDirectories(int DirId) {
216 std::stringstream sql;
217 sql << "SELECT dir_name FROM instr_dirs ";
218 sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
219 StringListPtr dirs = ExecSqlStringList(sql.str());
220
221 for (int i = 0; i < dirs->size(); i++) {
222 for (int j = 0; j < dirs->at(i).length(); j++) {
223 if (dirs->at(i).at(j) == '/') dirs->at(i).at(j) = '\0';
224 }
225 }
226
227 return dirs;
228 }
229
230 int InstrumentsDb::GetDirectoryId(String Dir) {
231 dmsg(2,("InstrumentsDb: GetDirectoryId(Dir=%s)\n", Dir.c_str()));
232 CheckPathName(Dir);
233
234 if (Dir.empty() || Dir.at(0) != '/') {
235 return -1;
236 } else if (Dir.length() == 1) {
237 // We expect the root directory id to be always 0.
238 return 0;
239 }
240
241 int id = 0, i = 1;
242 int j = Dir.find('/', i);
243
244 while(j != std::string::npos) {
245 id = GetDirectoryId(id, Dir.substr(i, j - i));
246 i = j + 1;
247 if (i >= Dir.length()) return id;
248 j = Dir.find('/', i);
249 }
250
251 return GetDirectoryId(id, Dir.substr(i));
252 }
253
254 int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {
255 dmsg(2,("InstrumentsDb: GetDirectoryId(ParentDirId=%d, DirName=%s)\n", ParentDirId, DirName.c_str()));
256 DirName = toDbName(DirName);
257 std::stringstream sql;
258 sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";
259 sql << ParentDirId << " AND dir_name=?";
260 return ExecSqlInt(sql.str(), DirName);
261 }
262
263 String InstrumentsDb::GetDirectoryName(int DirId) {
264 String sql = "SELECT dir_name FROM instr_dirs WHERE dir_id=" + ToString(DirId);
265 String name = ExecSqlString(sql);
266 if (name.empty()) throw Exception("Directory ID not found");
267 return name;
268 }
269
270 int InstrumentsDb::GetParentDirectoryId(int DirId) {
271 if (DirId == 0) throw Exception("The root directory is specified");
272 String sql = "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" + ToString(DirId);
273 int parentId = ExecSqlInt(sql);
274 if (parentId == -1) throw Exception("DB directory not found");
275 return parentId;
276 }
277
278 String InstrumentsDb::GetDirectoryPath(int DirId) {
279 String path = "";
280 int count = 1000; // used to prevent infinite loops
281
282 while(--count) {
283 if (DirId == 0) {
284 path = "/" + path;
285 break;
286 }
287 path = GetDirectoryName(DirId) + path;
288 DirId = GetParentDirectoryId(DirId);
289 }
290
291 if (!count) throw Exception("Possible infinite loop detected");
292
293 return path;
294 }
295
296 void InstrumentsDb::AddDirectory(String Dir) {
297 dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));
298 CheckPathName(Dir);
299 String ParentDir = GetParentDirectory(Dir);
300
301 BeginTransaction();
302 try {
303 if (Dir.length() > 1) {
304 if (Dir.at(Dir.length() - 1) == '/') Dir.erase(Dir.length() - 1);
305 }
306
307 String dirName = GetFileName(Dir);
308 if(ParentDir.empty() || dirName.empty()) {
309 throw Exception("Failed to add DB directory: " + toEscapedPath(Dir));
310 }
311
312 int id = GetDirectoryId(ParentDir);
313 if (id == -1) throw Exception("DB directory doesn't exist: " + toEscapedPath(ParentDir));
314 int id2 = GetDirectoryId(id, dirName);
315 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(Dir));
316 id2 = GetInstrumentId(id, dirName);
317 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(Dir));
318
319 std::stringstream sql;
320 sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";
321 sql << id << ", ?)";
322
323 ExecSql(sql.str(), toDbName(dirName));
324 } catch (Exception e) {
325 EndTransaction();
326 throw e;
327 }
328
329 EndTransaction();
330
331 FireDirectoryCountChanged(ParentDir);
332 }
333
334 void InstrumentsDb::RemoveDirectory(String Dir, bool Force) {
335 dmsg(2,("InstrumentsDb: RemoveDirectory(Dir=%s,Force=%d)\n", Dir.c_str(), Force));
336
337 String ParentDir = GetParentDirectory(Dir);
338
339 BeginTransaction();
340 try {
341 int dirId = GetDirectoryId(Dir);
342 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
343 if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);
344 if(ParentDir.empty()) throw Exception("Unknown parent directory");
345 if (Force) RemoveDirectoryContent(dirId);
346 RemoveDirectory(dirId);
347 } catch (Exception e) {
348 EndTransaction();
349 throw e;
350 }
351
352 EndTransaction();
353 FireDirectoryCountChanged(ParentDir);
354 }
355
356 void InstrumentsDb::RemoveDirectoryContent(int DirId, int Level) {
357 dmsg(2,("InstrumentsDb: RemoveDirectoryContent(DirId=%d,Level=%d)\n", DirId, Level));
358 if (Level > 1000) throw Exception("Directory level too deep: " + ToString(Level));
359 IntListPtr dirIds = GetDirectoryIDs(DirId);
360
361 for (int i = 0; i < dirIds->size(); i++) {
362 RemoveDirectoryContent(dirIds->at(i), Level + 1);
363 }
364
365 RemoveAllDirectories(DirId);
366 RemoveAllInstruments(DirId);
367 }
368
369 void InstrumentsDb::RemoveDirectory(int DirId) {
370 dmsg(2,("InstrumentsDb: RemoveDirectory(DirId=%d)\n", DirId));
371 if (GetInstrumentCount(DirId) > 0 || GetDirectoryCount(DirId) > 0) {
372 throw Exception("The specified DB directory is not empty");
373 }
374
375 std::stringstream sql;
376 sql << "DELETE FROM instr_dirs WHERE dir_id=" << DirId;
377
378 ExecSql(sql.str());
379 }
380
381 void InstrumentsDb::RemoveAllDirectories(int DirId) {
382 dmsg(2,("InstrumentsDb: RemoveAllDirectories(DirId=%d)\n", DirId));
383 IntListPtr dirIds = GetDirectoryIDs(DirId);
384
385 for (int i = 0; i < dirIds->size(); i++) {
386 if (!IsDirectoryEmpty(dirIds->at(i))) {
387 throw Exception("DB directory not empty!");
388 }
389 }
390 std::stringstream sql;
391 sql << "DELETE FROM instr_dirs WHERE parent_dir_id=" << DirId;
392 sql << " AND dir_id!=0";
393
394 ExecSql(sql.str());
395 }
396
397 bool InstrumentsDb::IsDirectoryEmpty(int DirId) {
398 dmsg(2,("InstrumentsDb: IsDirectoryEmpty(DirId=%d)\n", DirId));
399 int dirCount = GetDirectoryCount(DirId);
400 int instrCount = GetInstrumentCount(DirId);
401 dmsg(3,("InstrumentsDb: IsDirectoryEmpty: dirCount=%d,instrCount=%d\n", dirCount, instrCount));
402 if (dirCount == -1 || instrCount == -1) return false;
403 return dirCount == 0 && instrCount == 0;
404 }
405
406 bool InstrumentsDb::DirectoryExist(String Dir) {
407 dmsg(2,("InstrumentsDb: DirectoryExist(Dir=%s)\n", Dir.c_str()));
408 bool b;
409
410 DbInstrumentsMutex.Lock();
411 try { b = GetDirectoryId(Dir) != -1; }
412 catch (Exception e) {
413 DbInstrumentsMutex.Unlock();
414 throw e;
415 }
416 DbInstrumentsMutex.Unlock();
417
418 return b;
419 }
420
421 DbDirectory InstrumentsDb::GetDirectoryInfo(String Dir) {
422 dmsg(2,("InstrumentsDb: GetDirectoryInfo(Dir=%s)\n", Dir.c_str()));
423 DbDirectory d;
424
425 BeginTransaction();
426
427 try {
428 int id = GetDirectoryId(Dir);
429 if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
430
431 sqlite3_stmt *pStmt = NULL;
432 std::stringstream sql;
433 sql << "SELECT created,modified,description FROM instr_dirs ";
434 sql << "WHERE dir_id=" << id;
435
436 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
437 if (res != SQLITE_OK) {
438 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
439 }
440
441 res = sqlite3_step(pStmt);
442 if(res == SQLITE_ROW) {
443 d.Created = ToString(sqlite3_column_text(pStmt, 0));
444 d.Modified = ToString(sqlite3_column_text(pStmt, 1));
445 d.Description = ToString(sqlite3_column_text(pStmt, 2));
446 } else {
447 sqlite3_finalize(pStmt);
448
449 if (res != SQLITE_DONE) {
450 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
451 } else {
452 throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
453 }
454 }
455
456 sqlite3_finalize(pStmt);
457 } catch (Exception e) {
458 EndTransaction();
459 throw e;
460 }
461
462 EndTransaction();
463 return d;
464 }
465
466 void InstrumentsDb::RenameDirectory(String Dir, String Name) {
467 dmsg(2,("InstrumentsDb: RenameDirectory(Dir=%s,Name=%s)\n", Dir.c_str(), Name.c_str()));
468 CheckFileName(Name);
469 String dbName = toDbName(Name);
470
471 BeginTransaction();
472 try {
473 int dirId = GetDirectoryId(Dir);
474 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedText(Dir));
475
476 std::stringstream sql;
477 sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" << dirId;
478
479 int parent = ExecSqlInt(sql.str());
480 if (parent == -1) throw Exception("Unknown parent directory: " + toEscapedPath(Dir));
481
482 if (GetDirectoryId(parent, dbName) != -1) {
483 String s = toEscapedPath(Name);
484 throw Exception("Cannot rename. Directory with that name already exists: " + s);
485 }
486
487 if (GetInstrumentId(parent, dbName) != -1) {
488 throw Exception("Cannot rename. Instrument with that name exist: " + toEscapedPath(Dir));
489 }
490
491 sql.str("");
492 sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;
493 ExecSql(sql.str(), dbName);
494 } catch (Exception e) {
495 EndTransaction();
496 throw e;
497 }
498
499 EndTransaction();
500 FireDirectoryNameChanged(Dir, toAbstractName(Name));
501 }
502
503 void InstrumentsDb::MoveDirectory(String Dir, String Dst) {
504 dmsg(2,("InstrumentsDb: MoveDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
505
506 if(Dir.compare("/") == 0) throw Exception("Cannot move the root directory");
507 String ParentDir = GetParentDirectory(Dir);
508 if(ParentDir.empty()) throw Exception("Unknown parent directory");
509
510 BeginTransaction();
511 try {
512 int dirId = GetDirectoryId(Dir);
513 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
514 int dstId = GetDirectoryId(Dst);
515 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
516 if (dirId == dstId) {
517 throw Exception("Cannot move directory to itself");
518 }
519
520 if (Dir.at(Dir.length() - 1) != '/') Dir.append("/");
521 if (Dst.length() > Dir.length()) {
522 if (Dir.compare(Dst.substr(0, Dir.length())) == 0) {
523 throw Exception("Cannot move a directory to a subdirectory of itself.");
524 }
525 }
526
527 Dir.erase(Dir.length() - 1);
528 String dirName = GetFileName(Dir);
529
530 int id2 = GetDirectoryId(dstId, dirName);
531 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
532 id2 = GetInstrumentId(dstId, dirName);
533 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
534
535 std::stringstream sql;
536 sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;
537 sql << " WHERE dir_id=" << dirId;
538 ExecSql(sql.str());
539 } catch (Exception e) {
540 EndTransaction();
541 throw e;
542 }
543
544 EndTransaction();
545 FireDirectoryCountChanged(ParentDir);
546 FireDirectoryCountChanged(Dst);
547 }
548
549 void InstrumentsDb::CopyDirectory(String Dir, String Dst) {
550 dmsg(2,("InstrumentsDb: CopyDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
551
552 if(Dir.compare("/") == 0) throw Exception("Cannot copy the root directory");
553 String ParentDir = GetParentDirectory(Dir);
554 if(ParentDir.empty()) throw Exception("Unknown parent directory");
555
556 BeginTransaction();
557 try {
558 int dirId = GetDirectoryId(Dir);
559 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
560 int dstId = GetDirectoryId(Dst);
561 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
562 if (dirId == dstId) {
563 throw Exception("Cannot copy directory to itself");
564 }
565
566 if (Dir.at(Dir.length() - 1) != '/') Dir.append("/");
567 if (Dst.length() > Dir.length()) {
568 if (Dir.compare(Dst.substr(0, Dir.length())) == 0) {
569 throw Exception("Cannot copy a directory to a subdirectory of itself.");
570 }
571 }
572
573 Dir.erase(Dir.length() - 1);
574 String dirName = GetFileName(Dir);
575
576 int id2 = GetDirectoryId(dstId, dirName);
577 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
578 id2 = GetInstrumentId(dstId, dirName);
579 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
580
581 DirectoryCopier directoryCopier(ParentDir, Dst);
582 DirectoryTreeWalk(Dir, &directoryCopier);
583 } catch (Exception e) {
584 EndTransaction();
585 throw e;
586 }
587
588 EndTransaction();
589 }
590
591 void InstrumentsDb::SetDirectoryDescription(String Dir, String Desc) {
592 dmsg(2,("InstrumentsDb: SetDirectoryDescription(Dir=%s,Desc=%s)\n", Dir.c_str(), Desc.c_str()));
593
594 BeginTransaction();
595 try {
596 int id = GetDirectoryId(Dir);
597 if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
598
599 std::stringstream sql;
600 sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";
601 sql << "WHERE dir_id="<< id;
602
603 ExecSql(sql.str(), Desc);
604 } catch (Exception e) {
605 EndTransaction();
606 throw e;
607 }
608 EndTransaction();
609
610 FireDirectoryInfoChanged(Dir);
611 }
612
613 int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground) {
614 dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground));
615 if(!bBackground) {
616 switch (Mode) {
617 case NON_RECURSIVE:
618 AddInstrumentsNonrecursive(DbDir, FsDir);
619 break;
620 case RECURSIVE:
621 AddInstrumentsRecursive(DbDir, FsDir);
622 break;
623 case FLAT:
624 AddInstrumentsRecursive(DbDir, FsDir, true);
625 break;
626 default:
627 throw Exception("Unknown scan mode");
628 }
629
630 return -1;
631 }
632
633 ScanJob job;
634 int jobId = Jobs.AddJob(job);
635 InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir));
636
637 return jobId;
638 }
639
640 int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {
641 dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));
642 if(!bBackground) {
643 AddInstruments(DbDir, FilePath, Index);
644 return -1;
645 }
646
647 ScanJob job;
648 int jobId = Jobs.AddJob(job);
649 InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index));
650
651 return jobId;
652 }
653
654 void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
655 dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
656 if (DbDir.empty() || FilePath.empty()) return;
657
658 DbInstrumentsMutex.Lock();
659 try {
660 int dirId = GetDirectoryId(DbDir);
661 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir));
662
663 File f = File(FilePath);
664 if (!f.Exist()) {
665 std::stringstream ss;
666 ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
667 throw Exception(ss.str());
668 }
669
670 if (!f.IsFile()) {
671 std::stringstream ss;
672 ss << "`" << FilePath << "` is not an instrument file";
673 throw Exception(ss.str());
674 }
675
676 AddInstrumentsFromFile(DbDir, FilePath, Index, pProgress);
677 } catch (Exception e) {
678 DbInstrumentsMutex.Unlock();
679 throw e;
680 }
681
682 DbInstrumentsMutex.Unlock();
683 }
684
685 void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, ScanProgress* pProgress) {
686 dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s)\n", DbDir.c_str(), FsDir.c_str()));
687 if (DbDir.empty() || FsDir.empty()) return;
688
689 DbInstrumentsMutex.Lock();
690 try {
691 int dirId = GetDirectoryId(DbDir);
692 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
693
694 File f = File(FsDir);
695 if (!f.Exist()) {
696 std::stringstream ss;
697 ss << "Fail to stat `" << FsDir << "`: " << f.GetErrorMsg();
698 throw Exception(ss.str());
699 }
700
701 if (!f.IsDirectory()) {
702 throw Exception("Directory expected: " + FsDir);
703 }
704
705 if (FsDir.at(FsDir.length() - 1) != File::DirSeparator) {
706 FsDir.push_back(File::DirSeparator);
707 }
708
709 try {
710 FileListPtr fileList = File::GetFiles(FsDir);
711 for (int i = 0; i < fileList->size(); i++) {
712 AddInstrumentsFromFile(DbDir, FsDir + fileList->at(i), -1, pProgress);
713 }
714 } catch(Exception e) {
715 e.PrintMessage();
716 DbInstrumentsMutex.Unlock();
717 return;
718 }
719 } catch (Exception e) {
720 DbInstrumentsMutex.Unlock();
721 throw e;
722 }
723
724 DbInstrumentsMutex.Unlock();
725 }
726
727 void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {
728 dmsg(2,("InstrumentsDb: AddInstrumentsRecursive(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));
729 if (pProgress != NULL) {
730 InstrumentFileCounter c;
731 pProgress->SetTotalFileCount(c.Count(FsDir));
732 }
733
734 DirectoryScanner d;
735 d.Scan(DbDir, FsDir, Flat, pProgress);
736 }
737
738 int InstrumentsDb::GetInstrumentCount(int DirId) {
739 dmsg(2,("InstrumentsDb: GetInstrumentCount(DirId=%d)\n", DirId));
740 if(DirId == -1) return -1;
741
742 std::stringstream sql;
743 sql << "SELECT COUNT(*) FROM instruments WHERE dir_id=" << DirId;
744
745 return ExecSqlInt(sql.str());
746 }
747
748 int InstrumentsDb::GetInstrumentCount(String Dir, bool Recursive) {
749 dmsg(2,("InstrumentsDb: GetInstrumentCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
750 int i;
751
752 BeginTransaction();
753 try {
754 if (Recursive) {
755 InstrumentCounter instrumentCounter;
756 DirectoryTreeWalk(Dir, &instrumentCounter);
757 i = instrumentCounter.GetInstrumentCount();
758 } else {
759 i = GetInstrumentCount(GetDirectoryId(Dir));
760 }
761 } catch (Exception e) {
762 EndTransaction();
763 throw e;
764 }
765 EndTransaction();
766
767 if (i == -1) throw Exception("Unknown Db directory: " + toEscapedPath(Dir));
768 return i;
769 }
770
771 IntListPtr InstrumentsDb::GetInstrumentIDs(int DirId) {
772 std::stringstream sql;
773 sql << "SELECT instr_id FROM instruments WHERE dir_id=" << DirId;
774
775 return ExecSqlIntList(sql.str());
776 }
777
778 StringListPtr InstrumentsDb::GetInstruments(String Dir, bool Recursive) {
779 dmsg(2,("InstrumentsDb: GetInstruments(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
780 BeginTransaction();
781 try {
782 int dirId = GetDirectoryId(Dir);
783 if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
784
785 StringListPtr pInstrs;
786
787 if(Recursive) {
788 SearchQuery q;
789 InstrumentFinder instrumentFinder(&q);
790 DirectoryTreeWalk(Dir, &instrumentFinder);
791 pInstrs = instrumentFinder.GetInstruments();
792 } else {
793 std::stringstream sql;
794 sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;
795
796 pInstrs = ExecSqlStringList(sql.str());
797 // Converting to abstract names
798 for (int i = 0; i < pInstrs->size(); i++) {
799 for (int j = 0; j < pInstrs->at(i).length(); j++) {
800 if (pInstrs->at(i).at(j) == '/') pInstrs->at(i).at(j) = '\0';
801 }
802 }
803 }
804 EndTransaction();
805 return pInstrs;
806 } catch (Exception e) {
807 EndTransaction();
808 throw e;
809 }
810 }
811
812 int InstrumentsDb::GetInstrumentId(String Instr) {
813 dmsg(2,("InstrumentsDb: GetInstrumentId(Instr=%s)\n", Instr.c_str()));
814 String Dir = GetDirectoryPath(Instr);
815 if (Dir.empty()) return -1;
816
817 return GetInstrumentId(GetDirectoryId(Dir), GetFileName(Instr));
818 }
819
820 int InstrumentsDb::GetInstrumentId(int DirId, String InstrName) {
821 dmsg(2,("InstrumentsDb: GetInstrumentId(DirId=%d,InstrName=%s)\n", DirId, InstrName.c_str()));
822 if (DirId == -1 || InstrName.empty()) return -1;
823
824 std::stringstream sql;
825 sql << "SELECT instr_id FROM instruments WHERE dir_id=";
826 sql << DirId << " AND instr_name=?";
827 return ExecSqlInt(sql.str(), toDbName(InstrName));
828 }
829
830 String InstrumentsDb::GetInstrumentName(int InstrId) {
831 dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));
832 std::stringstream sql;
833 sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;
834 return toAbstractName(ExecSqlString(sql.str()));
835 }
836
837 void InstrumentsDb::RemoveInstrument(String Instr) {
838 dmsg(2,("InstrumentsDb: RemoveInstrument(Instr=%s)\n", Instr.c_str()));
839 String ParentDir = GetDirectoryPath(Instr);
840 if(ParentDir.empty()) throw Exception("Unknown parent directory");
841
842 BeginTransaction();
843 try {
844 int instrId = GetInstrumentId(Instr);
845 if(instrId == -1) {
846 throw Exception("The specified instrument does not exist: " + toEscapedPath(Instr));
847 }
848 RemoveInstrument(instrId);
849 } catch (Exception e) {
850 EndTransaction();
851 throw e;
852 }
853 EndTransaction();
854 FireInstrumentCountChanged(ParentDir);
855 }
856
857 void InstrumentsDb::RemoveInstrument(int InstrId) {
858 dmsg(2,("InstrumentsDb: RemoveInstrument(InstrId=%d)\n", InstrId));
859
860 std::stringstream sql;
861 sql << "DELETE FROM instruments WHERE instr_id=" << InstrId;
862
863 ExecSql(sql.str());
864 }
865
866 void InstrumentsDb::RemoveAllInstruments(int DirId) {
867 dmsg(2,("InstrumentsDb: RemoveAllInstruments(DirId=%d)\n", DirId));
868
869 std::stringstream sql;
870 sql << "DELETE FROM instruments WHERE dir_id=" << DirId;
871 ExecSql(sql.str());
872 }
873
874 DbInstrument InstrumentsDb::GetInstrumentInfo(String Instr) {
875 dmsg(2,("InstrumentsDb: GetInstrumentInfo(Instr=%s)\n", Instr.c_str()));
876 DbInstrument i;
877
878 BeginTransaction();
879 try {
880 int id = GetInstrumentId(Instr);
881 if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
882 i = GetInstrumentInfo(id);
883 } catch (Exception e) {
884 EndTransaction();
885 throw e;
886 }
887 EndTransaction();
888
889 return i;
890 }
891
892 DbInstrument InstrumentsDb::GetInstrumentInfo(int InstrId) {
893 sqlite3_stmt *pStmt = NULL;
894 std::stringstream sql;
895 sql << "SELECT instr_file,instr_nr,format_family,format_version,";
896 sql << "instr_size,created,modified,description,is_drum,product,";
897 sql << "artists,keywords FROM instruments WHERE instr_id=" << InstrId;
898
899 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
900 if (res != SQLITE_OK) {
901 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
902 }
903
904 DbInstrument i;
905 res = sqlite3_step(pStmt);
906 if(res == SQLITE_ROW) {
907 i.InstrFile = ToString(sqlite3_column_text(pStmt, 0));
908 i.InstrNr = sqlite3_column_int(pStmt, 1);
909 i.FormatFamily = ToString(sqlite3_column_text(pStmt, 2));
910 i.FormatVersion = ToString(sqlite3_column_text(pStmt, 3));
911 i.Size = sqlite3_column_int64(pStmt, 4);
912 i.Created = ToString(sqlite3_column_text(pStmt, 5));
913 i.Modified = ToString(sqlite3_column_text(pStmt, 6));
914 i.Description = ToString(sqlite3_column_text(pStmt, 7));
915 i.IsDrum = sqlite3_column_int(pStmt, 8);
916 i.Product = ToString(sqlite3_column_text(pStmt, 9));
917 i.Artists = ToString(sqlite3_column_text(pStmt, 10));
918 i.Keywords = ToString(sqlite3_column_text(pStmt, 11));
919 } else {
920 sqlite3_finalize(pStmt);
921
922 if (res != SQLITE_DONE) {
923 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
924 } else {
925 throw Exception("Unknown DB instrument");
926 }
927 }
928
929 sqlite3_finalize(pStmt);
930 return i;
931 }
932
933 void InstrumentsDb::RenameInstrument(String Instr, String Name) {
934 dmsg(2,("InstrumentsDb: RenameInstrument(Instr=%s,Name=%s)\n", Instr.c_str(), Name.c_str()));
935 CheckFileName(Name);
936
937 BeginTransaction();
938 try {
939 int dirId = GetDirectoryId(GetDirectoryPath(Instr));
940 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
941
942 int instrId = GetInstrumentId(dirId, GetFileName(Instr));
943 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
944
945 if (GetInstrumentId(dirId, Name) != -1) {
946 String s = toEscapedPath(Name);
947 throw Exception("Cannot rename. Instrument with that name already exists: " + s);
948 }
949
950 if (GetDirectoryId(dirId, Name) != -1) {
951 String s = toEscapedPath(Name);
952 throw Exception("Cannot rename. Directory with that name already exists: " + s);
953 }
954
955 std::stringstream sql;
956 sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;
957 ExecSql(sql.str(), toDbName(Name));
958 } catch (Exception e) {
959 EndTransaction();
960 throw e;
961 }
962 EndTransaction();
963 FireInstrumentNameChanged(Instr, toAbstractName(Name));
964 }
965
966 void InstrumentsDb::MoveInstrument(String Instr, String Dst) {
967 dmsg(2,("InstrumentsDb: MoveInstrument(Instr=%s,Dst=%s)\n", Instr.c_str(), Dst.c_str()));
968 String ParentDir = GetDirectoryPath(Instr);
969 if(ParentDir.empty()) throw Exception("Unknown parent directory");
970
971 BeginTransaction();
972 try {
973 int dirId = GetDirectoryId(ParentDir);
974 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
975
976 String instrName = GetFileName(Instr);
977 int instrId = GetInstrumentId(dirId, instrName);
978 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
979
980 int dstId = GetDirectoryId(Dst);
981 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
982 if (dirId == dstId) {
983 EndTransaction();
984 return;
985 }
986
987 if (GetInstrumentId(dstId, instrName) != -1) {
988 String s = toEscapedPath(instrName);
989 throw Exception("Cannot move. Instrument with that name already exists: " + s);
990 }
991
992 if (GetDirectoryId(dstId, instrName) != -1) {
993 String s = toEscapedPath(instrName);
994 throw Exception("Cannot move. Directory with that name already exists: " + s);
995 }
996
997 std::stringstream sql;
998 sql << "UPDATE instruments SET dir_id=" << dstId;
999 sql << " WHERE instr_id=" << instrId;
1000 ExecSql(sql.str());
1001 } catch (Exception e) {
1002 EndTransaction();
1003 throw e;
1004 }
1005 EndTransaction();
1006 FireInstrumentCountChanged(ParentDir);
1007 FireInstrumentCountChanged(Dst);
1008 }
1009
1010 void InstrumentsDb::CopyInstrument(String Instr, String Dst) {
1011 dmsg(2,("InstrumentsDb: CopyInstrument(Instr=%s,Dst=%s)\n", Instr.c_str(), Dst.c_str()));
1012 String ParentDir = GetDirectoryPath(Instr);
1013 if(ParentDir.empty()) throw Exception("Unknown parent directory");
1014
1015 BeginTransaction();
1016 try {
1017 int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1018 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1019
1020 String instrName = GetFileName(Instr);
1021 int instrId = GetInstrumentId(dirId, instrName);
1022 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1023
1024 int dstId = GetDirectoryId(Dst);
1025 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1026 if (dirId == dstId) {
1027 EndTransaction();
1028 return;
1029 }
1030
1031 CopyInstrument(instrId, instrName, dstId, Dst);
1032 } catch (Exception e) {
1033 EndTransaction();
1034 throw e;
1035 }
1036 EndTransaction();
1037
1038 }
1039
1040 void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {
1041 if (GetInstrumentId(DstDirId, InstrName) != -1) {
1042 String s = toEscapedPath(InstrName);
1043 throw Exception("Cannot copy. Instrument with that name already exists: " + s);
1044 }
1045
1046 if (GetDirectoryId(DstDirId, InstrName) != -1) {
1047 String s = toEscapedPath(InstrName);
1048 throw Exception("Cannot copy. Directory with that name already exists: " + s);
1049 }
1050
1051 DbInstrument i = GetInstrumentInfo(InstrId);
1052 sqlite3_stmt *pStmt = NULL;
1053 std::stringstream sql;
1054 sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,instr_nr,format_family,";
1055 sql << "format_version,instr_size,description,is_drum,product,artists,keywords) ";
1056 sql << "VALUES (" << DstDirId << ",?,?," << i.InstrNr << ",?,?," << i.Size << ",?,";
1057 sql << i.IsDrum << ",?,?,?)";
1058
1059 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1060 if (res != SQLITE_OK) {
1061 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1062 }
1063
1064 String s = toDbName(InstrName);
1065 BindTextParam(pStmt, 1, s);
1066 BindTextParam(pStmt, 2, i.InstrFile);
1067 BindTextParam(pStmt, 3, i.FormatFamily);
1068 BindTextParam(pStmt, 4, i.FormatVersion);
1069 BindTextParam(pStmt, 5, i.Description);
1070 BindTextParam(pStmt, 6, i.Product);
1071 BindTextParam(pStmt, 7, i.Artists);
1072 BindTextParam(pStmt, 8, i.Keywords);
1073
1074 res = sqlite3_step(pStmt);
1075 if(res != SQLITE_DONE) {
1076 sqlite3_finalize(pStmt);
1077 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1078 }
1079
1080 sqlite3_finalize(pStmt);
1081 FireInstrumentCountChanged(DstDir);
1082 }
1083
1084 void InstrumentsDb::SetInstrumentDescription(String Instr, String Desc) {
1085 dmsg(2,("InstrumentsDb: SetInstrumentDescription(Instr=%s,Desc=%s)\n", Instr.c_str(), Desc.c_str()));
1086
1087 BeginTransaction();
1088 try {
1089 int id = GetInstrumentId(Instr);
1090 if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1091
1092 std::stringstream sql;
1093 sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";
1094 sql << "WHERE instr_id="<< id;
1095
1096 ExecSql(sql.str(), Desc);
1097 } catch (Exception e) {
1098 EndTransaction();
1099 throw e;
1100 }
1101 EndTransaction();
1102 FireInstrumentInfoChanged(Instr);
1103 }
1104
1105 void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index, ScanProgress* pProgress) {
1106 dmsg(2,("InstrumentsDb: AddInstrumentsFromFile(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));
1107
1108 if(File.length() < 4) return;
1109
1110 try {
1111 if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {
1112 if (pProgress != NULL) {
1113 pProgress->SetStatus(0);
1114 pProgress->CurrentFile = File;
1115 }
1116
1117 AddGigInstruments(DbDir, File, Index, pProgress);
1118
1119 if (pProgress != NULL) {
1120 pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);
1121 }
1122 }
1123 } catch(Exception e) {
1124 e.PrintMessage();
1125 }
1126 }
1127
1128 void InstrumentsDb::AddGigInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
1129 dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
1130 int dirId = GetDirectoryId(DbDir);
1131 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
1132
1133 File f = File(FilePath);
1134 if (!f.Exist()) {
1135 std::stringstream ss;
1136 ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
1137 throw Exception(ss.str());
1138 }
1139
1140 if (!f.IsFile()) {
1141 std::stringstream ss;
1142 ss << "`" << FilePath << "` is not a regular file";
1143 throw Exception(ss.str());
1144 }
1145
1146 RIFF::File* riff = NULL;
1147 gig::File* gig = NULL;
1148 try {
1149 riff = new RIFF::File(FilePath);
1150 gig::File* gig = new gig::File(riff);
1151 gig->SetAutoLoad(false); // avoid time consuming samples scanning
1152
1153 std::stringstream sql;
1154 sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";
1155 sql << "instr_nr,format_family,format_version,instr_size,";
1156 sql << "description,is_drum,product,artists,keywords) VALUES (";
1157 sql << dirId << ",?,?,?,'GIG',?," << f.GetSize() << ",?,?,?,?,?)";
1158
1159 sqlite3_stmt* pStmt = NULL;
1160
1161 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1162 if (res != SQLITE_OK) {
1163 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1164 }
1165
1166 String s = toEscapedFsPath(FilePath);
1167 BindTextParam(pStmt, 2, s);
1168 String ver = "";
1169 if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);
1170 BindTextParam(pStmt, 4, ver);
1171
1172 if (Index == -1) {
1173 int instrIndex = 0;
1174 if (pProgress != NULL) gig->GetInstrument(0, &(pProgress->GigFileProgress)); // TODO: this workaround should be fixed
1175 gig::Instrument* pInstrument = gig->GetFirstInstrument();
1176 while (pInstrument) {
1177 BindTextParam(pStmt, 7, gig->pInfo->Product);
1178 BindTextParam(pStmt, 8, gig->pInfo->Artists);
1179 BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1180 AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, instrIndex);
1181
1182 instrIndex++;
1183 pInstrument = gig->GetNextInstrument();
1184 }
1185 } else {
1186 gig::Instrument* pInstrument;
1187 if (pProgress == NULL) pInstrument = gig->GetInstrument(Index);
1188 else pInstrument = gig->GetInstrument(Index, &(pProgress->GigFileProgress));
1189 if (pInstrument != NULL) {
1190 BindTextParam(pStmt, 7, gig->pInfo->Product);
1191 BindTextParam(pStmt, 8, gig->pInfo->Artists);
1192 BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1193 AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, Index);
1194 }
1195 }
1196
1197 sqlite3_finalize(pStmt);
1198 delete gig;
1199 delete riff;
1200 } catch (RIFF::Exception e) {
1201 if (gig != NULL) delete gig;
1202 if (riff != NULL) delete riff;
1203 std::stringstream ss;
1204 ss << "Failed to scan `" << FilePath << "`: " << e.Message;
1205
1206 throw Exception(ss.str());
1207 } catch (Exception e) {
1208 if (gig != NULL) delete gig;
1209 if (riff != NULL) delete riff;
1210 throw e;
1211 } catch (...) {
1212 if (gig != NULL) delete gig;
1213 if (riff != NULL) delete riff;
1214 throw Exception("Failed to scan `" + FilePath + "`");
1215 }
1216 }
1217
1218 void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {
1219 dmsg(2,("InstrumentsDb: AddGigInstrument(DbDir=%s,DirId=%d,File=%s,Index=%d)\n", DbDir.c_str(), DirId, File.c_str(), Index));
1220 String name = pInstrument->pInfo->Name;
1221 if (name == "") return;
1222 name = GetUniqueInstrumentName(DirId, name);
1223
1224 std::stringstream sql2;
1225 sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";
1226 sql2 << "instr_nr=" << Index;
1227 String s = toEscapedFsPath(File);
1228 if (ExecSqlInt(sql2.str(), s) > 0) return;
1229
1230 BindTextParam(pStmt, 1, name);
1231 BindIntParam(pStmt, 3, Index);
1232
1233 BindTextParam(pStmt, 5, pInstrument->pInfo->Comments);
1234 BindIntParam(pStmt, 6, pInstrument->IsDrum);
1235
1236 if (!pInstrument->pInfo->Product.empty()) {
1237 BindTextParam(pStmt, 7, pInstrument->pInfo->Product);
1238 }
1239 if (!pInstrument->pInfo->Artists.empty()) {
1240 BindTextParam(pStmt, 8, pInstrument->pInfo->Artists);
1241 }
1242
1243 if (!pInstrument->pInfo->Keywords.empty()) {
1244 BindTextParam(pStmt, 9, pInstrument->pInfo->Keywords);
1245 }
1246
1247 int res = sqlite3_step(pStmt);
1248 if(res != SQLITE_DONE) {
1249 sqlite3_finalize(pStmt);
1250 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1251 }
1252
1253 res = sqlite3_reset(pStmt);
1254 FireInstrumentCountChanged(DbDir);
1255 }
1256
1257 void InstrumentsDb::DirectoryTreeWalk(String AbstractPath, DirectoryHandler* pHandler) {
1258 int DirId = GetDirectoryId(AbstractPath);
1259 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(AbstractPath));
1260 DirectoryTreeWalk(pHandler, AbstractPath, DirId, 0);
1261 }
1262
1263 void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String AbstractPath, int DirId, int Level) {
1264 if(Level == 1000) throw Exception("Possible infinite loop detected");
1265 pHandler->ProcessDirectory(AbstractPath, DirId);
1266
1267 String s;
1268 StringListPtr pDirs = GetDirectories(DirId);
1269 for(int i = 0; i < pDirs->size(); i++) {
1270 if (AbstractPath.length() == 1 && AbstractPath.at(0) == '/') {
1271 s = "/" + pDirs->at(i);
1272 } else {
1273 s = AbstractPath + "/" + pDirs->at(i);
1274 }
1275 DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);
1276 }
1277 }
1278
1279 StringListPtr InstrumentsDb::FindDirectories(String Dir, SearchQuery* pQuery, bool Recursive) {
1280 dmsg(2,("InstrumentsDb: FindDirectories(Dir=%s)\n", Dir.c_str()));
1281 DirectoryFinder directoryFinder(pQuery);
1282
1283 BeginTransaction();
1284 try {
1285 int DirId = GetDirectoryId(Dir);
1286 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1287
1288 if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);
1289 else directoryFinder.ProcessDirectory(Dir, DirId);
1290 } catch (Exception e) {
1291 EndTransaction();
1292 throw e;
1293 }
1294 EndTransaction();
1295
1296 return directoryFinder.GetDirectories();
1297 }
1298
1299 StringListPtr InstrumentsDb::FindInstruments(String Dir, SearchQuery* pQuery, bool Recursive) {
1300 dmsg(2,("InstrumentsDb: FindInstruments(Dir=%s)\n", Dir.c_str()));
1301 InstrumentFinder instrumentFinder(pQuery);
1302
1303 BeginTransaction();
1304 try {
1305 int DirId = GetDirectoryId(Dir);
1306 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1307
1308 if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);
1309 else instrumentFinder.ProcessDirectory(Dir, DirId);
1310 } catch (Exception e) {
1311 EndTransaction();
1312 throw e;
1313 }
1314 EndTransaction();
1315
1316 return instrumentFinder.GetInstruments();
1317 }
1318
1319 void InstrumentsDb::BeginTransaction() {
1320 dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));
1321 DbInstrumentsMutex.Lock();
1322 if (InTransaction) return;
1323
1324 if(db == NULL) return;
1325 sqlite3_stmt *pStmt = NULL;
1326
1327 InTransaction = true;
1328 int res = sqlite3_prepare(db, "BEGIN TRANSACTION", -1, &pStmt, NULL);
1329 if (res != SQLITE_OK) {
1330 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1331 return;
1332 }
1333
1334 res = sqlite3_step(pStmt);
1335 if(res != SQLITE_DONE) {
1336 sqlite3_finalize(pStmt);
1337 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1338 return;
1339 }
1340
1341 sqlite3_finalize(pStmt);
1342 }
1343
1344 void InstrumentsDb::EndTransaction() {
1345 dmsg(2,("InstrumentsDb: EndTransaction(InTransaction=%d)\n", InTransaction));
1346 if (!InTransaction) {
1347 DbInstrumentsMutex.Unlock();
1348 return;
1349 }
1350 InTransaction = false;
1351
1352 if(db == NULL) {
1353 DbInstrumentsMutex.Unlock();
1354 return;
1355 }
1356 sqlite3_stmt *pStmt = NULL;
1357
1358 int res = sqlite3_prepare(db, "END TRANSACTION", -1, &pStmt, NULL);
1359 if (res != SQLITE_OK) {
1360 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1361 DbInstrumentsMutex.Unlock();
1362 return;
1363 }
1364
1365 res = sqlite3_step(pStmt);
1366 if(res != SQLITE_DONE) {
1367 sqlite3_finalize(pStmt);
1368 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1369 DbInstrumentsMutex.Unlock();
1370 return;
1371 }
1372
1373 sqlite3_finalize(pStmt);
1374 DbInstrumentsMutex.Unlock();
1375 }
1376
1377 void InstrumentsDb::ExecSql(String Sql) {
1378 dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));
1379 sqlite3_stmt *pStmt = NULL;
1380
1381 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1382 if (res != SQLITE_OK) {
1383 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1384 }
1385
1386 res = sqlite3_step(pStmt);
1387 if(res != SQLITE_DONE) {
1388 sqlite3_finalize(pStmt);
1389 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1390 }
1391
1392 sqlite3_finalize(pStmt);
1393 }
1394
1395 void InstrumentsDb::ExecSql(String Sql, String Param) {
1396 dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1397 sqlite3_stmt *pStmt = NULL;
1398
1399 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1400 if (res != SQLITE_OK) {
1401 sqlite3_finalize(pStmt);
1402 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1403 }
1404
1405 BindTextParam(pStmt, 1, Param);
1406
1407 res = sqlite3_step(pStmt);
1408 if (res != SQLITE_DONE) {
1409 sqlite3_finalize(pStmt);
1410 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1411 }
1412
1413 sqlite3_finalize(pStmt);
1414 }
1415
1416 int InstrumentsDb::ExecSqlInt(String Sql) {
1417 dmsg(2,("InstrumentsDb: ExecSqlInt(Sql=%s)\n", Sql.c_str()));
1418 sqlite3_stmt *pStmt = NULL;
1419
1420 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1421 if (res != SQLITE_OK) {
1422 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1423 }
1424
1425 int i = -1;
1426 res = sqlite3_step(pStmt);
1427 if(res == SQLITE_ROW) {
1428 i = sqlite3_column_int(pStmt, 0);
1429 } else if (res != SQLITE_DONE) {
1430 sqlite3_finalize(pStmt);
1431 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1432 }
1433
1434 sqlite3_finalize(pStmt);
1435
1436 return i;
1437 }
1438
1439 int InstrumentsDb::ExecSqlInt(String Sql, String Param) {
1440 dmsg(2,("InstrumentsDb: ExecSqlInt(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1441 sqlite3_stmt *pStmt = NULL;
1442
1443 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1444 if (res != SQLITE_OK) {
1445 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1446 }
1447
1448 BindTextParam(pStmt, 1, Param);
1449
1450 int i = -1;
1451 res = sqlite3_step(pStmt);
1452 if(res == SQLITE_ROW) {
1453 i = sqlite3_column_int(pStmt, 0);
1454 } else if (res != SQLITE_DONE) {
1455 sqlite3_finalize(pStmt);
1456 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1457 }
1458
1459 sqlite3_finalize(pStmt);
1460 return i;
1461 }
1462
1463 String InstrumentsDb::ExecSqlString(String Sql) {
1464 dmsg(2,("InstrumentsDb: ExecSqlString(Sql=%s)\n", Sql.c_str()));
1465 sqlite3_stmt *pStmt = NULL;
1466
1467 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1468 if (res != SQLITE_OK) {
1469 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1470 }
1471
1472 String s;
1473 res = sqlite3_step(pStmt);
1474 if(res == SQLITE_ROW) {
1475 s = ToString(sqlite3_column_text(pStmt, 0));
1476 } else if (res != SQLITE_DONE) {
1477 sqlite3_finalize(pStmt);
1478 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1479 }
1480
1481 sqlite3_finalize(pStmt);
1482
1483 return s;
1484 }
1485
1486 IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {
1487 IntListPtr intList(new std::vector<int>);
1488
1489 sqlite3_stmt *pStmt = NULL;
1490
1491 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1492 if (res != SQLITE_OK) {
1493 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1494 }
1495
1496 res = sqlite3_step(pStmt);
1497 while(res == SQLITE_ROW) {
1498 intList->push_back(sqlite3_column_int(pStmt, 0));
1499 res = sqlite3_step(pStmt);
1500 }
1501
1502 if (res != SQLITE_DONE) {
1503 sqlite3_finalize(pStmt);
1504 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1505 }
1506
1507 sqlite3_finalize(pStmt);
1508
1509 return intList;
1510 }
1511
1512 StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {
1513 StringListPtr stringList(new std::vector<String>);
1514
1515 sqlite3_stmt *pStmt = NULL;
1516
1517 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1518 if (res != SQLITE_OK) {
1519 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1520 }
1521
1522 res = sqlite3_step(pStmt);
1523 while(res == SQLITE_ROW) {
1524 stringList->push_back(ToString(sqlite3_column_text(pStmt, 0)));
1525 res = sqlite3_step(pStmt);
1526 }
1527
1528 if (res != SQLITE_DONE) {
1529 sqlite3_finalize(pStmt);
1530 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1531 }
1532
1533 sqlite3_finalize(pStmt);
1534
1535 return stringList;
1536 }
1537
1538 void InstrumentsDb::BindTextParam(sqlite3_stmt* pStmt, int Index, String Text) {
1539 if (pStmt == NULL) return;
1540 int res = sqlite3_bind_text(pStmt, Index, Text.c_str(), -1, SQLITE_STATIC);
1541 if (res != SQLITE_OK) {
1542 sqlite3_finalize(pStmt);
1543 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1544 }
1545 }
1546
1547 void InstrumentsDb::BindIntParam(sqlite3_stmt* pStmt, int Index, int Param) {
1548 if (pStmt == NULL) return;
1549 int res = sqlite3_bind_int(pStmt, Index, Param);
1550 if (res != SQLITE_OK) {
1551 sqlite3_finalize(pStmt);
1552 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1553 }
1554 }
1555
1556 void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {
1557 if (argc != 2) return;
1558
1559 String pattern = ToString(sqlite3_value_text(ppValue[0]));
1560 String str = ToString(sqlite3_value_text(ppValue[1]));
1561
1562 if(!fnmatch(pattern.c_str(), str.c_str(), FNM_CASEFOLD)) {
1563 sqlite3_result_int(pContext, 1);
1564 }
1565 }
1566
1567 String InstrumentsDb::GetDirectoryPath(String File) {
1568 if (File.empty()) return String("");
1569 if (File.at(0) != '/') String("");
1570 if (File.length() == 1) return File;
1571 if (File.at(File.length() - 1) == '/') return File.substr(0, File.length() - 1);
1572 int i = File.rfind('/', File.length() - 1);
1573 if(i == std::string::npos) return String("");
1574 if(i == 0) return String("/");
1575 return File.substr(0, i);
1576 }
1577
1578 String InstrumentsDb::GetFileName(String Path) {
1579 if (Path.length() < 2) return String("");
1580 if (Path.at(0) != '/') String("");
1581 if (Path.at(Path.length() - 1) == '/') return String("");
1582 int i = Path.rfind('/', Path.length() - 1);
1583 return Path.substr(i + 1);
1584 }
1585
1586 void InstrumentsDb::CheckPathName(String Path) {
1587 if (Path.empty()) return;
1588
1589 int i = 0, j = Path.find('/', i);
1590
1591 while(j != std::string::npos) {
1592 if (j + 1 >= Path.length()) return;
1593 if (Path.at(j + 1) == '/') throw Exception("Invalid path name: " + Path);
1594
1595 i = j + 1;
1596 j = Path.find('/', i);
1597 }
1598 }
1599
1600 String InstrumentsDb::GetParentDirectory(String Dir) {
1601 if (Dir.length() < 2) return String("");
1602 if (Dir.at(0) != '/') String("");
1603 int i = Dir.rfind('/', Dir.length() - 2);
1604 if (i == 0) return "/";
1605 return Dir.substr(0, i);
1606 }
1607
1608 void InstrumentsDb::Format() {
1609 DbInstrumentsMutex.Lock();
1610 if (db != NULL) {
1611 sqlite3_close(db);
1612 db = NULL;
1613 }
1614
1615 if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
1616 String bkp = DbFile + ".bkp";
1617 remove(bkp.c_str());
1618 if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) {
1619 DbInstrumentsMutex.Unlock();
1620 throw Exception(String("Failed to backup database: ") + strerror(errno));
1621 }
1622
1623 String f = DbFile;
1624 DbFile = "";
1625 try { CreateInstrumentsDb(f); }
1626 catch(Exception e) {
1627 DbInstrumentsMutex.Unlock();
1628 throw e;
1629 }
1630 DbInstrumentsMutex.Unlock();
1631
1632 FireDirectoryCountChanged("/");
1633 FireInstrumentCountChanged("/");
1634 }
1635
1636 void InstrumentsDb::CheckFileName(String File) {
1637 if (File.empty()) throw Exception("Invalid file name: " + File);
1638 }
1639
1640 String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {
1641 dmsg(2,("InstrumentsDb: GetUniqueInstrumentName(DirId=%d,Name=%s)\n", DirId, Name.c_str()));
1642
1643 if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;
1644 std::stringstream ss;
1645 for(int i = 2; i < 1001; i++) {
1646 ss.str("");
1647 ss << Name << '[' << i << ']';
1648 if (GetInstrumentId(DirId, ss.str()) == -1 && GetInstrumentId(DirId, ss.str()) == -1) {
1649 return ss.str();
1650 }
1651 }
1652
1653 throw Exception("Unable to find an unique name: " + Name);
1654 }
1655
1656 String InstrumentsDb::toDbName(String AbstractName) {
1657 for (int i = 0; i < AbstractName.length(); i++) {
1658 if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1659 }
1660 return AbstractName;
1661 }
1662
1663 String InstrumentsDb::toEscapedPath(String AbstractName) {
1664 for (int i = 0; i < AbstractName.length(); i++) {
1665 if (AbstractName.at(i) == '\0') AbstractName.replace(i++, 1, "\\x2f");
1666 else if (AbstractName.at(i) == '\\') AbstractName.replace(i++, 1, "\\\\");
1667 else if (AbstractName.at(i) == '\'') AbstractName.replace(i++, 1, "\\'");
1668 else if (AbstractName.at(i) == '"') AbstractName.replace(i++, 1, "\\\"");
1669 else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1670 else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1671 }
1672 return AbstractName;
1673 }
1674
1675 String InstrumentsDb::toEscapedText(String text) {
1676 for (int i = 0; i < text.length(); i++) {
1677 if (text.at(i) == '\\') text.replace(i++, 1, "\\\\");
1678 else if (text.at(i) == '\'') text.replace(i++, 1, "\\'");
1679 else if (text.at(i) == '"') text.replace(i++, 1, "\\\"");
1680 else if (text.at(i) == '\r') text.replace(i++, 1, "\\r");
1681 else if (text.at(i) == '\n') text.replace(i++, 1, "\\n");
1682 }
1683 return text;
1684 }
1685
1686 String InstrumentsDb::toEscapedFsPath(String FsPath) {
1687 return toEscapedText(FsPath);
1688 }
1689
1690 String InstrumentsDb::toAbstractName(String DbName) {
1691 for (int i = 0; i < DbName.length(); i++) {
1692 if (DbName.at(i) == '/') DbName.at(i) = '\0';
1693 }
1694 return DbName;
1695 }
1696
1697 void InstrumentsDb::FireDirectoryCountChanged(String Dir) {
1698 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1699 llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);
1700 }
1701 }
1702
1703 void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {
1704 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1705 llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);
1706 }
1707 }
1708
1709 void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {
1710 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1711 llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);
1712 }
1713 }
1714
1715 void InstrumentsDb::FireInstrumentCountChanged(String Dir) {
1716 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1717 llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);
1718 }
1719 }
1720
1721 void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {
1722 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1723 llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);
1724 }
1725 }
1726
1727 void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {
1728 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1729 llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);
1730 }
1731 }
1732
1733 void InstrumentsDb::FireJobStatusChanged(int JobId) {
1734 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1735 llInstrumentsDbListeners.GetListener(i)->JobStatusChanged(JobId);
1736 }
1737 }
1738
1739 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC