databaseinterface.cpp 412 KB
Newer Older
1
/*
Matthieu Gallien's avatar
Matthieu Gallien committed
2
3
4
5
   SPDX-FileCopyrightText: 2016 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>

   SPDX-License-Identifier: LGPL-3.0-or-later
  */
6
7
8

#include "databaseinterface.h"

9
10
#include "databaseLogging.h"

11
12
#include <KI18n/KLocalizedString>

13
14
#include <QCoreApplication>

15
16
17
18
19
#include <QSqlDatabase>
#include <QSqlDriver>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QSqlError>
20

21
#include <QDateTime>
22
23
#include <QMutex>
#include <QVariant>
24
#include <QAtomicInt>
25
#include <QElapsedTimer>
26
#include <QDebug>
27

28
29
#include <algorithm>

30
31
32
33
class DatabaseInterfacePrivate
{
public:

34
    DatabaseInterfacePrivate(const QSqlDatabase &tracksDatabase)
35
        : mTracksDatabase(tracksDatabase), mSelectAlbumQuery(mTracksDatabase),
36
          mSelectTrackQuery(mTracksDatabase), mSelectAlbumIdFromTitleQuery(mTracksDatabase),
37
          mInsertAlbumQuery(mTracksDatabase), mSelectTrackIdFromTitleAlbumIdArtistQuery(mTracksDatabase),
38
          mInsertTrackQuery(mTracksDatabase), mSelectTracksFromArtist(mTracksDatabase),
39
          mSelectTracksFromGenre(mTracksDatabase),
Jerome Guidon's avatar
Jerome Guidon committed
40
41
          mSelectTrackFromIdQuery(mTracksDatabase), mSelectRadioFromIdQuery(mTracksDatabase),
          mSelectCountAlbumsForArtistQuery(mTracksDatabase),
42
          mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery(mTracksDatabase),
43
          mSelectAllAlbumsFromArtistQuery(mTracksDatabase), mSelectAllArtistsQuery(mTracksDatabase),
44
          mInsertArtistsQuery(mTracksDatabase), mSelectArtistByNameQuery(mTracksDatabase),
45
          mSelectArtistQuery(mTracksDatabase), mUpdateTrackStatistics(mTracksDatabase),
46
          mRemoveTrackQuery(mTracksDatabase), mRemoveAlbumQuery(mTracksDatabase),
47
          mRemoveArtistQuery(mTracksDatabase), mSelectAllTracksQuery(mTracksDatabase),
Jerome Guidon's avatar
Jerome Guidon committed
48
          mSelectAllRadiosQuery(mTracksDatabase),
49
          mInsertTrackMapping(mTracksDatabase), mUpdateTrackFirstPlayStatistics(mTracksDatabase),
50
          mInsertMusicSource(mTracksDatabase), mSelectMusicSource(mTracksDatabase),
51
          mUpdateTrackPriority(mTracksDatabase), mUpdateTrackFileModifiedTime(mTracksDatabase),
52
          mSelectTracksMapping(mTracksDatabase), mSelectTracksMappingPriority(mTracksDatabase),
Jerome Guidon's avatar
Jerome Guidon committed
53
          mSelectRadioIdFromHttpAddress(mTracksDatabase),
54
          mUpdateAlbumArtUriFromAlbumIdQuery(mTracksDatabase), mSelectTracksMappingPriorityByTrackId(mTracksDatabase),
55
          mSelectAlbumIdsFromArtist(mTracksDatabase), mSelectAllTrackFilesQuery(mTracksDatabase),
56
57
          mRemoveTracksMappingFromSource(mTracksDatabase), mRemoveTracksMapping(mTracksDatabase),
          mSelectTracksWithoutMappingQuery(mTracksDatabase), mSelectAlbumIdFromTitleAndArtistQuery(mTracksDatabase),
58
          mSelectAlbumIdFromTitleWithoutArtistQuery(mTracksDatabase),
59
60
          mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery(mTracksDatabase), mSelectAlbumArtUriFromAlbumIdQuery(mTracksDatabase),
          mInsertComposerQuery(mTracksDatabase), mSelectComposerByNameQuery(mTracksDatabase),
61
          mSelectComposerQuery(mTracksDatabase), mInsertLyricistQuery(mTracksDatabase),
62
63
          mSelectLyricistByNameQuery(mTracksDatabase), mSelectLyricistQuery(mTracksDatabase),
          mInsertGenreQuery(mTracksDatabase), mSelectGenreByNameQuery(mTracksDatabase),
64
65
66
          mSelectGenreQuery(mTracksDatabase), mSelectAllTracksShortQuery(mTracksDatabase),
          mSelectAllAlbumsShortQuery(mTracksDatabase), mSelectAllComposersQuery(mTracksDatabase),
          mSelectAllLyricistsQuery(mTracksDatabase), mSelectCountAlbumsForComposerQuery(mTracksDatabase),
67
          mSelectCountAlbumsForLyricistQuery(mTracksDatabase), mSelectAllGenresQuery(mTracksDatabase),
68
          mSelectGenreForArtistQuery(mTracksDatabase), mSelectGenreForAlbumQuery(mTracksDatabase),
69
          mUpdateTrackQuery(mTracksDatabase), mUpdateAlbumArtistQuery(mTracksDatabase),
Jerome Guidon's avatar
Jerome Guidon committed
70
          mUpdateRadioQuery(mTracksDatabase),
71
72
73
          mUpdateAlbumArtistInTracksQuery(mTracksDatabase), mQueryMaximumTrackIdQuery(mTracksDatabase),
          mQueryMaximumAlbumIdQuery(mTracksDatabase), mQueryMaximumArtistIdQuery(mTracksDatabase),
          mQueryMaximumLyricistIdQuery(mTracksDatabase), mQueryMaximumComposerIdQuery(mTracksDatabase),
74
          mQueryMaximumGenreIdQuery(mTracksDatabase), mSelectAllArtistsWithGenreFilterQuery(mTracksDatabase),
75
          mSelectAllAlbumsShortWithGenreArtistFilterQuery(mTracksDatabase), mSelectAllAlbumsShortWithArtistFilterQuery(mTracksDatabase),
76
          mSelectAllRecentlyPlayedTracksQuery(mTracksDatabase), mSelectAllFrequentlyPlayedTracksQuery(mTracksDatabase),
77
78
          mClearTracksDataTable(mTracksDatabase), mClearTracksTable(mTracksDatabase),
          mClearAlbumsTable(mTracksDatabase), mClearArtistsTable(mTracksDatabase),
79
          mClearComposerTable(mTracksDatabase), mClearGenreTable(mTracksDatabase), mClearLyricistTable(mTracksDatabase),
Jerome Guidon's avatar
Jerome Guidon committed
80
          mArtistMatchGenreQuery(mTracksDatabase), mSelectTrackIdQuery(mTracksDatabase),
81
          mInsertRadioQuery(mTracksDatabase), mDeleteRadioQuery(mTracksDatabase),
82
83
          mSelectTrackFromIdAndUrlQuery(mTracksDatabase),
          mUpdateDatabaseVersionQuery(mTracksDatabase), mSelectDatabaseVersionQuery(mTracksDatabase)
84
85
86
    {
    }

87
88
    QSqlDatabase mTracksDatabase;

89
90
91
92
93
94
95
96
    QSqlQuery mSelectAlbumQuery;

