void LibraryScanner::slotStartScan() { qDebug() << "LibraryScanner::slotStartScan"; QSet<QString> trackLocations = m_trackDao.getTrackLocations(); QHash<QString, int> directoryHashes = m_libraryHashDao.getDirectoryHashes(); QRegExp extensionFilter = QRegExp(SoundSourceProxy::supportedFileExtensionsRegex(), Qt::CaseInsensitive); QRegExp coverExtensionFilter = QRegExp(CoverArtUtils::supportedCoverArtExtensionsRegex(), Qt::CaseInsensitive); QStringList directoryBlacklist = ScannerUtil::getDirectoryBlacklist(); m_scannerGlobal = ScannerGlobalPointer( new ScannerGlobal(trackLocations, directoryHashes, extensionFilter, coverExtensionFilter, directoryBlacklist)); m_scannerGlobal->startTimer(); emit(scanStarted()); // Try to upgrade the library from 1.7 (XML) to 1.8+ (DB) if needed. If the // upgrade_filename already exists, then do not try to upgrade since we have // already done it. // TODO(XXX) SETTINGS_PATH may change in new Mixxx Versions. Here we need // the SETTINGS_PATH from Mixxx V <= 1.7 QString upgrade_filename = QDir::homePath().append("/").append(SETTINGS_PATH).append("DBUPGRADED"); qDebug() << "upgrade filename is " << upgrade_filename; QFile upgradefile(upgrade_filename); if (!upgradefile.exists()) { QTime t2; t2.start(); LegacyLibraryImporter libImport(m_trackDao, m_playlistDao); connect(&libImport, SIGNAL(progress(QString)), this, SIGNAL(progressLoading(QString))); ScopedTransaction transaction(m_database); libImport.import(); transaction.commit(); qDebug("Legacy importer took %d ms", t2.elapsed()); } // First, we're going to mark all the directories that we've previously // hashed as needing verification. As we search through the directory tree // when we rescan, we'll mark any directory that does still exist as // verified. m_libraryHashDao.invalidateAllDirectories(); // Mark all the tracks in the library as needing verification of their // existence. (ie. we want to check they're still on your hard drive where // we think they are) m_trackDao.invalidateTrackLocationsInLibrary(); qDebug() << "Recursively scanning library."; // Start scanning the library. This prepares insertion queries in TrackDAO // (must be called before calling addTracksAdd) and begins a transaction. m_trackDao.addTracksPrepare(); // Recursivly scan each directory in the directories table. QStringList dirs = m_directoryDao.getDirs(); // If there are no directories then we have nothing to do. Cleanup and // finish the scan immediately. if (dirs.isEmpty()) { slotFinishScan(); return; } // Queue up recursive scan tasks for every directory. When all tasks are // done, TaskWatcher will signal slotFinishScan. TaskWatcher* pWatcher = &m_scannerGlobal->getTaskWatcher(); connect(pWatcher, SIGNAL(allTasksDone()), this, SLOT(slotFinishScan())); foreach (const QString& dirPath, dirs) { // Acquire a security bookmark for this directory if we are in a // sandbox. For speed we avoid opening security bookmarks when recursive // scanning so that relies on having an open bookmark for the containing // directory. MDir dir(dirPath); queueTask(new RecursiveScanDirectoryTask(this, m_scannerGlobal, dir.dir(), dir.token())); }
/** Upgrade from <= 1.7 library to 1.8 DB format */ void LegacyLibraryImporter::import() { // TODO(XXX) SETTINGS_PATH may change in new Mixxx Versions. Here we need // the SETTINGS_PATH from Mixxx V <= 1.7 QString settingPath17 = QDir::homePath().append("/").append(SETTINGS_PATH); QString trackXML = settingPath17.append("mixxxtrack.xml"); QFile file(trackXML); QDomDocument doc("TrackList"); if(!file.open(QIODevice::ReadOnly)) { //qDebug() << "Could not import legacy 1.7 XML library: " << trackXML; return; } QString* errorMsg = NULL; int* errorLine = NULL; int* errorColumn = NULL; qDebug() << "Starting upgrade from 1.7 library..."; QHash<int, QString> playlistHashTable; //Maps track indices onto track locations QList<LegacyPlaylist> legacyPlaylists; // <= 1.7 playlists if (doc.setContent(&file, false, errorMsg, errorLine, errorColumn)) { QDomNodeList playlistList = doc.elementsByTagName("Playlist"); QDomNode playlist; for (int i = 0; i < playlistList.size(); i++) { LegacyPlaylist legPlaylist; playlist = playlistList.at(i); QString name = playlist.firstChildElement("Name").text(); legPlaylist.name = name; //Store the IDs in the hash table so we can map them to track locations later, //and also store them in-order in a temporary playlist struct. QDomElement listNode = playlist.firstChildElement("List").toElement(); QDomNodeList trackIDs = listNode.elementsByTagName("Id"); for (int j = 0; j < trackIDs.size(); j++) { int id = trackIDs.at(j).toElement().text().toInt(); if (!playlistHashTable.contains(id)) playlistHashTable.insert(id, ""); legPlaylist.indexes.push_back(id); //Save this track id. } //Save this playlist in our list. legacyPlaylists.push_back(legPlaylist); } QDomNodeList trackList = doc.elementsByTagName("Track"); QDomNode track; for (int i = 0; i < trackList.size(); i++) { //blah, can't figure out how to use an iterator with QDomNodeList track = trackList.at(i); TrackInfoObject trackInfo17(track); //Only add the track to the DB if the file exists on disk, //because Mixxx <= 1.7 had no logic to deal with detecting deleted //files. if (trackInfo17.exists()) { //Create a TrackInfoObject by directly parsing //the actual MP3/OGG/whatever because 1.7 didn't parse //genre and album tags (so the imported TIO doesn't have //those fields). emit(progress("Upgrading Mixxx 1.7 Library: " + trackInfo17.getTitle())); // Read the metadata we couldn't support in <1.8 from file. QFileInfo fileInfo(trackInfo17.getLocation()); //Ensure we have the absolute file path stored trackInfo17.setLocation(fileInfo.absoluteFilePath()); TrackInfoObject trackInfoNew(trackInfo17.getLocation()); trackInfo17.setGenre(trackInfoNew.getGenre()); trackInfo17.setAlbum(trackInfoNew.getAlbum()); trackInfo17.setYear(trackInfoNew.getYear()); trackInfo17.setType(trackInfoNew.getType()); trackInfo17.setTrackNumber(trackInfoNew.getTrackNumber()); trackInfo17.setKey(trackInfoNew.getKey()); trackInfo17.setHeaderParsed(true); // Import the track's saved cue point if it is non-zero. float fCuePoint = trackInfo17.getCuePoint(); if (fCuePoint != 0.0f) { Cue* pCue = trackInfo17.addCue(); pCue->setType(Cue::CUE); pCue->setPosition(fCuePoint); } // Provide a no-op deleter b/c this Track is on the stack. TrackPointer pTrack(&trackInfo17, &doNothing); m_trackDao.saveTrack(pTrack); //Check if this track is used in a playlist anywhere. If it is, save the //track location. (The "id" of a track in 1.8 is a database index, so it's totally //different. Using the track location is the best way for us to identify the song.) int id = trackInfo17.getId(); if (playlistHashTable.contains(id)) playlistHashTable[id] = trackInfo17.getLocation(); } } //Create the imported playlists QListIterator<LegacyPlaylist> it(legacyPlaylists); LegacyPlaylist current; while (it.hasNext()) { current = it.next(); emit(progress("Upgrading Mixxx 1.7 Playlists: " + current.name)); //Create the playlist with the imported name. //qDebug() << "Importing playlist:" << current.name; int playlistId = m_playlistDao.createPlaylist(current.name); //For each track ID in the XML... QList<int> trackIDs = current.indexes; for (int i = 0; i < trackIDs.size(); i++) { QString trackLocation; int id = trackIDs[i]; //qDebug() << "track ID:" << id; //Try to resolve the (XML's) track ID to a track location. if (playlistHashTable.contains(id)) { trackLocation = playlistHashTable[id]; //qDebug() << "Resolved to:" << trackLocation; } //Get the database's track ID (NOT the XML's track ID!) int dbTrackId = m_trackDao.getTrackId(trackLocation); if (dbTrackId >= 0) { // Add it to the database's playlist. // TODO(XXX): Care if the append succeeded. m_playlistDao.appendTrackToPlaylist(dbTrackId, playlistId); } } } QString upgrade_filename = settingPath17.append("DBUPGRADED"); //now create stub so that the library is not readded next time program loads QFile upgradefile(upgrade_filename); if (!upgradefile.open(QIODevice::WriteOnly | QIODevice::Text)) qDebug() << "Couldn't open" << upgrade_filename << "for writing"; else { file.write("",0); file.close(); } } else { qDebug() << errorMsg << " line: " << errorLine << " column: " << errorColumn; } file.close(); }