1 /* DatabaseConnector.cpp */
2
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4 *
5 * This file is part of sayonara player
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General License for more details.
16
17 * You should have received a copy of the GNU General License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "Database/Connector.h"
22 #include "Database/Query.h"
23 #include "Database/LibraryDatabase.h"
24 #include "Database/Bookmarks.h"
25 #include "Database/Equalizer.h"
26 #include "Database/Playlist.h"
27 #include "Database/Podcasts.h"
28 #include "Database/Streams.h"
29 #include "Database/Session.h"
30 #include "Database/Settings.h"
31 #include "Database/Shortcuts.h"
32 #include "Database/VisualStyles.h"
33 #include "Database/CoverConnector.h"
34
35 #include "Utils/MetaData/Album.h"
36 #include "Utils/MetaData/Artist.h"
37 #include "Utils/MetaData/MetaDataList.h"
38 #include "Utils/Logger/Logger.h"
39 #include "Utils/Utils.h"
40 #include "Utils/StandardPaths.h"
41 #include "Utils/Algorithm.h"
42 #include "Utils/RawShortcutMap.h"
43
44 #include <QFileInfo>
45 #include <QDateTime>
46 #include <QTime>
47
48 #include <tuple>
49 #include <algorithm>
50
51 using DB::Connector;
52 using DB::LibraryDatabase;
53
54 using LibDbIterator=DB::LibraryDatabases::Iterator;
55 namespace Algorithm=Util::Algorithm;
56
highestDatabaseVersion()57 int Connector::highestDatabaseVersion()
58 {
59 return 29;
60 }
61
62 struct Connector::Private
63 {
64 QString connection_name;
65 QString defaultSourcedirectory;
66 QString defaultTargetdirectory;
67 QString defaultDatabsefilename;
68
69 DB::Bookmarks* bookmarkConnector=nullptr;
70 DB::Equalizer* equalizerConnector=nullptr;
71 DB::Playlist* playlistConnector=nullptr;
72 DB::Podcasts* podcastConnector=nullptr;
73 DB::Streams* streamConnector=nullptr;
74 DB::VisualStyles* visualStyleConnector=nullptr;
75 DB::Session* sessionConnector=nullptr;
76 DB::Settings* settingsConnector=nullptr;
77 DB::Shortcuts* shortcutConnector=nullptr;
78 DB::Covers* coverConnector=nullptr;
79 DB::Library* libraryConnector=nullptr;
80
81 QList<LibraryDatabase*> libraryDbs;
82 LibraryDatabase* genericLibraryDatabase=nullptr;
83
84 int oldDbVersion;
85
86
PrivateConnector::Private87 Private() : oldDbVersion(0) {}
~PrivateConnector::Private88 ~Private()
89 {
90 if(bookmarkConnector){
91 delete bookmarkConnector; bookmarkConnector = nullptr;
92 }
93
94 if(equalizerConnector){
95 delete equalizerConnector; equalizerConnector = nullptr;
96 }
97
98 if(podcastConnector){
99 delete podcastConnector; podcastConnector = nullptr;
100 }
101
102 if(streamConnector){
103 delete streamConnector; streamConnector = nullptr;
104 }
105
106 if(visualStyleConnector){
107 delete visualStyleConnector; visualStyleConnector = nullptr;
108 }
109
110 if(settingsConnector){
111 delete settingsConnector; settingsConnector = nullptr;
112 }
113
114 if(shortcutConnector){
115 delete shortcutConnector; shortcutConnector = nullptr;
116 }
117
118 if(coverConnector){
119 delete coverConnector; coverConnector = nullptr;
120 }
121
122 if(libraryConnector){
123 delete libraryConnector; libraryConnector = nullptr;
124 }
125
126 if(sessionConnector){
127 delete sessionConnector; sessionConnector = nullptr;
128 }
129 }
130 };
131
132 class DatabaseNotCreatedException : public std::exception
133 {
134 public:
135 const char* what() const noexcept;
136 };
137
138
Connector(const QString & sourceDirectory,const QString & targetDirectory,const QString & databseFilename)139 Connector::Connector(const QString& sourceDirectory, const QString& targetDirectory, const QString& databseFilename) :
140 DB::Base(0, sourceDirectory, targetDirectory, databseFilename, nullptr)
141 {
142 m = Pimpl::make<Private>();
143
144 if(!this->isInitialized()){
145 throw DatabaseNotCreatedException();
146 }
147
148 else
149 {
150 m->genericLibraryDatabase = new LibraryDatabase(connectionName(), databaseId(), -1);
151 m->libraryDbs << m->genericLibraryDatabase;
152
153 applyFixes();
154 }
155 }
156
157 Connector::~Connector() = default;
158
instance()159 DB::Connector* Connector::instance()
160 {
161 return instance_custom(QString(), QString(), QString());
162 }
163
instance_custom(QString sourceDirectory,QString targetDirectory,QString databseFilename)164 DB::Connector* Connector::instance_custom(QString sourceDirectory, QString targetDirectory, QString databseFilename)
165 {
166 if(sourceDirectory.isEmpty()) {
167 sourceDirectory = ":/Database";
168 }
169
170 if(targetDirectory.isEmpty()) {
171 targetDirectory = Util::xdgConfigPath();
172 }
173
174 if(databseFilename.isEmpty()) {
175 databseFilename = "player.db";
176 }
177
178 static Connector connector(sourceDirectory, targetDirectory, databseFilename);
179 return &connector;
180 }
181
182
updateAlbumCissearchFix()183 bool Connector::updateAlbumCissearchFix()
184 {
185 #ifdef DEBUG_DB
186 sp_log(Log::Debug, this) << Q_FUNC_INFO;
187 #endif
188
189 AlbumList albums;
190
191 LibraryDatabase* lib_db = libraryDatabase(-1, 0);
192 lib_db->getAllAlbums(albums, true);
193
194 for(const Album& album : albums)
195 {
196 QString str = "UPDATE albums SET cissearch=:cissearch WHERE albumID=:id;";
197 Query q(this);
198 q.prepare(str);
199 q.bindValue(":cissearch", Util::convertNotNull(album.name().toLower()));
200 q.bindValue(":id", album.id());
201
202 if(!q.exec()){
203 q.showError("Cannot update album cissearch");
204 }
205 }
206
207 return true;
208 }
209
updateArtistCissearchFix()210 bool Connector::updateArtistCissearchFix()
211 {
212 ArtistList artists;
213 LibraryDatabase* lib_db = libraryDatabase(-1, 0);
214 lib_db->getAllArtists(artists, true);
215 for(const Artist& artist : artists)
216 {
217 QString str =
218 "UPDATE artists SET cissearch=:cissearch WHERE artistID=:id;";
219
220 Query q(this);
221 q.prepare(str);
222 q.bindValue(":cissearch", Util::convertNotNull(artist.name().toLower()));
223 q.bindValue(":id", artist.id());
224
225 if(!q.exec()){
226 q.showError("Cannot update artist cissearch");
227 }
228 }
229
230 return true;
231 }
232
updateTrackCissearchFix()233 bool Connector::updateTrackCissearchFix()
234 {
235 MetaDataList v_md;
236 LibraryDatabase* lib_db = libraryDatabase(-1, 0);
237 lib_db->getAllTracks(v_md);
238 for(const MetaData& md : v_md) {
239 lib_db->updateTrack(md);
240 }
241
242 return true;
243 }
244
updateLostArtists()245 bool Connector::updateLostArtists()
246 {
247 LibraryDatabase* lib_db = libraryDatabase(-1, 0);
248 if(!lib_db){
249 spLog(Log::Error, this) << "Cannot find Library";
250 return false;
251 }
252
253 ArtistId id = lib_db->insertArtistIntoDatabase(QString());
254
255 const QStringList queries {
256 QString("UPDATE tracks SET artistID=:artistID WHERE artistID IN (SELECT artistID FROM artists WHERE name IS NULL);"),
257 QString("UPDATE tracks SET artistID=:artistID WHERE artistID NOT IN (SELECT artistID FROM artists);"),
258 QString("UPDATE tracks SET albumArtistID=:artistID WHERE albumArtistID IN (SELECT artistID FROM artists WHERE name IS NULL);"),
259 QString("UPDATE tracks SET albumArtistID=:artistID WHERE albumArtistID NOT IN (SELECT artistID FROM artists);"),
260 QString("DELETE FROM artists WHERE name IS NULL;")
261 };
262
263 this->transaction();
264 for(const QString& query : queries)
265 {
266 DB::Query q(this);
267 q.prepare(query);
268 q.bindValue(":artistID", id);
269 bool success = q.exec();
270 if(!success){
271 this->rollback();
272 return false;
273 }
274 }
275
276 this->commit();
277 return true;
278 }
279
updateLostAlbums()280 bool Connector::updateLostAlbums()
281 {
282 LibraryDatabase* libraryDatabase = this->libraryDatabase(-1, 0);
283 if(!libraryDatabase) {
284 spLog(Log::Error, this) << "Cannot find Library database";
285 return false;
286 }
287
288 return libraryDatabase->fixEmptyAlbums();
289 }
290
oldDatabaseVersion() const291 int Connector::oldDatabaseVersion() const
292 {
293 return m->oldDbVersion;
294 }
295
applyFixes()296 bool Connector::applyFixes()
297 {
298 QString versionString;
299 int version;
300 bool success;
301 const int LatestVersion = highestDatabaseVersion();
302
303 success = settingsConnector()->loadSetting("version", versionString);
304 version = versionString.toInt(&success);
305 m->oldDbVersion = version;
306
307 spLog(Log::Info, this)
308 << "Database Version: " << version << ". "
309 << "Latest Version: " << LatestVersion;
310
311 if(version == LatestVersion) {
312 spLog(Log::Info, this) << "No need to update db";
313 return true;
314 }
315
316 else if(!success){
317 spLog(Log::Warning, this) << "Cannot get database version";
318 }
319
320 settingsConnector()->loadSettings();
321
322 spLog(Log::Info, this) << "Apply fixes";
323
324 if(version < 1)
325 {
326 checkAndInsertColumn("playlisttotracks", "position", "INTEGER");
327 checkAndInsertColumn("playlisttotracks", "filepath", "VARCHAR(512)");
328 checkAndInsertColumn("tracks", "genre", "VARCHAR(1024)");
329
330 QString create_savedstreams = QString("CREATE TABLE savedstreams ") +
331 "( " +
332 " name VARCHAR(255) PRIMARY KEY, " +
333 " url VARCHAR(255) " +
334 ");";
335
336 checkAndCreateTable("savedstreams", create_savedstreams);
337
338
339 QString create_savedpodcasts = QString("CREATE TABLE savedpodcasts ") +
340 "( " +
341 " name VARCHAR(255) PRIMARY KEY, " +
342 " url VARCHAR(255) " +
343 ");";
344
345 checkAndCreateTable("savedpodcasts", create_savedpodcasts);
346 }
347
348 if(version < 3)
349 {
350 db().transaction();
351
352 bool success = true;
353 success &= checkAndInsertColumn("tracks", "cissearch", "VARCHAR(512)");
354 success &= checkAndInsertColumn("albums", "cissearch", "VARCHAR(512)");
355 success &= checkAndInsertColumn("artists", "cissearch", "VARCHAR(512)");
356
357 Q_UNUSED(success)
358
359 updateAlbumCissearchFix();
360 updateArtistCissearchFix();
361 updateTrackCissearchFix();
362
363 db().commit();
364 }
365
366
367 if(version == 3) {
368 checkAndDropTable("VisualStyles");
369 }
370
371 if(version < 4) {
372 QString create_vis_styles = QString("CREATE TABLE VisualStyles ") +
373 "( " +
374 " name VARCHAR(255) PRIMARY KEY, " +
375 " col1 VARCHAR(20), "
376 " col2 VARCHAR(20), "
377 " col3 VARCHAR(20), "
378 " col4 VARCHAR(20), "
379 " nBinsSpectrum INTEGER, "
380 " rectHeightSpectrum INTEGER, "
381 " fadingStepsSpectrum INTEGER, "
382 " horSpacingSpectrum INTEGER, "
383 " vertSpacingSpectrum INTEGER, "
384 " rectWidthLevel INTEGER, "
385 " rectHeightLevel INTEGER, "
386 " horSpacingLevel INTEGER, "
387 " verSpacingLevel INTEGER, "
388 " fadingStepsLevel INTEGER "
389 ");";
390
391 bool success = checkAndCreateTable("VisualStyles", create_vis_styles);
392 if(success) settingsConnector()->storeSetting("version", 4);
393 }
394
395 if(version < 5) {
396 bool success = checkAndInsertColumn("tracks", "rating", "integer");
397 if(success) settingsConnector()->storeSetting("version", 5);
398 }
399
400 if(version < 6) {
401 QString create_savedbookmarks = QString("CREATE TABLE savedbookmarks ") +
402 "( " +
403 " trackid INTEGER, " +
404 " name VARCHAR(255), " +
405 " timeidx INTEGER, " +
406 " PRIMARY KEY (trackid, timeidx), " +
407 " FOREIGN KEY (trackid) REFERENCES tracks(trackid) " +
408 ");";
409
410 bool success = checkAndCreateTable("savedbookmarks", create_savedbookmarks);
411 if(success) settingsConnector()->storeSetting("version", 6);
412 }
413
414 if(version < 7) {
415 bool success = checkAndInsertColumn("albums", "rating", "integer");
416 if(success) settingsConnector()->storeSetting("version", 7);
417 }
418
419 if(version < 9) {
420 bool success = checkAndInsertColumn("playlists", "temporary", "integer");
421
422 if(success) {
423 Query q(this);
424 QString querytext = "UPDATE playlists SET temporary=0;";
425 q.prepare(querytext);
426 if(q.exec()){
427 settingsConnector()->storeSetting("version", 9);
428 };
429 }
430 }
431
432 if(version < 10){
433 bool success = checkAndInsertColumn("playlisttotracks", "db_id", "integer");
434 if(success) {
435 Query q(this);
436 Query q_index(this);
437 QString querytext = "UPDATE playlisttotracks SET db_id = (CASE WHEN trackid > 0 THEN 0 ELSE -1 END)";
438 QString index_query = "CREATE INDEX album_search ON albums(cissearch, albumID);"
439 "CREATE INDEX artist_search ON artists(cissearch, artistID);"
440 "CREATE INDEX track_search ON tracks(cissearch, trackID);";
441
442 q.prepare(querytext);
443 q_index.prepare(index_query);
444
445 if(q.exec()){
446 settingsConnector()->storeSetting("version", 10);
447 };
448
449 q_index.exec();
450 }
451 }
452
453 if(version < 11)
454 {
455 // look in UpdateDatesThread
456 }
457
458 if(version < 12){
459 QString querytext =
460 "CREATE VIEW album_info_view AS "
461 "SELECT "
462 " albums.albumID as albumID, "
463 " albums.name as name, "
464 " albums.cissearch as cissearch, "
465 " albums.rating as rating, "
466 " COUNT(artists.artistID) as artistCount, "
467 " COUNT(tracks.trackID) as trackCount, "
468 " CASE WHEN COUNT(DISTINCT artists.artistID) > 1 "
469 " THEN 1 "
470 " ELSE 0 "
471 " END as Sampler "
472 "FROM albums, artists, tracks "
473 "WHERE albums.albumID = tracks.albumID "
474 "AND artists.artistID = tracks.artistID "
475 "GROUP BY albums.albumID, albums.name";
476 ;
477
478 Query q(this);
479 q.prepare(querytext);
480
481 if(q.exec()){
482 settingsConnector()->storeSetting("version", 12);
483 }
484 }
485
486 if(version < 13){
487 bool success = checkAndInsertColumn("tracks", "albumArtistID", "integer", "-1");
488
489 Query q(this);
490 q.prepare("UPDATE tracks SET albumArtistID=artistID;");
491 success = success && q.exec();
492
493 if(success){
494 settingsConnector()->storeSetting("version", 13);
495 }
496 }
497
498 if(version < 14){
499 bool success=checkAndInsertColumn("tracks", "libraryID", "integer", "0");
500 Query q(this);
501 q.prepare("UPDATE tracks SET libraryID=0;");
502 success = success && q.exec();
503
504 if(success){
505 settingsConnector()->storeSetting("version", 14);
506 }
507 }
508
509 if(version < 15)
510 {
511 QString create_string =
512 "CREATE TABLE Libraries "
513 "( "
514 " libraryID INTEGER NOT NULL, "
515 " libraryName VARCHAR(128) NOT NULL, "
516 " libraryPath VARCHAR(512) NOT NULL, "
517 " libraryIndex INTEGER NOT NULL,"
518 " PRIMARY KEY (libraryID, libraryPath) "
519 "); ";
520
521 bool success=checkAndCreateTable("Libraries", create_string);
522 if(success)
523 {
524 settingsConnector()->storeSetting("version", 15);
525 }
526 }
527
528 if(version < 16)
529 {
530 bool success = checkAndInsertColumn("tracks", "fileCissearch", "VARCHAR(256)");
531
532 if(success)
533 {
534 settingsConnector()->storeSetting("version", 16);
535
536 MetaDataList v_md;
537 LibraryDatabase* lib_db = new DB::LibraryDatabase(connectionName(), databaseId(), -1);
538 lib_db->getAllTracks(v_md);
539 this->transaction();
540 for(const MetaData& md : v_md) {
541 lib_db->updateTrack(md);
542 }
543 this->commit();
544
545 delete lib_db;
546 }
547 }
548
549 if(version < 17)
550 {
551 bool success = checkAndInsertColumn("tracks", "comment", "VARCHAR(1024)");
552
553 if(success)
554 {
555 settingsConnector()->storeSetting("version", 17);
556 }
557 }
558
559 if(version < 18)
560 {
561 if(updateLostArtists() && updateLostAlbums())
562 {
563 settingsConnector()->storeSetting("version", 18);
564 }
565 }
566
567 if(version < 19)
568 {
569 QString create_string =
570 "CREATE TABLE Shortcuts "
571 "( "
572 " id INTEGER NOT NULL PRIMARY KEY, "
573 " identifier VARCHAR(32) NOT NULL, "
574 " shortcut VARCHAR(32) NOT NULL "
575 "); ";
576
577 bool success = checkAndCreateTable("Shortcuts", create_string);
578 if(success)
579 {
580 QString raw;
581 settingsConnector()->loadSetting("shortcuts", raw);
582
583 RawShortcutMap rsm = RawShortcutMap::fromString(raw);
584 for(const QString& key : rsm.keys())
585 {
586 this->shortcutConnector()->setShortcuts(key, rsm.value(key));
587 }
588
589 settingsConnector()->storeSetting("shortcuts", "<deprecated>");
590 settingsConnector()->storeSetting("version", 19);
591 }
592 }
593
594 if(version < 20)
595 {
596 checkAndDropTable("Covers");
597
598 bool success;
599 {
600 QString create_string =
601 "CREATE TABLE Covers "
602 "("
603 " coverId INTEGER PRIMARY KEY,"
604 " hash VARCHAR(64),"
605 " coverKey VARCHAR(128),"
606 " data BLOB "
607 ");";
608
609 success = checkAndCreateTable("Covers", create_string);
610 }
611
612 {
613 QString create_string =
614 "CREATE TABLE TrackCoverMap "
615 "("
616 " metadataId INTEGER,"
617 " coverId INTEGER,"
618 " PRIMARY KEY(metadataId, coverId),"
619 " FOREIGN KEY(metadataId) REFERENCES Tracks(trackId) ON DELETE CASCADE,"
620 " FOREIGN KEY(coverId) REFERENCES Covers(coverId) ON DELETE CASCADE"
621 ");";
622
623 success &= checkAndCreateTable("TrackCoverMap", create_string);
624 }
625
626 {
627 QString create_string =
628 "CREATE TABLE AlbumCoverMap "
629 "("
630 " albumId INTEGER,"
631 " coverId INTEGER,"
632 " PRIMARY KEY(albumId, coverId),"
633 " FOREIGN KEY(albumId) REFERENCES Albums(albumId) ON DELETE CASCADE,"
634 " FOREIGN KEY(coverId) REFERENCES Covers(coverId) ON DELETE CASCADE"
635 ");";
636
637 success &= checkAndCreateTable("AlbumCoverMap", create_string);
638 }
639
640 if(success)
641 {
642 settingsConnector()->storeSetting("version", 20);
643 }
644 }
645
646 if(version < 21)
647 {
648 checkAndDropTable("Statistics");
649 checkAndDropTable("Lyrics");
650 checkAndDropTable("Genres");
651
652 settingsConnector()->storeSetting("version", 21);
653 }
654
655 if(version < 22)
656 {
657 LibraryDatabase* lib_db = libraryDatabase(-1, 0);
658
659 QMap<QString, AlbumId> albums;
660 QMap<QString, ArtistId> artists;
661
662 MetaDataList tracks;
663 lib_db->getAllTracks(tracks);
664
665 for(auto it=tracks.begin(); it != tracks.end(); it++)
666 {
667 albums[it->album()] = it->albumId();
668 artists[it->artist()] = it->artistId();
669 artists[it->albumArtist()] = it->albumArtistId();
670 }
671
672 for(auto it=tracks.begin(); it != tracks.end(); it++)
673 {
674 AlbumId correct_albumId = albums[it->album()];
675 ArtistId correct_artistId = artists[it->artist()];
676 ArtistId correct_album_artistId = artists[it->albumArtist()];
677 this->transaction();
678 if( (it->albumId() != correct_albumId) ||
679 (it->artistId() != correct_artistId) ||
680 (it->albumArtistId() != correct_album_artistId))
681 {
682 spLog(Log::Info, this) << "Move track " << it->filepath() << "from album " << it->albumId() << " to " << correct_albumId;
683
684 it->setAlbumId(correct_albumId);
685 it->setArtistId(correct_artistId);
686 it->setAlbumArtistId(correct_album_artistId);
687
688 lib_db->updateTrack(*it);
689 }
690 this->commit();
691 }
692
693 {
694 QString query("DELETE FROM albums WHERE albums.albumID NOT IN (SELECT albumId from tracks);");
695 Query q(this);
696 q.prepare(query);
697 q.exec();
698 }
699
700 {
701 QString query("DELETE FROM artists WHERE artists.artistID NOT IN (SELECT artistId from tracks UNION SELECT albumArtistId FROM tracks);");
702 Query q(this);
703 q.prepare(query);
704 q.exec();
705 }
706
707 settingsConnector()->storeSetting("version", 22);
708 }
709
710 if(version < 23)
711 {
712 {
713 checkAndCreateTable(
714 "Sessions",
715 "CREATE TABLE IF NOT EXISTS Sessions "
716 "("
717 " sessionID INTEGER, "
718 " date INTEGER, "
719 " trackID INTEGER DEFAULT -1 REFERENCES Tracks(trackID) ON DELETE SET DEFAULT, "
720 " title VARCHAR(128), "
721 " artist VARCHAR(128), "
722 " album VARCHAR(128)"
723 ");"
724 );
725 }
726
727 {
728 const QString query
729 (
730 "INSERT INTO Sessions "
731 "SELECT session.date, session.date, session.trackID, tracks.title, artists.name, albums.name "
732 "FROM Session session, Tracks tracks, Artists artists, Albums albums "
733 "WHERE tracks.artistID = artists.artistID AND tracks.albumID = albums.albumID AND Session.trackID = tracks.trackID "
734 ";"
735 );
736
737 Query q(this);
738 q.prepare(query);
739 q.exec();
740 }
741
742 {
743 checkAndDropTable("Session");
744 }
745
746 settingsConnector()->storeSetting("version", 23);
747 }
748
749 if(version < 24)
750 {
751
752 {
753 const QString query
754 (
755 "UPDATE Sessions SET sessionID = (sessionID + 20000000000000) WHERE sessionID < 1000000000000;"
756 );
757
758 Query q(this);
759 q.prepare(query);
760 q.exec();
761
762 }
763 {
764 const QString query
765 (
766 "UPDATE Sessions SET date = (date + 20000000000000) WHERE date < 1000000000000;"
767 );
768
769 Query q(this);
770 q.prepare(query);
771 q.exec();
772 }
773
774 settingsConnector()->storeSetting("version", 24);
775 }
776
777 if(version < 25)
778 {
779 checkAndInsertColumn("savedpodcasts", "reversed", "INTEGER", "0");
780 settingsConnector()->storeSetting("version", 25);
781 }
782
783 if(version < 26)
784 {
785 checkAndInsertColumn("playlistToTracks", "stationName", "VARCHAR(255)");
786 checkAndInsertColumn("playlistToTracks", "station", "VARCHAR(512)");
787 checkAndInsertColumn("playlistToTracks", "isRadio", "INTEGER", "0");
788
789 settingsConnector()->storeSetting("version", 26);
790 }
791
792 if(version < 27)
793 {
794 bool success = checkAndInsertColumn("tracks", "genreCissearch", "VARCHAR(512)");
795
796 if(success)
797 {
798 settingsConnector()->storeSetting("version", 27);
799
800 MetaDataList tracks;
801 LibraryDatabase* libraryDb = new DB::LibraryDatabase(connectionName(), databaseId(), -1);
802 libraryDb->getAllTracks(tracks);
803 this->transaction();
804 for(const MetaData& md : tracks) {
805 libraryDb->updateTrack(md);
806 }
807 this->commit();
808
809 delete libraryDb;
810 }
811 }
812
813 if(version < 28)
814 {
815 bool success = checkAndInsertColumn("playlistToTracks", "coverDownloadUrl", "VARCHAR(512)");
816 if(success)
817 {
818 settingsConnector()->storeSetting("version", 28);
819 }
820 }
821
822 if(version < 29)
823 {
824 const auto createStatement = R"create(
825 CREATE TABLE Equalizer
826 (
827 id INTEGER PRIMARY KEY AUTOINCREMENT,
828 name VARCHAR(32),
829 equalizerValues VARCHAR(32),
830 defaultValues VARCHAR(32)
831 );
832 )create";
833
834 bool success = checkAndCreateTable("Equalizer", createStatement);
835 success &= equalizerConnector()->restoreFactoryDefaults();
836
837 if(success) {
838 settingsConnector()->storeSetting("version", 29);
839 }
840 }
841
842 return true;
843 }
844
libraryDatabases() const845 DB::LibraryDatabases Connector::libraryDatabases() const
846 {
847 return m->libraryDbs;
848 }
849
libraryDatabase(LibraryId libraryId,DbId databaseId)850 DB::LibraryDatabase* Connector::libraryDatabase(LibraryId libraryId, DbId databaseId)
851 {
852 LibDbIterator it = Algorithm::find(m->libraryDbs, [&](DB::LibraryDatabase* db){
853 return (db->libraryId() == libraryId && db->databaseId() == databaseId);
854 });
855
856 if(it == m->libraryDbs.end())
857 {
858 spLog(Log::Warning, this) << "Could not find Library:"
859 " DB ID = " << int(databaseId)
860 << " LibraryID = " << int(libraryId);
861
862 return m->genericLibraryDatabase;
863 }
864
865 return *it;
866 }
867
868
registerLibraryDatabase(LibraryId libraryId)869 DB::LibraryDatabase* Connector::registerLibraryDatabase(LibraryId libraryId)
870 {
871 DB::LibraryDatabase* lib_db = nullptr;
872 LibDbIterator it = Algorithm::find(m->libraryDbs, [=](DB::LibraryDatabase* db){
873 return (db->libraryId() == libraryId);
874 });
875
876 if(it == m->libraryDbs.end())
877 {
878 lib_db = new DB::LibraryDatabase(this->connectionName(), this->databaseId(), libraryId);
879 m->libraryDbs << lib_db;
880 }
881
882 else
883 {
884 lib_db = *it;
885 }
886
887 return lib_db;
888 }
889
deleteLibraryDatabase(LibraryId libraryId)890 void Connector::deleteLibraryDatabase(LibraryId libraryId)
891 {
892 LibDbIterator it = Algorithm::find(m->libraryDbs, [=](DB::LibraryDatabase* db){
893 return (db->libraryId() == libraryId);
894 });
895
896 if(it != m->libraryDbs.end())
897 {
898 LibraryDatabase* db = *it;
899 db->deleteAllTracks(true);
900 m->libraryDbs.removeAll(db);
901
902 delete db; db = nullptr;
903 }
904 }
905
906
playlistConnector()907 DB::Playlist* Connector::playlistConnector()
908 {
909 if(!m->playlistConnector){
910 m->playlistConnector = new DB::Playlist(this->connectionName(), this->databaseId());
911 }
912
913 return m->playlistConnector;
914 }
915
916
bookmarkConnector()917 DB::Bookmarks* Connector::bookmarkConnector()
918 {
919 if(!m->bookmarkConnector){
920 m->bookmarkConnector = new DB::Bookmarks(this->connectionName(), this->databaseId());
921 }
922
923 return m->bookmarkConnector;
924 }
925
streamConnector()926 DB::Streams* Connector::streamConnector()
927 {
928 if(!m->streamConnector){
929 m->streamConnector = new DB::Streams(this->connectionName(), this->databaseId());
930 }
931
932 return m->streamConnector;
933 }
934
podcastConnector()935 DB::Podcasts* Connector::podcastConnector()
936 {
937 if(!m->podcastConnector){
938 m->podcastConnector = new DB::Podcasts(this->connectionName(), this->databaseId());
939 }
940
941 return m->podcastConnector;
942 }
943
visualStyleConnector()944 DB::VisualStyles* Connector::visualStyleConnector()
945 {
946 if(!m->visualStyleConnector){
947 m->visualStyleConnector = new DB::VisualStyles(this->connectionName(), this->databaseId());
948 }
949
950 return m->visualStyleConnector;
951 }
952
settingsConnector()953 DB::Settings* Connector::settingsConnector()
954 {
955 if(!m->settingsConnector){
956 m->settingsConnector = new DB::Settings(this->connectionName(), this->databaseId());
957 }
958
959 return m->settingsConnector;
960 }
961
shortcutConnector()962 DB::Shortcuts*Connector::shortcutConnector()
963 {
964 if(!m->shortcutConnector){
965 m->shortcutConnector = new DB::Shortcuts(this->connectionName(), this->databaseId());
966 }
967
968 return m->shortcutConnector;
969 }
970
libraryConnector()971 DB::Library* Connector::libraryConnector()
972 {
973 if(!m->libraryConnector){
974 m->libraryConnector = new DB::Library(this->connectionName(), this->databaseId());
975 }
976
977 return m->libraryConnector;
978 }
979
coverConnector()980 DB::Covers* Connector::coverConnector()
981 {
982 if(!m->coverConnector){
983 m->coverConnector = new DB::Covers(this->connectionName(), this->databaseId());
984 }
985
986 return m->coverConnector;
987 }
988
sessionConnector()989 DB::Session* DB::Connector::sessionConnector()
990 {
991 if(!m->sessionConnector){
992 m->sessionConnector = new DB::Session(this->connectionName(), this->databaseId());
993 }
994
995 return m->sessionConnector;
996 }
997
equalizerConnector()998 DB::Equalizer* DB::Connector::equalizerConnector()
999 {
1000 if(!m->equalizerConnector){
1001 m->equalizerConnector = new DB::Equalizer(this->connectionName(), this->databaseId());
1002 }
1003
1004 return m->equalizerConnector;
1005 }
1006
what() const1007 const char* DatabaseNotCreatedException::what() const noexcept
1008 {
1009 return "Database could not be created";
1010 }
1011