    QSqlQuery mSelectTrackQuery;

    QSqlQuery mSelectAlbumIdFromTitleQuery;

    QSqlQuery mInsertAlbumQuery;

97
    QSqlQuery mSelectTrackIdFromTitleAlbumIdArtistQuery;
98
99
100

    QSqlQuery mInsertTrackQuery;

101
102
    QSqlQuery mSelectTracksFromArtist;

103
104
    QSqlQuery mSelectTracksFromGenre;

105
106
    QSqlQuery mSelectTrackFromIdQuery;

Jerome Guidon's avatar
Jerome Guidon committed
107
108
    QSqlQuery mSelectRadioFromIdQuery;

109
110
    QSqlQuery mSelectCountAlbumsForArtistQuery;

111
    QSqlQuery mSelectTrackIdFromTitleArtistAlbumTrackDiscNumberQuery;
112

113
114
    QSqlQuery mSelectAllAlbumsFromArtistQuery;

115
    QSqlQuery mSelectAllArtistsQuery;
116
117
118
119
120

    QSqlQuery mInsertArtistsQuery;

    QSqlQuery mSelectArtistByNameQuery;

121
122
    QSqlQuery mSelectArtistQuery;

123
    QSqlQuery mUpdateTrackStatistics;
124

125
126
127
128
129
130
    QSqlQuery mRemoveTrackQuery;

    QSqlQuery mRemoveAlbumQuery;

    QSqlQuery mRemoveArtistQuery;

131
132
    QSqlQuery mSelectAllTracksQuery;

Jerome Guidon's avatar
Jerome Guidon committed
133
134
    QSqlQuery mSelectAllRadiosQuery;

135
136
    QSqlQuery mInsertTrackMapping;

137
    QSqlQuery mUpdateTrackFirstPlayStatistics;
138
139
140
141
142

    QSqlQuery mInsertMusicSource;

