diff --git a/db/librarydb.cpp b/db/librarydb.cpp index 8ba86fecc..f41c97dbd 100644 --- a/db/librarydb.cpp +++ b/db/librarydb.cpp @@ -424,6 +424,7 @@ bool LibraryDb::init(QString &dbName) "disc integer, " "time integer, " "year integer, " + "type integer, " "primary key (version, file))"); return true; } @@ -432,8 +433,8 @@ 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) " - "values(:version, :file, :artist, :artistId, :albumArtist, :artistSort, :composer, :album, :albumId, :albumSort, :title, :genre, :track, :disc, :time, :year)"); + 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); @@ -451,6 +452,7 @@ void LibraryDb::insertSong(const Song &s) 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; } @@ -734,6 +736,7 @@ Song LibraryDb::getSong(QSqlQuery *query) 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); diff --git a/mpd-interface/mpdconnection.cpp b/mpd-interface/mpdconnection.cpp index 09040d777..951c36d4c 100644 --- a/mpd-interface/mpdconnection.cpp +++ b/mpd-interface/mpdconnection.cpp @@ -1896,7 +1896,7 @@ bool MPDConnection::listDirInfo(const QString &dir) if (response.ok) { QSet childDirs; QList *songs=new QList(); - MPDParseUtils::parseLibraryItems(response.data, *songs, !topLevel, &childDirs); + MPDParseUtils::parseLibraryItems(response.data, details.dir, ver, *songs, !topLevel, &childDirs); emit librarySongs(songs); foreach (const QString &child, childDirs) { if (!listDirInfo(child)) { diff --git a/mpd-interface/mpdparseutils.cpp b/mpd-interface/mpdparseutils.cpp index feb24dac2..e589f99c6 100644 --- a/mpd-interface/mpdparseutils.cpp +++ b/mpd-interface/mpdparseutils.cpp @@ -541,12 +541,12 @@ void MPDParseUtils::setGroupSingle(bool g) } #ifdef CANTATA_WEB -void MPDParseUtils::parseLibraryItems(const QByteArray &data, QList &songs, bool parsePlaylists, QSet *childDirs) +void MPDParseUtils::parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, QList &songs, bool parsePlaylists, QSet *childDirs) { QList currentItem; QList lines = data.split('\n'); int amountOfLines = lines.size(); - + for (int i = 0; i < amountOfLines; i++) { const QByteArray &line=lines.at(i); if (childDirs && line.startsWith(constDirectoryKey)) { @@ -559,12 +559,131 @@ void MPDParseUtils::parseLibraryItems(const QByteArray &data, QList &songs if (currentSong.file.isEmpty()) { continue; } + + if (Song::Playlist==currentSong.type && !songs.isEmpty()) { - // lsinfo / will return all stored playlists - but this is deprecated. - if (Song::Playlist==currentSong.type&& !parsePlaylists) { + // lsinfo / will return all stored playlists - but this is deprecated. + if (!parsePlaylists) { + continue; + } + + Song firstSong=songs.at(0); + QList cueSongs; // List of songs from cue file + QSet cueFiles; // List of source (flac, mp3, etc) files referenced in cue file + + DBUG << "Got playlist item" << currentSong.file; + + bool canSplitCue=mpdVersion>=CANTATA_MAKE_VERSION(0,17,0); + bool parseCue=canSplitCue && currentSong.isCueFile() && !mpdDir.startsWith(constHttpProtocol) && QFile::exists(mpdDir+currentSong.file); + bool cueParseStatus=false; + if (parseCue) { + DBUG << "Parsing cue file:" << currentSong.file << "mpdDir:" << mpdDir; + cueParseStatus=CueFile::parse(currentSong.file, mpdDir, cueSongs, cueFiles); + if (!cueParseStatus) { + DBUG << "Failed to parse cue file!"; + continue; + } else DBUG << "Parsed cue file, songs:" << cueSongs.count() << "files:" << cueFiles; + } + if (cueParseStatus && cueSongs.count()>=songs.count() && + (cueFiles.count() fixedCueSongs; // Songs taken from cueSongs that have been updated... + + if (songs.size()==cueFiles.size()) { + quint32 albumTime=0; + QMap origFiles; + foreach (const Song &s, songs) { + origFiles.insert(s.file, s); + albumTime+=s.time; + } + DBUG << "Original files:" << origFiles.keys(); + + bool setTimeFromSource=origFiles.size()==cueSongs.size(); + quint32 usedAlbumTime=0; + foreach (const Song &orig, cueSongs) { + Song s=orig; + Song albumSong=origFiles[s.name()]; + s.setName(QString()); // CueFile has placed source file name here! + if (s.artist.isEmpty() && !albumSong.artist.isEmpty()) { + s.artist=albumSong.artist; + DBUG << "Get artist from album" << albumSong.artist; + } + if (s.composer().isEmpty() && !albumSong.composer().isEmpty()) { + s.setComposer(albumSong.composer()); + DBUG << "Get composer from album" << albumSong.composer(); + } + if (s.album.isEmpty() && !albumSong.album.isEmpty()) { + s.album=albumSong.album; + DBUG << "Get album from album" << albumSong.album; + } + if (s.albumartist.isEmpty() && !albumSong.albumartist.isEmpty()) { + s.albumartist=albumSong.albumartist; + DBUG << "Get albumartist from album" << albumSong.albumartist; + } + if (0==s.year && 0!=albumSong.year) { + s.year=albumSong.year; + DBUG << "Get year from album" << albumSong.year; + } + if (0==s.time && setTimeFromSource) { + s.time=albumSong.time; + } else if (0!=albumTime && 1==cueFiles.size()) { + // Try to set duration of last track by subtracting previous track durations from album duration... + if (0==s.time) { + s.time=albumTime-usedAlbumTime; + } else { + usedAlbumTime+=s.time; + } + } + fixedCueSongs.append(s); + } + canUseCueFileTracks=true; + } else DBUG << "ERROR: file count mismatch" << songs.size() << cueFiles.size(); + + if (!canUseCueFileTracks) { + // Album had a different number of source files to the CUE file. If so, then we need to ensure + // all tracks have meta data - otherwise just fallback to listing file + cue + foreach (const Song &orig, cueSongs) { + Song s=orig; + s.setName(QString()); // CueFile has placed source file name here! + if (s.artist.isEmpty() || s.album.isEmpty()) { + break; + } + fixedCueSongs.append(s); + } + + if (fixedCueSongs.count()==cueSongs.count()) { + canUseCueFileTracks=true; + } else DBUG << "ERROR: Not all cue tracks had meta data"; + } + + if (canUseCueFileTracks) { + songs=cueSongs; + continue; + } + } + + if (!firstSong.albumArtist().isEmpty() && !firstSong.album.isEmpty()) { + currentSong.albumartist=firstSong.albumArtist(); + currentSong.album=firstSong.album; + songs.append(currentSong); + } continue; } - + currentSong.fillEmptyFields(); songs.append(currentSong); } diff --git a/mpd-interface/mpdparseutils.h b/mpd-interface/mpdparseutils.h index 31e927497..3666a0096 100644 --- a/mpd-interface/mpdparseutils.h +++ b/mpd-interface/mpdparseutils.h @@ -78,7 +78,7 @@ namespace MPDParseUtils extern bool groupSingle(); extern void setGroupSingle(bool g); #ifdef CANTATA_WEB - extern void parseLibraryItems(const QByteArray &data, QList &songs, bool parsePlaylists=true, QSet *childDirs=0); + extern void parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, QList &songs, bool parsePlaylists=true, QSet *childDirs=0); #else extern void parseLibraryItems(const QByteArray &data, const QString &mpdDir, long mpdVersion, bool isMopidy, MusicLibraryItemRoot *rootItem, bool parsePlaylists=true, diff --git a/web/TODO b/web/TODO index d70f89d89..c18ac0dcd 100644 --- a/web/TODO +++ b/web/TODO @@ -31,7 +31,7 @@ Navbar could be taller - skinny and control buttons close + |> buttons quite close together General: -- Cue files +- Cue files. Code re-added, but needs checking! - Multiple genres - WebApp icon - Long press to bring up menu