From bf0f9dd275a1593682d54e4550476929b76bd851 Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 6 Jan 2015 22:25:14 +0300 Subject: [PATCH 1/4] Implement functions for generating partial responses of requests. --- src/webui/btjson.cpp | 168 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 5059594aa..4106e47c5 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -143,6 +143,15 @@ static const char KEY_TRANSFER_UPRATELIMIT[] = "up_rate_limit"; static const char KEY_TRANSFER_DHT_NODES[] = "dht_nodes"; static const char KEY_TRANSFER_CONNECTION_STATUS[] = "connection_status"; +static const char KEY_FULL_UPDATE[] = "full_update"; +static const char KEY_RESPONSE_ID[] = "rid"; +static const char KEY_SUFFIX_REMOVED[] = "_removed"; + +void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData); +void processHash(QVariantHash prevData, QVariantHash data, QVariantHash &syncData, QVariantList &removedItems); +void processList(QVariantList prevData, QVariantList data, QVariantList &syncData, QVariantList &removedItems); +QVariantMap generateSyncData(int acceptedResponseId, QVariantMap data, QVariantMap &lastAcceptedData, QVariantMap &lastData); + class QTorrentCompare { public: @@ -472,3 +481,162 @@ QByteArray btjson::getTransferInfo() info[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled"; return json::toJson(info); } + +// Compare two structures (prevData, data) and calculate difference (syncData). +// Structures encoded as map. +void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData) +{ + // initialize output variable + syncData.clear(); + + QVariantList removedItems; + foreach (QString key, data.keys()) { + removedItems.clear(); + + switch (data[key].type()) { + case QVariant::Map: { + QVariantMap map; + processMap(prevData[key].toMap(), data[key].toMap(), map); + if (!map.isEmpty()) + syncData[key] = map; + } + break; + case QVariant::Hash: { + QVariantHash hash; + processHash(prevData[key].toHash(), data[key].toHash(), hash, removedItems); + if (!hash.isEmpty()) + syncData[key] = hash; + if (!removedItems.isEmpty()) + syncData[key + KEY_SUFFIX_REMOVED] = removedItems; + } + break; + case QVariant::List: { + QVariantList list; + processList(prevData[key].toList(), data[key].toList(), list, removedItems); + if (!list.isEmpty()) + syncData[key] = list; + if (!removedItems.isEmpty()) + syncData[key + KEY_SUFFIX_REMOVED] = removedItems; + } + break; + case QVariant::String: + case QVariant::LongLong: + case QMetaType::Float: + case QVariant::Int: + case QVariant::Bool: + case QVariant::Double: + case QVariant::ULongLong: + if (prevData[key] != data[key]) + syncData[key] = data[key]; + break; + default: + Q_ASSERT(0); + } + } +} + +// Compare two lists of structures (prevData, data) and calculate difference (syncData, removedItems). +// Structures encoded as map. +// Lists are encoded as hash table (indexed by structure key value) to improve ease of searching for removed items. +void processHash(QVariantHash prevData, QVariantHash data, QVariantHash &syncData, QVariantList &removedItems) +{ + // initialize output variables + syncData.clear(); + removedItems.clear(); + + if (prevData.isEmpty()) { + // If list was empty before, then difference is a whole new list. + syncData = data; + } + else { + foreach (QString key, data.keys()) { + switch (data[key].type()) { + case QVariant::Map: + if (!prevData.contains(key)) { + // new list item found - append it to syncData + syncData[key] = data[key]; + } + else { + QVariantMap map; + processMap(prevData[key].toMap(), data[key].toMap(), map); + // existing list item found - remove it from prevData + prevData.remove(key); + if (!map.isEmpty()) + // changed list item found - append its changes to syncData + syncData[key] = map; + } + break; + default: + Q_ASSERT(0); + } + } + + if (!prevData.isEmpty()) { + // prevData contains only items that are missing now - + // put them in removedItems + foreach (QString s, prevData.keys()) + removedItems << s; + } + } +} + +// Compare two lists of simple value (prevData, data) and calculate difference (syncData, removedItems). +void processList(QVariantList prevData, QVariantList data, QVariantList &syncData, QVariantList &removedItems) +{ + // initialize output variables + syncData.clear(); + removedItems.clear(); + + if (prevData.isEmpty()) { + // If list was empty before, then difference is a whole new list. + syncData = data; + } + else { + foreach (QVariant item, data) { + if (!prevData.contains(item)) + // new list item found - append it to syncData + syncData.append(item); + else + // unchanged list item found - remove it from prevData + prevData.removeOne(item); + } + + if (!prevData.isEmpty()) + // prevData contains only items that are missing now - + // put them in removedItems + removedItems = prevData; + } +} + +QVariantMap generateSyncData(int acceptedResponseId, QVariantMap data, QVariantMap &lastAcceptedData, QVariantMap &lastData) +{ + QVariantMap syncData; + bool fullUpdate = true; + int lastResponseId = 0; + if (acceptedResponseId > 0) { + lastResponseId = lastData[KEY_RESPONSE_ID].toInt(); + + if (lastResponseId == acceptedResponseId) + lastAcceptedData = lastData; + + int lastAcceptedResponseId = lastAcceptedData[KEY_RESPONSE_ID].toInt(); + + if (lastAcceptedResponseId == acceptedResponseId) { + processMap(lastAcceptedData, data, syncData); + fullUpdate = false; + } + } + + if (fullUpdate) { + lastAcceptedData.clear(); + syncData = data; + syncData[KEY_FULL_UPDATE] = true; + } + + lastResponseId = lastResponseId % 1000000 + 1; // cycle between 1 and 1000000 + lastData = data; + lastData[KEY_RESPONSE_ID] = lastResponseId; + syncData[KEY_RESPONSE_ID] = lastResponseId; + + return syncData; +} \ No newline at end of file From 0d43ee7076e26ab51338608e6c2abb7bf84a2e28 Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 6 Jan 2015 22:42:44 +0300 Subject: [PATCH 2/4] Follow project coding style. Issue #2192. --- src/webui/abstractrequesthandler.h | 66 +++++++++++++++--------------- src/webui/webapplication.h | 46 ++++++++++----------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/webui/abstractrequesthandler.h b/src/webui/abstractrequesthandler.h index 9f2c3b605..b279d1272 100644 --- a/src/webui/abstractrequesthandler.h +++ b/src/webui/abstractrequesthandler.h @@ -37,53 +37,53 @@ struct WebSession; class AbstractRequestHandler { - friend class WebApplication; + friend class WebApplication; public: - AbstractRequestHandler( - const HttpRequest& request, const HttpEnvironment& env, - WebApplication* app); + AbstractRequestHandler( + const HttpRequest& request, const HttpEnvironment& env, + WebApplication* app); - HttpResponse run(); + HttpResponse run(); protected: - virtual void processRequest() = 0; + virtual void processRequest() = 0; - void status(uint code, const QString& text); - void header(const QString& name, const QString& value); - void print(const QString& text, const QString& type = CONTENT_TYPE_HTML); - void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML); - void printFile(const QString& path); + void status(uint code, const QString& text); + void header(const QString& name, const QString& value); + void print(const QString& text, const QString& type = CONTENT_TYPE_HTML); + void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML); + void printFile(const QString& path); - // Session management - bool sessionActive() const { return session_ != 0; } - void sessionInitialize(); - void sessionStart(); - void sessionEnd(); + // Session management + bool sessionActive() const { return session_ != 0; } + void sessionInitialize(); + void sessionStart(); + void sessionEnd(); - // Ban management - bool isBanned() const; - int failedAttempts() const; - void resetFailedAttempts(); - void increaseFailedAttempts(); + // Ban management + bool isBanned() const; + int failedAttempts() const; + void resetFailedAttempts(); + void increaseFailedAttempts(); - bool isAuthNeeded(); + bool isAuthNeeded(); - // save data to temporary file on disk and return its name (or empty string if fails) - static QString saveTmpFile(const QByteArray& data); + // save data to temporary file on disk and return its name (or empty string if fails) + static QString saveTmpFile(const QByteArray& data); - inline WebSession* session() { return session_; } - inline HttpRequest request() const { return request_; } - inline HttpEnvironment env() const { return env_; } + inline WebSession* session() { return session_; } + inline HttpRequest request() const { return request_; } + inline HttpEnvironment env() const { return env_; } private: - WebApplication* app_; - WebSession* session_; - const HttpRequest request_; - const HttpEnvironment env_; - HttpResponse response_; + WebApplication* app_; + WebSession* session_; + const HttpRequest request_; + const HttpEnvironment env_; + HttpResponse response_; - void print_impl(const QByteArray& data, const QString& type); + void print_impl(const QByteArray& data, const QString& type); }; #endif // ABSTRACTREQUESTHANDLER_H diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 0e005f0b3..be03f6b17 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -36,9 +36,9 @@ struct WebSession { - const QString id; + const QString id; - WebSession(const QString& id): id(id) {} + WebSession(const QString& id): id(id) {} }; const QString C_SID = "SID"; // name of session id cookie @@ -49,39 +49,39 @@ class AbstractRequestHandler; class WebApplication: public QObject { - Q_OBJECT - Q_DISABLE_COPY(WebApplication) + Q_OBJECT + Q_DISABLE_COPY(WebApplication) public: - WebApplication(QObject* parent = 0); - virtual ~WebApplication(); + WebApplication(QObject* parent = 0); + virtual ~WebApplication(); - static WebApplication* instance(); + static WebApplication* instance(); - bool isBanned(const AbstractRequestHandler* _this) const; - int failedAttempts(const AbstractRequestHandler *_this) const; - void resetFailedAttempts(AbstractRequestHandler* _this); - void increaseFailedAttempts(AbstractRequestHandler* _this); + bool isBanned(const AbstractRequestHandler* _this) const; + int failedAttempts(const AbstractRequestHandler *_this) const; + void resetFailedAttempts(AbstractRequestHandler* _this); + void increaseFailedAttempts(AbstractRequestHandler* _this); - bool sessionStart(AbstractRequestHandler* _this); - bool sessionEnd(AbstractRequestHandler* _this); - bool sessionInitialize(AbstractRequestHandler* _this); + bool sessionStart(AbstractRequestHandler* _this); + bool sessionEnd(AbstractRequestHandler* _this); + bool sessionInitialize(AbstractRequestHandler* _this); - bool readFile(const QString &path, QByteArray& data, QString& type); + bool readFile(const QString &path, QByteArray& data, QString& type); private slots: - void UnbanTimerEvent(); + void UnbanTimerEvent(); private: - QMap sessions_; - QHash clientFailedAttempts_; - QMap translatedFiles_; + QMap sessions_; + QHash clientFailedAttempts_; + QMap translatedFiles_; - QString generateSid(); - static void translateDocument(QString& data); + QString generateSid(); + static void translateDocument(QString& data); - static const QStringMap CONTENT_TYPE_BY_EXT; - static QStringMap initializeContentTypeByExtMap(); + static const QStringMap CONTENT_TYPE_BY_EXT; + static QStringMap initializeContentTypeByExtMap(); }; #endif // WEBAPPLICATION_H From 383c0e14189bda74826f58673391d53cab4bcb2b Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 6 Jan 2015 23:04:23 +0300 Subject: [PATCH 3/4] Implement sync/maindata request --- src/webui/btjson.cpp | 171 +++++++++++++++++++++++++---------- src/webui/btjson.h | 2 + src/webui/requesthandler.cpp | 10 ++ src/webui/requesthandler.h | 1 + src/webui/webapplication.h | 4 +- 5 files changed, 140 insertions(+), 48 deletions(-) diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 4106e47c5..2e21eca3f 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -101,6 +101,7 @@ static const char KEY_TORRENT_ETA[] = "eta"; static const char KEY_TORRENT_STATE[] = "state"; static const char KEY_TORRENT_SEQUENTIAL_DOWNLOAD[] = "seq_dl"; static const char KEY_TORRENT_FIRST_LAST_PIECE_PRIO[] = "f_l_piece_prio"; +static const char KEY_TORRENT_LABEL[] = "label"; // Tracker keys static const char KEY_TRACKER_URL[] = "url"; @@ -147,6 +148,8 @@ static const char KEY_FULL_UPDATE[] = "full_update"; static const char KEY_RESPONSE_ID[] = "rid"; static const char KEY_SUFFIX_REMOVED[] = "_removed"; +QVariantMap getTranserInfoMap(); +QVariantMap toMap(const QTorrentHandle& h); void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData); void processHash(QVariantHash prevData, QVariantHash data, QVariantHash &syncData, QVariantList &removedItems); void processList(QVariantList prevData, QVariantList data, QVariantList &syncData, QVariantList &removedItems); @@ -204,36 +207,6 @@ private: #endif }; -static QVariantMap toMap(const QTorrentHandle& h) -{ - libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); - - QVariantMap ret; - ret[KEY_TORRENT_HASH] = h.hash(); - ret[KEY_TORRENT_NAME] = h.name(); - ret[KEY_TORRENT_SIZE] = static_cast(status.total_wanted); - ret[KEY_TORRENT_PROGRESS] = h.progress(status); - ret[KEY_TORRENT_DLSPEED] = status.download_payload_rate; - ret[KEY_TORRENT_UPSPEED] = status.upload_payload_rate; - if (QBtSession::instance()->isQueueingEnabled() && h.queue_position(status) >= 0) - ret[KEY_TORRENT_PRIORITY] = h.queue_position(status); - else - ret[KEY_TORRENT_PRIORITY] = -1; - ret[KEY_TORRENT_SEEDS] = status.num_seeds; - ret[KEY_TORRENT_NUM_COMPLETE] = status.num_complete; - ret[KEY_TORRENT_LEECHS] = status.num_peers - status.num_seeds; - ret[KEY_TORRENT_NUM_INCOMPLETE] = status.num_incomplete; - const qreal ratio = QBtSession::instance()->getRealRatio(status); - ret[KEY_TORRENT_RATIO] = (ratio > QBtSession::MAX_RATIO) ? -1 : ratio; - ret[KEY_TORRENT_STATE] = h.torrentState().toString(); - ret[KEY_TORRENT_ETA] = h.eta(); - ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = status.sequential_download; - if (h.has_metadata()) - ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = h.first_last_piece_first(); - - return ret; -} - /** * Returns all the torrents in JSON format. * @@ -266,7 +239,7 @@ QByteArray btjson::getTorrents(QString filter, QString label, std::vector::const_iterator end = torrents.end(); QTorrentFilter torrentFilter(filter, label); - for(; it != end; ++it) { + for (; it != end; ++it) { QTorrentHandle torrent = QTorrentHandle(*it); if (torrentFilter.apply(torrent)) @@ -290,6 +263,76 @@ QByteArray btjson::getTorrents(QString filter, QString label, return json::toJson(torrent_list); } +/** + * The function returns the changed data from the server to synchronize with the web client. + * Return value is map in JSON format. + * Map contain the key: + * - "Rid": ID response + * Map can contain the keys: + * - "full_update": full data update flag + * - "torrents": dictionary contains information about torrents. + * - "torrents_removed": a list of hashes of removed torrents + * - "labels": list of labels + * - "labels_removed": list of removed labels + * - "queueing": priority system usage flag + * - "server_state": map contains information about the status of the server + * The keys of the 'torrents' dictionary are hashes of torrents. + * Each value of the 'torrents' dictionary contains map. The map can contain following keys: + * - "name": Torrent name + * - "size": Torrent size + * - "progress: Torrent progress + * - "dlspeed": Torrent download speed + * - "upspeed": Torrent upload speed + * - "priority": Torrent priority (-1 if queuing is disabled) + * - "num_seeds": Torrent seeds connected to + * - "num_complete": Torrent seeds in the swarm + * - "num_leechs": Torrent leechers connected to + * - "num_incomplete": Torrent leechers in the swarm + * - "ratio": Torrent share ratio + * - "eta": Torrent ETA + * - "state": Torrent state + * - "seq_dl": Torrent sequential download state + * - "f_l_piece_prio": Torrent first last piece priority state + * Server state map may contain the following keys: + * - "connection_status": conection status + * - "dht_nodes": DHT nodes count + * - "dl_info_data": bytes downloaded + * - "dl_info_speed": download speed + * - "dl_rate_limit: downlaod rate limit + * - "up_info_data: bytes uploaded + * - "up_info_speed: upload speed + * - "up_rate_limit: upload speed limit + */ +QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData) +{ + QVariantMap data; + + QVariantHash torrents; + + std::vector torrentsList = QBtSession::instance()->getTorrents(); + std::vector::const_iterator it = torrentsList.begin(); + std::vector::const_iterator end = torrentsList.end(); + + for (; it != end; ++it) { + QTorrentHandle torrent = QTorrentHandle(*it); + QVariantMap map = toMap(torrent); + map.remove(KEY_TORRENT_HASH); + torrents[torrent.hash()] = map; + } + + data["torrents"] = torrents; + data["queueing"] = QBtSession::instance()->isQueueingEnabled(); + + QVariantList labels; + foreach (QString s, Preferences::instance()->getTorrentLabels()) + labels << s; + + data["labels"] = labels; + data["server_state"] = getTranserInfoMap(); + + return json::toJson(generateSyncData(acceptedResponseId, data, lastAcceptedData, lastData)); +} + /** * Returns the trackers for a torrent in JSON format. * @@ -331,7 +374,7 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash) tracker_list.append(tracker_dict); } } - catch(const std::exception& e) { + catch (const std::exception& e) { qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what()); return QByteArray(); } @@ -393,7 +436,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash) const qreal ratio = QBtSession::instance()->getRealRatio(status); data[KEY_PROP_RATIO] = ratio > QBtSession::MAX_RATIO ? -1 : ratio; } - catch(const std::exception& e) { + catch (const std::exception& e) { qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what()); return QByteArray(); } @@ -463,23 +506,57 @@ QByteArray btjson::getFilesForTorrent(const QString& hash) */ QByteArray btjson::getTransferInfo() { - QVariantMap info; + return json::toJson(getTranserInfoMap()); +} + +QVariantMap getTranserInfoMap() +{ + QVariantMap map; session_status sessionStatus = QBtSession::instance()->getSessionStatus(); session_settings sessionSettings = QBtSession::instance()->getSession()->settings(); - info[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate; - info[KEY_TRANSFER_DLDATA] = static_cast(sessionStatus.total_payload_download); - info[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate; - info[KEY_TRANSFER_UPDATA] = static_cast(sessionStatus.total_payload_upload); - if (sessionSettings.download_rate_limit) - info[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit; - if (sessionSettings.upload_rate_limit) - info[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit; - info[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes; + map[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate; + map[KEY_TRANSFER_DLDATA] = static_cast(sessionStatus.total_payload_download); + map[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate; + map[KEY_TRANSFER_UPDATA] = static_cast(sessionStatus.total_payload_upload); + map[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit; + map[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit; + map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes; if (!QBtSession::instance()->getSession()->is_listening()) - info[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected"; + map[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected"; else - info[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled"; - return json::toJson(info); + map[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled"; + return map; +} + +QVariantMap toMap(const QTorrentHandle& h) +{ + libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); + + QVariantMap ret; + ret[KEY_TORRENT_HASH] = h.hash(); + ret[KEY_TORRENT_NAME] = h.name(); + ret[KEY_TORRENT_SIZE] = static_cast(status.total_wanted); + ret[KEY_TORRENT_PROGRESS] = h.progress(status); + ret[KEY_TORRENT_DLSPEED] = status.download_payload_rate; + ret[KEY_TORRENT_UPSPEED] = status.upload_payload_rate; + if (QBtSession::instance()->isQueueingEnabled() && h.queue_position(status) >= 0) + ret[KEY_TORRENT_PRIORITY] = h.queue_position(status); + else + ret[KEY_TORRENT_PRIORITY] = -1; + ret[KEY_TORRENT_SEEDS] = status.num_seeds; + ret[KEY_TORRENT_NUM_COMPLETE] = status.num_complete; + ret[KEY_TORRENT_LEECHS] = status.num_peers - status.num_seeds; + ret[KEY_TORRENT_NUM_INCOMPLETE] = status.num_incomplete; + const qreal ratio = QBtSession::instance()->getRealRatio(status); + ret[KEY_TORRENT_RATIO] = (ratio > QBtSession::MAX_RATIO) ? -1 : ratio; + ret[KEY_TORRENT_STATE] = h.torrentState().toString(); + ret[KEY_TORRENT_ETA] = h.eta(); + ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = status.sequential_download; + if (h.has_metadata()) + ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = h.first_last_piece_first(); + ret[KEY_TORRENT_LABEL] = TorrentPersistentData::getLabel(h.hash()); + + return ret; } // Compare two structures (prevData, data) and calculate difference (syncData). @@ -639,4 +716,4 @@ QVariantMap generateSyncData(int acceptedResponseId, QVariantMap data, QVariantM syncData[KEY_RESPONSE_ID] = lastResponseId; return syncData; -} \ No newline at end of file +} diff --git a/src/webui/btjson.h b/src/webui/btjson.h index e8bf66896..2d5f7e8eb 100644 --- a/src/webui/btjson.h +++ b/src/webui/btjson.h @@ -33,6 +33,7 @@ #include #include +#include class QTorrentHandle; @@ -46,6 +47,7 @@ private: public: static QByteArray getTorrents(QString filter = "all", QString label = QString(), QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0); + static QByteArray getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData); static QByteArray getTrackersForTorrent(const QString& hash); static QByteArray getPropertiesForTorrent(const QString& hash); static QByteArray getFilesForTorrent(const QString& hash); diff --git a/src/webui/requesthandler.cpp b/src/webui/requesthandler.cpp index bbb9cc436..ba893bf9b 100644 --- a/src/webui/requesthandler.cpp +++ b/src/webui/requesthandler.cpp @@ -48,6 +48,7 @@ #include "prefjson.h" #include "qbtsession.h" #include "requesthandler.h" +#include "webapplication.h" using namespace libtorrent; @@ -82,6 +83,7 @@ QMap > RequestHandler::initialize ADD_ACTION(json, propertiesGeneral); ADD_ACTION(json, propertiesTrackers); ADD_ACTION(json, propertiesFiles); + ADD_ACTION(sync, maindata); ADD_ACTION(command, shutdown); ADD_ACTION(command, download); ADD_ACTION(command, upload); @@ -261,6 +263,14 @@ void RequestHandler::action_json_propertiesFiles() print(btjson::getFilesForTorrent(args_.front()), CONTENT_TYPE_JS); } +// GET param: +// - rid (int): last response id +void RequestHandler::action_sync_maindata() +{ + CHECK_URI(0); + print(btjson::getSyncMainData(request().gets["rid"].toInt(), session()->syncMainDataLastResponse, session()->syncMainDataLastAcceptedResponse)); +} + void RequestHandler::action_version_api() { CHECK_URI(0); diff --git a/src/webui/requesthandler.h b/src/webui/requesthandler.h index 5597dc4bf..afa3564ff 100644 --- a/src/webui/requesthandler.h +++ b/src/webui/requesthandler.h @@ -56,6 +56,7 @@ private: void action_json_propertiesGeneral(); void action_json_propertiesTrackers(); void action_json_propertiesFiles(); + void action_sync_maindata(); void action_command_shutdown(); void action_command_download(); void action_command_upload(); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index be03f6b17..f35f67009 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -33,11 +33,13 @@ #include #include #include "httptypes.h" +#include struct WebSession { const QString id; - + QVariantMap syncMainDataLastResponse; + QVariantMap syncMainDataLastAcceptedResponse; WebSession(const QString& id): id(id) {} }; From 574c57ee182c092cd1500059e3c47bae85475841 Mon Sep 17 00:00:00 2001 From: buinsky Date: Tue, 6 Jan 2015 23:24:54 +0300 Subject: [PATCH 4/4] Use sync/maindata request in WebUI --- src/webui/www/public/downloadlimit.html | 2 +- src/webui/www/public/scripts/client.js | 171 ++++++++++----------- src/webui/www/public/scripts/mocha-init.js | 18 +-- src/webui/www/public/uploadlimit.html | 2 +- 4 files changed, 88 insertions(+), 105 deletions(-) diff --git a/src/webui/www/public/downloadlimit.html b/src/webui/www/public/downloadlimit.html index e375de8db..ca1e521ab 100644 --- a/src/webui/www/public/downloadlimit.html +++ b/src/webui/www/public/downloadlimit.html @@ -31,7 +31,7 @@ 'limit': limit }, onComplete: function() { - window.parent.updateTransferInfo(); + window.parent.updateMainData(); window.parent.closeWindows(); } }).send(); diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index 94e132fe0..9f3d07cc7 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -25,8 +25,7 @@ myTable = new dynamicTable(); var updatePropertiesPanel = function(){}; -var updateTransferInfo = function(){}; -var updateTransferList = function(){}; +var updateMainData = function(){}; var alternativeSpeedLimits = false; selected_filter = getLocalStorageItem('selected_filter', 'all'); @@ -49,7 +48,6 @@ var saveSelectedLabel = function () { } } - window.addEvent('load', function () { var saveColumnSizes = function () { @@ -104,7 +102,7 @@ window.addEvent('load', function () { localStorage.setItem('selected_filter', f); // Reload torrents if (typeof myTable.table != 'undefined') - updateTransferList(); + updateMainData(); } new MochaUI.Panel({ @@ -131,114 +129,99 @@ window.addEvent('load', function () { if (!speedInTitle) $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; - var loadTorrentsInfoTimer; - var loadTorrentsInfo = function () { - var url = new URI('json/torrents'); + var syncMainDataLastResponseId = 0; + var serverState = {}; + + var syncMainDataTimer; + var syncMainData = function () { + var url = new URI('sync/maindata'); + url.setData('rid', syncMainDataLastResponseId); var request = new Request.JSON({ url : url, noCache : true, method : 'get', onFailure : function () { $('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR'); - clearTimeout(loadTorrentsInfoTimer); - loadTorrentsInfoTimer = loadTorrentsInfo.delay(2000); + clearTimeout(syncMainDataTimer); + syncMainDataTimer = syncMainData.delay(2000); }, onSuccess : function (response) { $('error_div').set('html', ''); if (response) { - var queueing_enabled = false; - var torrents_hashes = new Array(); - for (var i = 0; i < response.length; i++) { - torrents_hashes.push(response[i].hash); - if (response[i].priority > -1) - queueing_enabled = true; - myTable.updateRowData(response[i]) - } - - var keys = myTable.rows.getKeys(); - for (var i = 0; i < keys.length; i++) { - if (!torrents_hashes.contains(keys[i])) - myTable.rows.erase(keys[i]); - } - - myTable.columns['priority'].force_hide = !queueing_enabled; - myTable.updateColumn('priority'); - if (queueing_enabled) { - $('queueingButtons').removeClass('invisible'); - $('queueingMenuItems').removeClass('invisible'); - } - else { - $('queueingButtons').addClass('invisible'); - $('queueingMenuItems').addClass('invisible'); + var full_update = (response['full_update'] == true); + if (full_update) + myTable.rows.erase(); + if (response['rid']) + syncMainDataLastResponseId = response['rid']; + if ('queueing' in response) { + var queueing_enabled = response['queueing']; + myTable.columns['priority'].force_hide = !queueing_enabled; + myTable.updateColumn('priority'); + if (queueing_enabled) { + $('queueingButtons').removeClass('invisible'); + $('queueingMenuItems').removeClass('invisible'); + } + else { + $('queueingButtons').addClass('invisible'); + $('queueingMenuItems').addClass('invisible'); + } } - - myTable.updateTable(true); + if (response['torrents']) + for (var key in response['torrents']) { + response['torrents'][key]['hash'] = key; + myTable.updateRowData(response['torrents'][key]); + } + myTable.updateTable(full_update); + if (response['torrents_removed']) + response['torrents_removed'].each(function (hash) { + myTable.removeRow(hash); + }); myTable.altRow(); + if (response['server_state']) { + var tmp = response['server_state']; + for(var key in tmp) + serverState[key] = tmp[key]; + processServerState(); + } } - clearTimeout(loadTorrentsInfoTimer); - loadTorrentsInfoTimer = loadTorrentsInfo.delay(1500); + clearTimeout(syncMainDataTimer); + syncMainDataTimer = syncMainData.delay(1500); } }).send(); }; - updateTransferList = function() { + updateMainData = function() { myTable.updateTable(); - clearTimeout(loadTorrentsInfoTimer); - loadTorrentsInfoTimer = loadTorrentsInfo.delay(30); + clearTimeout(syncMainDataTimer); + syncMainDataTimer = syncMainData.delay(100); } - var loadTransferInfoTimer; - var loadTransferInfo = function () { - var url = 'json/transferInfo'; - var request = new Request.JSON({ - url : url, - noCache : true, - method : 'get', - onFailure : function () { - $('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR'); - clearTimeout(loadTransferInfoTimer); - loadTransferInfoTimer = loadTransferInfo.delay(4000); - }, - onSuccess : function (info) { - if (info) { - var transfer_info = ""; - if (info.dl_rate_limit != undefined) - transfer_info += "[" + friendlyUnit(info.dl_rate_limit, true) + "] "; - transfer_info += friendlyUnit(info.dl_info_speed, true); - transfer_info += " (" + friendlyUnit(info.dl_info_data, false) + ")" - $("DlInfos").set('html', transfer_info); - transfer_info = ""; - if (info.up_rate_limit != undefined) - transfer_info += "[" + friendlyUnit(info.up_rate_limit, true) + "] "; - transfer_info += friendlyUnit(info.up_info_speed, true) - transfer_info += " (" + friendlyUnit(info.up_info_data, false) + ")" - $("UpInfos").set('html', transfer_info); - if (speedInTitle) - document.title = "QBT_TR(D:%1 U:%2)QBT_TR".replace("%1", friendlyUnit(info.dl_info_speed, true)).replace("%2", friendlyUnit(info.up_info_speed, true)); - else - document.title = "QBT_TR(qBittorrent web User Interface)QBT_TR"; - $('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR'.replace("%1", info.dht_nodes)); - if (info.connection_status == "connected") - $('connectionStatus').src = 'images/skin/connected.png'; - else if (info.connection_status == "firewalled") - $('connectionStatus').src = 'images/skin/firewalled.png'; - else - $('connectionStatus').src = 'images/skin/disconnected.png'; - clearTimeout(loadTransferInfoTimer); - loadTransferInfoTimer = loadTransferInfo.delay(3000); - } - } - }).send(); + var processServerState = function () { + var transfer_info = ""; + if (serverState.dl_rate_limit > 0) + transfer_info += "[" + friendlyUnit(serverState.dl_rate_limit, true) + "] "; + transfer_info += friendlyUnit(serverState.dl_info_speed, true); + transfer_info += " (" + friendlyUnit(serverState.dl_info_data, false) + ")" + $("DlInfos").set('html', transfer_info); + transfer_info = ""; + if (serverState.up_rate_limit > 0) + transfer_info += "[" + friendlyUnit(serverState.up_rate_limit, true) + "] "; + transfer_info += friendlyUnit(serverState.up_info_speed, true) + transfer_info += " (" + friendlyUnit(serverState.up_info_data, false) + ")" + $("UpInfos").set('html', transfer_info); + if (speedInTitle) + document.title = "QBT_TR(D:%1 U:%2)QBT_TR".replace("%1", friendlyUnit(serverState.dl_info_speed, true)).replace("%2", friendlyUnit(serverState.up_info_speed, true)); + else + document.title = "QBT_TR(qBittorrent web User Interface)QBT_TR"; + $('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR'.replace("%1", serverState.dht_nodes)); + if (serverState.connection_status == "connected") + $('connectionStatus').src = 'images/skin/connected.png'; + else if (serverState.connection_status == "firewalled") + $('connectionStatus').src = 'images/skin/firewalled.png'; + else + $('connectionStatus').src = 'images/skin/disconnected.png'; }; - updateTransferInfo = function() { - clearTimeout(loadTransferInfoTimer); - loadTransferInfo(); - } - - // Start fetching data now - loadTransferInfo(); - var updateAltSpeedIcon = function(enabled) { if (enabled) $('alternativeSpeedLimits').src = "images/slow.png"; @@ -264,7 +247,7 @@ window.addEvent('load', function () { method: 'post', onComplete: function() { alternativeSpeedLimits = !alternativeSpeedLimits; - updateTransferInfo(); + updateMainData(); }, onFailure: function() { // Restore icon in case of failure @@ -278,7 +261,7 @@ window.addEvent('load', function () { setSortedColumn = function (column) { myTable.setSortedColumn(column); - updateTransferList(); + updateMainData(); }; $('speedInBrowserTitleBarLink').addEvent('click', function(e) { @@ -288,7 +271,7 @@ window.addEvent('load', function () { $('speedInBrowserTitleBarLink').firstChild.style.opacity = '1'; else $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; - updateTransferInfo(); + updateMainData(); }); new MochaUI.Panel({ @@ -304,7 +287,7 @@ window.addEvent('load', function () { loadMethod : 'xhr', contentURL : 'transferlist.html', onContentLoaded : function () { - updateTransferList(); + updateMainData(); }, column : 'mainColumn', onResize : saveColumnSizes, diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index 47f2deefb..491b12a37 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -55,7 +55,7 @@ initializeWindows = function() { width: 500, height: 300 }); - updateTransferList(); + updateMainData(); }); addClickEvent('preferences', function(e) { @@ -95,7 +95,7 @@ initializeWindows = function() { width: 600, height: 130 }); - updateTransferList(); + updateMainData(); }); globalUploadLimitFN = function() { @@ -144,7 +144,7 @@ initializeWindows = function() { hashes: h.join("|") } }).send(); - updateTransferList(); + updateMainData(); } }; @@ -158,7 +158,7 @@ initializeWindows = function() { hashes: h.join("|") } }).send(); - updateTransferList(); + updateMainData(); } }; @@ -218,7 +218,7 @@ initializeWindows = function() { width: 424, height: 140 }); - updateTransferList(); + updateMainData(); } }; @@ -239,7 +239,7 @@ initializeWindows = function() { } }).send(); }); - updateTransferList(); + updateMainData(); } }; @@ -255,7 +255,7 @@ initializeWindows = function() { } }).send(); }); - updateTransferList(); + updateMainData(); } }; @@ -288,7 +288,7 @@ initializeWindows = function() { } }).send(); }); - updateTransferList(); + updateMainData(); } }); @@ -317,7 +317,7 @@ initializeWindows = function() { hashes: h.join("|") } }).send(); - updateTransferList(); + updateMainData(); } } diff --git a/src/webui/www/public/uploadlimit.html b/src/webui/www/public/uploadlimit.html index c7a51ebc8..4b63183b3 100644 --- a/src/webui/www/public/uploadlimit.html +++ b/src/webui/www/public/uploadlimit.html @@ -31,7 +31,7 @@ 'limit': limit }, onComplete: function() { - window.parent.updateTransferInfo(); + window.parent.updateMainData(); window.parent.closeWindows(); } }).send();