void CGUIWindowMusicInfo::SetSongs(const VECSONGS &songs) { m_albumSongs->Clear(); for (unsigned int i = 0; i < songs.size(); i++) { const CSong& song = songs[i]; CFileItem *item = new CFileItem(song); m_albumSongs->Add(item); } }
void CGUIDialogMusicInfo::SetSongs(const VECSONGS &songs) const { m_albumSongs->Clear(); CMusicThumbLoader loader; for (unsigned int i = 0; i < songs.size(); i++) { const CSong& song = songs[i]; CFileItemPtr item(new CFileItem(song)); // Load the song art and related artist(s) (that may be different from album artist) art loader.LoadItem(item.get()); m_albumSongs->Add(item); } }
bool CMusicInfoScanner::HasSingleAlbum(const VECSONGS &songs, CStdString &album, CStdString &artist) { // check how many unique albums are in this path, and if there's only one, and it has a thumb // then cache the thumb as the folder thumb for (unsigned int i = 0; i < songs.size(); i++) { const CSong &song = songs[i]; // don't bother with empty album tags - they're treated as singles, and there's no way to determine // whether more than one track in the folder is supposed to mean they belong to an "album" if (song.strAlbum.IsEmpty()) return false; CStdString albumArtist = song.strAlbumArtist.IsEmpty() ? song.strArtist : song.strAlbumArtist; if (!album.IsEmpty() && (album != song.strAlbum || artist != albumArtist)) return false; // have more than one album album = song.strAlbum; artist = albumArtist; } return !album.IsEmpty(); }
bool CMusicInfoScanner::HasSingleAlbum(const VECSONGS &songs, CStdString &album, CStdString &artist) { // check how many unique albums are in this path, and if there's only one, and it has a thumb // then cache the thumb as the folder thumb for (unsigned int i = 0; i < songs.size(); i++) { const CSong &song = songs[i]; // don't bother with empty album tags - they're unlikely to have an embedded thumb anyway, // and if one is not tagged correctly, how can we know whether there is only one album? if (song.strAlbum.IsEmpty()) return false; CStdString albumArtist = song.strAlbumArtist.IsEmpty() ? song.strArtist : song.strAlbumArtist; if (!album.IsEmpty() && (album != song.strAlbum || artist != albumArtist)) return false; // have more than one album album = song.strAlbum; artist = albumArtist; } return !album.IsEmpty(); }
int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory) { CSongMap 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; CStdStringArray regexps = g_advancedSettings.m_audioExcludeFromScanRegExps; // for every file found, but skip folder for (int i = 0; i < items.Size(); ++i) { CFileItemPtr pItem = items[i]; CStdString strExtension; URIUtils::GetExtension(pItem->GetPath(), strExtension); if (m_bStop) return 0; // Discard all excluded files defined by m_musicExcludeRegExps if (CUtil::ExcludeFileOrFolder(pItem->GetPath(), regexps)) continue; // dont try reading id3tags for folders, playlists or shoutcast streams if (!pItem->m_bIsFolder && !pItem->IsPlayList() && !pItem->IsPicture() && !pItem->IsLyrics() ) { m_currentItem++; // CLog::Log(LOGDEBUG, "%s - Reading tag for: %s", __FUNCTION__, pItem->GetPath().c_str()); // grab info from the song CSong *dbSong = songsMap.Find(pItem->GetPath()); CMusicInfoTag& tag = *pItem->GetMusicInfoTag(); if (!tag.Loaded() ) { // read the tag from a file auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->GetPath())); if (NULL != pLoader.get()) pLoader->Load(pItem->GetPath(), tag); } // if we have the itemcount, update our // dialog with the progress we made if (m_handle && m_itemCount>0) m_handle->SetPercentage(m_currentItem/(float)m_itemCount*100); if (tag.Loaded()) { CSong song(tag); // ensure our song has a valid filename or else it will assert in AddSong() if (song.strFileName.IsEmpty()) { // 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; } 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()); } } 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) { vector<int> songIDs; int idAlbum = m_musicDatabase.AddAlbum(*i, songIDs); numAdded += i->songs.size(); if (m_bStop) { m_musicDatabase.RollbackTransaction(); return numAdded; } // 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()); } std::vector<int> albumArtists; m_musicDatabase.GetArtistsByAlbum(idAlbum, false, albumArtists); artistsToScan.insert(albumArtists.begin(), albumArtists.end()); } m_musicDatabase.CommitTransaction(); // Download info & artwork bool bCanceled; for (set<int>::iterator it = artistsToScan.begin(); it != artistsToScan.end(); ++it) { bCanceled = false; if (find(m_artistsScanned.begin(),m_artistsScanned.end(), *it) == m_artistsScanned.end()) { CStdString strArtist = m_musicDatabase.GetArtistById(*it); 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 m_artistsScanned.pop_back(); } else { map<string, string> artwork = GetArtistArtwork(*it); m_musicDatabase.SetArtForItem(*it, "artist", artwork); } } } if (m_flags & SCAN_ONLINE) { for (set<int>::iterator it = albumsToScan.begin(); it != albumsToScan.end(); ++it) { if (m_bStop) return songsToAdd.size(); CStdString strPath; strPath.Format("musicdb://3/%u/",*it); CAlbum album; m_musicDatabase.GetAlbumInfo(*it, album, NULL); bCanceled = false; if (find(m_albumsScanned.begin(), m_albumsScanned.end(), *it) == m_albumsScanned.end()) { CMusicAlbumInfo albumInfo; if (DownloadAlbumInfo(strPath, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator), album.strAlbum, bCanceled, albumInfo)) m_albumsScanned.push_back(*it); } } } if (m_handle) m_handle->SetTitle(g_localizeStrings.Get(505)); return songsToAdd.size(); }
void CMusicInfoScanner::CheckForVariousArtists(VECSONGS &songsToCheck) { // first, find all the album names for these songs map<CStdString, vector<CSong *> > albumsToAdd; map<CStdString, vector<CSong *> >::iterator it; for (unsigned int i = 0; i < songsToCheck.size(); ++i) { CSong &song = songsToCheck[i]; if (!song.strAlbumArtist.IsEmpty()) // albumartist specified, so assume the user knows what they're doing continue; it = albumsToAdd.find(song.strAlbum); if (it == albumsToAdd.end()) { vector<CSong *> songs; songs.push_back(&song); albumsToAdd.insert(make_pair(song.strAlbum, songs)); } else it->second.push_back(&song); } // as an additional check for those that have multiple albums in the same folder, ignore albums // that have overlapping track numbers for (it = albumsToAdd.begin(); it != albumsToAdd.end();) { vector<CSong *> &songs = it->second; bool overlappingTrackNumbers(false); if (songs.size() > 1) { sort(songs.begin(), songs.end(), SortSongsByTrack); for (unsigned int i = 0; i < songs.size() - 1; i++) { CSong *song = songs[i]; CSong *song2 = songs[i+1]; if (song->iTrack == song2->iTrack) { overlappingTrackNumbers = true; break; } } } if (overlappingTrackNumbers) { // remove this album albumsToAdd.erase(it++); } else it++; } // ok, now run through these albums, and check whether they qualify as a "various artist" album // an album is considered a various artists album if the songs' primary artist differs // it qualifies as a "single artist with featured artists" album if the primary artist is the same, but secondary artists differ for (it = albumsToAdd.begin(); it != albumsToAdd.end(); it++) { const CStdString &album = it->first; vector<CSong *> &songs = it->second; if (!album.IsEmpty() && songs.size() > 1) { bool variousArtists(false); bool singleArtistWithFeaturedArtists(false); for (unsigned int i = 0; i < songs.size() - 1; i++) { CSong *song1 = songs[i]; CSong *song2 = songs[i+1]; CStdStringArray vecArtists1, vecArtists2; StringUtils::SplitString(song1->strArtist, g_advancedSettings.m_musicItemSeparator, vecArtists1); StringUtils::SplitString(song2->strArtist, g_advancedSettings.m_musicItemSeparator, vecArtists2); CStdString primaryArtist1 = vecArtists1[0]; primaryArtist1.TrimRight(); CStdString primaryArtist2 = vecArtists2[0]; primaryArtist2.TrimRight(); if (primaryArtist1 != primaryArtist2) { // primary artist differs -> a various artists album variousArtists = true; break; } else if (song1->strArtist != song2->strArtist) { // have more than one artist, the first artist(s) agree, but the full artist name doesn't // so this is likely a single-artist compilation (ie with other artists featured on some tracks) album singleArtistWithFeaturedArtists = true; } } if (variousArtists) { // have a various artists album - update all songs to be the various artist for (unsigned int i = 0; i < songs.size(); i++) { CSong *song = songs[i]; song->strAlbumArtist = g_localizeStrings.Get(340); // Various Artists } } else if (singleArtistWithFeaturedArtists) { // have an album where all the first artists agree - make this the album artist CStdStringArray vecArtists; StringUtils::SplitString(songs[0]->strArtist, g_advancedSettings.m_musicItemSeparator, vecArtists); CStdString albumArtist(vecArtists[0]); for (unsigned int i = 0; i < songs.size(); i++) { CSong *song = songs[i]; song->strAlbumArtist = albumArtist; // first artist of all tracks } } } } }
int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory) { CSongMap 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; CStdStringArray regexps = g_advancedSettings.m_audioExcludeFromScanRegExps; // for every file found, but skip folder for (int i = 0; i < items.Size(); ++i) { CFileItemPtr pItem = items[i]; CStdString strExtension; URIUtils::GetExtension(pItem->GetPath(), strExtension); if (m_bStop) return 0; // Discard all excluded files defined by m_musicExcludeRegExps if (CUtil::ExcludeFileOrFolder(pItem->GetPath(), regexps)) continue; // dont try reading id3tags for folders, playlists or shoutcast streams if (!pItem->m_bIsFolder && !pItem->IsPlayList() && !pItem->IsPicture() && !pItem->IsLyrics() ) { m_currentItem++; // CLog::Log(LOGDEBUG, "%s - Reading tag for: %s", __FUNCTION__, pItem->GetPath().c_str()); // grab info from the song CSong *dbSong = songsMap.Find(pItem->GetPath()); CMusicInfoTag& tag = *pItem->GetMusicInfoTag(); if (!tag.Loaded() ) { // read the tag from a file auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->GetPath())); if (NULL != pLoader.get()) pLoader->Load(pItem->GetPath(), tag); } // if we have the itemcount, notify our // observer with the progress we made if (m_pObserver && m_itemCount>0) m_pObserver->OnSetProgress(m_currentItem, m_itemCount); if (tag.Loaded()) { CSong song(tag); // ensure our song has a valid filename or else it will assert in AddSong() if (song.strFileName.IsEmpty()) { // 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; 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; } pItem->SetMusicThumb(); song.strThumb = pItem->GetThumbnailImage(); 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()); } } CheckForVariousArtists(songsToAdd); if (!items.HasThumbnail()) UpdateFolderThumb(songsToAdd, items.GetPath()); // finally, add these to the database set<CStdString> artistsToScan; set< pair<CStdString, CStdString> > albumsToScan; m_musicDatabase.BeginTransaction(); for (unsigned int i = 0; i < songsToAdd.size(); ++i) { if (m_bStop) { m_musicDatabase.RollbackTransaction(); return i; } CSong &song = songsToAdd[i]; m_musicDatabase.AddSong(song, false); artistsToScan.insert(song.strArtist); albumsToScan.insert(make_pair(song.strAlbum, song.strArtist)); } m_musicDatabase.CommitTransaction(); bool bCanceled; for (set<CStdString>::iterator i = artistsToScan.begin(); i != artistsToScan.end(); ++i) { bCanceled = false; long iArtist = m_musicDatabase.GetArtistByName(*i); if (find(m_artistsScanned.begin(),m_artistsScanned.end(),iArtist) == m_artistsScanned.end()) { m_artistsScanned.push_back(iArtist); if (!m_bStop && g_guiSettings.GetBool("musiclibrary.downloadinfo")) { CStdString strPath; strPath.Format("musicdb://2/%u/",iArtist); if (!DownloadArtistInfo(strPath,*i, bCanceled)) // assume we want to retry m_artistsScanned.pop_back(); } else GetArtistArtwork(iArtist, *i); } } if (g_guiSettings.GetBool("musiclibrary.downloadinfo")) { for (set< pair<CStdString, CStdString> >::iterator i = albumsToScan.begin(); i != albumsToScan.end(); ++i) { if (m_bStop) return songsToAdd.size(); long iAlbum = m_musicDatabase.GetAlbumByName(i->first, i->second); CStdString strPath; strPath.Format("musicdb://3/%u/",iAlbum); bCanceled = false; if (find(m_albumsScanned.begin(), m_albumsScanned.end(), iAlbum) == m_albumsScanned.end()) { CMusicAlbumInfo albumInfo; if (DownloadAlbumInfo(strPath, i->second, i->first, bCanceled, albumInfo)) m_albumsScanned.push_back(iAlbum); } } } if (m_pObserver) m_pObserver->OnStateChanged(READING_MUSIC_INFO); return songsToAdd.size(); }
int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory) { CSongMap 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; // for every file found, but skip folder for (int i = 0; i < items.Size(); ++i) { CFileItem* pItem = items[i]; CStdString strExtension; CUtil::GetExtension(pItem->m_strPath, strExtension); if (m_bStop) return 0; // dont try reading id3tags for folders, playlists or shoutcast streams if (!pItem->m_bIsFolder && !pItem->IsPlayList() && !pItem->IsShoutCast() && !pItem->IsPicture()) { m_currentItem++; // CLog::Log(LOGDEBUG, "%s - Reading tag for: %s", __FUNCTION__, pItem->m_strPath.c_str()); // grab info from the song CSong *dbSong = songsMap.Find(pItem->m_strPath); CMusicInfoTag& tag = *pItem->GetMusicInfoTag(); if (!tag.Loaded() ) { // read the tag from a file auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->m_strPath)); if (NULL != pLoader.get()) pLoader->Load(pItem->m_strPath, tag); } // if we have the itemcount, notify our // observer with the progress we made if (m_pObserver && m_itemCount>0) m_pObserver->OnSetProgress(m_currentItem, m_itemCount); if (tag.Loaded()) { CSong song(tag); song.iStartOffset = pItem->m_lStartOffset; song.iEndOffset = pItem->m_lEndOffset; if (dbSong) { // keep the db-only fields intact on rescan... song.iTimesPlayed = dbSong->iTimesPlayed; song.lastPlayed = dbSong->lastPlayed; if (song.rating == '0') song.rating = dbSong->rating; } pItem->SetMusicThumb(); song.strThumb = pItem->GetThumbnailImage(); songsToAdd.push_back(song); // CLog::Log(LOGDEBUG, "%s - Tag loaded for: %s", __FUNCTION__, spItem->m_strPath.c_str()); } else CLog::Log(LOGDEBUG, "%s - No tag found for: %s", __FUNCTION__, pItem->m_strPath.c_str()); } } CheckForVariousArtists(songsToAdd); if (!items.HasThumbnail()) UpdateFolderThumb(songsToAdd, items.m_strPath); // finally, add these to the database for (unsigned int i = 0; i < songsToAdd.size(); ++i) { if (m_bStop) return i; CSong &song = songsToAdd[i]; m_musicDatabase.AddSong(song, false); if (!m_bStop && g_guiSettings.GetBool("musiclibrary.autoartistinfo")) { long iArtist = m_musicDatabase.GetArtistByName(song.strArtist); CStdString strPath; strPath.Format("musicdb://2/%u/",iArtist); if (find(m_artistsScanned.begin(),m_artistsScanned.end(),iArtist) == m_artistsScanned.end()) if (DownloadArtistInfo(strPath,song.strArtist)) m_artistsScanned.push_back(iArtist); if (m_pObserver) m_pObserver->OnStateChanged(READING_MUSIC_INFO); } if (!m_bStop && g_guiSettings.GetBool("musiclibrary.autoalbuminfo")) { long iAlbum = m_musicDatabase.GetAlbumByName(song.strAlbum,song.strArtist); CStdString strPath; strPath.Format("musicdb://3/%u/",iAlbum); CMusicAlbumInfo albumInfo; bool bCanceled; if (find(m_albumsScanned.begin(),m_albumsScanned.end(),iAlbum) == m_albumsScanned.end()) if (DownloadAlbumInfo(strPath,song.strArtist,song.strAlbum,bCanceled,albumInfo)) m_albumsScanned.push_back(iAlbum); if (m_pObserver) m_pObserver->OnStateChanged(READING_MUSIC_INFO); } } return songsToAdd.size(); }
int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory) { CSongMap songsMap; // get all information for all files in current directory from database, and remove them if (m_musicDatabase.RemoveSongsFromPath(strDirectory, songsMap, false)) m_needsCleanup = true; VECSONGS songsToAdd; CStdStringArray regexps = g_advancedSettings.m_audioExcludeFromScanRegExps; // for every file found, but skip folder for (int i = 0; i < items.Size(); ++i) { CFileItemPtr pItem = items[i]; CStdString strExtension; URIUtils::GetExtension(pItem->GetPath(), strExtension); if (m_bStop) return 0; // Discard all excluded files defined by m_musicExcludeRegExps if (CUtil::ExcludeFileOrFolder(pItem->GetPath(), regexps)) continue; if (pItem.get()->IsPFC()) { m_currentItem++; // if we have the itemcount, notify our // observer with the progress we made if (m_pObserver && m_itemCount>0) m_pObserver->OnSetProgress(m_currentItem, m_itemCount); int got = RetrievePFCSongs(pItem, songsToAdd); if (got > 0) CLog::Log(LOGDEBUG, "%s: Got songs from PFC: '%s'", __FUNCTION__, pItem.get()->GetPath().c_str()); } } // Laureon: From here! // CheckForVariousArtists(songsToAdd); // if (!items.HasThumbnail()) // UpdateFolderThumb(songsToAdd, items.GetPath()); // // // finally, add these to the database // set<CStdString> artistsToScan; // set< pair<CStdString, CStdString> > albumsToScan; // m_musicDatabase.BeginTransaction(); // for (unsigned int i = 0; i < songsToAdd.size(); ++i) // { // if (m_bStop) // { // m_musicDatabase.RollbackTransaction(); // return i; // } // CSong &song = songsToAdd[i]; // m_musicDatabase.AddSong(song, true); // // artistsToScan.insert(song.strArtist); // albumsToScan.insert(make_pair(song.strAlbum, song.strArtist)); // } // m_musicDatabase.CommitTransaction(); // // bool bCanceled; // for (set<CStdString>::iterator i = artistsToScan.begin(); i != artistsToScan.end(); ++i) // { // bCanceled = false; // long iArtist = m_musicDatabase.GetArtistByName(*i); // if (find(m_artistsScanned.begin(),m_artistsScanned.end(),iArtist) == m_artistsScanned.end()) // { // m_artistsScanned.push_back(iArtist); // if (!m_bStop)// && g_guiSettings.GetBool("musiclibrary.downloadinfo")) // { // CStdString strPath; // strPath.Format("musicdb://2/%u/",iArtist); // if (!DownloadArtistInfo(strPath,*i, bCanceled)) // assume we want to retry // m_artistsScanned.pop_back(); // } // else // GetArtistArtwork(iArtist, *i); // } // } // // for (set< pair<CStdString, CStdString> >::iterator i = albumsToScan.begin(); i != albumsToScan.end(); ++i) // { // if (m_bStop) // return songsToAdd.size(); // // long iAlbum = m_musicDatabase.GetAlbumByName(i->first, i->second); // CStdString strPath; // strPath.Format("musicdb://3/%u/",iAlbum); // // bCanceled = false; // if (find(m_albumsScanned.begin(), m_albumsScanned.end(), iAlbum) == m_albumsScanned.end()) // { // CMusicAlbumInfo albumInfo; // if (DownloadAlbumInfo(strPath, i->second, i->first, bCanceled, albumInfo)) // m_albumsScanned.push_back(iAlbum); // } // } if (m_pObserver) m_pObserver->OnStateChanged(READING_MUSIC_INFO); return songsToAdd.size(); }