Permalink
Browse files

[infoscanner] add CategoriseAlbums to MusicInfoScanner as an improved…

… version of CheckForVariousArtists
  • Loading branch information...
1 parent 8f9ba90 commit 9f08d93e983d1f6aebbed5971ab79bd5cbd63126 Jonathan Marshall committed May 30, 2012
@@ -531,7 +531,9 @@ int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString&
}
}
- CheckForVariousArtists(songsToAdd);
+ VECALBUMS albums;
+ CategoriseAlbums(songsToAdd, albums);
+
if (!items.HasThumbnail())
UpdateFolderThumb(songsToAdd, items.GetPath());
@@ -627,6 +629,108 @@ static bool SortSongsByTrack(CSong *song, CSong *song2)
return song->iTrack < song2->iTrack;
}
+void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albums)
+{
+ /* Step 1: categorise on the album name */
+ map<string, vector<CSong *> > albumNames;
+ for (VECSONGS::iterator i = songsToCheck.begin(); i != songsToCheck.end(); ++i)
+ albumNames[i->strAlbum].push_back(&(*i));
+
+ /*
+ Step 2: Split into unique albums based on album name and album artist
+ In the case where the album artist is unknown, we use the primary artist
+ (i.e. first artist from each song).
+ */
+ albums.clear();
+ for (map<string, vector<CSong *> >::iterator i = albumNames.begin(); i != albumNames.end(); ++i)
+ {
+ // sort the songs by tracknumber to identify duplicate track numbers
+ vector<CSong *> &songs = i->second;
+ sort(songs.begin(), songs.end(), SortSongsByTrack);
+
+ // map the songs to their primary artists
+ bool compilation = !i->first.empty();
+ map<string, vector<CSong *> > artists;
+ for (vector<CSong *>::iterator j = songs.begin(); j != songs.end(); ++j)
+ {
+ CSong *song = *j;
+ // test for song overlap
+ if (j != songs.begin() && song->iTrack == (*(j-1))->iTrack)
+ compilation = false;
+
+ // get primary artist
+ string primary;
+ if (!song->albumArtist.empty())
+ {
+ primary = song->albumArtist[0];
+ compilation = false;
+ }
+ else if (!song->artist.empty())
+ primary = song->artist[0];
+
+ // add to the artist map
+ artists[primary].push_back(song);
+ }
+
+ /*
+ We have a compilation if
+ 1. album name is non-empty
+ 2. no tracks overlap
+ 3. no album artist is specified
+ 4. we have at least two different primary artists
+ */
+ if (compilation && artists.size() > 1)
+ {
+ artists.clear();
+ std::string various = g_localizeStrings.Get(340); // Various Artists
+ for (vector<CSong *>::iterator j = songs.begin(); j != songs.end(); ++j)
+ (*j)->albumArtist.push_back(various);
+ artists.insert(make_pair(various, songs));
+ }
+
+ /*
+ Step 3: Find the common albumartist for each song and assign
+ albumartist to those tracks that don't have it set.
+ */
+ for (map<string, vector<CSong *> >::iterator j = artists.begin(); j != artists.end(); ++j)
+ {
+ // find the common artist for these songs
+ vector<CSong *> &artistSongs = j->second;
+ vector<string> common = artistSongs.front()->albumArtist.empty() ? artistSongs.front()->artist : artistSongs.front()->albumArtist;
+ for (vector<CSong *>::iterator k = artistSongs.begin() + 1; k != artistSongs.end(); ++k)
+ {
+ unsigned int match = 0;
+ vector<string> &compare = (*k)->albumArtist.empty() ? (*k)->artist : (*k)->albumArtist;
+ for (; match < common.size(), match < compare.size(); match++)
+ {
+ if (compare[match] != common[match])
+ break;
+ }
+ common.erase(common.begin() + match, common.end());
+ }
+
+ /*
+ Step 4: Assign the album artist for each song that doesn't have it set
+ and add to the album vector
+ */
+ CAlbum album;
+ album.strAlbum = i->first;
+ album.artist = common;
+ for (vector<CSong *>::iterator k = artistSongs.begin(); k != artistSongs.end(); ++k)
+ {
+ if ((*k)->albumArtist.empty())
+ (*k)->albumArtist = common;
+ album.songs.push_back(*(*k));
+ // TODO: in future we may wish to union up the genres, for now we assume they're the same
+ if (album.genre.empty())
+ album.genre = (*k)->genre;
+ }
+
+ albums.push_back(album);
+ }
+ }
+}
+
void CMusicInfoScanner::CheckForVariousArtists(VECSONGS &songsToCheck)
{
// first, find all the album names for these songs
@@ -54,6 +54,29 @@ class CMusicInfoScanner : CThread, public IRunnable
void Stop();
void SetObserver(IMusicInfoScannerObserver* pObserver);
+ /*! \brief Categorise songs into albums
+ Albums are defined uniquely by the album name and album artist.
+
+ If albumartist is not available in a song, we determine it from the
+ common portion of each song's artist list.
+
+ eg the common artist for
+ Bob Dylan / Tom Petty / Roy Orbison
+ Bob Dylan / Tom Petty
+ would be "Bob Dylan / Tom Petty".
+
+ If all songs that share an album
+ 1. have a non-empty album name
+ 2. have at least two different primary artists
+ 3. have no album artist set
+ 4. and no track numbers overlap
+ we assume it is a various artists album, and set the albumartist field accordingly.
+
+ \param songs [in/out] list of songs to categorise - albumartist field may be altered.
+ \param albums [out] albums found within these songs.
+ */
+ static void CategoriseAlbums(VECSONGS &songs, VECALBUMS &albums);
+
static void CheckForVariousArtists(VECSONGS &songs);
static bool HasSingleAlbum(const VECSONGS &songs, CStdString &album, CStdString &artist);
@@ -1240,9 +1240,9 @@ void CGUIWindowMusicBase::UpdateThumb(const CAlbum &album, const CStdString &pat
songs.push_back(song);
}
}
- CMusicInfoScanner::CheckForVariousArtists(songs);
- CStdString album, artist;
- if (CMusicInfoScanner::HasSingleAlbum(songs, album, artist))
+ VECALBUMS albums;
+ CMusicInfoScanner::CategoriseAlbums(songs, albums);
+ if (albums.size() == 1)
{ // can cache as the folder thumb
CStdString folderThumb(CThumbnailCache::GetMusicThumb(albumPath));
CFile::Cache(albumThumb, folderThumb);

0 comments on commit 9f08d93

Please sign in to comment.