/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include "smdirwatcher.h" #include "smtreeitem.h" #include "helper.h" extern int errno; SmDirWatcher::SmDirWatcher(int numFields, QObject *parent) : QThread(parent), mFd(0), mDescr(0) { mBufLen = 1024 * (sizeof(struct inotify_event) + 16); mINdata = new char[mBufLen]; mFd = inotify_init(); mCollector = new SmDataColletor(numFields, this); mSemFree = new QSemaphore(1024); mSemUsed = new QSemaphore; mDataQueue = new QQueue >(); mCollector->init(mSemFree, mSemUsed, mDataQueue); mCollector->start(); } void SmDirWatcher::setDir(const QString &dir){ if(mDescr){ inotify_rm_watch(mFd, mDescr); //generates IN_IGNORE ??? } /* mask rationale: * IN_DELETE_SELF cannot happen since we're only * watching one directory at all time. * We don't care about the other events */ mDescr = inotify_add_watch(mFd, qPrintable(dir), IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO); mCurrent = dir; mSemFree->acquire(); QPair c = qMakePair(dir, Populate); mDataQueue->enqueue(c); mSemUsed->release(); } void SmDirWatcher::run(){ int r = read(mFd, mINdata, mBufLen); if(r <= 0){ return; } int i = 0; while(i < r){ inotify_event *e = reinterpret_cast(&mINdata[i]); if((!e->len) || (e->mask & IN_IGNORED)){ i += sizeof(inotify_event) + e->len; continue; } mSemFree->acquire(); quint32 mask = e->mask; QString name = QString("%1/%2").arg(mCurrent).arg(e->name); DWEvent curEvent = None; QPair c; if(mask & IN_CREATE || mask & IN_MOVED_TO){ curEvent = Added; }else if(mask & IN_DELETE || mask & IN_MOVED_FROM){ curEvent = Deleted; }else if(mask & IN_CLOSE_WRITE || e->mask & IN_MODIFY){ curEvent = Modified; }else{ goto out; } c = qMakePair(name, curEvent); mDataQueue->enqueue(c); out: mSemUsed->release(); i += sizeof(inotify_event) + e->len; } } void SmDirWatcher::stop(){ mSemFree->acquire(); mCollector->stop(); mSemUsed->release(); if(mCollector->isRunning()){ mCollector->wait(); } quit(); } SmDataColletor::SmDataColletor(const int numFields, QObject *parent) : QThread(parent), mSemFree(0), mSemUsed(0), mDataQueue(0), mNumFields(numFields), mCheckForPresent(true), mCancel(false) { QSqlDatabase db = QSqlDatabase::cloneDatabase(QSqlDatabase::database("treedb"), "collectordb"); db.open(); mPicPresentQ = new QSqlQuery(db); mPicPresentQ->prepare("SELECT COUNT(cmd5sum) FROM pics WHERE cmd5sum = :md5"); mMovPresentQ = new QSqlQuery(db); mMovPresentQ->prepare("SELECT COUNT(cmd5sum) FROM files WHERE cmd5sum = :md5"); } void SmDataColletor::init(QSemaphore *set, QSemaphore *get, QQueue > *data){ mSemFree = set; mSemUsed = get; mDataQueue = data; } void SmDataColletor::setCheckForPresent(bool present){ QMutexLocker l(&mCheckForPresentMx); mCheckForPresent = present; } void SmDataColletor::run(){ forever { mSemUsed->acquire(); if(mCancel){ QSqlDatabase::database("collectordb").close(); mSemFree->release(); break; } QPair cur = mDataQueue->dequeue(); if(cur.second == SmDirWatcher::Populate){ SmTreeItem *i = populate(cur.first); SmTreeItem *copy = new SmTreeItem(*i); emit population(copy); mSemFree->release(); continue; } QFileInfo fi(cur.first); QList fd = fileData(fi); emit newData(fd, cur.second); mSemFree->release(); } quit(); } SmTreeItem * SmDataColletor::populate(const QString &dir){ SmTreeItem *retval = new SmTreeItem(mNumFields); QDir d = QDir(dir); foreach(QFileInfo fi, d.entryInfoList()){ if(fi.fileName() == "."){ continue; } QList data = fileData(fi); SmTreeItem *newItem = new SmTreeItem(data, retval); retval->appendChild(newItem); } return retval; } const QList SmDataColletor::fileData(const QFileInfo &fi){ QList data; data << fi.fileName() << fi.size(); QString mime = Helper::mimeType(fi.absoluteFilePath()); data << mime; data << fi.lastModified(); QList si = QList() << QVariant() << QVariant(); if(mime.startsWith("video")){ QVariantMap ffData = Helper::ffmpegData(fi.absoluteFilePath()); si[0] = ffData.value("duration").toDouble(); si[1] = ffData.value("bit_rate").toInt(); }else if(mime.startsWith("image")){ QImage img(fi.absoluteFilePath()); si[0] = img.size(); } data << Helper::md5Sum(fi.absoluteFilePath()); data << si << fi.absoluteFilePath(); data << 0; mCheckForPresentMx.lock(); bool p = mCheckForPresent; mCheckForPresentMx.unlock(); if(p){ QSqlQuery *curQuery = 0; if(mime.startsWith("video")){ curQuery = mMovPresentQ; } if(mime.startsWith("image")){ curQuery = mPicPresentQ; } if(curQuery){ curQuery->bindValue(":md5", data.at(4)); if(curQuery->exec()){ QVariant res; while(curQuery->next()){ res = curQuery->value(0); } data[8] = res; } } } return data; }