    QSqlQuery mSelectMusicSource;

143
144
145
    QSqlQuery mUpdateTrackPriority;

    QSqlQuery mUpdateTrackFileModifiedTime;
146

147
148
149
150
    QSqlQuery mSelectTracksMapping;

    QSqlQuery mSelectTracksMappingPriority;

Jerome Guidon's avatar
Jerome Guidon committed
151
152
    QSqlQuery mSelectRadioIdFromHttpAddress;

153
154
    QSqlQuery mUpdateAlbumArtUriFromAlbumIdQuery;

155
156
    QSqlQuery mSelectTracksMappingPriorityByTrackId;

157
158
    QSqlQuery mSelectAlbumIdsFromArtist;

159
160
    QSqlQuery mSelectAllTrackFilesQuery;

161
162
163
164
165
166
    QSqlQuery mRemoveTracksMappingFromSource;

    QSqlQuery mRemoveTracksMapping;

    QSqlQuery mSelectTracksWithoutMappingQuery;

167
168
169
170
    QSqlQuery mSelectAlbumIdFromTitleAndArtistQuery;

    QSqlQuery mSelectAlbumIdFromTitleWithoutArtistQuery;

171
172
    QSqlQuery mSelectTrackIdFromTitleAlbumTrackDiscNumberQuery;

173
174
    QSqlQuery mSelectAlbumArtUriFromAlbumIdQuery;

175
176
177
178
179
180
    QSqlQuery mInsertComposerQuery;

    QSqlQuery mSelectComposerByNameQuery;

    QSqlQuery mSelectComposerQuery;

181
182
183
184
185
186
    QSqlQuery mInsertLyricistQuery;

    QSqlQuery mSelectLyricistByNameQuery;

    QSqlQuery mSelectLyricistQuery;

187
188
189
190
191
192
    QSqlQuery mInsertGenreQuery;

    QSqlQuery mSelectGenreByNameQuery;

    QSqlQuery mSelectGenreQuery;

193
194
195
196
197
198
199
200
201
202
203
204
205
206
    QSqlQuery mSelectAllTracksShortQuery;

    QSqlQuery mSelectAllAlbumsShortQuery;

    QSqlQuery mSelectAllComposersQuery;

    QSqlQuery mSelectAllLyricistsQuery;

    QSqlQuery mSelectCountAlbumsForComposerQuery;

    QSqlQuery mSelectCountAlbumsForLyricistQuery;

    QSqlQuery mSelectAllGenresQuery;

207
208
    QSqlQuery mSelectGenreForArtistQuery;

209
210
    QSqlQuery mSelectGenreForAlbumQuery;

211
212
    QSqlQuery mUpdateTrackQuery;

213
214
    QSqlQuery mUpdateAlbumArtistQuery;

Jerome Guidon's avatar
Jerome Guidon committed
215
216
    QSqlQuery mUpdateRadioQuery;

217
218
    QSqlQuery mUpdateAlbumArtistInTracksQuery;

219
220
221
222
223
224
225
226
227
228
229
230
    QSqlQuery mQueryMaximumTrackIdQuery;

    QSqlQuery mQueryMaximumAlbumIdQuery;

    QSqlQuery mQueryMaximumArtistIdQuery;

    QSqlQuery mQueryMaximumLyricistIdQuery;

    QSqlQuery mQueryMaximumComposerIdQuery;

    QSqlQuery mQueryMaximumGenreIdQuery;

231
    QSqlQuery mSelectAllArtistsWithGenreFilterQuery;
232

233
234
235
    QSqlQuery mSelectAllAlbumsShortWithGenreArtistFilterQuery;

    QSqlQuery mSelectAllAlbumsShortWithArtistFilterQuery;
236

237
238
239
240
    QSqlQuery mSelectAllRecentlyPlayedTracksQuery;

    QSqlQuery mSelectAllFrequentlyPlayedTracksQuery;

241
242
    QSqlQuery mClearTracksDataTable;

243
244
245
246
247
248
249
250
251
252
253
254
    QSqlQuery mClearTracksTable;

    QSqlQuery mClearAlbumsTable;

    QSqlQuery mClearArtistsTable;

    QSqlQuery mClearComposerTable;

    QSqlQuery mClearGenreTable;

    QSqlQuery mClearLyricistTable;

255
256
    QSqlQuery mArtistMatchGenreQuery;

257
258
    QSqlQuery mSelectTrackIdQuery;

Jerome Guidon's avatar
Jerome Guidon committed
259
260
261
262
    QSqlQuery mInsertRadioQuery;

    QSqlQuery mDeleteRadioQuery;

263
264
    QSqlQuery mSelectTrackFromIdAndUrlQuery;

265
266
267
268
    QSqlQuery mUpdateDatabaseVersionQuery;

