/* * Cantata Web * * Copyright (c) 2015 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "librarydb.h" #include #include #include #include #include static const int constSchemaVersion=1; bool LibraryDb::dbgEnabled=false; #define DBUG if (dbgEnabled) qWarning() << metaObject()->className() << __FUNCTION__ const QLatin1String LibraryDb::constNullGenre("-"); const QLatin1String LibraryDb::constArtistAlbumsSortYear("year"); const QLatin1String LibraryDb::constArtistAlbumsSortName("name"); const QLatin1String LibraryDb::constAlbumsSortAlArYr("al-ar-yr"); const QLatin1String LibraryDb::constAlbumsSortAlYrAr("al-yr-ar"); const QLatin1String LibraryDb::constAlbumsSortArAlYr("ar-al-yr"); const QLatin1String LibraryDb::constAlbumsSortArYrAl("ar-yr-al"); const QLatin1String LibraryDb::constAlbumsSortYrAlAr("yt-al-ar"); const QLatin1String LibraryDb::constAlbumsSortYrArAl("yr-ar-al"); static bool albumsSortAlArYr(const LibraryDb::Album &a, const LibraryDb::Album &b) { const QString &an=a.sort.isEmpty() ? a.name : a.sort; const QString &bn=b.sort.isEmpty() ? b.name : b.sort; int cmp=an.localeAwareCompare(bn); if (cmp!=0) { return cmp<0; } const QString &aa=a.artistSort.isEmpty() ? a.artist : a.artistSort; const QString &ba=b.artistSort.isEmpty() ? b.artist : b.artistSort; cmp=aa.localeAwareCompare(ba); if (cmp!=0) { return cmp<0; } return a.yearsetDatabaseName(dbName); if (!db->open()) { return false; } createTable("versions(collection integer, schema integer)"); QSqlQuery query("select collection, schema from versions", *db); int schemaVersion=0; if (query.next()) { currentVersion=query.value(0).toUInt(); schemaVersion=query.value(1).toUInt(); } if (schemaVersion>0 && schemaVersion!=constSchemaVersion) { QSqlQuery(*db).exec("delete from songs"); } if (0==currentVersion || (schemaVersion>0 && schemaVersion!=constSchemaVersion)) { QSqlQuery(*db).exec("delete from versions"); QSqlQuery(*db).exec("insert into versions (collection, schema) values(0, "+QString::number(constSchemaVersion)+")"); } DBUG << "Current version" << currentVersion; createTable("songs (" "version integer," "file string, " "artist string, " "artistId string, " "albumArtist string, " "artistSort string, " "composer string, " "album string, " "albumId string, " "albumSort string, " "title string, " "genre string, " "track integer, " "disc integer, " "time integer, " "year integer, " "type integer, " "primary key (version, file))"); return true; } void LibraryDb::insertSong(const Song &s) { if (!insertSongQuery) { insertSongQuery=new QSqlQuery(*db); insertSongQuery->prepare("insert into songs(version, file, artist, artistId, albumArtist, artistSort, composer, album, albumId, albumSort, title, genre, track, disc, time, year, type) " "values(:version, :file, :artist, :artistId, :albumArtist, :artistSort, :composer, :album, :albumId, :albumSort, :title, :genre, :track, :disc, :time, :year, :type)"); } insertSongQuery->bindValue(":version", (qulonglong)newVersion); insertSongQuery->bindValue(":file", s.file); insertSongQuery->bindValue(":artist", s.artist); insertSongQuery->bindValue(":artistId", s.artistOrComposer()); insertSongQuery->bindValue(":albumArtist", s.albumartist); insertSongQuery->bindValue(":artistSort", artistSort(s)); insertSongQuery->bindValue(":composer", s.composer()); insertSongQuery->bindValue(":album", s.album); insertSongQuery->bindValue(":albumId", s.albumId()); insertSongQuery->bindValue(":albumSort", albumSort(s)); insertSongQuery->bindValue(":title", s.displayTitle()); insertSongQuery->bindValue(":genre", s.genre.isEmpty() ? constNullGenre : s.genre); insertSongQuery->bindValue(":track", s.track); insertSongQuery->bindValue(":disc", s.disc); insertSongQuery->bindValue(":time", s.time); insertSongQuery->bindValue(":year", s.year); insertSongQuery->bindValue(":type", s.type); if (!insertSongQuery->exec()) { qWarning() << "insert failed" << insertSongQuery->lastError() << newVersion << s.file; } } QList LibraryDb::getGenres() { DBUG; QMap map; if (0!=currentVersion) { if (!genresQuery) { genresQuery=new QSqlQuery(*db); genresQuery->prepare("select distinct genre, artistId from songs"); } genresQuery->exec(); DBUG << "genresquery" << genresQuery->executedQuery() << genresQuery->size(); while (genresQuery->next()) { map[genresQuery->value(0).toString()]++; } } QList genres; QMap::ConstIterator it=map.constBegin(); QMap::ConstIterator end=map.constEnd(); for (; it!=end; ++it) { DBUG << it.key(); genres.append(Genre(it.key(), it.value())); } qSort(genres); return genres; } QList LibraryDb::getArtists(const QString &genre) { DBUG << genre; QMap sortMap; QMap albumMap; if (0!=currentVersion) { QSqlQuery *query=0; if (genre.isEmpty()) { if (!artistsQuery) { artistsQuery=new QSqlQuery(*db); artistsQuery->prepare("select distinct artistId, albumId, artistSort from songs"); } query=artistsQuery; } else { if (!artistsGenreQuery) { artistsGenreQuery=new QSqlQuery(*db); artistsGenreQuery->prepare("select distinct artistId, albumId, artistSort from songs where genre=:genre"); } query=artistsGenreQuery; artistsGenreQuery->bindValue(":genre", genre); } query->exec(); DBUG << "artistsquery" << query->executedQuery() << query->size(); while (query->next()) { QString artist=query->value(0).toString(); albumMap[artist]++; sortMap[artist]=query->value(2).toString(); } } QList artists; QMap::ConstIterator it=albumMap.constBegin(); QMap::ConstIterator end=albumMap.constEnd(); for (; it!=end; ++it) { DBUG << it.key(); artists.append(Artist(it.key(), sortMap[it.key()], it.value())); } qSort(artists); return artists; } QList LibraryDb::getAlbums(const QString &artistId, const QString &genre, const QString &sort) { DBUG << artistId << genre; QList albums; if (0!=currentVersion) { QSqlQuery *query=0; if (artistId.isEmpty()) { if (genre.isEmpty()) { if (!albumsNoArtistQuery) { albumsNoArtistQuery=new QSqlQuery(*db); albumsNoArtistQuery->prepare("select distinct album, albumId, albumSort, artistId, artistSort from songs"); } query=albumsNoArtistQuery; } else { if (!albumsGenreNoArtistQuery) { albumsGenreNoArtistQuery=new QSqlQuery(*db); albumsGenreNoArtistQuery->prepare("select distinct album, albumId, albumSort, artistId, artistSort from songs where genre=:genre"); } query=albumsGenreNoArtistQuery; albumsGenreNoArtistQuery->bindValue(":genre", genre); } } else { if (genre.isEmpty()) { if (!albumsQuery) { albumsQuery=new QSqlQuery(*db); albumsQuery->prepare("select distinct album, albumId, albumSort from songs where artistId=:artistId"); } query=albumsQuery; } else { if (!albumsGenreQuery) { albumsGenreQuery=new QSqlQuery(*db); albumsGenreQuery->prepare("select distinct album, albumId, albumSort from songs where artistId=:artistId and genre=:genre"); } query=albumsGenreQuery; albumsGenreQuery->bindValue(":genre", genre); } query->bindValue(":artistId", artistId); } query->exec(); DBUG << "albumsquery" << query->executedQuery() << query->size(); while (query->next()) { QSqlQuery *detailsQuery=0; if (genre.isEmpty()) { if (!albumsDetailsQuery) { albumsDetailsQuery=new QSqlQuery(*db); albumsDetailsQuery->prepare("select avg(year), count(track), sum(time) from songs where artistId=:artistId and albumId=:albumId"); } detailsQuery=albumsDetailsQuery; } else { if (!albumsDetailsGenreQuery) { albumsDetailsGenreQuery=new QSqlQuery(*db); albumsDetailsGenreQuery->prepare("select avg(year), count(track), sum(time) from songs where artistId=:artistId and albumId=:albumId and genre=:genre"); } detailsQuery=albumsDetailsGenreQuery; albumsDetailsGenreQuery->bindValue(":genre", genre ); } QString album=query->value("album").toString(); QString albumId=query->value("albumId").toString(); QString albumSort=query->value("albumSort").toString(); QString artist=artistId.isEmpty() ? query->value("artistId").toString() : QString(); QString artistSort=artistId.isEmpty() ? query->value("artistSort").toString() : QString(); detailsQuery->bindValue(":artistId", artistId.isEmpty() ? artist : artistId); detailsQuery->bindValue(":albumId", albumId); detailsQuery->exec(); DBUG << "albumdetailsquery" << detailsQuery->size(); if (detailsQuery->next()) { albums.append(Album(album, albumId, albumSort, artist, artistSort, detailsQuery->value(0).toInt(), detailsQuery->value(1).toInt(), detailsQuery->value(2).toInt())); } } } if (sort==constAlbumsSortAlYrAr) { qSort(albums.begin(), albums.end(), albumsSortAlYrAr); } else if (sort==constAlbumsSortArAlYr) { qSort(albums.begin(), albums.end(), albumsSortArAlYr); } else if (sort==constAlbumsSortArYrAl) { qSort(albums.begin(), albums.end(), albumsSortArYrAl); } else if (sort==constAlbumsSortYrAlAr) { qSort(albums.begin(), albums.end(), albumsSortYrAlAr); } else if (sort==constAlbumsSortYrArAl) { qSort(albums.begin(), albums.end(), albumsSortYrArAl); } else if (sort==constAlbumsSortAlArYr) { qSort(albums.begin(), albums.end(), albumsSortAlArYr); } else if (constArtistAlbumsSortName==sort) { qSort(albums.begin(), albums.end(), albumsSortArAlYr); } else { qSort(albums.begin(), albums.end(), albumsSortArYrAl); } return albums; } QList LibraryDb::getTracks(const QString &artistId, const QString &albumId, const QString &genre, const QString &sort) { DBUG << artistId << albumId << genre << sort; QList songs; if (0!=currentVersion) { QSqlQuery *query=0; if (artistId.isEmpty() || albumId.isEmpty()) { if (!tracksWildcardQuery) { tracksWildcardQuery=new QSqlQuery(*db); tracksWildcardQuery->prepare("select * from songs where artistId like :artistId and albumId like :albumId and genre like :genre"); } query=tracksWildcardQuery; tracksWildcardQuery->bindValue(":genre", genre.isEmpty() ? "%" : genre); } else if (genre.isEmpty()) { if (!tracksQuery) { tracksQuery=new QSqlQuery(*db); tracksQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId"); } query=tracksQuery; } else { if (!tracksGenreQuery) { tracksGenreQuery=new QSqlQuery(*db); tracksGenreQuery->prepare("select * from songs where artistId=:artistId and albumId=:albumId and genre=:genre"); } query=tracksGenreQuery; tracksGenreQuery->bindValue(":genre", genre); } query->bindValue(":artistId", artistId.isEmpty() ? "%" : artistId); query->bindValue(":albumId", albumId.isEmpty() ? "%" : albumId); query->exec(); DBUG << "tracksquery" << query->executedQuery() << query->size(); while (query->next()) { songs.append(getSong(query)); } } if (sort==constAlbumsSortAlYrAr) { qSort(songs.begin(), songs.end(), songsSortAlYrAr); } else if (sort==constAlbumsSortArAlYr) { qSort(songs.begin(), songs.end(), songsSortArAlYr); } else if (sort==constAlbumsSortArYrAl) { qSort(songs.begin(), songs.end(), songsSortArYrAl); } else if (sort==constAlbumsSortYrAlAr) { qSort(songs.begin(), songs.end(), songsSortYrAlAr); } else if (sort==constAlbumsSortYrArAl) { qSort(songs.begin(), songs.end(), songsSortYrArAl); } else if (sort==constAlbumsSortAlArYr) { qSort(songs.begin(), songs.end(), songsSortAlArYr); } else if (constArtistAlbumsSortName==sort) { qSort(songs.begin(), songs.end(), songsSortArAlYr); } else { qSort(songs.begin(), songs.end(), songsSortArYrAl); } return songs; } void LibraryDb::updateStarted(time_t ver) { newVersion=ver; timer.start(); db->transaction(); DBUG; } void LibraryDb::insertSongs(QList *songs) { // DBUG << (void *)songs; if (!songs) { return; } foreach (const Song &s, *songs) { insertSong(s); } delete songs; } void LibraryDb::updateFinished() { QSqlQuery("update versions set collection ="+QString::number(newVersion)).exec(); if (currentVersion>0) { QSqlQuery("delete from songs where version = "+QString::number(currentVersion)).exec(); } db->commit(); currentVersion=newVersion; DBUG << timer.elapsed(); } void LibraryDb::createTable(const QString &q) { QSqlQuery query("create table if not exists "+q); if (!query.exec()) { qWarning() << "Failed to create table" << query.lastError(); ::exit(-1); } } Song LibraryDb::getSong(QSqlQuery *query) { Song s; s.file=query->value("file").toString(); s.artist=query->value("artist").toString(); s.albumartist=query->value("albumArtist").toString(); s.setComposer(query->value("composer").toString()); s.album=query->value("album").toString(); QString val=query->value("albumId").toString(); if (!val.isEmpty() && val!=s.album) { s.setMbAlbumId(val); } s.title=query->value("title").toString(); s.genre=query->value("genre").toString(); s.track=query->value("track").toUInt(); s.disc=query->value("disc").toUInt(); s.time=query->value("time").toUInt(); s.year=query->value("year").toUInt(); s.type=(Song::Type)query->value("type").toUInt(); val=query->value("artistSort").toString(); if (!val.isEmpty() && val!=s.albumArtist()) { s.setArtistSort(val); } val=query->value("albumSort").toString(); if (!val.isEmpty() && val!=s.album) { s.setAlbumSort(val); } return s; }