1 /*
2  * Cantata
3  *
4  * Copyright (c) 2017-2020 Craig Drummond <craig.p.drummond@gmail.com>
5  *
6  * ----
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #ifndef LIBRARY_DB_H
25 #define LIBRARY_DB_H
26 
27 #include <QObject>
28 #include <QList>
29 #include <QMap>
30 #include <QElapsedTimer>
31 #include "mpd-interface/song.h"
32 #include <time.h>
33 
34 class QSqlDatabase;
35 class QSqlQuery;
36 
37 class LibraryDb : public QObject
38 {
39     Q_OBJECT
40 
41 public:
enableDebug()42     static void enableDebug() { dbgEnabled=true; }
debugEnabled()43     static bool debugEnabled() { return dbgEnabled; }
44 
45     static const QLatin1String constFileExt;
46     static const QLatin1String constNullGenre;
47 
48     enum AlbumSort {
49         AS_AlArYr,
50         AS_AlYrAr,
51         AS_ArAlYr,
52         AS_ArYrAl,
53         AS_YrAlAr,
54         AS_YrArAl,
55         AS_Modified,
56 
57         AS_Count
58     };
59 
60     static AlbumSort toAlbumSort(const QString &str);
61     static QString albumSortStr(AlbumSort m);
62 
63     struct Genre
64     {
65         Genre(const QString &n=QString(), int ac=0)
nameGenre66             : name(n), artistCount(ac) { }
67         QString name;
68         int artistCount;
69 
70         bool operator<(const Genre &o) const
71         {
72             if (constNullGenre==name) {
73                 return constNullGenre!=o.name;
74             }
75             return name.localeAwareCompare(o.name)<0;
76         }
77     };
78 
79     struct Artist
80     {
81         Artist(const QString &n=QString(), const QString &s=QString(), int ac=0)
nameArtist82             : name(n), sort(s), albumCount(ac) { }
83         QString name;
84         QString sort;
85         int albumCount;
86 
87         bool operator<(const Artist &o) const
88         {
89             const QString &field=sort.isEmpty() ? name : sort;
90             const QString &ofield=o.sort.isEmpty() ? o.name : o.sort;
91 
92             return field.localeAwareCompare(ofield)<0;
93         }
94     };
95 
96     struct Album
97     {
98         Album(const QString &n=QString(), const QString &i=QString(), const QString &s=QString(),
99               const QString &a=QString(), const QString &as=QString(),
100               int y=0, int tc=0, int d=0, int lm=0, bool onlyUseId=false)
nameAlbum101             : name(n), id(i), sort(s), artist(a), artistSort(as), year(y), trackCount(tc), duration(d), lastModified(lm), identifyById(onlyUseId) { }
102         QString name;
103         QString id;
104         QString sort;
105         QString artist;
106         QString artistSort;
107         int year;
108         int trackCount;
109         int duration;
110         int lastModified;
111         bool identifyById; // Should we jsut use albumId to locate tracks - Issue #1025
112     };
113 
114     LibraryDb(QObject *p, const QString &name);
115     ~LibraryDb() override;
116 
117     void clear();
118     void erase();
119     virtual bool init(const QString &dbFile);
120     void insertSong(const Song &s);
121     QList<Genre> getGenres();
122     QList<Artist> getArtists(const QString &genre=QString());
123     QList<Album> getAlbums(const QString &artistId=QString(), const QString &genre=QString(), AlbumSort sort=AS_YrAlAr);
124     QList<Song> getTracks(const QString &artistId, const QString &albumId, const QString &genre=QString(), AlbumSort sort=AS_YrAlAr, bool useFilter=true);
125     QList<Song> getTracks(int rowFrom, int count);
126     int trackCount();
127     QList<Song> songs(const QStringList &files, bool allowPlaylists=false) const;
128     QList<Album> getAlbumsWithArtistOrComposer(const QString &artist);
129     Album getRandomAlbum(const QString &genre, const QString &artist);
130     Album getRandomAlbum(const QStringList &genres, const QStringList &artists);
131     QSet<QString> get(const QString &type);
132     void getDetails(QSet<QString> &artists, QSet<QString> &albumArtists, QSet<QString> &composers, QSet<QString> &albums, QSet<QString> &genres);
133     bool songExists(const Song &song);
134     bool setFilter(const QString &f, const QString &genre=QString());
getFilter()135     const QString & getFilter() const { return filter; }
getCurrentVersion()136     int getCurrentVersion() const { return currentVersion; }
137 
138 Q_SIGNALS:
139     void libraryUpdated();
140     void error(const QString &str);
141 
142 public Q_SLOTS:
143     void updateStarted(time_t ver);
144     void insertSongs(QList<Song> *songs);
145     virtual void updateFinished();
146     void abortUpdate();
147 
148 protected:
149     bool createTable(const QString &q);
150     static Song getSong(const QSqlQuery &query);
151 
152 protected:
153     virtual void reset();
154     void clearSongs(bool startTransaction=true);
155 
156 protected:
157     static bool dbgEnabled;
158 
159     QString dbName;
160     QString dbFileName;
161     time_t currentVersion;
162     time_t newVersion;
163     QSqlDatabase *db;
164     QSqlQuery *insertSongQuery;
165     QElapsedTimer timer;
166     QString filter;
167     QString genreFilter;
168     QString yearFilter;
169     QMap<QString, QSet<QString> > detailsCache;
170 };
171 
172 #endif
173