    QSqlQuery mSelectDatabaseVersionQuery;

269
270
    QSet<qulonglong> mModifiedTrackIds;

271
272
273
274
    QSet<qulonglong> mModifiedAlbumIds;

    QSet<qulonglong> mModifiedArtistIds;

275
    QSet<qulonglong> mInsertedTracks;
276

277
    QSet<qulonglong> mInsertedAlbums;
278

279
    QSet<QPair<qulonglong, QString>> mInsertedArtists;
280

281
    qulonglong mAlbumId = 1;
282

283
284
    qulonglong mArtistId = 1;

285
286
    qulonglong mComposerId = 1;

287
288
    qulonglong mLyricistId = 1;

289
290
    qulonglong mGenreId = 1;

291
    qulonglong mTrackId = 1;
292

293
294
    QAtomicInt mStopRequest = 0;

295
296
    bool mInitFinished = false;

297
298
    bool mIsInBadState = false;

299
300
};

301
DatabaseInterface::DatabaseInterface(QObject *parent) : QObject(parent), d(nullptr)
302
{
303
304
305
306
}

DatabaseInterface::~DatabaseInterface()
{
307
308
309
    if (d) {
        d->mTracksDatabase.close();
    }
310
311
}

312
void DatabaseInterface::init(const QString &dbName, const QString &databaseFileName)
313
314
{
    QSqlDatabase tracksDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), dbName);
315

316
317
318
319
320
    if (!databaseFileName.isEmpty()) {
        tracksDatabase.setDatabaseName(QStringLiteral("file:") + databaseFileName);
    } else {
        tracksDatabase.setDatabaseName(QStringLiteral("file:memdb1?mode=memory"));
    }
321
322
    tracksDatabase.setConnectOptions(QStringLiteral("foreign_keys = ON;locking_mode = EXCLUSIVE;QSQLITE_OPEN_URI;QSQLITE_BUSY_TIMEOUT=500000"));

323
    auto result = tracksDatabase.open();
324
    if (result) {
325
        qCDebug(orgKdeElisaDatabase) << "database open";
326
    } else {
327
        qCDebug(orgKdeElisaDatabase) << "database not open";
328
    }
329
    qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::init" << (tracksDatabase.driver()->hasFeature(QSqlDriver::Transactions) ? "yes" : "no");
330

331
332
    tracksDatabase.exec(QStringLiteral("PRAGMA foreign_keys = ON;"));

333
    d = std::make_unique<DatabaseInterfacePrivate>(tracksDatabase);
334
335
336

    initDatabase();
    initRequest();
337
338
339
340

    if (!databaseFileName.isEmpty()) {
        reloadExistingDatabase();
    }
341
342
}

