From: APTX Date: Tue, 12 Jun 2012 18:05:24 +0000 (+0200) Subject: Add RenameHandler. X-Git-Url: https://gitweb.tyo.aptx.org/?a=commitdiff_plain;h=3489d08bfde6292188e5ff13c602cae7e4c186f0;p=localmylist.git Add RenameHandler. --- diff --git a/localmylist/addfiletask.cpp b/localmylist/addfiletask.cpp index 6880bd0..bb50ce7 100644 --- a/localmylist/addfiletask.cpp +++ b/localmylist/addfiletask.cpp @@ -59,7 +59,11 @@ void AddFileTask::hashingFinished() int fid = db->isKnownFile(hashResult->hash(), m_file.size()); if (fid) { - db->setFileLocation(fid, MyList::instance()->hostId(), m_file.canonicalFilePath()); + FileLocation fl; + fl.fid = fid; + fl.hostId = MyList::instance()->hostId(); + fl.path = m_file.canonicalFilePath(); + db->addFileLocation(fl); emit finished(); return; } diff --git a/localmylist/database.cpp b/localmylist/database.cpp index 5cfa2e6..69dea0c 100644 --- a/localmylist/database.cpp +++ b/localmylist/database.cpp @@ -60,6 +60,13 @@ File::File() myFileState = 0; } +FileLocation::FileLocation() +{ + fid = 0; + hostId = 0; + failedRename = false; +} + FileEpisodeRel::FileEpisodeRel() { fid = 0; @@ -268,13 +275,26 @@ int Database::isKnownFile(const QByteArray &ed2k, qint64 size) return fid; } -bool Database::setFileLocation(int fid, int hostId, const QString &location) +bool Database::addFileLocation(const FileLocation &fileLocation) +{ + QSqlQuery q(d->db); + q.prepare("INSERT INTO file_location VALUES(:fid, :hostId, :path, DEFAULT, DEFAULT)"); + q.bindValue(":fid", fileLocation.fid); + q.bindValue(":hostId", fileLocation.hostId); + q.bindValue(":path", fileLocation.path); + + return exec(q); +} + +bool Database::setFileLocation(const FileLocation &fileLocation) { QSqlQuery q(d->db); - q.prepare("INSERT INTO file_location VALUES(:fid, :hostId, :path)"); - q.bindValue(":fid", fid); - q.bindValue(":hostId", hostId); - q.bindValue(":path", location); + q.prepare("UPDATE file_location SET host_id = :hostId, path = :path, renamed = :renamed, failed_rename = :failedRename WHERE fid = :fid"); + q.bindValue(":fid", fileLocation.fid); + q.bindValue(":hostId", fileLocation.hostId); + q.bindValue(":path", fileLocation.path); + q.bindValue(":renamed", fileLocation.renamed); + q.bindValue(":failedRename", fileLocation.failedRename); return exec(q); } @@ -992,6 +1012,7 @@ void Database::prepareQueries() d->db.driver()->subscribeToNotification("new_pending_request"); d->db.driver()->subscribeToNotification("new_pending_mylist_update"); + d->db.driver()->subscribeToNotification("rename_data_changed"); } bool Database::exec(QSqlQuery &query) @@ -1038,6 +1059,11 @@ bool Database::exec(const QString &sql) return result; } +bool Database::notify(const QString ¬ification) +{ + return exec("NOTIFY " + notification); +} + void Database::handleNotification(const QString ¬ification) { qDebug() << "Recieved notification" << notification; @@ -1049,6 +1075,10 @@ void Database::handleNotification(const QString ¬ification) { emit newPendingMyListUpdate(); } + else if (notification == "rename_data_changed") + { + emit renameDataChanged(); + } } diff --git a/localmylist/database.h b/localmylist/database.h index 39435c7..fb8db87 100644 --- a/localmylist/database.h +++ b/localmylist/database.h @@ -97,7 +97,7 @@ struct LOCALMYLISTSHARED_EXPORT File QDateTime releaseDate; int version; bool censored; - QString type; + QString type; // TODO rename this to source int qualityId; QString quality; QString resolution; @@ -116,6 +116,17 @@ struct LOCALMYLISTSHARED_EXPORT File File(); }; +struct LOCALMYLISTSHARED_EXPORT FileLocation +{ + int fid; + int hostId; + QString path; + QDateTime renamed; + bool failedRename; + + FileLocation(); +}; + struct LOCALMYLISTSHARED_EXPORT UnknownFile { QByteArray ed2k; @@ -215,7 +226,8 @@ public: bool setConfig(const QString &key, const QVariant &value); int isKnownFile(const QByteArray &ed2k, qint64 size); - bool setFileLocation(int fid, int hostId, const QString &location); + bool addFileLocation(const FileLocation &fileLocation); + bool setFileLocation(const FileLocation &fileLocation); Anime getAnime(int aid); Episode getEpisode(int eid); @@ -249,6 +261,11 @@ public: QSqlDatabase connection() const; + bool exec(QSqlQuery &query); + bool exec(const QString &sql); + + bool notify(const QString ¬ification); + public slots: bool connect(); void disconnect(); @@ -259,13 +276,12 @@ signals: void newPendingRequest(); void newPendingMyListUpdate(); + void renameDataChanged(); private slots: void handleNotification(const QString ¬ification); private: - bool exec(QSqlQuery &query); - bool exec(const QString &sql); void prepareQueries(); DatabaseInternal *d; diff --git a/localmylist/localmylist.pro b/localmylist/localmylist.pro index 60c3db7..3c2951e 100644 --- a/localmylist/localmylist.pro +++ b/localmylist/localmylist.pro @@ -21,7 +21,8 @@ SOURCES += \ mylistnode.cpp \ animetitleparsetask.cpp \ mylistexportparsetask.cpp \ - settings.cpp + settings.cpp \ + renamehandler.cpp HEADERS += \ localmylist_global.h \ @@ -36,7 +37,8 @@ HEADERS += \ mylistnode.h \ animetitleparsetask.h \ mylistexportparsetask.h \ - settings.h + settings.h \ + renamehandler.h CONV_HEADERS += \ include/LocalMyList/AbstractTask \ diff --git a/localmylist/mylist.cpp b/localmylist/mylist.cpp index 50f8e33..70b9772 100644 --- a/localmylist/mylist.cpp +++ b/localmylist/mylist.cpp @@ -11,6 +11,7 @@ #include "mylistexportparsetask.h" #include "workthread.h" #include "requesthandler.h" +#include "renamehandler.h" #include namespace LocalMyList { @@ -20,6 +21,7 @@ MyList::MyList() init(); m_requestHandler = 0; + m_renameHandler = 0; workThread = 0; db = new Database("main"); @@ -31,7 +33,9 @@ MyList::~MyList() { delete db; if (workThread) - delete workThread; + { + QMetaObject::invokeMethod(workThread, "quit"); + } } QString MyList::hostName() const @@ -96,7 +100,7 @@ void MyList::setupUdpClient() void MyList::setupRequestHandler() { - if(m_requestHandler) + if (m_requestHandler) return; m_requestHandler = new RequestHandler(db, this); @@ -104,11 +108,24 @@ void MyList::setupRequestHandler() connect(db, SIGNAL(newPendingMyListUpdate()), m_requestHandler, SLOT(handleMyListUpdates())); } +void MyList::setupRenameHandler() +{ + if (m_renameHandler || !db->isConnected()) + return; + + setupWorkThread(); + + m_renameHandler = new RenameHandler(workThread->database()); + m_renameHandler->moveToThread(workThread); + connect(db, SIGNAL(renameDataChanged()), m_renameHandler, SLOT(handleRename()), Qt::QueuedConnection); +} + void MyList::setupWorkThread() { if (workThread) return; workThread = new WorkThread("workThread", dbs, this); + connect(workThread, SIGNAL(finished()), workThread, SLOT(deleteLater())); workThread->start(); } diff --git a/localmylist/mylist.h b/localmylist/mylist.h index 4f17cad..66391fd 100644 --- a/localmylist/mylist.h +++ b/localmylist/mylist.h @@ -17,6 +17,7 @@ namespace LocalMyList { class AbstractTask; class WorkThread; class RequestHandler; +class RenameHandler; class LOCALMYLISTSHARED_EXPORT MyList : public QObject { Q_OBJECT @@ -47,6 +48,7 @@ public slots: void setupUdpClient(); void setupRequestHandler(); + void setupRenameHandler(); void setupWorkThread(); void setupHostInfo(); @@ -65,6 +67,7 @@ private: Database *db; WorkThread *workThread; RequestHandler *m_requestHandler; + RenameHandler *m_renameHandler; Settings *m_settings; HostInfo hostInfo; diff --git a/localmylist/renamehandler.cpp b/localmylist/renamehandler.cpp new file mode 100644 index 0000000..246c083 --- /dev/null +++ b/localmylist/renamehandler.cpp @@ -0,0 +1,188 @@ +#include "renamehandler.h" + +#include "mylist.h" +#include "database.h" +#include "settings.h" +#include +#include +#include + +#include + +namespace LocalMyList { + +RenameHandler::RenameHandler(Database *db, QObject *parent) : + QObject(parent), renameEngine(0), validScript(false) +{ + this->db = db; + connect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()), Qt::QueuedConnection); + setupRenameEngine(); +} + + +void RenameHandler::handleRename() +{ + QSqlQuery q(db->connection()); + + q.prepare("SELECT fid, eid, aid, gid, anime_anidb_update, anime_entry_update, anime_my_update, anime_title_english, anime_title_romaji, anime_title_kanji, " + " description, year, start_date, end_date, anime_type, anime_rating, anime_votes, temp_rating, temp_votes, anime_my_vote, anime_my_vote_date, my_temp_vote, my_temp_vote_date, " + "episode_anidb_update, episode_entry_update, episode_my_update, epno, episode_title_english, episode_title_romaji, episode_title_kanji, episode_length, airdate, state, special, recap, " + " opening, ending, rating, votes, my_vote, my_vote_date, " + "anidb_update, entry_update, my_update, ed2k, size, length, extension, group_name, group_name_short, crc, release_date, version, censored, type, quality_id, quality, resolution, " + " video_codec, audio_codec, audio_language, subtitle_language, aspect_ratio, my_watched, my_state, my_file_state, my_storage, my_source, my_other, host_id, path, renamed " + "FROM rename_data " + "WHERE host_id = :host_id " + "LIMIT :limit"); + + q.bindValue(":host_id", MyList::instance()->hostId()); + q.bindValue(":limit", 10); + + if (!db->exec(q)) + return; + + qDebug() << "Rename: Got" << q.size() << "rows"; + + if (q.size() < 1) + return; + + if (!validScript) + return; + + while (q.next()) + { + QSqlRecord r = q.record(); + QFileInfo oldFile(r.value("path").toString()); + + RaiiTransaction t(db); + t.commit(); + + FileLocation fl; + fl.fid = q.value(0).toInt(); + fl.hostId = MyList::instance()->hostId(); + fl.path = r.value("path").toString(); + fl.renamed = QDateTime::currentDateTime(); + + qDebug() << "Rename: renaming" << oldFile.filePath(); + if (!oldFile.exists()) + { + db->log(tr("Rename: Failed to rename file <%1>. File does not exist").arg(oldFile.filePath()), 2); + fl.failedRename = true; + db->setFileLocation(fl); + continue; + } + + RenameParser::Environment env; + env["ATr"] = r.value("anime_title_romaji").toString(); + env["ATe"] = r.value("anime_title_english").toString(); + env["ATk"] = r.value("anime_title_kanji").toString(); + + env["ETr"] = r.value("episode_title_romaji").toString(); + env["ETe"] = r.value("episode_title_english").toString(); + env["ETk"] = r.value("episode_title_kanji").toString(); + + env["GTs"] = r.value("group_name_short").toString(); + env["GTl"] = r.value("group_name").toString(); + + env["EpNo"] = r.value("epno").toString(); + env["EpHiNo"] = r.value("episode_count").toString(); + env["EpCount"] = "0"; // TODO This is missing from the data + + QString year = r.value("year").toString(); + if (!year.contains('-')) + { + env["AYearBegin"] = year; + env["AYearEnd"] = ""; + } + else + { + QStringList years = year.split('-'); + env["AYearBegin"] = years[0].trimmed(); + env["AYearEnd"] = years[1].trimmed(); + } + + env["Type"] = r.value("anime_type").toString(); + env["Depr"] = /*r.value("")*/ false ? "1" : ""; // Data missing? + + env["Cen"] = r.value("censored").toBool() ? "1" : "0"; + + env["Ver"] = r.value("version").toString(); + env["Source"] = r.value("type").toString(); // TODO This is called source on AniDB + env["Quality"] = r.value("quality").toString(); + env["FCrc"] = r.value("crc").toString(); + env["FVideoRes"] = r.value("resolution").toString(); + env["FALng"] = r.value("audio_language").toString(); + env["FSLng"] = r.value("subtitle_language").toString(); + env["FACodec"] = r.value("audio_codec").toString(); + env["FVCodec"] = r.value("video_codec").toString(); + env["Watched"] = r.value("my_watched").toDateTime().isValid() ? "1" : "0"; + + renameEngine->evaluate(env); + + if (env.value("FileName", "").isEmpty()) + { + fl.failedRename = true; + db->setFileLocation(fl); + db->log(tr("Rename: Failed to rename file <%1>. Rename script did not set a file name.").arg(oldFile.filePath()), 2); + continue; + } + + QString newFileName = env.value("FileName", "") + "." + r.value("extension").toString(); + QString newFilePath = env.value("FilePath", ""); + + if (newFilePath.isEmpty()) + newFilePath = oldFile.canonicalPath(); + + QString newFileString = newFilePath + "/" + newFileName; + QFileInfo newFile(newFileString); + + if (newFile.exists()) + { + fl.failedRename = true; + db->setFileLocation(fl); + db->log(tr("Rename: Failed to rename file <%1>. Destination <%2> exists.").arg(oldFile.filePath()).arg(newFile.canonicalPath()), 2); + continue; + } + + if (!newFile.absoluteDir().exists()) + { + if (!QDir().mkpath(newFile.canonicalPath())) + { + fl.failedRename = true; + db->setFileLocation(fl); + db->log(tr("Rename: Failed to rename file <%1>. Destination path <%2> does not exists and could not be created.").arg(oldFile.filePath()).arg(newFile.canonicalPath()), 2); + continue; + } + } + + if (oldFile.canonicalPath() != newFileString && !QFile::rename(oldFile.canonicalFilePath(), newFileString)) + { + fl.failedRename = true; + db->setFileLocation(fl); + db->log(tr("Rename: Failed to rename file <%1>. Failed to rename file to <%2>").arg(oldFile.canonicalFilePath()).arg(newFileString), 2); + continue; + } + + fl.path = newFileString; + fl.failedRename = false; + db->setFileLocation(fl); + + db->log(tr("Rename: File <%1> was renamed to <%2>").arg(oldFile.canonicalFilePath()).arg(newFileString)); + } + + emit renameBatchFinished(); +} + +void RenameHandler::setupRenameEngine() +{ + if (!renameEngine) + renameEngine = new RenameParser::RenameEngine(); + + renameEngine->setCurrentParserType(RenameParser::RenameEngine::ParserType( + MyList::instance()->settings()->get("renameLanguage").toInt())); + validScript = renameEngine->parse(MyList::instance()->settings()->get("renameScript").toString()); + + if (validScript) + db->log(tr("Rename Current rename script is invalid: %1, line: %2, column: %3").arg(renameEngine->error()).arg(renameEngine->line()).arg(renameEngine->column()), 2); +} + +} // namespace LocalMyList diff --git a/localmylist/renamehandler.h b/localmylist/renamehandler.h new file mode 100644 index 0000000..ab51886 --- /dev/null +++ b/localmylist/renamehandler.h @@ -0,0 +1,38 @@ +#ifndef RENAMEHANDLER_H +#define RENAMEHANDLER_H + +#include "localmylist_global.h" +#include + +namespace RenameParser { + class RenameEngine; +} + +namespace LocalMyList { + +class Database; + +class RenameHandler : public QObject +{ + Q_OBJECT +public: + explicit RenameHandler(Database *db, QObject *parent = 0); + +signals: + void renameBatchFinished(); + +public slots: + void handleRename(); + + void setupRenameEngine(); + +private: + Database *db; + RenameParser::RenameEngine *renameEngine; + + bool validScript; +}; + +} // namespace LocalMyList + +#endif // RENAMEHANDLER_H diff --git a/localmylist/requesthandler.cpp b/localmylist/requesthandler.cpp index 7546780..f8beecd 100644 --- a/localmylist/requesthandler.cpp +++ b/localmylist/requesthandler.cpp @@ -49,6 +49,7 @@ void RequestHandler::handleRequests() | FileFlag::FileType | FileFlag::Crc32 | FileFlag::State + | FileFlag::Source | FileFlag::Quality | FileFlag::VideoResolution | FileFlag::VideoCodec @@ -298,7 +299,7 @@ void RequestHandler::fileRequestComplete(bool success) //next.releaseDate next.version = reply->version(); next.censored = reply->isCensored(); - next.type = reply->type(); + next.type = reply->source(); // TODO This is called sourcein AniDB // next.qualityId - can map quality to qualityId next.quality = reply->quality(); next.resolution = reply->videoResolution();