Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[musicscanner+db] reroll the infoscanner & music file addition to sto…

…p CSong being a copy of CMusicInfoTag
  • Loading branch information...
commit 932444e5b4192f5dad994acdb1267632d386b2bc 1 parent 5d87a9d
@night199uk authored
View
1  xbmc/FileItem.cpp
@@ -286,6 +286,7 @@ CFileItem::CFileItem(const CArtist& artist)
m_bIsFolder = true;
URIUtils::AddSlashAtEnd(m_strPath);
GetMusicInfoTag()->SetArtist(artist.strArtist);
+ GetMusicInfoTag()->SetDatabaseId(artist.idArtist, "artist");
}
CFileItem::CFileItem(const CGenre& genre)
View
323 xbmc/music/MusicDatabase.cpp
@@ -266,141 +266,188 @@ void CMusicDatabase::CreateViews()
" artist.idArtist = artistinfo.idArtist");
}
-int CMusicDatabase::AddAlbum(const CAlbum &album, vector<int> &songIDs)
+int CMusicDatabase::AddFileItem(const CFileItem& pItem, const VECALBUMS& albumHints)
{
- // add the album
- int idAlbum = AddAlbum(album.strAlbum, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(album.genre, g_advancedSettings.m_musicItemSeparator), album.iYear, album.bCompilation);
+ const MUSIC_INFO::CMusicInfoTag& tag = *pItem.GetMusicInfoTag();
- SetArtForItem(idAlbum, "album", album.art);
+ // Check for a valid path name
+ CStdString strPathAndFileName = tag.GetURL().IsEmpty() ? pItem.GetPath() : tag.GetURL();
+ if (strPathAndFileName.IsEmpty())
+ {
+ CLog::Log(LOGERROR, "skipping song since it doesn't have a filename");
+ return -1;
+ }
+
+ // Find any album hints for this song
+ bool albumCompilationHint;
+ std::vector<std::string> albumArtistHint;
+ std::map<std::string, std::string> albumArtHint;
+ for (VECALBUMS::const_iterator it = albumHints.begin(); it != albumHints.end(); ++it)
+ {
+ if (it->strAlbum == tag.GetAlbum())
+ {
+ albumArtistHint = it->artist;
+ albumCompilationHint = it->bCompilation;
+ albumArtHint = it->art;
+ }
+ }
+
+ int idAlbum;
+ if (!tag.GetAlbumArtist().empty())
+ idAlbum = AddAlbum(tag.GetAlbum(), StringUtils::Join(tag.GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator), StringUtils::Join(tag.GetGenre(), g_advancedSettings.m_musicItemSeparator), tag.GetYear(), tag.GetCompilation());
+ else
+ idAlbum = AddAlbum(tag.GetAlbum(), StringUtils::Join(albumArtistHint, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(tag.GetGenre(), g_advancedSettings.m_musicItemSeparator), tag.GetYear(), albumCompilationHint);
+
+ bool bHasKaraoke = false;
+#ifdef HAS_KARAOKE
+ bHasKaraoke = CKaraokeLyricsFactory::HasLyrics(strPathAndFileName);
+#endif
+
+ int idSong = AddSong(tag.GetTitle(),
+ StringUtils::Join(tag.GetArtist(), g_advancedSettings.m_musicItemSeparator),
+ idAlbum,
+ strPathAndFileName,
+ StringUtils::Join(tag.GetGenre(), g_advancedSettings.m_musicItemSeparator),
+ tag.GetTrackNumber(),
+ tag.GetDuration(),
+ tag.GetYear(),
+ tag.GetMusicBrainzTrackID(),
+ tag.GetComment(),
+ pItem.m_lStartOffset,
+ pItem.m_lEndOffset);
+
+ DeleteValues("song_artist", PrepareSQL("idSong = %d", idSong));
+ DeleteValues("song_genre", PrepareSQL("idSong = %d", idSong));
+
+ // Add the artists
+ const vector<string>& albumArtist = tag.GetAlbumArtist().empty() ? albumArtistHint : tag.GetAlbumArtist();
+ for (unsigned int index = 0; index < albumArtist.size(); index++)
+ {
+ int idAlbumArtist = AddArtist(albumArtist[index]);
+ AddAlbumArtist(idAlbumArtist, idAlbum, index > 0 ? true : false, index);
+ }
+
+ for (unsigned int index = 0; index < tag.GetArtist().size(); index++)
+ {
+ int idArtist = AddArtist(tag.GetArtist()[index]);
+ AddSongArtist(idArtist, idSong, index > 0 ? true : false, index);
+ }
+
+ unsigned int index = 0;
+ // If this is karaoke song, change the genre to 'Karaoke' (and add it if it's not there)
+ if ( bHasKaraoke && g_advancedSettings.m_karaokeChangeGenreForKaraokeSongs )
+ {
+ int idGenre = AddGenre("Karaoke");
+ AddSongGenre(idGenre, idSong, index);
+ AddAlbumGenre(idGenre, idAlbum, index++);
+ }
+
+ vector<string> genre = tag.GetGenre();
+ for (vector<string>::const_iterator i = genre.begin(); i != genre.end(); ++i)
+ {
+ // index will be wrong for albums, but ordering is not all that relevant
+ // for genres anyway
+ int idGenre = AddGenre(*i);
+ AddSongGenre(idGenre, idSong, index);
+ AddAlbumGenre(idGenre, idAlbum, index++);
+ }
+
+ // Add karaoke information
+ if (bHasKaraoke)
+ AddKaraokeData(idSong, strPathAndFileName);
+
+ if (!pItem.GetThumbnailImage().empty())
+ SetArtForItem(idSong, "song", "thumb", pItem.GetThumbnailImage());
- // add the songs
- for (VECSONGS::const_iterator i = album.songs.begin(); i != album.songs.end(); ++i)
- songIDs.push_back(AddSong(*i, false, idAlbum));
+ if (!albumArtHint.empty())
+ SetArtForItem(idAlbum, "album", albumArtHint);
- return idAlbum;
+ AnnounceUpdate("song", idSong);
+
+ return idSong;
}
-int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
+int CMusicDatabase::AddSong(const CStdString& strTitle, const CStdString& strArtists, int idAlbum, const CStdString& strPathAndFileName, const CStdString& strGenres, int iTrack, int iDuration, int iYear, const CStdString& strMusicBrainzTrackID, const CStdString& strComment, int iStartOffset, int iEndOffset)
{
+
int idSong = -1;
CStdString strSQL;
try
{
// We need at least the title
- if (song.strTitle.IsEmpty())
+ if (strTitle.IsEmpty())
return -1;
- CStdString strPath, strFileName;
- URIUtils::Split(song.strFileName, strPath, strFileName);
-
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
+ CStdString strPath, strFileName;
+ URIUtils::Split(strPathAndFileName, strPath, strFileName);
int idPath = AddPath(strPath);
- if (idAlbum < 0)
- {
- if (!song.albumArtist.empty()) // have an album artist
- idAlbum = AddAlbum(song.strAlbum, StringUtils::Join(song.albumArtist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator), song.iYear, song.bCompilation);
- else
- idAlbum = AddAlbum(song.strAlbum, StringUtils::Join(song.artist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator), song.iYear, song.bCompilation);
- }
- DWORD crc = ComputeCRC(song.strFileName);
+ DWORD crc = ComputeCRC(strPathAndFileName);
- bool bInsert = true;
- bool bHasKaraoke = false;
-#ifdef HAS_KARAOKE
- bHasKaraoke = CKaraokeLyricsFactory::HasLyrics( song.strFileName );
-#endif
+ int idSong;
+ strSQL = PrepareSQL("SELECT idSong FROM song WHERE idPath = %i AND strFileName = '%s'",
+ idPath, strFileName.c_str());
+ if (!m_pDS->query(strSQL.c_str()))
+ return -1;
- if (bCheck)
+ if (m_pDS->num_rows() == 0)
{
- strSQL=PrepareSQL("select * from song where idAlbum=%i and dwFileNameCRC='%ul' and strTitle='%s'",
- idAlbum, crc, song.strTitle.c_str());
-
- if (!m_pDS->query(strSQL.c_str()))
- return -1;
-
- if (m_pDS->num_rows() != 0)
- {
- idSong = m_pDS->fv("idSong").get_asInt();
- bInsert = false;
- }
m_pDS->close();
- }
- if (bInsert)
- {
- CStdString strSQL1;
-
- CStdString strIdSong;
- if (song.idSong < 0)
- strIdSong = "NULL";
- else
- strIdSong.Format("%d", song.idSong);
-
- // we use replace because it can handle both inserting a new song
- // and replacing an existing song's record if the given idSong already exists
- strSQL=PrepareSQL("replace into song (idSong,idAlbum,idPath,strArtists,strGenres,strTitle,iTrack,iDuration,iYear,dwFileNameCRC,strFileName,strMusicBrainzTrackID,iTimesPlayed,iStartOffset,iEndOffset,idThumb,lastplayed,rating,comment) values (%s,%i,%i,'%s','%s','%s',%i,%i,%i,'%ul','%s','%s'",
- strIdSong.c_str(),
- idAlbum, idPath,
- StringUtils::Join(song.artist, g_advancedSettings.m_musicItemSeparator).c_str(),
- StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator).c_str(),
- song.strTitle.c_str(),
- song.iTrack, song.iDuration, song.iYear,
- crc, strFileName.c_str(),
- song.strMusicBrainzTrackID.c_str());
-
- if (song.lastPlayed.IsValid())
- strSQL1=PrepareSQL(",%i,%i,%i,'%s','%c','%s')",
- song.iTimesPlayed, song.iStartOffset, song.iEndOffset, song.lastPlayed.GetAsDBDateTime().c_str(), song.rating, song.strComment.c_str());
- else
- strSQL1=PrepareSQL(",%i,%i,%i,NULL,'%c','%s')",
- song.iTimesPlayed, song.iStartOffset, song.iEndOffset, song.rating, song.strComment.c_str());
- strSQL+=strSQL1;
-
+ strSQL = PrepareSQL("INSERT INTO song "
+ " (idAlbum, idPath, "
+ " strArtists, strGenres, strTitle, "
+ " iTrack, iDuration, iYear, "
+ " dwFileNameCRC, "
+ " strFileName, strMusicBrainzTrackID, "
+ " iStartOffset, iEndOffset, "
+ " comment) "
+ "VALUES "
+ " ( %i, %i, "
+ " '%s', '%s', '%s', "
+ " %i, %i, %i,"
+ " '%ul', "
+ " '%s', '%s', "
+ " %i, %i,"
+ " '%s')",
+ idAlbum, idPath,
+ strArtists.c_str(), strGenres.c_str(), strTitle.c_str(),
+ iTrack, iDuration, iYear,
+ crc,
+ strFileName.c_str(), strMusicBrainzTrackID.c_str(),
+ iStartOffset, iEndOffset,
+ strComment.c_str());
m_pDS->exec(strSQL.c_str());
-
- if (song.idSong < 0)
- idSong = (int)m_pDS->lastinsertid();
- else
- idSong = song.idSong;
+ idSong = (int)m_pDS->lastinsertid();
}
-
- if (!song.strThumb.empty())
- SetArtForItem(idSong, "song", "thumb", song.strThumb);
-
- for (unsigned int index = 0; index < song.albumArtist.size(); index++)
- {
- int idAlbumArtist = AddArtist(song.albumArtist[index]);
- AddAlbumArtist(idAlbumArtist, idAlbum, index > 0 ? true : false, index);
- }
-
- for (unsigned int index = 0; index < song.artist.size(); index++)
- {
- int idArtist = AddArtist(song.artist[index]);
- AddSongArtist(idArtist, idSong, index > 0 ? true : false, index);
- }
-
- unsigned int index = 0;
- // If this is karaoke song, change the genre to 'Karaoke' (and add it if it's not there)
- if ( bHasKaraoke && g_advancedSettings.m_karaokeChangeGenreForKaraokeSongs )
- {
- int idGenre = AddGenre("Karaoke");
- AddSongGenre(idGenre, idSong, index);
- AddAlbumGenre(idGenre, idAlbum, index++);
- }
- for (vector<string>::const_iterator i = song.genre.begin(); i != song.genre.end(); ++i)
+ else
{
- // index will be wrong for albums, but ordering is not all that relevant
- // for genres anyway
- int idGenre = AddGenre(*i);
- AddSongGenre(idGenre, idSong, index);
- AddAlbumGenre(idGenre, idAlbum, index++);
+ idSong = m_pDS->fv(0).get_asInt();
+ m_pDS->close();
+ strSQL = PrepareSQL("UPDATE song "
+ " SET idAlbum = %i, idPath = %i, "
+ " strArtists = '%s', strGenres = '%s', strTitle = '%s', "
+ " iTrack = %i, iDuration = %i, iYear = %i, "
+ " dwFileNameCRC = '%ul', "
+ " strFileName = '%s', "
+ " strMusicBrainzTrackID = '%s', "
+ " iStartOffset = %i, iEndOffset = %i, "
+ " comment = '%s' "
+ " WHERE idSong = %i",
+ idAlbum, idPath,
+ strArtists.c_str(), strGenres.c_str(), strTitle.c_str(),
+ iTrack, iDuration, iYear,
+ crc,
+ strFileName.c_str(),
+ strMusicBrainzTrackID.c_str(),
+ iStartOffset, iEndOffset,
+ strComment.c_str(),
+ idSong);
+ m_pDS->exec(strSQL.c_str());
}
-
- // Add karaoke information (if any)
- if ( bHasKaraoke )
- AddKaraokeData(idSong, song );
+ return idSong;
AnnounceUpdate("song", idSong);
}
@@ -434,13 +481,22 @@ int CMusicDatabase::UpdateSong(const CSong& song, int idSong /* = -1 */)
// Make sure newSong.idSong has a valid value (> 0)
newSong.idSong = idSong;
// re-add the song
- newSong.idSong = AddSong(newSong, false);
+// newSong.idSong = AddSong(newSong, false);
if (newSong.idSong < 0)
return -1;
return newSong.idSong;
}
+bool CMusicDatabase::DeleteSong(int idSong)
+{
+ bool bSuccess = true;
+ bSuccess &= DeleteValues("song_artist", PrepareSQL("idSong = %d", idSong));
+ bSuccess &= DeleteValues("song_genre", PrepareSQL("idSong = %d", idSong));
+ bSuccess &= DeleteValues("song", PrepareSQL("idSong=%d", idSong));
+ return bSuccess;
+};
+
int CMusicDatabase::AddAlbum(const CStdString& strAlbum1, const CStdString &strArtist, const CStdString& strGenre, int year, bool bCompilation)
{
CStdString strSQL;
@@ -4604,41 +4660,20 @@ void CMusicDatabase::ImportFromXML(const CStdString &xmlFile)
progress->Close();
}
-void CMusicDatabase::AddKaraokeData(int idSong, const CSong& song)
-{
- try
- {
- CStdString strSQL;
-
- // If song.iKaraokeNumber is non-zero, we already have it in the database. Just replace the song ID.
- if ( song.iKaraokeNumber > 0 )
- {
- CStdString strSQL = PrepareSQL("UPDATE karaokedata SET idSong=%i WHERE iKaraNumber=%i", idSong, song.iKaraokeNumber);
- m_pDS->exec(strSQL.c_str());
- return;
- }
-
- // Add new karaoke data
- DWORD crc = ComputeCRC(song.strFileName);
-
- // Add the data
- strSQL=PrepareSQL("INSERT INTO karaokedata "
- " (idSong, iKaraDelay, "
- " strKaraEncoding, strKaralyrics, "
- " strKaraLyrFileCRC) "
- " VALUES "
- " (%i, 0, "
- " NULL, NULL, "
- " '%ul' )",
- song.idSong,
- crc);
-
- m_pDS->exec(strSQL.c_str());
- }
- catch (...)
- {
- CLog::Log(LOGERROR, "%s -(%s) failed", __FUNCTION__, song.strFileName.c_str());
- }
+void CMusicDatabase::AddKaraokeData(int idSong, const CStdString& strPathAndFileName)
+{
+ DWORD crc = ComputeCRC(strPathAndFileName);
+ if (GetSingleValue("karaokedata", "iKaraNumber", PrepareSQL("idSong = %d", idSong)) == StringUtils::EmptyString)
+ ExecuteQuery(PrepareSQL("INSERT INTO karaokedata "
+ " (idSong, iKaraDelay, "
+ " strKaraEncoding, strKaralyrics, "
+ " strKaraLyrFileCRC) "
+ " VALUES "
+ " (%i, 0, "
+ " NULL, NULL, "
+ " '%ul' )",
+ idSong,
+ crc));
}
bool CMusicDatabase::GetSongByKaraokeNumber(int number, CSong & song)
View
25 xbmc/music/MusicDatabase.h
@@ -70,6 +70,7 @@ typedef std::set<CStdString>::iterator ISETPATHES;
class CGUIDialogProgress;
class CFileItemList;
+class CFileItem;
/*!
\ingroup music
@@ -102,15 +103,24 @@ class CMusicDatabase : public CDatabase
bool LookupCDDBInfo(bool bRequery=false);
void DeleteCDDBInfo();
- /*! \brief Add an album and all its songs to the database
+ /*! \brief Add a file item to the database
\param album the album to add
- \param songIDs [out] the ids of the added songs
+ \param albumHints a vector of albums with hints for album
+ artist and compilation flags, calculated in the scanner
+ \a CMusicInfoScanner::CategorizeAlbums
\return the id of the album
*/
- int AddAlbum(const CAlbum &album, std::vector<int> &songIDs);
+ int AddFileItem(const CFileItem& pItem, const VECALBUMS& albumHints);
int UpdateSong(const CSong& song, int idSong = -1);
+ /*! \brief Delete a song record from the database
+ \param idSong the ID of the song to remove
+ \sa CMusicDatabase::AddSong
+ \return whether the delete succeeded or failed
+ */
+ bool DeleteSong(int idSong);
+
/*! \brief Update an artist in the database
\param idArtist the ID of the artist to update
\param strArtist the new name of the artist
@@ -293,7 +303,7 @@ class CMusicDatabase : public CDatabase
virtual int GetMinVersion() const { return 30; };
const char *GetBaseDBName() const { return "MyMusic"; };
- int AddSong(const CSong& song, bool bCheck = true, int idAlbum = -1);
+ int AddSong(const CStdString& strTitle, const CStdString& strArtists, int idAlbum, const CStdString& strPathAndFileName, const CStdString& strGenres, int iTrack, int iDuration, int iYear, const CStdString& strMusicBrainzTrackID, const CStdString& strComment, int iStartOffset, int iEndOffset);
int AddAlbum(const CStdString& strAlbum1, const CStdString &strArtist1, const CStdString& strGenre, int year, bool bCompilation);
int AddGenre(const CStdString& strGenre);
int AddArtist(const CStdString& strArtist);
@@ -304,7 +314,12 @@ class CMusicDatabase : public CDatabase
bool AddSongGenre(int idGenre, int idSong, int iOrder);
bool AddAlbumGenre(int idGenre, int idAlbum, int iOrder);
- void AddKaraokeData(int idSong, const CSong& song);
+ /*! \brief Add a row to the karaoke table for the given song.
+ \param idSong the id in the song table.
+ \param strPathAndFileName the full, real path on disk to the song.
+ \sa AddFileItem
+ */
+ void AddKaraokeData(int idSong, const CStdString& strPathAndFileName);
bool SetAlbumInfoSongs(int idAlbumInfo, const VECSONGS& songs);
bool GetAlbumInfoSongs(int idAlbumInfo, VECSONGS& songs);
private:
View
330 xbmc/music/infoscanner/MusicInfoScanner.cpp
@@ -179,7 +179,7 @@ void CMusicInfoScanner::Process()
m_pObserver->OnStateChanged(DOWNLOADING_ARTIST_INFO);
int iCurrentItem=1;
- for (set<CArtist>::iterator it=m_artistsToScan.begin();it != m_artistsToScan.end();++it)
+ for (set<CArtist>::iterator it = m_artistsToScan.begin();it != m_artistsToScan.end();++it)
{
if (m_pObserver)
{
@@ -307,16 +307,16 @@ void CMusicInfoScanner::FetchArtistInfo(const CStdString& strDirectory,
{
if (CMusicDatabaseDirectory::IsAllItem(items[i]->GetPath()) || items[i]->IsParentFolder())
continue;
-
+
CArtist artist;
- artist.strArtist = StringUtils::Join(items[i]->GetMusicInfoTag()->GetArtist(), g_advancedSettings.m_musicItemSeparator);
- artist.genre.push_back(items[i]->GetPath()); // a bit hacky use of field
- m_artistsToScan.insert(artist);
- if (refresh)
+ int idArtist = items[i]->GetMusicInfoTag()->GetDatabaseId();
+ if (m_musicDatabase.GetArtistInfo(idArtist, artist))
{
- int id = m_musicDatabase.GetArtistByName(artist.strArtist);
- if (id > -1)
- m_musicDatabase.DeleteArtistInfo(id);
+ m_artistsToScan.insert(artist);
+ if (refresh)
+ {
+ m_musicDatabase.DeleteArtistInfo(items[i]->GetMusicInfoTag()->GetDatabaseId());
+ }
}
}
m_musicDatabase.Close();
@@ -475,6 +475,7 @@ INFO_RET CMusicInfoScanner::ScanTags(const CFileItemList& items, CFileItemList&
CLog::Log(LOGDEBUG, "%s - No tag found for: %s", __FUNCTION__, pItem->GetPath().c_str());
}
}
+ scannedItems.SetPath(items.GetPath());
return INFO_ADDED;
}
@@ -482,99 +483,65 @@ int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString&
{
MAPSONGS songsMap;
- // get all information for all files in current directory from database, and remove them
- if (m_musicDatabase.RemoveSongsFromPath(strDirectory, songsMap))
- m_needsCleanup = true;
-
- VECSONGS songsToAdd;
+ // get all information for all files in current directory from database
+ m_musicDatabase.GetSongsByPath(strDirectory, songsMap);
CFileItemList scannedItems;
if (ScanTags(items, scannedItems) == INFO_CANCELLED)
return 0;
- for (int i = 0; i < scannedItems.Size(); ++i)
- {
- CFileItemPtr pItem = scannedItems[i];
- // grab info from the song
- CSong *dbSong = NULL;
- MAPSONGS::iterator it = songsMap.find(pItem->GetPath());
- if (it != songsMap.end())
- dbSong = &it->second;
+ VECALBUMS albumHints;
+ CategoriseAlbums(scannedItems, albumHints);
- CMusicInfoTag& tag = *pItem->GetMusicInfoTag();
+ vector<int> songs;
+ // add the scanned items to the database
+ m_musicDatabase.BeginTransaction();
+ for (int i = 0; i < scannedItems.Size(); ++i)
+ {
+ if (m_bStop)
+ {
+ m_musicDatabase.RollbackTransaction();
+ return i;
+ }
- if (tag.Loaded())
+ CFileItemPtr pItem = scannedItems[i];
+ int idSong = m_musicDatabase.AddFileItem(*pItem, albumHints);
+ if (idSong > -1)
{
- CSong song(tag);
-
- // ensure our song has a valid filename or else it will assert in AddSong()
- if (song.strFileName.IsEmpty())
+ songs.push_back(idSong);
+ for (MAPSONGS::iterator it = songsMap.begin(); it != songsMap.end(); ++it)
{
- // copy filename from path in case UPnP or other tag loaders didn't specify one (FIXME?)
- song.strFileName = pItem->GetPath();
-
- // if we still don't have a valid filename, skip the song
- if (song.strFileName.IsEmpty())
- {
- // this shouldn't ideally happen!
- CLog::Log(LOGERROR, "Skipping song since it doesn't seem to have a filename");
- continue;
- }
- }
-
- song.iStartOffset = pItem->m_lStartOffset;
- song.iEndOffset = pItem->m_lEndOffset;
- song.strThumb = pItem->GetUserMusicThumb(true);
- if (dbSong)
- { // keep the db-only fields intact on rescan...
- song.iTimesPlayed = dbSong->iTimesPlayed;
- song.lastPlayed = dbSong->lastPlayed;
- song.iKaraokeNumber = dbSong->iKaraokeNumber;
-
- if (song.rating == '0') song.rating = dbSong->rating;
- if (song.strThumb.empty())
- song.strThumb = dbSong->strThumb;
+ if (it->second.idSong == idSong)
+ songsMap.erase(it);
}
- songsToAdd.push_back(song);
- // CLog::Log(LOGDEBUG, "%s - Tag loaded for: %s", __FUNCTION__, pItem->GetPath().c_str());
}
- else
- CLog::Log(LOGDEBUG, "%s - No tag found for: %s", __FUNCTION__, pItem->GetPath().c_str());
}
+ for (MAPSONGS::const_iterator it = songsMap.begin(); it != songsMap.end(); ++it)
+ m_musicDatabase.DeleteSong(it->second.idSong);
+ m_musicDatabase.CommitTransaction();
- VECALBUMS albums;
- CategoriseAlbums(songsToAdd, albums);
- FindArtForAlbums(albums, items.GetPath());
-
- // finally, add these to the database
- m_musicDatabase.BeginTransaction();
- int numAdded = 0;
set<int> albumsToScan;
set<int> artistsToScan;
- for (VECALBUMS::iterator i = albums.begin(); i != albums.end(); ++i)
+ for (vector<int>::const_iterator i = songs.begin(); i != songs.end(); ++i)
{
- vector<int> songIDs;
- int idAlbum = m_musicDatabase.AddAlbum(*i, songIDs);
- numAdded += i->songs.size();
- if (m_bStop)
- {
- m_musicDatabase.RollbackTransaction();
- return numAdded;
- }
+ CSong song;
+ m_musicDatabase.GetSongById(*i, song);
- // Build the artist & album sets
- albumsToScan.insert(idAlbum);
- for (vector<int>::iterator j = songIDs.begin(); j != songIDs.end(); ++j)
- {
- vector<int> songArtists;
- m_musicDatabase.GetArtistsBySong(*j, false, songArtists);
- artistsToScan.insert(songArtists.begin(), songArtists.end());
- }
+ albumsToScan.insert(song.iAlbumId);
+
+ vector<int> songArtists;
+ m_musicDatabase.GetArtistsBySong(*i, false, songArtists);
+ artistsToScan.insert(songArtists.begin(), songArtists.end());
+ }
+
+ for (set<int>::const_iterator i = albumsToScan.begin(); i != albumsToScan.end(); ++i)
+ {
std::vector<int> albumArtists;
- m_musicDatabase.GetArtistsByAlbum(idAlbum, false, albumArtists);
+ m_musicDatabase.GetArtistsByAlbum(*i, false, albumArtists);
artistsToScan.insert(albumArtists.begin(), albumArtists.end());
}
- m_musicDatabase.CommitTransaction();
+
+ std::vector<int> albums(albumsToScan.begin(), albumsToScan.end());
// Download info & artwork
bool bCanceled;
@@ -583,14 +550,17 @@ int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString&
bCanceled = false;
if (find(m_artistsScanned.begin(),m_artistsScanned.end(), *it) == m_artistsScanned.end())
{
- CStdString strArtist = m_musicDatabase.GetArtistById(*it);
+ CArtist artist;
+ if (!m_musicDatabase.GetArtistInfo(*it, artist))
+ continue;
+
m_artistsScanned.push_back(*it);
if (!m_bStop && (m_flags & SCAN_ONLINE))
{
CStdString strPath;
strPath.Format("musicdb://2/%u/", *it);
- if (!DownloadArtistInfo(strPath, strArtist, bCanceled)) // assume we want to retry
+ if (!DownloadArtistInfo(strPath, artist.strArtist, bCanceled)) // assume we want to retry
m_artistsScanned.pop_back();
}
else
@@ -606,7 +576,7 @@ int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString&
for (set<int>::iterator it = albumsToScan.begin(); it != albumsToScan.end(); ++it)
{
if (m_bStop)
- return songsToAdd.size();
+ return scannedItems.Size();
CStdString strPath;
strPath.Format("musicdb://3/%u/",*it);
@@ -625,57 +595,96 @@ int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString&
if (m_pObserver)
m_pObserver->OnStateChanged(READING_MUSIC_INFO);
- return songsToAdd.size();
+ return scannedItems.Size();
}
-static bool SortSongsByTrack(CSong *song, CSong *song2)
+static bool SortSongsByTrack(CFileItemPtr song, CFileItemPtr song2)
{
- return song->iTrack < song2->iTrack;
+ return song->GetMusicInfoTag()->GetTrackNumber() < song2->GetMusicInfoTag()->GetTrackNumber();
}
-void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albums)
+void CMusicInfoScanner::CategoriseAlbums(const CFileItemList& items, VECALBUMS& albumHints)
{
/* 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));
+ map<string, vector<CFileItemPtr> > albumNames;
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ CMusicInfoTag& tag = *(items[i])->GetMusicInfoTag();
+ albumNames[tag.GetAlbum()].push_back(items[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)
+ for (map<string, vector<CFileItemPtr> >::iterator i = albumNames.begin(); i != albumNames.end(); ++i)
{
+ /*
+ If there's a single album in the folder, then art can be taken from
+ the folder art.
+ */
+ std::string albumArt;
+ if (albumNames.size() == 1)
+ {
+ CFileItem album(items.GetPath(), true);
+ albumArt = album.GetUserMusicThumb(true);
+ }
+
// sort the songs by tracknumber to identify duplicate track numbers
- vector<CSong *> &songs = i->second;
+ vector<CFileItemPtr> &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)
+ bool singleArt = true;
+ CFileItemPtr art;
+ map<string, vector<CFileItemPtr> > artists;
+ for (vector<CFileItemPtr>::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;
+ CMusicInfoTag* currTag = (*j)->GetMusicInfoTag();
+
+ if (j != songs.begin())
+ {
+ CMusicInfoTag* prevTag = (*(j-1))->GetMusicInfoTag();
+
+ // test for song overlap
+ if (currTag->GetTrackNumber() == prevTag->GetTrackNumber())
+ compilation = false;
+ }
// get primary artist
string primary;
- if (!song->albumArtist.empty())
+ if (!currTag->GetAlbumArtist().empty())
{
- primary = song->albumArtist[0];
+ primary = currTag->GetAlbumArtist()[0];
compilation = false;
}
- else if (!song->artist.empty())
- primary = song->artist[0];
+ else if (!currTag->GetArtist().empty())
+ primary = currTag->GetArtist()[0];
// add to the artist map
- artists[primary].push_back(song);
+ artists[primary].push_back(*j);
+
+ // test for a common cover art image
+ if (!currTag->GetCoverArtInfo().empty())
+ {
+ if (art && !art->GetMusicInfoTag()->GetCoverArtInfo().matches(currTag->GetCoverArtInfo()))
+ singleArt = false;
+
+ if (!art)
+ art = *j;
+ }
}
+ if (art && albumArt.empty())
+ albumArt = CTextureCache::GetWrappedImageURL(art->GetPath(), "music");
+
+ if (!singleArt)
+ for (vector<CFileItemPtr>::iterator j = songs.begin(); j != songs.end(); ++j)
+ if (!(*j)->GetMusicInfoTag()->GetCoverArtInfo().empty())
+ (*j)->SetThumbnailImage(CTextureCache::GetWrappedImageURL((*j)->GetPath(), "music"));
+
/*
We have a compilation if
1. album name is non-empty
@@ -689,24 +698,26 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
{
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));
}
+ // assign to folder thumb as well
+ if (albumHints.size() == 1 && !albumArt.empty())
+ CMusicThumbLoader::SetCachedImage(items.GetPath(), "thumb", albumArt);
+
/*
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)
+ for (map<string, vector<CFileItemPtr> >::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)
+ vector<CFileItemPtr> &artistSongs = j->second;
+ vector<string> common = artistSongs.front()->GetMusicInfoTag()->GetAlbumArtist().empty() ? artistSongs.front()->GetMusicInfoTag()->GetArtist() : artistSongs.front()->GetMusicInfoTag()->GetAlbumArtist();
+ for (vector<CFileItemPtr>::iterator k = artistSongs.begin() + 1; k != artistSongs.end(); ++k)
{
unsigned int match = 0;
- vector<string> &compare = (*k)->albumArtist.empty() ? (*k)->artist : (*k)->albumArtist;
+ const vector<string> &compare = (*k)->GetMusicInfoTag()->GetAlbumArtist().empty() ? (*k)->GetMusicInfoTag()->GetArtist() : (*k)->GetMusicInfoTag()->GetAlbumArtist();
for (; match < common.size() && match < compare.size(); match++)
{
if (compare[match] != common[match])
@@ -723,101 +734,12 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
album.strAlbum = i->first;
album.artist = common;
album.bCompilation = compilation;
- 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;
- // in addition, we may want to use year as discriminating for albums
- if (album.iYear == 0)
- album.iYear = (*k)->iYear;
- }
-
- albums.push_back(album);
+ album.art["thumb"] = albumArt;
+ albumHints.push_back(album);
}
}
}
-void CMusicInfoScanner::FindArtForAlbums(VECALBUMS &albums, const CStdString &path)
-{
- /*
- If there's a single album in the folder, then art can be taken from
- the folder art.
- */
- std::string albumArt;
- if (albums.size() == 1)
- {
- CFileItem album(path, true);
- albumArt = album.GetUserMusicThumb(true);
- albums[0].art["thumb"] = albumArt;
- }
- for (VECALBUMS::iterator i = albums.begin(); i != albums.end(); ++i)
- {
- CAlbum &album = *i;
-
- if (albums.size() != 1)
- albumArt = "";
-
- /*
- Find art that is common across these items
- If we find a single art image we treat it as the album art
- and discard song art else we use first as album art and
- keep everything as song art.
- */
- bool singleArt = true;
- CSong *art = NULL;
- for (VECSONGS::iterator k = album.songs.begin(); k != album.songs.end(); ++k)
- {
- CSong &song = *k;
- if (song.HasArt())
- {
- if (art && !art->ArtMatches(song))
- {
- singleArt = false;
- break;
- }
- if (!art)
- art = &song;
- }
- }
-
- /*
- assign the first art found to the album - better than no art at all
- */
-
- if (art && albumArt.empty())
- {
- if (!art->strThumb.empty())
- albumArt = art->strThumb;
- else
- albumArt = CTextureCache::GetWrappedImageURL(art->strFileName, "music");
- }
-
- album.art["thumb"] = albumArt;
-
- if (singleArt)
- { //if singleArt then we can clear the artwork for all songs
- for (VECSONGS::iterator k = album.songs.begin(); k != album.songs.end(); ++k)
- k->strThumb.clear();
- }
- else
- { // more than one piece of art was found for these songs, so cache per song
- for (VECSONGS::iterator k = album.songs.begin(); k != album.songs.end(); ++k)
- {
- if (k->strThumb.empty() && !k->embeddedArt.empty())
- k->strThumb = CTextureCache::GetWrappedImageURL(k->strFileName, "music");
- }
- }
- }
- if (albums.size() == 1 && !albumArt.empty())
- { // assign to folder thumb as well
- CMusicThumbLoader::SetCachedImage(path, "thumb", albumArt);
- }
-}
-
// This function is run by another thread
void CMusicInfoScanner::Run()
{
View
31 xbmc/music/infoscanner/MusicInfoScanner.h
@@ -73,7 +73,10 @@ class CMusicInfoScanner : CThread, public IRunnable
void SetObserver(IMusicInfoScannerObserver* pObserver);
/*! \brief Categorise songs into albums
- Albums are defined uniquely by the album name and album artist.
+ For users that don't have correct tags, we need to provide some hints
+ to the DB about the correct album artist and compilation settings.
+
+ In this case, 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.
@@ -88,29 +91,12 @@ class CMusicInfoScanner : CThread, public IRunnable
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.
+ we assume it is a various artists album.
- \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);
-
- /*! \brief Find art for albums
- Based on the albums in the folder, finds whether we have unique album art
- and assigns to the album if we do.
-
- In order of priority:
- 1. If there is a single album in the folder, then the folder art is assigned to the album.
- 2. We find the art for each song. A .tbn file takes priority over embedded art.
- 3. If we have a unique piece of art for all songs in the album, we assign that to the album
- and remove that art from each song so that they inherit from the album.
- 4. If there is not a unique piece of art for each song, then no art is assigned
- to the album.
-
- \param albums [in/out] list of albums to categorise - art field may be altered.
- \param path [in] path containing albums.
+ \param items [in] list of file items (songs) to categorise.
+ \param albumHints [out] list of albums with our guess at artist and compilation flags.
*/
- static void FindArtForAlbums(VECALBUMS &albums, const CStdString &path);
+ static void CategoriseAlbums(const CFileItemList& items, VECALBUMS& albumHints);
bool DownloadAlbumInfo(const CStdString& strPath, const CStdString& strArtist, const CStdString& strAlbum, bool& bCanceled, MUSIC_GRABBER::CMusicAlbumInfo& album, CGUIDialogProgress* pDialog=NULL);
bool DownloadArtistInfo(const CStdString& strPath, const CStdString& strArtist, bool& bCanceled, CGUIDialogProgress* pDialog=NULL);
@@ -148,3 +134,4 @@ class CMusicInfoScanner : CThread, public IRunnable
int m_flags;
};
}
+
View
9 xbmc/music/windows/GUIWindowMusicBase.cpp
@@ -1164,18 +1164,15 @@ void CGUIWindowMusicBase::UpdateThumb(const CAlbum &album, const CStdString &pat
CFileItemList items;
GetDirectory(albumPath, items);
OnRetrieveMusicInfo(items);
- VECSONGS songs;
+ CFileItemList scannedItems;
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr item = items[i];
if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->Loaded())
- {
- CSong song(*item->GetMusicInfoTag());
- songs.push_back(song);
- }
+ scannedItems.Add(item);
}
VECALBUMS albums;
- CMusicInfoScanner::CategoriseAlbums(songs, albums);
+ CMusicInfoScanner::CategoriseAlbums(scannedItems, albums);
if (albums.size() == 1)
{ // set as folder thumb as well
CThumbLoader::SetCachedImage(items, "thumb", albumPath);
Please sign in to comment.
Something went wrong with that request. Please try again.