void CMusicInfoTag::SetAlbum(const CAlbum& album) { Clear(); //Set all artist infomation from album artist credits and artist description SetArtistDesc(album.GetAlbumArtistString()); SetArtist(album.GetAlbumArtist()); SetMusicBrainzArtistID(album.GetMusicBrainzAlbumArtistID()); SetAlbumArtistDesc(album.GetAlbumArtistString()); SetAlbumArtist(album.GetAlbumArtist()); SetMusicBrainzAlbumArtistID(album.GetMusicBrainzAlbumArtistID()); SetAlbumId(album.idAlbum); SetAlbum(album.strAlbum); SetTitle(album.strAlbum); SetMusicBrainzAlbumID(album.strMusicBrainzAlbumID); SetMusicBrainzReleaseType(album.strType); SetGenre(album.genre); SetMood(StringUtils::Join(album.moods, g_advancedSettings.m_musicItemSeparator)); SetRecordLabel(album.strLabel); SetRating(album.fRating); SetUserrating(album.iUserrating); SetVotes(album.iVotes); SetCompilation(album.bCompilation); SYSTEMTIME stTime; stTime.wYear = album.iYear; SetReleaseDate(stTime); SetAlbumReleaseType(album.releaseType); SetDateAdded(album.dateAdded); SetPlayCount(album.iTimesPlayed); SetDatabaseId(album.idAlbum, MediaTypeAlbum); SetLastPlayed(album.lastPlayed); SetLoaded(); }
INFO_RET CMusicInfoScanner::UpdateDatabaseAlbumInfo(CAlbum& album, const ADDON::ScraperPtr& scraper, bool bAllowSelection, CGUIDialogProgress* pDialog /* = NULL */) { if (!scraper) return INFO_ERROR; CMusicAlbumInfo albumInfo; loop: CLog::Log(LOGDEBUG, "%s downloading info for: %s", __FUNCTION__, album.strAlbum.c_str()); INFO_RET albumDownloadStatus = DownloadAlbumInfo(album, scraper, albumInfo, pDialog); if (albumDownloadStatus == INFO_NOT_FOUND) { if (pDialog && bAllowSelection) { if (!CGUIKeyboardFactory::ShowAndGetInput(album.strAlbum, CVariant{g_localizeStrings.Get(16011)}, false)) return INFO_CANCELLED; std::string strTempArtist(album.GetAlbumArtistString()); if (!CGUIKeyboardFactory::ShowAndGetInput(strTempArtist, CVariant{g_localizeStrings.Get(16025)}, false)) return INFO_CANCELLED; album.strArtistDesc = strTempArtist; goto loop; } else { CEventLog::GetInstance().Add(EventPtr(new CMediaLibraryEvent( MediaTypeAlbum, album.strPath, 24146, StringUtils::Format(g_localizeStrings.Get(24147).c_str(), MediaTypeAlbum, album.strAlbum.c_str()), CScraperUrl::GetThumbURL(album.thumbURL.GetFirstThumb()), CURL::GetRedacted(album.strPath), EventLevel::Warning))); } } else if (albumDownloadStatus == INFO_ADDED) { bool overridetags = CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_OVERRIDETAGS); album.MergeScrapedAlbum(albumInfo.GetAlbum(), overridetags); m_musicDatabase.Open(); m_musicDatabase.UpdateAlbum(album, overridetags); GetAlbumArtwork(album.idAlbum, album); m_musicDatabase.Close(); albumInfo.SetLoaded(true); } return albumDownloadStatus; }
void CMusicInfoScanner::Process() { ANNOUNCEMENT::CAnnouncementManager::GetInstance().Announce(ANNOUNCEMENT::AudioLibrary, "xbmc", "OnScanStarted"); try { if (m_showDialog && !CServiceBroker::GetSettings().GetBool(CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE)) { CGUIDialogExtendedProgressBar* dialog = g_windowManager.GetWindow<CGUIDialogExtendedProgressBar>(WINDOW_DIALOG_EXT_PROGRESS); if (dialog) m_handle = dialog->GetHandle(g_localizeStrings.Get(314)); } // check if we only need to perform a cleaning if (m_bClean && m_pathsToScan.empty()) { CleanDatabase(false); m_handle = NULL; m_bRunning = false; return; } unsigned int tick = XbmcThreads::SystemClockMillis(); m_musicDatabase.Open(); m_bCanInterrupt = true; if (m_scanType == 0) // load info from files { CLog::Log(LOGDEBUG, "%s - Starting scan", __FUNCTION__); if (m_handle) m_handle->SetTitle(g_localizeStrings.Get(505)); // Reset progress vars m_currentItem=0; m_itemCount=-1; // Create the thread to count all files to be scanned SetPriority( GetMinPriority() ); if (m_handle) m_fileCountReader.Create(); // Database operations should not be canceled // using Interrupt() while scanning as it could // result in unexpected behaviour. m_bCanInterrupt = false; m_needsCleanup = false; bool commit = true; for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); ++it) { if (!CDirectory::Exists(*it) && !m_bClean) { /* * Note that this will skip scanning (if m_bClean is disabled) if the directory really * doesn't exist. Since the music scanner is fed with a list of existing paths from the DB * and cleans out all songs under that path as its first step before re-adding files, if * the entire source is offline we totally empty the music database in one go. */ CLog::Log(LOGWARNING, "%s directory '%s' does not exist - skipping scan.", __FUNCTION__, it->c_str()); m_seenPaths.insert(*it); continue; } else if (!DoScan(*it)) { commit = false; break; } } if (commit) { g_infoManager.ResetLibraryBools(); if (m_needsCleanup) { if (m_handle) { m_handle->SetTitle(g_localizeStrings.Get(700)); m_handle->SetText(""); } m_musicDatabase.CleanupOrphanedItems(); if (m_handle) m_handle->SetTitle(g_localizeStrings.Get(331)); m_musicDatabase.Compress(false); } } m_fileCountReader.StopThread(); m_musicDatabase.EmptyCache(); tick = XbmcThreads::SystemClockMillis() - tick; CLog::Log(LOGNOTICE, "My Music: Scanning for music info using worker thread, operation took %s", StringUtils::SecondsToTimeString(tick / 1000).c_str()); } if (m_scanType == 1) // load album info { for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); ++it) { CQueryParams params; CDirectoryNode::GetDatabaseInfo(*it, params); if (m_musicDatabase.HasAlbumBeenScraped(params.GetAlbumId())) // should this be here? continue; CAlbum album; m_musicDatabase.GetAlbum(params.GetAlbumId(), album); if (m_handle) { float percentage = (float) std::distance(it, m_pathsToScan.end()) / m_pathsToScan.size(); m_handle->SetText(album.GetAlbumArtistString() + " - " + album.strAlbum); m_handle->SetPercentage(percentage); } // find album info ADDON::ScraperPtr scraper; if (!m_musicDatabase.GetScraperForPath(*it, scraper, ADDON::ADDON_SCRAPER_ALBUMS)) continue; UpdateDatabaseAlbumInfo(album, scraper, false); if (m_bStop) break; } } if (m_scanType == 2) // load artist info { for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); ++it) { CQueryParams params; CDirectoryNode::GetDatabaseInfo(*it, params); if (m_musicDatabase.HasArtistBeenScraped(params.GetArtistId())) // should this be here? continue; CArtist artist; m_musicDatabase.GetArtist(params.GetArtistId(), artist); m_musicDatabase.GetArtistPath(params.GetArtistId(), artist.strPath); if (m_handle) { float percentage = (float) (std::distance(m_pathsToScan.begin(), it) / m_pathsToScan.size()) * 100; m_handle->SetText(artist.strArtist); m_handle->SetPercentage(percentage); } // find album info ADDON::ScraperPtr scraper; if (!m_musicDatabase.GetScraperForPath(*it, scraper, ADDON::ADDON_SCRAPER_ARTISTS) || !scraper) continue; UpdateDatabaseArtistInfo(artist, scraper, false); if (m_bStop) break; } } } catch (...) { CLog::Log(LOGERROR, "MusicInfoScanner: Exception while scanning."); } m_musicDatabase.Close(); CLog::Log(LOGDEBUG, "%s - Finished scan", __FUNCTION__); m_bRunning = false; ANNOUNCEMENT::CAnnouncementManager::GetInstance().Announce(ANNOUNCEMENT::AudioLibrary, "xbmc", "OnScanFinished"); // we need to clear the musicdb cache and update any active lists CUtil::DeleteMusicDatabaseDirectoryCache(); CGUIMessage msg(GUI_MSG_SCAN_FINISHED, 0, 0, 0); g_windowManager.SendThreadMessage(msg); if (m_handle) m_handle->MarkFinished(); m_handle = NULL; }
INFO_RET CMusicInfoScanner::DownloadAlbumInfo(const CAlbum& album, const ADDON::ScraperPtr& info, CMusicAlbumInfo& albumInfo, CGUIDialogProgress* pDialog) { if (m_handle) { m_handle->SetTitle(StringUtils::Format(g_localizeStrings.Get(20321).c_str(), info->Name().c_str())); m_handle->SetText(album.GetAlbumArtistString() + " - " + album.strAlbum); } // clear our scraper cache info->ClearCache(); CMusicInfoScraper scraper(info); bool bMusicBrainz = false; if (!album.strMusicBrainzAlbumID.empty()) { CScraperUrl musicBrainzURL; if (ResolveMusicBrainz(album.strMusicBrainzAlbumID, info, musicBrainzURL)) { CMusicAlbumInfo albumNfo("nfo", musicBrainzURL); scraper.GetAlbums().clear(); scraper.GetAlbums().push_back(albumNfo); bMusicBrainz = true; } } // handle nfo files std::string path = album.strPath; if (path.empty()) m_musicDatabase.GetAlbumPath(album.idAlbum, path); std::string strNfo = URIUtils::AddFileToFolder(path, "album.nfo"); CNfoFile::NFOResult result = CNfoFile::NO_NFO; CNfoFile nfoReader; if (XFILE::CFile::Exists(strNfo)) { CLog::Log(LOGDEBUG,"Found matching nfo file: %s", CURL::GetRedacted(strNfo).c_str()); result = nfoReader.Create(strNfo, info); if (result == CNfoFile::FULL_NFO) { CLog::Log(LOGDEBUG, "%s Got details from nfo", __FUNCTION__); nfoReader.GetDetails(albumInfo.GetAlbum()); return INFO_ADDED; } else if (result == CNfoFile::URL_NFO || result == CNfoFile::COMBINED_NFO) { CScraperUrl scrUrl(nfoReader.ScraperUrl()); CMusicAlbumInfo albumNfo("nfo",scrUrl); ADDON::ScraperPtr nfoReaderScraper = nfoReader.GetScraperInfo(); CLog::Log(LOGDEBUG,"-- nfo-scraper: %s", nfoReaderScraper->Name().c_str()); CLog::Log(LOGDEBUG,"-- nfo url: %s", scrUrl.m_url[0].m_url.c_str()); scraper.SetScraperInfo(nfoReaderScraper); scraper.GetAlbums().clear(); scraper.GetAlbums().push_back(albumNfo); } else if (result != CNfoFile::PARTIAL_NFO) CLog::Log(LOGERROR,"Unable to find an url in nfo file: %s", strNfo.c_str()); } if (!scraper.CheckValidOrFallback(CServiceBroker::GetSettings().GetString(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER))) { // the current scraper is invalid, as is the default - bail CLog::Log(LOGERROR, "%s - current and default scrapers are invalid. Pick another one", __FUNCTION__); return INFO_ERROR; } if (!scraper.GetAlbumCount()) { scraper.FindAlbumInfo(album.strAlbum, album.GetAlbumArtistString()); while (!scraper.Completed()) { if (m_bStop) { scraper.Cancel(); return INFO_CANCELLED; } Sleep(1); } } CGUIDialogSelect *pDlg = NULL; int iSelectedAlbum=0; if ((result == CNfoFile::NO_NFO || result == CNfoFile::PARTIAL_NFO) && !bMusicBrainz) { iSelectedAlbum = -1; // set negative so that we can detect a failure if (scraper.Succeeded() && scraper.GetAlbumCount() >= 1) { double bestRelevance = 0; double minRelevance = THRESHOLD; if (scraper.GetAlbumCount() > 1) // score the matches { //show dialog with all albums found if (pDialog) { pDlg = g_windowManager.GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); pDlg->SetHeading(CVariant{g_localizeStrings.Get(181)}); pDlg->Reset(); pDlg->EnableButton(true, 413); // manual } for (int i = 0; i < scraper.GetAlbumCount(); ++i) { CMusicAlbumInfo& info = scraper.GetAlbum(i); double relevance = info.GetRelevance(); if (relevance < 0) relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, album.strAlbum, info.GetAlbum().GetAlbumArtistString(), album.GetAlbumArtistString()); // if we're doing auto-selection (ie querying all albums at once, then allow 95->100% for perfect matches) // otherwise, perfect matches only if (relevance >= std::max(minRelevance, bestRelevance)) { // we auto-select the best of these bestRelevance = relevance; iSelectedAlbum = i; } if (pDialog) { // set the label to [relevance] album - artist std::string strTemp = StringUtils::Format("[%0.2f] %s", relevance, info.GetTitle2().c_str()); CFileItem item(strTemp); item.m_idepth = i; // use this to hold the index of the album in the scraper pDlg->Add(item); } if (relevance > .99f) // we're so close, no reason to search further break; } if (pDialog && bestRelevance < THRESHOLD) { pDlg->Sort(false); pDlg->Open(); // and wait till user selects one if (pDlg->GetSelectedItem() < 0) { // none chosen if (!pDlg->IsButtonPressed()) return INFO_CANCELLED; // manual button pressed std::string strNewAlbum = album.strAlbum; if (!CGUIKeyboardFactory::ShowAndGetInput(strNewAlbum, CVariant{g_localizeStrings.Get(16011)}, false)) return INFO_CANCELLED; if (strNewAlbum == "") return INFO_CANCELLED; std::string strNewArtist = album.GetAlbumArtistString(); if (!CGUIKeyboardFactory::ShowAndGetInput(strNewArtist, CVariant{g_localizeStrings.Get(16025)}, false)) return INFO_CANCELLED; pDialog->SetLine(0, CVariant{strNewAlbum}); pDialog->SetLine(1, CVariant{strNewArtist}); pDialog->Progress(); CAlbum newAlbum = album; newAlbum.strAlbum = strNewAlbum; newAlbum.strArtistDesc = strNewArtist; return DownloadAlbumInfo(newAlbum, info, albumInfo, pDialog); } iSelectedAlbum = pDlg->GetSelectedFileItem()->m_idepth; } } else { CMusicAlbumInfo& info = scraper.GetAlbum(0); double relevance = info.GetRelevance(); if (relevance < 0) relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, album.strAlbum, info.GetAlbum().GetAlbumArtistString(), album.GetAlbumArtistString()); if (relevance < THRESHOLD) return INFO_NOT_FOUND; iSelectedAlbum = 0; } } if (iSelectedAlbum < 0) return INFO_NOT_FOUND; } scraper.LoadAlbumInfo(iSelectedAlbum); while (!scraper.Completed()) { if (m_bStop) { scraper.Cancel(); return INFO_CANCELLED; } Sleep(1); } if (!scraper.Succeeded()) return INFO_ERROR; albumInfo = scraper.GetAlbum(iSelectedAlbum); if (result == CNfoFile::COMBINED_NFO || result == CNfoFile::PARTIAL_NFO) nfoReader.GetDetails(albumInfo.GetAlbum(), NULL, true); return INFO_ADDED; }