#include #include #include #include #include #include #include #include #include #include "taglib/tag.h" #include "indexerwidget.h" IndexerWidget::IndexerWidget(QWidget *parent) : QWidget(parent) { //widgets mLog = new QTextEdit; mLog->setFont(QFont("courier new")); mLog->setTextColor(Qt::darkGreen); mError = new QTextEdit; mError->setFont(QFont("courier new")); mError->setTextColor(Qt::red); QPushButton *startB = new QPushButton(tr("Index")); connect(startB, SIGNAL(clicked()), this, SLOT(startIndexing())); QPushButton *cancelB = new QPushButton(tr("Cancel")); connect(cancelB, SIGNAL(clicked()), this, SLOT(stopIndexing())); //reader mReader = new BeetReader; connect(mReader, SIGNAL(message(QString)), this, SLOT(addToLog(QString))); connect(mReader, SIGNAL(errorMsg(QString)), this, SLOT(addToError(QString))); //layout QVBoxLayout *mainLayout = new QVBoxLayout; QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(startB); buttonLayout->addWidget(cancelB); buttonLayout->addStretch(); mainLayout->addWidget(mLog); mainLayout->addWidget(mError); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); } void IndexerWidget::startIndexing(){ mLog->clear(); mError->clear(); mReader->start(); } void IndexerWidget::stopIndexing(){ mReader->cancel(); } void IndexerWidget::addToLog(QString msg){ mLog->append(msg); } void IndexerWidget::addToError(QString msg){ mError->append(msg); } BeetReader::BeetReader() : mCanceled(false){ mDb = QSqlDatabase::database("beetplayerdb"); mInsertArtistsQ = new QSqlQuery(mDb); mInsertArtistsQ->prepare("INSERT INTO artists (tartists_name) VALUES(:name)"); mCurArtistQ = new QSqlQuery(mDb); mCurArtistQ->prepare("SELECT currval('artist_iartists_id__seq')"); mInsertGenresQ = new QSqlQuery(mDb); mInsertGenresQ->prepare("INSERT INTO genres (tgenres_name) VALUES(:name)"); mCurGenresQ = new QSqlQuery(mDb); mCurGenresQ->prepare("SELECT currval('genres_igenre_id__seq')"); mInsertAlbumQ = new QSqlQuery(mDb); mInsertAlbumQ->prepare("INSERT INTO albums(iartists_id, igenres_id, talbum_name, siyear) VALUES(:aid, :genid, :name, :year)"); mCurAlbumQ = new QSqlQuery(mDb); mCurAlbumQ->prepare("SELECT currval('albums_ialbums_id__seq')"); mInsertSongQ = new QSqlQuery(mDb); mInsertSongQ->prepare("INSERT INTO songs (ialbums_id, sipos, ttitle, tfullpath) VALUES(:aid, :pos, :title, :tfp)"); } void BeetReader::run(){ emit message(QString(tr("Clearing everything..."))); clearAll(); QProcess lister; lister.start("beet", QStringList() << "ls" << "-p"); qApp->setOverrideCursor(QCursor(Qt::WaitCursor)); emit message(QString(tr("Waiting for beet ls -p..."))); lister.waitForStarted(); lister.waitForFinished(); qApp->restoreOverrideCursor(); QByteArray lOut = lister.readAllStandardOutput(); QList files = lOut.split('\n'); int totalCount = files.size(); QString foundMsg = QString(tr("Found %1 file(s)\n").arg(QString::number(totalCount))); emit message(foundMsg); int ctr = 1; foreach(QByteArray s, files){ //fetch data from file TagLib::FileRef file(QString(s).toUtf8()); if(file.isNull()){ QString fn = QString(s).toUtf8(); QString error = QString(tr("IsNull: %1")).arg(fn); emit errorMsg(error); continue; } QString artist = toQString(file.tag()->artist()); QString album = toQString(file.tag()->album()); QString title = toQString(file.tag()->title()); QString genre = toQString(file.tag()->genre()); quint16 track = file.tag()->track(); quint16 year = file.tag()->year(); //write to database int artistId = doArtist(artist); int genreId = doGenre(genre); int albumId = doAlbum(album, genreId, artistId, year, QString(s).toUtf8()); doSong(title, track, albumId, s); if(ctr % 100 == 0){ QString msg = QString(tr("Processed %1 files of %2")).arg(QString::number(ctr)).arg(QString::number(totalCount)); emit message(msg); } mCancelMx.lock(); if(mCanceled == true){ mCanceled = false; mCancelMx.unlock(); return; } mCancelMx.unlock(); ++ctr; } emit message(QString(tr("Indexing DONE!"))); } void BeetReader::cancel(){ mCancelMx.lock(); mCanceled = true; mCancelMx.unlock(); } QString BeetReader::toQString(TagLib::String string){ QString retval = QString::fromStdWString(string.toWString()); retval = retval.simplified().toLower(); return retval; } void BeetReader::clearAll(){ QSqlQuery q1("DELETE FROM songs", mDb); QSqlQuery q2("DELETE FROM albums", mDb); QSqlQuery q3("DELETE FROM genres", mDb); QSqlQuery q4("DELETE FROM artists", mDb); mArtistH.clear(); mAlbumH.clear(); mGenreH.clear(); emit cleared(); } int BeetReader::doArtist(QString name){ if(mArtistH.contains(name)){ return mArtistH.value(name); } mDb.transaction(); mInsertArtistsQ->bindValue(":name", name); mInsertArtistsQ->exec(); mCurArtistQ->exec(); int retval = -1; while(mCurArtistQ->next()){ retval = mCurArtistQ->value(0).toInt(); } mArtistH.insert(name, retval); mDb.commit(); return retval; } int BeetReader::doGenre(QString name){ if(mGenreH.contains(name)){ return mGenreH.value(name); } mDb.transaction(); mInsertGenresQ->bindValue(":name", name); mInsertGenresQ->exec(); mCurGenresQ->exec(); int retval = -1; mCurGenresQ->exec(); while(mCurGenresQ->next()){ retval = mCurGenresQ->value(0).toInt(); } mGenreH.insert(name, retval); mDb.commit(); return retval; } int BeetReader::doAlbum(QString name, int genre, int artist, int year, QString fullPath){ QFileInfo fi(fullPath); QString key = fi.absolutePath(); if(mAlbumH.contains(key)){ return mAlbumH.value(key); } mDb.transaction(); mInsertAlbumQ->bindValue(":name", name); mInsertAlbumQ->bindValue(":aid", artist); mInsertAlbumQ->bindValue(":genid", genre); mInsertAlbumQ->bindValue(":year", year); mInsertAlbumQ->exec(); int retval = -1; mCurAlbumQ->exec(); while(mCurAlbumQ->next()){ retval = mCurAlbumQ->value(0).toInt(); } mAlbumH.insert(key, retval); mDb.commit(); return retval; } void BeetReader::doSong(QString title, int pos, int album, QString fullpath){ mDb.transaction(); mInsertSongQ->bindValue(":title", title); mInsertSongQ->bindValue(":pos", pos); mInsertSongQ->bindValue(":aid", album); mInsertSongQ->bindValue(":tfp", fullpath); mInsertSongQ->exec(); mDb.commit(); }