343
qulonglong DatabaseInterface::albumIdFromTitleAndArtist(const QString &title, const QString &artist, const QString &albumPath)
344
345
346
347
348
349
350
351
{
    auto result = qulonglong{0};

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

352
    result = internalAlbumIdFromTitleAndArtist(title, artist, albumPath);
353
354
355
356
357
358
359
360
361

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

362
DataTypes::ListTrackDataType DatabaseInterface::allTracksData()
363
{
364
    auto result = DataTypes::ListTrackDataType{};
365
366
367
368

    if (!d) {
        return result;
    }
369
370
371
372
373
374

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

375
376
377
378
379
380
381
382
383
384
    result = internalAllTracksPartialData();

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

385
DataTypes::ListRadioDataType DatabaseInterface::allRadiosData()
Jerome Guidon's avatar
Jerome Guidon committed
386
{
387
    auto result = DataTypes::ListRadioDataType{};
Jerome Guidon's avatar
Jerome Guidon committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalAllRadiosPartialData();

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

408
DataTypes::ListTrackDataType DatabaseInterface::recentlyPlayedTracksData(int count)
409
{
410
    auto result = DataTypes::ListTrackDataType{};
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalRecentlyPlayedTracksData(count);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

431
DataTypes::ListTrackDataType DatabaseInterface::frequentlyPlayedTracksData(int count)
432
{
433
    auto result = DataTypes::ListTrackDataType{};
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalFrequentlyPlayedTracksData(count);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

454
DataTypes::ListAlbumDataType DatabaseInterface::allAlbumsData()
455
{
456
    auto result = DataTypes::ListAlbumDataType{};
457
458
459
460
461
462
463
464
465
466

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

467
468
469
470
471
472
473
474
475
476
    result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortQuery);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

477
DataTypes::ListAlbumDataType DatabaseInterface::allAlbumsDataByGenreAndArtist(const QString &genre, const QString &artist)
478
{
479
    auto result = DataTypes::ListAlbumDataType{};
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.bindValue(QStringLiteral(":artistFilter"), artist);
    d->mSelectAllAlbumsShortWithGenreArtistFilterQuery.bindValue(QStringLiteral(":genreFilter"), genre);

    result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortWithGenreArtistFilterQuery);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

503
DataTypes::ListAlbumDataType DatabaseInterface::allAlbumsDataByArtist(const QString &artist)
504
{
505
    auto result = DataTypes::ListAlbumDataType{};
506
507
508
509
510
511
512
513
514
515
516
517
518

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    d->mSelectAllAlbumsShortWithArtistFilterQuery.bindValue(QStringLiteral(":artistFilter"), artist);

    result = internalAllAlbumsPartialData(d->mSelectAllAlbumsShortWithArtistFilterQuery);
519
520
521
522
523
524
525
526
527

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

528
DataTypes::AlbumDataType DatabaseInterface::albumDataFromDatabaseId(qulonglong id)
529
{
530
    auto result = DataTypes::AlbumDataType{};
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalOneAlbumPartialData(id);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

551
DataTypes::ListTrackDataType DatabaseInterface::albumData(qulonglong databaseId)
552
{
553
    auto result = DataTypes::ListTrackDataType{};
554
555
556
557
558
559
560
561
562
563
564
565

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    d->mSelectTrackQuery.bindValue(QStringLiteral(":albumId"), databaseId);

566
    auto queryResult = execQuery(d->mSelectTrackQuery);
567
568
569
570

    if (!queryResult || !d->mSelectTrackQuery.isSelect() || !d->mSelectTrackQuery.isActive()) {
        Q_EMIT databaseError();

571
572
573
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::albumData" << d->mSelectTrackQuery.lastError();
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
    }

    while (d->mSelectTrackQuery.next()) {
        const auto &currentRecord = d->mSelectTrackQuery.record();

        result.push_back(buildTrackDataFromDatabaseRecord(currentRecord));
    }

    d->mSelectTrackQuery.finish();

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

592
DataTypes::ListArtistDataType DatabaseInterface::allArtistsData()
593
{
594
    auto result = DataTypes::ListArtistDataType{};
595
596
597
598
599
600
601
602
603
604

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

605
606
607
608
609
610
611
612
613
614
    result = internalAllArtistsPartialData(d->mSelectAllArtistsQuery);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

615
DataTypes::ListArtistDataType DatabaseInterface::allArtistsDataByGenre(const QString &genre)
616
{
617
    qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::allArtistsDataByGenre" << genre;
618

619
    auto result = DataTypes::ListArtistDataType{};
620
621
622
623
624
625
626
627
628
629
630
631
632
633

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    d->mSelectAllArtistsWithGenreFilterQuery.bindValue(QStringLiteral(":genreFilter"), genre);

    result = internalAllArtistsPartialData(d->mSelectAllArtistsWithGenreFilterQuery);

634
635
636
637
638
639
640
641
    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
DataTypes::ArtistDataType DatabaseInterface::artistDataFromDatabaseId(qulonglong id)
{
    auto result = DataTypes::ArtistDataType{};

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalOneArtistPartialData(id);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

qulonglong DatabaseInterface::artistIdFromName(const QString &name)
{
    auto result = qulonglong{0};

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalArtistIdFromName(name);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

684
DataTypes::ListGenreDataType DatabaseInterface::allGenresData()
685
{
686
    auto result = DataTypes::ListGenreDataType{};
687
688
689
690
691
692
693
694
695
696
697

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalAllGenresPartialData();
698
699
700
701
702
703
704
705
706

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

707
708
709
710
711
712
713
714
715
716
717
bool DatabaseInterface::internalArtistMatchGenre(qulonglong databaseId, const QString &genre)
{
    auto result = true;

    if (!d) {
        return result;
    }

    d->mArtistMatchGenreQuery.bindValue(QStringLiteral(":databaseId"), databaseId);
    d->mArtistMatchGenreQuery.bindValue(QStringLiteral(":genreFilter"), genre);

718
    auto queryResult = execQuery(d->mArtistMatchGenreQuery);
719
720
721
722

    if (!queryResult || !d->mArtistMatchGenreQuery.isSelect() || !d->mArtistMatchGenreQuery.isActive()) {
        Q_EMIT databaseError();

723
724
725
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::artistMatchGenre" << d->mArtistMatchGenreQuery.lastError();
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740

        d->mArtistMatchGenreQuery.finish();

        auto transactionResult = finishTransaction();
        if (!transactionResult) {
            return result;
        }

        return result;
    }

    result = d->mArtistMatchGenreQuery.next();

    d->mArtistMatchGenreQuery.finish();

741
    qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::internalArtistMatchGenre" << databaseId << (result ? "match" : "does not match");
742
743
744
745

    return result;
}

746
DataTypes::ListTrackDataType DatabaseInterface::tracksDataFromAuthor(const QString &ArtistName)
747
{
748
    auto allTracks = DataTypes::ListTrackDataType{};
749
750
751
752
753
754

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return allTracks;
    }

755
    allTracks = internalTracksFromAuthor(ArtistName);
756
757
758
759
760
761
762
763
764

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return allTracks;
    }

    return allTracks;
}

765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
DataTypes::ListTrackDataType DatabaseInterface::tracksDataFromGenre(const QString &genre)
{
    auto allTracks = DataTypes::ListTrackDataType{};

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return allTracks;
    }

    allTracks = internalTracksFromGenre(genre);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return allTracks;
    }

    return allTracks;
}
783
DataTypes::TrackDataType DatabaseInterface::trackDataFromDatabaseId(qulonglong id)
784
{
785
    auto result = DataTypes::TrackDataType();
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalOneTrackPartialData(id);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
DataTypes::TrackDataType DatabaseInterface::trackDataFromDatabaseIdAndUrl(qulonglong id, const QUrl &trackUrl)
{
    auto result = DataTypes::TrackDataType();

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalOneTrackPartialDataByIdAndUrl(id, trackUrl);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

829
DataTypes::TrackDataType DatabaseInterface::radioDataFromDatabaseId(qulonglong id)
Jerome Guidon's avatar
Jerome Guidon committed
830
{
831
    auto result = DataTypes::TrackDataType();
Jerome Guidon's avatar
Jerome Guidon committed
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalOneRadioPartialData(id);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

852
qulonglong DatabaseInterface::trackIdFromTitleAlbumTrackDiscNumber(const QString &title, const QString &artist, const std::optional<QString> &album,
Jerome Guidon's avatar
Jerome Guidon committed
853
                                                                     std::optional<int> trackNumber, std::optional<int> discNumber)
854
855
856
{
    auto result = qulonglong(0);

857
858
859
860
    if (!d) {
        return result;
    }

861
    auto transactionResult = startTransaction();
862
863
864
865
    if (!transactionResult) {
        return result;
    }

866
    result = internalTrackIdFromTitleAlbumTracDiscNumber(title, artist, album, trackNumber, discNumber);
867

868
    transactionResult = finishTransaction();
869
870
871
872
    if (!transactionResult) {
        return result;
    }

873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
    return result;
}

qulonglong DatabaseInterface::trackIdFromFileName(const QUrl &fileName)
{
    auto result = qulonglong(0);

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalTrackIdFromFileName(fileName);

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

896
897
898
    return result;
}

899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
qulonglong DatabaseInterface::radioIdFromFileName(const QUrl &fileName)
{
    auto result = qulonglong(0);

    if (!d) {
        return result;
    }

    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return result;
    }

    result = internalRadioIdFromHttpAddress(fileName.toString());

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return result;
    }

    return result;
}

922
923
924
925
926
void DatabaseInterface::applicationAboutToQuit()
{
    d->mStopRequest = 1;
}

927
void DatabaseInterface::askRestoredTracks()
928
929
930
931
932
933
{
    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return;
    }

934
    auto result = internalAllFileName();
935

936
    Q_EMIT restoredTracks(result);
937
938
939
940
941
942
943

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return;
    }
}

944
945
946
947
948
949
950
void DatabaseInterface::trackHasStartedPlaying(const QUrl &fileName, const QDateTime &time)
{
    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return;
    }

951
    updateTrackStatistics(fileName, time);
952
953
954
955
956
957
958

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return;
    }
}

959
960
961
962
963
964
965
void DatabaseInterface::clearData()
{
    auto transactionResult = startTransaction();
    if (!transactionResult) {
        return;
    }

966
    auto queryResult = execQuery(d->mClearTracksTable);
967
968
969
970

    if (!queryResult || !d->mClearTracksTable.isActive()) {
        Q_EMIT databaseError();

971
972
973
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksTable.lastError();
974
975
976
977
    }

    d->mClearTracksTable.finish();

978
979
980
981
982
983
984
985
986
987
988
989
    queryResult = execQuery(d->mClearTracksDataTable);

    if (!queryResult || !d->mClearTracksDataTable.isActive()) {
        Q_EMIT databaseError();

        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksDataTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksDataTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearTracksDataTable.lastError();
    }

    d->mClearTracksDataTable.finish();

990
    queryResult = execQuery(d->mClearAlbumsTable);
991
992
993
994

    if (!queryResult || !d->mClearAlbumsTable.isActive()) {
        Q_EMIT databaseError();

995
996
997
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearAlbumsTable.lastError();
998
999
1000
1001
    }

    d->mClearAlbumsTable.finish();

1002
    queryResult = execQuery(d->mClearComposerTable);
1003
1004
1005
1006

    if (!queryResult || !d->mClearComposerTable.isActive()) {
        Q_EMIT databaseError();

1007
1008
1009
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearComposerTable.lastError();
1010
1011
1012
1013
    }

    d->mClearComposerTable.finish();

1014
    queryResult = execQuery(d->mClearLyricistTable);
1015
1016
1017
1018

    if (!queryResult || !d->mClearLyricistTable.isActive()) {
        Q_EMIT databaseError();

1019
1020
1021
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearLyricistTable.lastError();
1022
1023
1024
1025
    }

    d->mClearLyricistTable.finish();

1026
    queryResult = execQuery(d->mClearGenreTable);
1027
1028
1029
1030

    if (!queryResult || !d->mClearGenreTable.isActive()) {
        Q_EMIT databaseError();

1031
1032
1033
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearGenreTable.lastError();
1034
1035
1036
1037
    }

    d->mClearGenreTable.finish();

1038
    queryResult = execQuery(d->mClearArtistsTable);
1039
1040
1041
1042

    if (!queryResult || !d->mClearArtistsTable.isActive()) {
        Q_EMIT databaseError();

1043
1044
1045
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.lastQuery();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.boundValues();
        qCDebug(orgKdeElisaDatabase) << "DatabaseInterface::clearData" << d->mClearArtistsTable.lastError();
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
    }

    d->mClearArtistsTable.finish();

    transactionResult = finishTransaction();
    if (!transactionResult) {
        return;
    }

    Q_EMIT cleanedDatabase();
}

1058
1059
1060
void DatabaseInterface::initChangesTrackers()
{
    d->mModifiedTrackIds.clear();
1061
1062
    d->mModifiedAlbumIds.clear();
    d->mModifiedArtistIds.clear();
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
    d->mInsertedTracks.clear();
    d->mInsertedAlbums.clear();
    d->mInsertedArtists.clear();
}

void DatabaseInterface::recordModifiedTrack(qulonglong trackId)
{
    d->mModifiedTrackIds.insert(trackId);
}

1073
1074
1075
1076
1077
void DatabaseInterface::recordModifiedAlbum(qulonglong albumId)
{
    d->mModifiedAlbumIds.insert(albumId);
}

1078
void DatabaseInterface::insertTracksList(const DataTypes::ListTrackDataType &tracks, const QHash<QString, QUrl> &covers)
1079
{
1080
    qCDebug(orgKdeElisaDatabase()) << "DatabaseInterface::insertTracksList" << tracks.count();
1081
    if (d->mStopRequest == 1) {
1082
        Q_EMIT finishInsertingTracksList();
1083
1084
1085
        return;
    }

1086
1087
    auto transactionResult = startTransaction();
    if (!transactionResult) {
1088
        Q_EMIT finishInsertingTracksList();
1089
1090
1091
        return;
    }

1092
    initChangesTrackers();
1093

1094
    for(const auto &oneTrack : tracks) {
1095
1096
1097
1098
1099
1100
1101
        switch (oneTrack.elementType())
        {
        case ElisaUtils::Track:
        {
            qCDebug(orgKdeElisaDatabase()) << "DatabaseInterface::insertTracksList" << "insert one track";
            internalInsertOneTrack(oneTrack, covers);
            break;
1102
        }
1103
1104
1105
1106
1107
        case ElisaUtils::Radio:
        {
            qCDebug(orgKdeElisaDatabase()) << "DatabaseInterface::insertTracksList" << "insert one radio";
            internalInsertOneRadio(oneTrack);
            break;
1108
        }
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
        case ElisaUtils::Album:
        case ElisaUtils::Artist:
        case ElisaUtils::Composer:
        case ElisaUtils::Container:
        case ElisaUtils::FileName:
        case ElisaUtils::Genre:
        case ElisaUtils::Lyricist:
        case ElisaUtils::Unknown:
            qCDebug(orgKdeElisaDatabase()) << "DatabaseInterface::insertTracksList" << "invalid track data";
            break;
1119
        }
1120
1121
1122
1123

        if (d->mStopRequest == 1) {
            transactionResult = finishTransaction();
            if (!transactionResult) {
1124
                Q_EMIT finishInsertingTracksList();
1125
1126
                return;
            }
1127
            Q_EMIT finishInsertingTracksList();
1128
1129
            return;
        }
1130
    }
1131

1132
    if (!d->mInsertedArtists.isEmpty()) {
1133
        DataTypes::ListArtistDataType newArtists;
1134

1135
1136
1137
1138
        for (auto newArtistData : qAsConst(d->mInsertedArtists)) {
            newArtists.push_back({{DataTypes::DatabaseIdRole, newArtistData.first},
                                  {DataTypes::TitleRole, newArtistData.second},
                                  {DataTypes::ElementTypeRole, ElisaUtils::Artist}});
1139
        }
1140
        qCInfo(orgKdeElisaDatabase) << "artistsAdded" << newArtists.size();
1141
1142
1143
        Q_EMIT artistsAdded(newArtists);
    }

1144
    if (!d->mInsertedAlbums.isEmpty()) {
1145
        DataTypes::ListAlbumDataType newAlbums;
1146
1147
1148

        for (auto albumId : qAsConst(d->mInsertedAlbums)) {
            d->mModifiedAlbumIds.remove(albumId);
1149
            newAlbums.push_back(internalOneAlbumPartialData(albumId));
1150
        }
1151

1152
        qCInfo(orgKdeElisaDatabase) << "albumsAdded" << newAlbums.size();
1153
1154
1155
        Q_EMIT albumsAdded(newAlbums);
    }

1156
    for (auto albumId : qAsConst(d->mModifiedAlbumIds)) {
1157
        Q_EMIT albumModified({{DataTypes::DatabaseIdRole, albumId}}, albumId);
1158
    }
1159

1160
    if (!d->mInsertedTracks.isEmpty()) {
1161
        DataTypes::ListTrackDataType newTracks;
1162

1163
        for (auto trackId : qAsConst(d->mInsertedTracks)) {
1164
            newTracks.push_back(internalOneTrackPartialData(trackId));
1165
1166
            d->mModifiedTrackIds.remove(trackId);
        }
1167

1168
        qCInfo(orgKdeElisaDatabase) << "tracksAdded" << newTracks.size();
1169
        Q_EMIT tracksAdded(newTracks);
1170
1171
    }

1172
1173
    for (auto trackId : qAsConst(d->mModifiedTrackIds)) {
        Q_EMIT trackModified(internalOneTrackPartialData(trackId));
1174
1175
    }

1176
1177
    transactionResult = finishTransaction();
    if (!transactionResult) {
1178
        Q_EMIT finishInsertingTracksList();
1179
1180
        return;
    }
1181
    Q_EMIT finishInsertingTracksList();
1182
}
1183

1184
void DatabaseInterface::removeTracksList(const QList<QUrl> &removedTracks)
1185
1186
1187
{
    auto transactionResult = startTransaction();
    if (!transactionResult) {
1188
        Q_EMIT finishRemovingTracksList();
1189
1190
1191
        return;
    }

1192
    initChangesTrackers();
1193

1194
    internalRemoveTracksList(removedTracks);
1195

1196
    if (!d->mInsertedArtists.isEmpty()) {
1197
        DataTypes::ListArtistDataType newArtists;
1198
1199
1200
1201
        for (auto newArtistData : qAsConst(d->mInsertedArtists)) {
            newArtists.push_back({{DataTypes::DatabaseIdRole, newArtistData.first},
                                  {DataTypes::TitleRole, newArtistData.second},
                                  {DataTypes::ElementTypeRole, ElisaUtils::Artist}});
1202
1203
1204
        }
        Q_EMIT artistsAdded(newArtists);
    }
1205

1206
1207
    transactionResult = finishTransaction();
    if (!transactionResult) {
1208
        Q_EMIT finishRemovingTracksList();
1209
1210
        return;
    }
1211
1212

    Q_EMIT finishRemovingTracksList();
1213
1214
}

1215
bool DatabaseInterface::startTransaction()
1216
{
1217
1218
    auto result = false;

1219
1220
    auto transactionResult = d->mTracksDatabase.transaction();
    if (!transactionResult) {
1221
        qCDebug(orgKdeElisaDatabase) << "transaction failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().driverText();
1222
1223
1224
1225
1226
1227
1228
1229
1230

        return result;
    }

    result = true;

    return result;
}

1231
bool DatabaseInterface::finishTransaction()
1232
1233
1234
1235
1236
1237
{
    auto result = false;

    auto transactionResult = d->mTracksDatabase.commit();

    if (!transactionResult) {
1238
        qCDebug(orgKdeElisaDatabase) << "commit failed" << d->mTracksDatabase.lastError() << d->mTracksDatabase.lastError().nativeErrorCode();
1239
1240
1241
1242
1243
1244
1245
1246
1247

        return result;
    }

    result = true;

    return result;
}

1248
bool DatabaseInterface::rollBackTransaction()