From eaca1f34f3c8e308e191fd2ef8a0a489e48dd2da Mon Sep 17 00:00:00 2001 From: Craig Drummond Date: Sun, 16 Oct 2016 22:33:41 +0100 Subject: [PATCH] When listing albums where composer is used for artist grouping, place album artist name after album name if different from composer. Issue #896 --- CMakeLists.txt | 2 +- ChangeLog | 2 + context/songview.cpp | 2 +- db/librarydb.cpp | 72 +++++++++++++------- dbus/mpris.cpp | 4 +- devices/albumdetailsdialog.cpp | 2 +- devices/deviceoptions.cpp | 13 ++-- devices/filenameschemedialog.cpp | 2 +- devices/mtpdevice.cpp | 5 +- devices/musicbrainz.cpp | 2 +- gui/interfacesettings.cpp | 9 +-- http/httpserver.cpp | 6 +- models/mpdlibrarymodel.cpp | 8 +-- models/musiclibraryitemartist.cpp | 6 +- models/musiclibraryitemroot.cpp | 21 ++++-- models/musiclibraryitemsong.cpp | 67 ------------------- models/musiclibraryitemsong.h | 15 ++--- models/playqueuemodel.cpp | 2 +- mpd-interface/cuefile.cpp | 4 +- mpd-interface/mpdparseutils.cpp | 5 +- mpd-interface/song.cpp | 106 +++++++++++++++--------------- mpd-interface/song.h | 11 ++-- online/jamendoservice.cpp | 6 +- online/magnatuneservice.cpp | 2 +- online/onlinedbservice.cpp | 2 +- online/onlineservice.cpp | 4 +- online/soundcloudservice.cpp | 2 +- tags/tageditor.cpp | 33 +++++++--- tags/tags.cpp | 75 +++++++++------------ 29 files changed, 227 insertions(+), 263 deletions(-) delete mode 100644 models/musiclibraryitemsong.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d01aad027..d46e82a61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -417,7 +417,7 @@ set(CANTATA_CORE_SRCS ${CANTATA_CORE_SRCS} models/musiclibraryitemroot.cpp models/musiclibraryitemartist.cpp models/musiclibraryitemalbum.cpp models/musiclibraryproxymodel.cpp models/playlistsmodel.cpp models/playlistsproxymodel.cpp models/playqueuemodel.cpp models/proxymodel.cpp models/actionmodel.cpp models/musiclibraryitem.cpp models/browsemodel.cpp - models/searchmodel.cpp models/streamsmodel.cpp models/searchproxymodel.cpp models/musiclibraryitemsong.cpp + models/searchmodel.cpp models/streamsmodel.cpp models/searchproxymodel.cpp models/sqllibrarymodel.cpp models/mpdlibrarymodel.cpp models/mpdsearchmodel.cpp models/playqueueproxymodel.cpp mpd-interface/mpdconnection.cpp mpd-interface/mpdparseutils.cpp mpd-interface/mpdstats.cpp mpd-interface/mpdstatus.cpp mpd-interface/song.cpp mpd-interface/cuefile.cpp diff --git a/ChangeLog b/ChangeLog index d7884a229..6af27ab04 100644 --- a/ChangeLog +++ b/ChangeLog @@ -41,6 +41,8 @@ 28. Use same sidebar inactive tab mouse-over for all styles. 29. Fix colouring issues with some Kvantum styles. 30. Abort network connections before closing. +31. When listing albums where composer is used for artist grouping, place + album artist name after album name if different from composer. 2.0.1 ----- diff --git a/context/songview.cpp b/context/songview.cpp index d6a508f9c..be24e212a 100644 --- a/context/songview.cpp +++ b/context/songview.cpp @@ -587,7 +587,7 @@ void SongView::loadMetadata() tags.insert(pos++, createRow(i18n("Album"), currentSong.album)); tags.insert(pos++, createRow(i18n("Track number"), 0==currentSong.track ? QString() : QString::number(currentSong.track))); tags.insert(pos++, createRow(i18n("Disc number"), 0==currentSong.disc ? QString() : QString::number(currentSong.disc))); - tags.insert(pos++, createRow(i18n("Genre"), currentSong.genres().join(", "))); + tags.insert(pos++, createRow(i18n("Genre"), currentSong.displayGenre())); tags.insert(pos++, createRow(i18n("Year"), 0==currentSong.track ? QString() : QString::number(currentSong.year))); tags.insert(pos++, createRow(i18n("Comment"), fixNewLine(currentSong.comment()))); } diff --git a/db/librarydb.cpp b/db/librarydb.cpp index 591d30ef2..e5db4fa7b 100644 --- a/db/librarydb.cpp +++ b/db/librarydb.cpp @@ -32,7 +32,6 @@ #include static const int constSchemaVersion=4; -static const int constNumGenres=4; bool LibraryDb::dbgEnabled=false; #define DBUG if (dbgEnabled) qWarning() << metaObject()->className() << __FUNCTION__ << (void *)this @@ -202,7 +201,7 @@ static bool songSort(const Song &a, const Song &b) if (0!=cmp) { return cmp<0; } - cmp=a.genre.localeAwareCompare(b.genre); + cmp=a.displayGenre().localeAwareCompare(b.displayGenre()); if (0!=cmp) { return cmp<0; } @@ -401,9 +400,9 @@ public: whereClauses << QString("%1 %2 %3").arg(column, op, value.toString()); } else if ("genre"==column) { QString clause("("); - for (int i=0; ibindValue(":albumId", albumId); insertSongQuery->bindValue(":albumSort", albumSort(s)); insertSongQuery->bindValue(":title", s.title); - QStringList genres=s.genre.split(Song::constGenreSep); - for (int i=0; ibindValue(":genre"+QString::number(i+1), genre.isEmpty() ? constNullGenre : genre); + for (int i=0; ibindValue(":genre"+QString::number(i+1), s.genres[i].isEmpty() ? constNullGenre : s.genres[i]); } insertSongQuery->bindValue(":track", s.track); insertSongQuery->bindValue(":disc", s.disc); @@ -661,16 +658,21 @@ QList LibraryDb::getGenres() DBUG; QMap > map; if (0!=currentVersion && db) { - SqlQuery query("distinct genre1, genre2, genre3, genre4, artistId", *db); + QString queryStr("distinct "); + for (int i=0; i LibraryDb::getAlbums(const QString &artistId, const QStr if (0!=currentVersion && db) { bool wantModified=AS_Modified==sort; bool wantArtist=artistId.isEmpty(); - int artistCol=wantModified ? 6 : 5; - SqlQuery query(QLatin1String("album, albumId, albumSort, year, time")+(wantModified ? ", lastModified" : "")+(wantArtist ? ", artistId, artistSort" : ""), *db); + QString queryString="album, albumId, albumSort, artist, albumArtist, composer"; + for (int i=0; i LibraryDb::getAlbums(const QString &artistId, const QStr QMap entries; while (query.next()) { count++; - QString album=query.value(0).toString(); - QString albumId=query.value(1).toString(); - QString albumSort=query.value(2).toString(); - int year=query.value(3).toInt(); - int time=query.value(4).toInt(); - int lastModified=wantModified ? query.value(5).toInt() : 0; - QString artist=wantArtist ? query.value(artistCol).toString() : QString(); - QString artistSort=wantArtist ? query.value(artistCol+1).toString() : QString(); + int col=0; + QString album=query.value(col++).toString(); + QString albumId=query.value(col++).toString(); + QString albumSort=query.value(col++).toString(); + + Song s; + s.artist=query.value(col++).toString(); + s.albumartist=query.value(col++).toString(); + s.setComposer(query.value(col++).toString()); + s.album=album.isEmpty() ? albumId : album, albumId; + for (int i=0; i::iterator it=entries.find(key); @@ -981,7 +1007,7 @@ QSet LibraryDb::get(const QString &type) bool isGenre="genre"==type; if (isGenre) { - for (int i=0; i0) { metadataMap.insert("xesam:trackNumber", currentSong.track); diff --git a/devices/albumdetailsdialog.cpp b/devices/albumdetailsdialog.cpp index 472d31830..2a2c6afa6 100644 --- a/devices/albumdetailsdialog.cpp +++ b/devices/albumdetailsdialog.cpp @@ -318,7 +318,7 @@ Song AlbumDetailsDialog::toSong(QTreeWidgetItem *i, const CdAlbum &album) Song s; s.albumartist=album.artist; s.album=album.name; - s.genre=album.genre; + s.genres[0]=album.genre; s.year=album.year; s.disc=album.disc; s.artist=singleArtist->isChecked() ? s.albumartist : i->text(COL_ARTIST); diff --git a/devices/deviceoptions.cpp b/devices/deviceoptions.cpp index 9f0599034..a1f702dea 100644 --- a/devices/deviceoptions.cpp +++ b/devices/deviceoptions.cpp @@ -339,7 +339,10 @@ Song DeviceOptions::clean(const Song &s) const copy.artist=clean(copy.artist); copy.album=clean(copy.album); copy.title=clean(copy.title); - copy.genre=clean(copy.genre); + for (int i=0; idate).mid(0, 4).toUInt(); s.title=QString::fromUtf8(track->title); - s.genre=QString::fromUtf8(track->genre); + s.genres[0]=QString::fromUtf8(track->genre); s.track=track->tracknumber; s.time=(track->duration/1000.0)+0.5; s.size=track->filesize; @@ -432,7 +432,6 @@ void MtpConnection::updateLibrary(const DeviceOptions &opts) albumItem = artistItem->album(s); } MusicLibraryItemSong *songItem = new MusicLibraryItemSong(s, albumItem); - const QSet &songGenres=songItem->allGenres(); albumItem->append(songItem); #ifdef MTP_FAKE_ALBUMARTIST_SUPPORT @@ -962,7 +961,7 @@ void MtpConnection::putSong(const Song &s, bool fixVa, const DeviceOptions &opts meta->title=createString(song.title); meta->artist=createString(song.artist); meta->composer=createString(song.composer()); - meta->genre=createString(song.genre); + meta->genre=createString(song.genres[0]); meta->album=createString(song.album); meta->date=createString(QString().sprintf("%4d0101T0000.0", song.year)); meta->filename=createString(destName); diff --git a/devices/musicbrainz.cpp b/devices/musicbrainz.cpp index cd24ed62c..cc1a7a038 100644 --- a/devices/musicbrainz.cpp +++ b/devices/musicbrainz.cpp @@ -337,7 +337,7 @@ void MusicBrainz::lookup(bool full) song.albumartist=album.artist; song.album=album.name; - song.genre=album.genre; + song.genres[0]=album.genre; song.id=song.track=track->Position(); song.time=track->Length()/1000; song.disc=album.disc; diff --git a/gui/interfacesettings.cpp b/gui/interfacesettings.cpp index 57ee4657d..bdb4b2db3 100644 --- a/gui/interfacesettings.cpp +++ b/gui/interfacesettings.cpp @@ -107,6 +107,7 @@ static inline QString getStrValue(QComboBox *box) } static const char * constValueProperty="value"; +static const char * constSep=","; InterfaceSettings::InterfaceSettings(QWidget *p) : QWidget(p) @@ -232,9 +233,9 @@ InterfaceSettings::InterfaceSettings(QWidget *p) void InterfaceSettings::load() { - ignorePrefixes->setText(QStringList(Settings::self()->ignorePrefixes().toList()).join(QString(Song::constGenreSep))); - composerGenres->setText(QStringList(Settings::self()->composerGenres().toList()).join(QString(Song::constGenreSep))); - singleTracksFolders->setText(QStringList(Settings::self()->singleTracksFolders().toList()).join(QString(Song::constGenreSep))); + ignorePrefixes->setText(QStringList(Settings::self()->ignorePrefixes().toList()).join(QString(constSep))); + composerGenres->setText(QStringList(Settings::self()->composerGenres().toList()).join(QString(constSep))); + singleTracksFolders->setText(QStringList(Settings::self()->singleTracksFolders().toList()).join(QString(constSep))); selectEntry(cueSupport, Settings::self()->cueSupport()); #ifdef ENABLE_DEVICES_SUPPORT showDeleteAction->setChecked(Settings::self()->showDeleteAction()); @@ -310,7 +311,7 @@ void InterfaceSettings::load() static QSet toSet(const QString &str) { - QStringList parts=str.split(Song::constGenreSep, QString::SkipEmptyParts); + QStringList parts=str.split(constSep, QString::SkipEmptyParts); QSet set; foreach (QString s, parts) { set.insert(s.trimmed()); diff --git a/http/httpserver.cpp b/http/httpserver.cpp index 0583095df..9ef8399e0 100644 --- a/http/httpserver.cpp +++ b/http/httpserver.cpp @@ -187,8 +187,8 @@ QByteArray HttpServer::encodeUrl(const Song &s) if (!s.title.isEmpty()) { query.addQueryItem("title", s.title); } - if (!s.genre.isEmpty()) { - query.addQueryItem("genre", s.genre); + if (!s.genres[0].isEmpty()) { + query.addQueryItem("genre", s.firstGenre()); } if (s.disc) { query.addQueryItem("disc", QString::number(s.disc)); @@ -279,7 +279,7 @@ Song HttpServer::decodeUrl(const QUrl &url) const s.title=q.queryItemValue("title"); } if (q.hasQueryItem("genre")) { - s.genre=q.queryItemValue("genre"); + s.addGenre(q.queryItemValue("genre")); } if (q.hasQueryItem("disc")) { s.disc=q.queryItemValue("disc").toInt(); diff --git a/models/mpdlibrarymodel.cpp b/models/mpdlibrarymodel.cpp index 0beb256b4..bdc6ca114 100644 --- a/models/mpdlibrarymodel.cpp +++ b/models/mpdlibrarymodel.cpp @@ -67,7 +67,7 @@ QVariant MpdLibraryModel::data(const QModelIndex &index, int role) const Song song=static_cast(db)->getCoverSong(T_Album==topLevel() ? static_cast(item)->getArtistId() : item->getParent()->getId(), item->getId()); item->setSong(song); if (T_Genre==topLevel()) { - song.genre=item->getParent()->getParent()->getId(); + song.addGenre(item->getParent()->getParent()->getId()); } } v.setValue(item->getSong()); @@ -84,7 +84,7 @@ QVariant MpdLibraryModel::data(const QModelIndex &index, int role) const song.setArtistImageRequest(); } if (T_Genre==topLevel()) { - song.genre=item->getParent()->getId(); + song.addGenre(item->getParent()->getId()); } item->setSong(song); } @@ -182,7 +182,7 @@ void MpdLibraryModel::cover(const Song &song, const QImage &img, const QString & } switch(topLevel()) { case T_Genre: { - const Item *genre=root ? root->getChild(song.genre) : 0; + const Item *genre=root ? root->getChild(song.genres[0]) : 0; if (genre) { const Item *artist=static_cast(genre)->getChild(song.artistOrComposer()); if (artist) { @@ -234,7 +234,7 @@ void MpdLibraryModel::artistImage(const Song &song, const QImage &img, const QSt } switch(topLevel()) { case T_Genre: { - const Item *genre=root ? root->getChild(song.genre) : 0; + const Item *genre=root ? root->getChild(song.genres[0]) : 0; if (genre) { const Item *artist=static_cast(genre)->getChild(song.artistOrComposer()); if (artist) { diff --git a/models/musiclibraryitemartist.cpp b/models/musiclibraryitemartist.cpp index eb547385f..88f338ba3 100644 --- a/models/musiclibraryitemartist.cpp +++ b/models/musiclibraryitemartist.cpp @@ -50,7 +50,7 @@ bool MusicLibraryItemArtist::lessThan(const MusicLibraryItem *a, const MusicLibr MusicLibraryItemArtist::MusicLibraryItemArtist(const Song &song, MusicLibraryItemContainer *parent) : MusicLibraryItemContainer(song.artistOrComposer(), parent) , m_sortString(song.hasAlbumArtistSort() ? song.albumArtistSort() : QString()) - , m_actualArtist(song.hasComposer() && song.isComposerGenre(song.genre) ? song.albumArtist() : QString()) + , m_actualArtist(song.useComposer() ? song.albumArtist() : QString()) { } @@ -107,11 +107,11 @@ Song MusicLibraryItemArtist::coverSong() const //if (Song::useComposer() && !firstSong->song().composer().isEmpty()) { song.albumartist=firstSong->song().albumArtist(); //} - song.genre=firstSong->song().genre; + song.addGenre(firstSong->song().firstGenre()); song.setComposer(firstSong->song().composer()); } } - if (!m_actualArtist.isEmpty() && Song::isComposerGenre(song.genre)) { + if (!m_actualArtist.isEmpty() && song.useComposer()) { song.setComposerImageRequest(); } else { song.setArtistImageRequest(); diff --git a/models/musiclibraryitemroot.cpp b/models/musiclibraryitemroot.cpp index 376bb2391..c087d330e 100644 --- a/models/musiclibraryitemroot.cpp +++ b/models/musiclibraryitemroot.cpp @@ -134,7 +134,9 @@ void MusicLibraryItemRoot::getDetails(QSet &artists, QSet &alb albumArtists.insert(s.albumArtist()); composers.insert(s.composer()); albums.insert(s.album); - genres+=s.genres().toSet(); + for (int i=0; iitemType()) { foreach (const MusicLibraryItem *album, static_cast(child)->childItems()) { foreach (const MusicLibraryItem *song, static_cast(album)->childItems()) { @@ -143,7 +145,9 @@ void MusicLibraryItemRoot::getDetails(QSet &artists, QSet &alb albumArtists.insert(s.albumArtist()); composers.insert(s.composer()); albums.insert(s.album); - genres+=s.genres().toSet(); + for (int i=0; imultipleGenres() ? Song::combineGenres(track->allGenres()) : track->genre(); + QString trackGenre=track->song().genres[0]; + for (int i=1; isong().genres[i].isEmpty(); ++i) { + trackGenre+=","+track->song().genres[i]; + } if (!trackGenre.isEmpty() && trackGenre!=Song::unknown()) { - writer.writeAttribute(constGenreAttribute, song.genre); + writer.writeAttribute(constGenreAttribute, trackGenre); } if (Song::Playlist==song.type) { writer.writeAttribute(constPlaylistAttribute, constTrueValue); @@ -437,9 +444,9 @@ bool MusicLibraryItemRoot::fromXML(QXmlStreamReader &reader, const QString &base song.setAlbumArtistSort(attributes.value(constAlbumArtistSortAttribute).toString()); } if (attributes.hasAttribute(constGenreAttribute)) { - QStringList genres=attributes.value(constGenreAttribute).toString().split(Song::constGenreSep, QString::SkipEmptyParts); - foreach (const QString &genre, genres) { - song.addGenre(genre); + QStringList genres=attributes.value(constGenreAttribute).toString().split(",", QString::SkipEmptyParts); + for (int i=0; i - * - * ---- - * - * 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 "musiclibraryitemsong.h" - -MusicLibraryItemSong::~MusicLibraryItemSong() -{ - if (m_genres>(void *)1) { - delete m_genres; - m_genres=0; - } -} - -void MusicLibraryItemSong::setSong(const Song &s) -{ - m_song=s; - if (m_genres>(void *)1) { - delete m_genres; - m_genres=0; - } -} - -bool MusicLibraryItemSong::hasGenre(const QString &genre) const -{ - initGenres(); - return (void *)1==m_genres ? m_song.genre==genre : allGenres().contains(genre); -} - -QSet MusicLibraryItemSong::allGenres() const -{ - initGenres(); - return (void *)1==m_genres ? (QSet() << m_song.genre) : *m_genres; -} - -void MusicLibraryItemSong::initGenres() const -{ - // Reduce memory usage by only storing genres in a set if required... - if (0==m_genres) { - QStringList g=m_song.genres(); - if (g.count()<2) { - m_genres=(QSet *)1; - } else { - m_genres=new QSet(); - *m_genres=g.toSet(); - } - } -} diff --git a/models/musiclibraryitemsong.h b/models/musiclibraryitemsong.h index 38154f769..38d25bf9a 100644 --- a/models/musiclibraryitemsong.h +++ b/models/musiclibraryitemsong.h @@ -38,32 +38,25 @@ class MusicLibraryItemSong : public MusicLibraryItem { public: MusicLibraryItemSong(const Song &s, MusicLibraryItemContainer *parent) - : MusicLibraryItem(parent), m_song(s), m_genres(0) { } + : MusicLibraryItem(parent), m_song(s) { } - virtual ~MusicLibraryItemSong(); + virtual ~MusicLibraryItemSong() { } QString data() const { return m_song.displayTitle(); } const QString & file() const { return m_song.file; } - void setSong(const Song &s); + void setSong(const Song &s) { m_song=s; } void setFile(const QString &f) { m_song.file=f; } quint16 track() const { return m_song.track; } void setTrack(quint16 t) { m_song.track=t; } void setPlayed(bool p) { m_song.setPlayed(p); } quint16 disc() const { return m_song.disc; } quint32 time() const { return m_song.time; } - const QString & genre() const { return m_song.genre; } + QString genre() const { return m_song.firstGenre(); } const Song & song() const { return m_song; } Type itemType() const { return Type_Song; } - bool hasGenre(const QString &genre) const; - QSet allGenres() const; - bool multipleGenres() const { initGenres(); return m_genres>(void *)1; } - -private: - void initGenres() const; protected: Song m_song; - mutable QSet *m_genres; }; #endif diff --git a/models/playqueuemodel.cpp b/models/playqueuemodel.cpp index 255717ea8..768b7b219 100644 --- a/models/playqueuemodel.cpp +++ b/models/playqueuemodel.cpp @@ -1294,7 +1294,7 @@ static bool albumSort(const Song *s1, const Song *s2) static bool genreSort(const Song *s1, const Song *s2) { - int c=s1->genre.localeAwareCompare(s2->genre); + int c=s1->compareGenres(*s2); return c<0 || (c==0 && (*s1)<(*s2)); } diff --git a/mpd-interface/cuefile.cpp b/mpd-interface/cuefile.cpp index a7bbac1ba..ab287bb1b 100644 --- a/mpd-interface/cuefile.cpp +++ b/mpd-interface/cuefile.cpp @@ -154,7 +154,7 @@ static bool updateSong(const CueEntry &entry, const QString &nextIndex, Song &so song.artist=entry.artist; song.album=entry.album; song.albumartist=entry.albumArtist; - song.genre=entry.genre; + song.addGenre(entry.genre); song.year=entry.date.toInt(); song.time=(end-beginning)/constMsecPerSec; if (!entry.composer.isEmpty()) { @@ -180,7 +180,7 @@ static bool updateLastSong(const CueEntry &entry, Song &song) song.artist=entry.artist; song.album=entry.album; song.albumartist=entry.albumArtist; - song.genre=entry.genre; + song.addGenre(entry.genre); song.year=entry.date.toInt(); if (!entry.composer.isEmpty()) { song.setComposer(entry.composer); diff --git a/mpd-interface/mpdparseutils.cpp b/mpd-interface/mpdparseutils.cpp index 4c06dc955..4842130d5 100644 --- a/mpd-interface/mpdparseutils.cpp +++ b/mpd-interface/mpdparseutils.cpp @@ -365,14 +365,13 @@ Song MPDParseUtils::parseSong(const QList &lines, Location location) } } - if (Song::Playlist!=song.type && song.genre.isEmpty()) { - song.genre = Song::unknown(); + if (Song::Playlist!=song.type && song.genres[0].isEmpty()) { + song.addGenre(Song::unknown()); } if (Loc_Library==location) { song.guessTags(); song.fillEmptyFields(); - song.orderGenres(); } else if (Loc_Streams==location) { song.setName(getAndRemoveStreamName(song.file)); } else { diff --git a/mpd-interface/song.cpp b/mpd-interface/song.cpp index 943b821d8..b6ac0bac0 100644 --- a/mpd-interface/song.cpp +++ b/mpd-interface/song.cpp @@ -176,7 +176,9 @@ Song & Song::operator=(const Song &s) disc = s.disc; priority = s.priority; year = s.year; - genre = s.genre; + for (int i=0; i0 ? albumName+QLatin1String(" (")+QString::number(albumYear)+QLatin1Char(')') : albumName; } -QString Song::combineGenres(const QSet &genres) -{ - if (1==genres.size()) { - return *(genres.begin()); - } - - QStringList list=genres.toList(); - qSort(list); - QString g; - foreach (const QString &e, list) { - if (!g.isEmpty()) { - g+=constGenreSep; - } - g+=e; - } - return g; -} - static QSet prefixesToIngore=QSet() << QLatin1String("The"); QSet Song::ignorePrefixes() @@ -441,37 +425,23 @@ void Song::clear() // pos = 0; disc = 0; year = 0; - genre.clear(); + for (int i=0; i1) { - qSort(g); - genre=g.join(QString()+constGenreSep); + for (int i=0; i>(QDataStream &stream, Song &song) quint16 year; bool guessed; stream >> song.id >> song.file >> song.album >> song.artist >> song.albumartist >> song.title - >> song.genre >> song.disc >> song.priority >> song.time >> song.track >> year + >> song.disc >> song.priority >> song.time >> song.track >> year >> type >> guessed >> song.size >> song.extra >> song.extraFields; song.type=(Song::Type)type; song.year=year; song.guessed=guessed; + for (int i=0; i> song.genres[i]; + } + return stream; } #endif diff --git a/mpd-interface/song.h b/mpd-interface/song.h index d74f09106..b2ba0ed9b 100644 --- a/mpd-interface/song.h +++ b/mpd-interface/song.h @@ -47,7 +47,7 @@ struct Song Rating_Null = 0xFF }; - static const QLatin1Char constGenreSep; + static const int constNumGenres = 4; static const QLatin1Char constFieldSep; static const QSet & composerGenres(); @@ -90,7 +90,7 @@ struct Song QString artist; QString albumartist; QString title; - QString genre; + QString genres[constNumGenres]; QHash extra; quint16 extraFields; quint8 disc; @@ -120,7 +120,6 @@ struct Song static QString encodePath(const QString &file); static void clearKeyStore(int location); static QString displayAlbum(const QString &albumName, quint16 albumYear); - static QString combineGenres(const QSet &genres); static bool isComposerGenre(const QString &genre) { return composerGenres().contains(genre); } static QSet ignorePrefixes(); static void setIgnorePrefixes(const QSet &prefixes); @@ -142,8 +141,6 @@ struct Song quint16 setKey(int location); virtual void clear(); void addGenre(const QString &g); - QStringList genres() const; - void orderGenres(); QString entryName() const; QString artistOrComposer() const; QString albumName() const; @@ -153,7 +150,9 @@ struct Song QString displayTitle() const { return !albumartist.isEmpty() && albumartist!=artist ? artistSong() : title; } QString trackAndTitleStr(bool showArtistIfDifferent=true) const; QString toolTip() const; - QString displayGenre() const { return QString(genre).replace(constGenreSep, QLatin1String(", ")); } + QString displayGenre() const; + const QString & firstGenre() const { return genres[0]; } + int compareGenres(const Song &o) const; QString extraField(quint16 f) const { return hasExtraField(f) ? extra[f] : QString(); } bool hasExtraField(quint16 f) const { return extraFields&f; } diff --git a/online/jamendoservice.cpp b/online/jamendoservice.cpp index 59eb10e9c..f04f8c503 100644 --- a/online/jamendoservice.cpp +++ b/online/jamendoservice.cpp @@ -282,7 +282,7 @@ void JamendoXmlParser::parseSong(Song &song, const QString &albumGenre, QXmlStre { song.time=0; song.title=QString(); - song.genre=albumGenre; + song.genres[0]=albumGenre; while (!xml.atEnd()) { xml.readNext(); @@ -297,7 +297,7 @@ void JamendoXmlParser::parseSong(Song &song, const QString &albumGenre, QXmlStre } else if (QLatin1String("id3genre")==name && albumGenre.isEmpty()) { int g=xml.readElementText().toInt(); if (0!=g) { - song.genre=id3Genre(g); + song.genres[0]=id3Genre(g); } } else if (QLatin1String("id")==name) { song.file=xml.readElementText().trimmed(); @@ -393,7 +393,7 @@ Song & JamendoService::fixPath(Song &s) const { s.file=QString(constStreamUrl).replace("id=%1", "id="+s.file); s.file+=FMT_MP3==format ? QLatin1String("mp31") : QLatin1String("ogg2"); - s.genre=FMT_MP3==format ? QLatin1String("mp3") : QLatin1String("ogg"); + s.genres[0]=FMT_MP3==format ? QLatin1String("mp3") : QLatin1String("ogg"); s.type=Song::OnlineSvrTrack; s.setIsFromOnlineService(name()); return encode(s); diff --git a/online/magnatuneservice.cpp b/online/magnatuneservice.cpp index 452b9ce09..38f6330db 100644 --- a/online/magnatuneservice.cpp +++ b/online/magnatuneservice.cpp @@ -80,7 +80,7 @@ Song MagnatuneXmlParser::parseSong(QXmlStreamReader &xml) // foreach (const QString &g, genres) { // s.addGenre(g); // } - s.genre=genres.first(); + s.genres[0]=genres.first(); } else if (QLatin1String("seconds")==name) { s.time=value.toInt(); // } else if (QLatin1String("cover_small")==name) { diff --git a/online/onlinedbservice.cpp b/online/onlinedbservice.cpp index 9969be10d..dc8c7f3c3 100644 --- a/online/onlinedbservice.cpp +++ b/online/onlinedbservice.cpp @@ -145,7 +145,7 @@ void OnlineDbService::cover(const Song &song, const QImage &img, const QString & return; } - const Item *genre=root ? root->getChild(song.genre) : 0; + const Item *genre=root ? root->getChild(song.genres[0]) : 0; if (genre) { const Item *artist=static_cast(genre)->getChild(song.artistOrComposer()); if (artist) { diff --git a/online/onlineservice.cpp b/online/onlineservice.cpp index 552ed2ce2..7288b876a 100644 --- a/online/onlineservice.cpp +++ b/online/onlineservice.cpp @@ -39,7 +39,7 @@ Song & OnlineService::encode(Song &song) fixString(song.albumartist)+constDeliminator+ fixString(song.album)+constDeliminator+ fixString(song.title)+constDeliminator+ - fixString(song.genre)+constDeliminator+ + fixString(song.genres[0])+constDeliminator+ QString::number(song.time)+constDeliminator+ QString::number(song.year)+constDeliminator+ QString::number(song.track)+constDeliminator+ @@ -63,7 +63,7 @@ bool OnlineService::decode(Song &song) song.albumartist=parts.at(1); song.album=parts.at(2); song.title=parts.at(3); - song.genre=parts.at(4); + song.genres[0]=parts.at(4); song.time=parts.at(5).toUInt(); song.year=parts.at(6).toUInt(); song.track=parts.at(7).toUInt(); diff --git a/online/soundcloudservice.cpp b/online/soundcloudservice.cpp index fd2dcd2f7..7f3512fb5 100644 --- a/online/soundcloudservice.cpp +++ b/online/soundcloudservice.cpp @@ -137,7 +137,7 @@ void SoundCloudService::jobFinished() // We don't have a real artist name, but username is the most similar thing we have song.artist=details["user"].toMap()["username"].toString(); song.title=details["title"].toString(); - song.genre=details["genre"].toString(); + song.genres[0]=details["genre"].toString(); song.year=details["release_year"].toInt(); song.time=details["duration"].toUInt()/1000; song.fillEmptyFields(); diff --git a/tags/tageditor.cpp b/tags/tageditor.cpp index 05b65bb4e..1c4e6638a 100644 --- a/tags/tageditor.cpp +++ b/tags/tageditor.cpp @@ -56,7 +56,7 @@ static bool equalTags(const Song &a, const Song &b, bool compareCommon, bool composerSupport, bool commentSupport) { return (compareCommon || a.track==b.track) && a.year==b.year && a.disc==b.disc && - a.artist==b.artist && a.genre==b.genre && a.album==b.album && + a.artist==b.artist && a.genres[0]==b.genres[0] && a.album==b.album && a.albumartist==b.albumartist && (!composerSupport || a.composer()==b.composer()) && (!commentSupport || a.comment()==b.comment()) && (compareCommon || a.title==b.title); } @@ -77,6 +77,14 @@ static QString trim(QString str) { return str; } +// Split genres back out +static void splitGenres(Song &song) { + QStringList genres=song.genres[0].split(",", QString::SkipEmptyParts); + for (int i=0; i &songs, if (s.guessed) { song.revertGuessedTags(); } + // Store all Genres's in 1st genre + song.genres[0]=song.displayGenre(); + song.genres[1]=QString(); original.append(song); } @@ -275,7 +286,9 @@ TagEditor::TagEditor(QWidget *parent, const QList &songs, songArtists.insert(s.artist); songAlbumArtists.insert(s.albumartist); songAlbums.insert(s.album); - songGenres.insert(s.genre); + for (int i=0; i &songs, all.setComment(1==songComments.count() ? *(songComments.begin()) : QString()); all.albumartist=1==songAlbumArtists.count() ? *(songAlbumArtists.begin()) : QString(); all.album=1==songAlbums.count() ? *(songAlbums.begin()) : QString(); - all.genre=1==songGenres.count() ? *(songGenres.begin()) : QString(); + all.genres[0]=1==songGenres.count() ? *(songGenres.begin()) : QString(); all.year=1==songYears.count() ? *(songYears.begin()) : 0; all.disc=1==songDiscs.count() ? *(songDiscs.begin()) : 0; all.rating=Song::Rating_Null; @@ -409,7 +422,7 @@ void TagEditor::fillSong(Song &s, bool isAll, bool skipEmpty) const if (!isAll || 0!=disc->value()) { s.disc=disc->value(); } - setString(s.genre, trim(genre->text()), skipEmpty && (!haveAll || all.genre.isEmpty())); + setString(s.genres[0], trim(genre->text()), skipEmpty && (!haveAll || all.genres[0].isEmpty())); if (!isAll || 0!=year->value()) { s.year=year->value(); } @@ -431,7 +444,7 @@ void TagEditor::setVariousHint() if (commentSupport) { comment->setPlaceholderText(all.comment().isEmpty() && haveComments ? TagSpinBox::variousStr() : QString()); } - genre->setPlaceholderText(all.genre.isEmpty() && haveGenres ? TagSpinBox::variousStr() : QString()); + genre->setPlaceholderText(all.genres[0].isEmpty() && haveGenres ? TagSpinBox::variousStr() : QString()); disc->setVarious(0==all.disc && haveDiscs); year->setVarious(0==all.year && haveYears); if (ratingVarious) { @@ -482,7 +495,7 @@ void TagEditor::setLabelStates() albumLabel->setOn(o.album!=e.album); trackLabel->setOn(!isAll && o.track!=e.track); discLabel->setOn(o.disc!=e.disc); - genreLabel->setOn(o.genre!=e.genre); + genreLabel->setOn(o.genres[0]!=e.genres[0]); yearLabel->setOn(o.year!=e.year); if (ratingLabel) { ratingLabel->setOn(o.rating<=Song::Rating_Max && @@ -932,8 +945,8 @@ void TagEditor::updateEdited(bool isFromAll) if (all.album.isEmpty() && s.album.isEmpty() && !o.album.isEmpty()) { s.album=o.album; } - if (all.genre.isEmpty() && s.genre.isEmpty() && !o.genre.isEmpty()) { - s.genre=o.genre; + if (all.genres[0].isEmpty() && s.genres[0].isEmpty() && !o.genres[0].isEmpty()) { + s.genres[0]=o.genres[0]; } } @@ -967,7 +980,7 @@ void TagEditor::setSong(const Song &s) album->setText(s.album); track->setValue(s.track); disc->setValue(s.disc); - genre->setText(s.genre); + genre->setText(s.genres[0]); year->setValue(s.year); if (ratingWidget) { ratingWidget->setValue(s.rating); @@ -1162,6 +1175,8 @@ bool TagEditor::applyUpdates() } QString file=orig.filePath(); + splitGenres(orig); + splitGenres(edit); switch(Tags::update(baseDir+file, orig, edit, -1, commentSupport)) { case Tags::Update_Modified: edit.setComment(QString()); diff --git a/tags/tags.cpp b/tags/tags.cpp index e20b59011..c6c3f570f 100644 --- a/tags/tags.cpp +++ b/tags/tags.cpp @@ -328,7 +328,6 @@ static void readID3v2Tags(TagLib::ID3v2::Tag *tag, Song *song, ReplayGain *rg, Q if (!tcon.isEmpty()) { TagLib::ID3v2::FrameList::ConstIterator it = tcon.begin(); TagLib::ID3v2::FrameList::ConstIterator end = tcon.end(); - QSet genres; for (; it!=end; ++it) { TagLib::ID3v2::TextIdentificationFrame *f = static_cast(*it); @@ -349,13 +348,7 @@ static void readID3v2Tags(TagLib::ID3v2::Tag *tag, Song *song, ReplayGain *rg, Q } else { genre = tString2QString(*fIt); } - if (!genres.contains(genre)) { - genres.insert(genre); - if (!song->genre.isEmpty()) { - song->genre+=Song::constGenreSep; - } - song->genre+=genre; - } + song->addGenre(genre); } } } @@ -504,20 +497,18 @@ static bool writeID3v2Tags(TagLib::ID3v2::Tag *tag, const Song &from, const Song if (from.disc!=to.disc&& updateID3v2Tag(tag, "TPOS", 0==to.disc ? QString() : QString::number(to.disc))) { changed=true; } - DBUG << "genres" << from.genre << to.genre; - if (from.genre!=to.genre) { + DBUG << "genres" << from.genres << to.genres; + if (0!=from.compareGenres(to)) { tag->removeFrames("TCON"); - QStringList genres=to.genres(); - DBUG << "num genres:" << genres.count(); - if (genres.count()<2) { - DBUG << "set genre" << (genres.isEmpty() ? QString() : genres.first().trimmed()); - tag->setGenre(qString2TString(genres.isEmpty() ? QString() : genres.first().trimmed())); + if (to.genres[1].isEmpty()) { + DBUG << "set genre" << (to.firstGenre().trimmed()); + tag->setGenre(qString2TString(to.firstGenre().trimmed())); } else { - foreach (const QString &genre, genres) { + for(int i=0; iaddFrame(frame); - DBUG << "add genre" << genre.trimmed(); - frame->setText(qString2TString(genre.trimmed())); + DBUG << "add genre" << to.genres[i].trimmed(); + frame->setText(qString2TString(to.genres[i].trimmed())); } } changed=true; @@ -659,14 +650,13 @@ static bool writeAPETags(TagLib::APE::Tag *tag, const Song &from, const Song &to if (from.disc!=to.disc && updateAPETag(tag, "Disc", 0==to.disc ? QString() : QString::number(to.disc))) { changed=true; } - if (from.genre!=to.genre) { + if (0!=from.compareGenres(to)) { tag->removeItem("GENRE"); - QStringList genres=to.genres(); - if (genres.count()<2) { - tag->setGenre(qString2TString(genres.isEmpty() ? QString() : genres.first().trimmed())); + if (to.genres[1].isEmpty()) { + tag->setGenre(qString2TString(to.firstGenre().trimmed())); } else { - foreach (const QString &genre, genres) { - tag->addValue("GENRE", qString2TString(genre.trimmed()), false); + for(int i=0; iaddValue("GENRE", qString2TString(to.genres[i].trimmed()), false); } } changed=true; @@ -831,14 +821,13 @@ static bool writeVorbisCommentTags(TagLib::Ogg::XiphComment *tag, const Song &fr if (from.disc!=to.disc && updateVorbisCommentTag(tag, "DISCNUMBER", 0==to.disc ? QString() : QString::number(to.disc))) { changed=true; } - if (from.genre!=to.genre) { + if (0!=from.compareGenres(to)) { tag->removeField("GENRE"); - QStringList genres=to.genres(); - if (genres.count()<2) { - tag->setGenre(qString2TString(genres.isEmpty() ? QString() : genres.first().trimmed())); + if (to.genres[1].isEmpty()) { + tag->setGenre(qString2TString(to.firstGenre().trimmed())); } else { - foreach (const QString &genre, genres) { - tag->addField("GENRE", qString2TString(genre.trimmed()), false); + for(int i=0; iaddField("GENRE", qString2TString(to.genres[i].trimmed()), false); } } changed=true; @@ -965,14 +954,13 @@ static bool writeMP4Tags(TagLib::MP4::Tag *tag, const Song &from, const Song &to if (from.disc!=to.disc && updateMP4Tag(tag, "disk", 0==to.disc ? QString() : QString::number(to.disc))) { changed=true; } - if (from.genre!=to.genre) { - QStringList genres=to.genres(); - if (genres.count()<2) { - tag->setGenre(qString2TString(genres.isEmpty() ? QString() : genres.first().trimmed())); + if (0!=from.compareGenres(to)) { + if (to.genres[1].isEmpty()) { + tag->setGenre(qString2TString(to.firstGenre().trimmed())); } else { TagLib::StringList tagGenres; - foreach (const QString &genre, genres) { - tagGenres.append(qString2TString(genre.trimmed())); + for(int i=0; iitemListMap()["\251gen"]=tagGenres; } @@ -1083,14 +1071,13 @@ static bool writeASFTags(TagLib::ASF::Tag *tag, const Song &from, const Song &to if (from.disc!=to.disc && updateASFTag(tag, "WM/PartOfSet", 0==to.disc ? QString() : QString::number(to.disc))) { changed=true; } - if (from.genre!=to.genre) { + if (0!=from.compareGenres(to)) { tag->removeItem("WM/Genre"); - QStringList genres=to.genres(); - if (genres.count()<2) { - tag->setGenre(qString2TString(genres.isEmpty() ? QString() : genres.first().trimmed())); + if (to.genres[1].isEmpty()) { + tag->setGenre(qString2TString(to.firstGenre().trimmed())); } else { - foreach (const QString &genre, genres) { - tag->addAttribute("WM/Genre", qString2TString(genre.trimmed())); + for(int i=0; iaddAttribute("WM/Genre", qString2TString(to.genres[i].trimmed())); } } changed=true; @@ -1202,8 +1189,8 @@ static void readTags(const TagLib::FileRef fileref, Song *song, ReplayGain *rg, // readID3v1Tags(fileref, song, rg); } } - if (song && song->genre.isEmpty()) { - song->genre=tString2QString(tag->genre()); + if (song && song->genres[0].isEmpty()) { + song->addGenre(tString2QString(tag->genre())); } }