Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/library/rhythmbox/rhythmboxfeature.cpp

Go to the documentation of this file.
00001 #include <QMessageBox>
00002 #include <QtDebug>
00003 #include <QStringList>
00004 
00005 #include "library/rhythmbox/rhythmboxtrackmodel.h"
00006 #include "library/rhythmbox/rhythmboxplaylistmodel.h"
00007 #include "library/rhythmbox/rhythmboxfeature.h"
00008 #include "library/treeitem.h"
00009 
00010 RhythmboxFeature::RhythmboxFeature(QObject* parent, TrackCollection* pTrackCollection)
00011     : LibraryFeature(parent),
00012       m_pTrackCollection(pTrackCollection),
00013       m_cancelImport(false) {
00014     QString tableName = "rhythmbox_library";
00015     QString idColumn = "id";
00016     QStringList columns;
00017     columns << "id"
00018             << "artist"
00019             << "title"
00020             << "album"
00021             << "year"
00022             << "genre"
00023             << "tracknumber"
00024             << "location"
00025             << "comment"
00026             << "rating"
00027             << "duration"
00028             << "bitrate"
00029             << "bpm";
00030     pTrackCollection->addTrackSource(QString("rhythmbox"), QSharedPointer<BaseTrackCache>(
00031         new BaseTrackCache(m_pTrackCollection, tableName, idColumn,
00032                            columns, false)));
00033 
00034     m_pRhythmboxTrackModel = new RhythmboxTrackModel(this, m_pTrackCollection);
00035     m_pRhythmboxPlaylistModel = new RhythmboxPlaylistModel(this, m_pTrackCollection);
00036     m_isActivated =  false;
00037     m_title = tr("Rhythmbox");
00038 
00039     if (!m_database.isOpen()) {
00040         m_database = QSqlDatabase::addDatabase("QSQLITE", "RHYTHMBOX_SCANNER");
00041         m_database.setHostName("localhost");
00042         m_database.setDatabaseName(MIXXX_DB_PATH);
00043         m_database.setUserName("mixxx");
00044         m_database.setPassword("mixxx");
00045 
00046         //Open the database connection in this thread.
00047         if (!m_database.open()) {
00048             qDebug() << "Failed to open database for Rhythmbox scanner." << m_database.lastError();
00049         }
00050     }
00051     connect(&m_track_watcher, SIGNAL(finished()),
00052             this, SLOT(onTrackCollectionLoaded()),
00053             Qt::QueuedConnection);
00054 
00055 }
00056 
00057 RhythmboxFeature::~RhythmboxFeature() {
00058     // stop import thread, if still running
00059     m_cancelImport = true;
00060     m_track_future.waitForFinished();
00061     delete m_pRhythmboxTrackModel;
00062     delete m_pRhythmboxPlaylistModel;
00063 }
00064 
00065 bool RhythmboxFeature::isSupported() {
00066     return (QFile::exists(QDir::homePath() + "/.gnome2/rhythmbox/rhythmdb.xml") ||
00067             QFile::exists(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml"));
00068 }
00069 
00070 QVariant RhythmboxFeature::title() {
00071     return m_title;
00072 }
00073 
00074 QIcon RhythmboxFeature::getIcon() {
00075     return QIcon(":/images/library/ic_library_rhythmbox.png");
00076 }
00077 
00078 TreeItemModel* RhythmboxFeature::getChildModel() {
00079     return &m_childModel;
00080 }
00081 
00082 void RhythmboxFeature::activate() {
00083      qDebug() << "RhythmboxFeature::activate()";
00084 
00085     if(!m_isActivated){
00086         m_isActivated =  true;
00087         /* Ususally the maximum number of threads
00088          * is > 2 depending on the CPU cores
00089          * Unfortunately, within VirtualBox
00090          * the maximum number of allowed threads
00091          * is 1 at all times We'll need to increase
00092          * the number to > 1, otherwise importing the music collection
00093          * takes place when the GUI threads terminates, i.e., on
00094          * Mixxx shutdown.
00095          */
00096         QThreadPool::globalInstance()->setMaxThreadCount(4); //Tobias decided to use 4
00097         m_track_future = QtConcurrent::run(this, &RhythmboxFeature::importMusicCollection);
00098         m_track_watcher.setFuture(m_track_future);
00099         m_title = "(loading) Rhythmbox";
00100         //calls a slot in the sidebar model such that 'Rhythmbox (isLoading)' is displayed.
00101         emit (featureIsLoading(this));
00102     }
00103     else
00104         emit(showTrackModel(m_pRhythmboxTrackModel));
00105 
00106 }
00107 
00108 void RhythmboxFeature::activateChild(const QModelIndex& index) {
00109     //qDebug() << "RhythmboxFeature::activateChild()" << index;
00110     QString playlist = index.data().toString();
00111     qDebug() << "Activating " << playlist;
00112     m_pRhythmboxPlaylistModel->setPlaylist(playlist);
00113     emit(showTrackModel(m_pRhythmboxPlaylistModel));
00114 }
00115 
00116 void RhythmboxFeature::onRightClick(const QPoint& globalPos) {
00117 }
00118 
00119 void RhythmboxFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) {
00120 }
00121 
00122 bool RhythmboxFeature::dropAccept(QUrl url) {
00123     return false;
00124 }
00125 
00126 bool RhythmboxFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
00127     return false;
00128 }
00129 
00130 bool RhythmboxFeature::dragMoveAccept(QUrl url) {
00131     return false;
00132 }
00133 
00134 bool RhythmboxFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
00135     return false;
00136 }
00137 
00138 TreeItem* RhythmboxFeature::importMusicCollection()
00139 {
00140     qDebug() << "importMusicCollection Thread Id: " << QThread::currentThread();
00141     /*
00142      * Try and open the Rhythmbox DB. An API call which tells us where
00143      * the file is would be nice.
00144      */
00145     QFile db(QDir::homePath() + "/.gnome2/rhythmbox/rhythmdb.xml");
00146     if ( ! db.exists()) {
00147         db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml");
00148         if ( ! db.exists())
00149             return false;
00150     }
00151 
00152     if (!db.open(QIODevice::ReadOnly | QIODevice::Text))
00153         return false;
00154 
00155     //Delete all table entries of Traktor feature
00156     m_database.transaction();
00157     clearTable("rhythmbox_playlist_tracks");
00158     clearTable("rhythmbox_library");
00159     clearTable("rhythmbox_playlists");
00160     m_database.commit();
00161 
00162     m_database.transaction();
00163     QSqlQuery query(m_database);
00164     query.prepare("INSERT INTO rhythmbox_library (artist, title, album, year, genre, comment, tracknumber,"
00165                   "bpm, bitrate,"
00166                   "duration, location,"
00167                   "rating ) "
00168                   "VALUES (:artist, :title, :album, :year, :genre, :comment, :tracknumber,"
00169                   ":bpm, :bitrate,"
00170                   ":duration, :location, :rating )");
00171 
00172 
00173     QXmlStreamReader xml(&db);
00174     while (!xml.atEnd() && !m_cancelImport) {
00175         xml.readNext();
00176         if (xml.isStartElement() && xml.name() == "entry") {
00177             QXmlStreamAttributes attr = xml.attributes();
00178             //Check if we really parse a track and not album art information
00179             if(attr.value("type").toString() == "song"){
00180                 importTrack(xml, query);
00181             }
00182         }
00183     }
00184     m_database.commit();
00185 
00186     if (xml.hasError()) {
00187         // do error handling
00188         qDebug() << "Cannot process Rhythmbox music collection";
00189         qDebug() << "XML ERROR: " << xml.errorString();
00190         return false;
00191     }
00192 
00193     db.close();
00194     if (m_cancelImport) {
00195         return NULL;
00196     }
00197     return importPlaylists();
00198 }
00199 
00200 TreeItem* RhythmboxFeature::importPlaylists()
00201 {
00202     QFile db(QDir::homePath() + "/.gnome2/rhythmbox/playlists.xml");
00203     if ( ! db.exists()) {
00204         db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/playlists.xml");
00205         if (!db.exists())
00206             return NULL;
00207     }
00208     //Open file
00209      if (!db.open(QIODevice::ReadOnly | QIODevice::Text))
00210         return NULL;
00211 
00212     QSqlQuery query_insert_to_playlists(m_database);
00213     query_insert_to_playlists.prepare("INSERT INTO rhythmbox_playlists (id, name) "
00214                                       "VALUES (:id, :name)");
00215 
00216     QSqlQuery query_insert_to_playlist_tracks(m_database);
00217     query_insert_to_playlist_tracks.prepare(
00218         "INSERT INTO rhythmbox_playlist_tracks (playlist_id, track_id, position) "
00219         "VALUES (:playlist_id, :track_id, :position)");
00220     //The tree structure holding the playlists
00221     TreeItem* rootItem = new TreeItem();
00222 
00223     QXmlStreamReader xml(&db);
00224     while (!xml.atEnd() && !m_cancelImport) {
00225         xml.readNext();
00226         if (xml.isStartElement() && xml.name() == "playlist") {
00227             QXmlStreamAttributes attr = xml.attributes();
00228 
00229             //Only parse non build-in playlists
00230             if(attr.value("type").toString() == "static"){
00231                 QString playlist_name = attr.value("name").toString();
00232 
00233                 //Construct the childmodel
00234                 TreeItem * item = new TreeItem(playlist_name,playlist_name, this, rootItem);
00235                 rootItem->appendChild(item);
00236 
00237                 //Execute SQL statement
00238                 query_insert_to_playlists.bindValue(":name", playlist_name);
00239 
00240                 bool success = query_insert_to_playlists.exec();
00241                 if(!success){
00242                     qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " "
00243                                     << query_insert_to_playlists.lastError();
00244                     return NULL;
00245                 }
00246                 //get playlist_id
00247                 int playlist_id = query_insert_to_playlists.lastInsertId().toInt();
00248 
00249                 //Process playlist entries
00250                 importPlaylist(xml, query_insert_to_playlist_tracks, playlist_id);
00251 
00252             }
00253         }
00254     }
00255 
00256     m_database.commit();
00257 
00258 
00259     if (xml.hasError()) {
00260         // do error handling
00261         qDebug() << "Cannot process Rhythmbox music collection";
00262         qDebug() << "XML ERROR: " << xml.errorString();
00263         return NULL;
00264     }
00265     db.close();
00266 
00267     return rootItem;
00268 
00269 }
00270 
00271 void RhythmboxFeature::importTrack(QXmlStreamReader &xml, QSqlQuery &query)
00272 {
00273     QString title;
00274     QString artist;
00275     QString album;
00276     QString year;
00277     QString genre;
00278     QString location;
00279 
00280     int bpm = 0;
00281     int bitrate = 0;
00282 
00283     //duration of a track
00284     int playtime = 0;
00285     int rating = 0;
00286     QString comment;
00287     QString tracknumber;
00288 
00289     while (!xml.atEnd()) {
00290         xml.readNext();
00291         if (xml.isStartElement()) {
00292             if(xml.name() == "title"){
00293                 title = xml.readElementText();
00294                 continue;
00295             }
00296             if(xml.name() == "artist"){
00297                 artist = xml.readElementText();
00298                 continue;
00299             }
00300             if(xml.name() == "genre"){
00301                 genre = xml.readElementText();
00302                 continue;
00303             }
00304             if(xml.name() == "album"){
00305                 album = xml.readElementText();
00306                 continue;
00307             }
00308             if(xml.name() == "track-number"){
00309                 tracknumber = xml.readElementText();
00310                 continue;
00311             }
00312             if(xml.name() == "duration"){
00313                 playtime = xml.readElementText().toInt();;
00314                 continue;
00315             }
00316             if(xml.name() == "bitrate"){
00317                 bitrate = xml.readElementText().toInt();
00318                 continue;
00319             }
00320             if(xml.name() == "beats-per-minute"){
00321                 bpm = xml.readElementText().toInt();
00322                 continue;
00323             }
00324             if(xml.name() == "comment"){
00325                 comment = xml.readElementText();
00326                 continue;
00327             }
00328             if(xml.name() == "location"){
00329                 location = xml.readElementText();
00330                 location.remove("file://");
00331                 QByteArray strlocbytes = location.toUtf8();
00332                 QUrl locationUrl = QUrl::fromEncoded(strlocbytes);
00333                 location = locationUrl.toLocalFile();
00334                 continue;
00335             }
00336         }
00337         //exit the loop if we reach the closing <entry> tag
00338         if (xml.isEndElement() && xml.name() == "entry") {
00339             break;
00340         }
00341 
00342     }
00343     query.bindValue(":artist", artist);
00344     query.bindValue(":title", title);
00345     query.bindValue(":album", album);
00346     query.bindValue(":genre", genre);
00347     query.bindValue(":year", year);
00348     query.bindValue(":duration", playtime);
00349     query.bindValue(":location", location);
00350     query.bindValue(":rating", rating);
00351     query.bindValue(":comment", comment);
00352     query.bindValue(":tracknumber", tracknumber);
00353     query.bindValue(":bpm", bpm);
00354     query.bindValue(":bitrate", bitrate);
00355 
00356     bool success = query.exec();
00357 
00358     if (!success) {
00359         qDebug() << "SQL Error in rhythmboxfeature.cpp: line" << __LINE__ << " " << query.lastError();
00360         return;
00361     }
00362 
00363 }
00364 
00366 void RhythmboxFeature::importPlaylist(QXmlStreamReader &xml, QSqlQuery &query_insert_to_playlist_tracks, int playlist_id)
00367 {
00368     int playlist_position = 1;
00369     while(!xml.atEnd())
00370     {
00371         //read next XML element
00372         xml.readNext();
00373         if(xml.isStartElement() && xml.name() == "location")
00374         {
00375             QString location = xml.readElementText();
00376             location.remove("file://");
00377             QByteArray strlocbytes = location.toUtf8();
00378             QUrl locationUrl = QUrl::fromEncoded(strlocbytes);
00379             location = locationUrl.toLocalFile();
00380 
00381             //get the ID of the file in the rhythmbox_library table
00382             int track_id = -1;
00383             QSqlQuery finder_query(m_database);
00384             finder_query.prepare("select id from rhythmbox_library where location=:path");
00385             finder_query.bindValue(":path", location);
00386             bool success = finder_query.exec();
00387 
00388 
00389             if(success){
00390                 while (finder_query.next()) {
00391                     track_id = finder_query.value(finder_query.record().indexOf("id")).toInt();
00392                 }
00393              }
00394              else
00395                 qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " " << finder_query.lastError();
00396 
00397             query_insert_to_playlist_tracks.bindValue(":playlist_id", playlist_id);
00398             query_insert_to_playlist_tracks.bindValue(":track_id", track_id);
00399             query_insert_to_playlist_tracks.bindValue(":position", playlist_position++);
00400             success = query_insert_to_playlist_tracks.exec();
00401 
00402             if(!success){
00403                 qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " "
00404                              << query_insert_to_playlist_tracks.lastError();
00405                 qDebug() << "trackid" << track_id;
00406                 qDebug() << "playlis ID " << playlist_id;
00407                 qDebug() << "-----------------";
00408             }
00409         }
00410         // Exit the the loop if we reach the closing <playlist> tag
00411         if (xml.isEndElement() && xml.name() == "playlist") {
00412             break;
00413         }
00414     }
00415 }
00416 
00417 void RhythmboxFeature::clearTable(QString table_name)
00418 {
00419     qDebug() << "clearTable Thread Id: " << QThread::currentThread();
00420     QSqlQuery query(m_database);
00421     query.prepare("delete from "+table_name);
00422     bool success = query.exec();
00423 
00424     if (!success) {
00425         qDebug() << "Could not delete remove old entries from table "
00426                  << table_name << " : " << query.lastError();
00427     } else {
00428         qDebug() << "Rhythmbox table entries of '" << table_name
00429                  << "' have been cleared.";
00430     }
00431 }
00432 
00433 void RhythmboxFeature::onTrackCollectionLoaded() {
00434     TreeItem* root = m_track_future.result();
00435     if (root) {
00436         m_childModel.setRootItem(root);
00437 
00438         // Tell the rhythmbox track source that it should re-build its index.
00439         m_pTrackCollection->getTrackSource("rhythmbox")->buildIndex();
00440 
00441         //m_pRhythmboxTrackModel->select();
00442     } else {
00443          qDebug() << "Rhythmbox Playlists loaded: false";
00444     }
00445 
00446     // calls a slot in the sidebarmodel such that 'isLoading' is removed from
00447     // the feature title.
00448     m_title = tr("Rhythmbox");
00449     emit(featureLoadingFinished(this));
00450     activate();
00451 }
00452 void RhythmboxFeature::onLazyChildExpandation(const QModelIndex &index){
00453     //Nothing to do because the childmodel is not of lazy nature.
00454 }
00455 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines