Browse Source

Merge pull request #2389 from buinsky/WebUI3

Web API: Implement sync/maindata request
adaptive-webui-19844
sledgehammer999 10 years ago
parent
commit
f2c6981711
  1. 66
      src/webui/abstractrequesthandler.h
  2. 337
      src/webui/btjson.cpp
  3. 2
      src/webui/btjson.h
  4. 10
      src/webui/requesthandler.cpp
  5. 1
      src/webui/requesthandler.h
  6. 50
      src/webui/webapplication.h
  7. 2
      src/webui/www/public/downloadlimit.html
  8. 171
      src/webui/www/public/scripts/client.js
  9. 18
      src/webui/www/public/scripts/mocha-init.js
  10. 2
      src/webui/www/public/uploadlimit.html

66
src/webui/abstractrequesthandler.h

@ -37,53 +37,53 @@ struct WebSession;
class AbstractRequestHandler class AbstractRequestHandler
{ {
friend class WebApplication; friend class WebApplication;
public: public:
AbstractRequestHandler( AbstractRequestHandler(
const HttpRequest& request, const HttpEnvironment& env, const HttpRequest& request, const HttpEnvironment& env,
WebApplication* app); WebApplication* app);
HttpResponse run(); HttpResponse run();
protected: protected:
virtual void processRequest() = 0; virtual void processRequest() = 0;
void status(uint code, const QString& text); void status(uint code, const QString& text);
void header(const QString& name, const QString& value); void header(const QString& name, const QString& value);
void print(const QString& text, const QString& type = CONTENT_TYPE_HTML); void print(const QString& text, const QString& type = CONTENT_TYPE_HTML);
void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML); void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML);
void printFile(const QString& path); void printFile(const QString& path);
// Session management // Session management
bool sessionActive() const { return session_ != 0; } bool sessionActive() const { return session_ != 0; }
void sessionInitialize(); void sessionInitialize();
void sessionStart(); void sessionStart();
void sessionEnd(); void sessionEnd();
// Ban management // Ban management
bool isBanned() const; bool isBanned() const;
int failedAttempts() const; int failedAttempts() const;
void resetFailedAttempts(); void resetFailedAttempts();
void increaseFailedAttempts(); void increaseFailedAttempts();
bool isAuthNeeded(); bool isAuthNeeded();
// save data to temporary file on disk and return its name (or empty string if fails) // save data to temporary file on disk and return its name (or empty string if fails)
static QString saveTmpFile(const QByteArray& data); static QString saveTmpFile(const QByteArray& data);
inline WebSession* session() { return session_; } inline WebSession* session() { return session_; }
inline HttpRequest request() const { return request_; } inline HttpRequest request() const { return request_; }
inline HttpEnvironment env() const { return env_; } inline HttpEnvironment env() const { return env_; }
private: private:
WebApplication* app_; WebApplication* app_;
WebSession* session_; WebSession* session_;
const HttpRequest request_; const HttpRequest request_;
const HttpEnvironment env_; const HttpEnvironment env_;
HttpResponse response_; HttpResponse response_;
void print_impl(const QByteArray& data, const QString& type); void print_impl(const QByteArray& data, const QString& type);
}; };
#endif // ABSTRACTREQUESTHANDLER_H #endif // ABSTRACTREQUESTHANDLER_H

337
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_STATE[] = "state";
static const char KEY_TORRENT_SEQUENTIAL_DOWNLOAD[] = "seq_dl"; 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_FIRST_LAST_PIECE_PRIO[] = "f_l_piece_prio";
static const char KEY_TORRENT_LABEL[] = "label";
// Tracker keys // Tracker keys
static const char KEY_TRACKER_URL[] = "url"; static const char KEY_TRACKER_URL[] = "url";
@ -143,6 +144,17 @@ static const char KEY_TRANSFER_UPRATELIMIT[] = "up_rate_limit";
static const char KEY_TRANSFER_DHT_NODES[] = "dht_nodes"; static const char KEY_TRANSFER_DHT_NODES[] = "dht_nodes";
static const char KEY_TRANSFER_CONNECTION_STATUS[] = "connection_status"; 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";
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);
QVariantMap generateSyncData(int acceptedResponseId, QVariantMap data, QVariantMap &lastAcceptedData, QVariantMap &lastData);
class QTorrentCompare class QTorrentCompare
{ {
public: public:
@ -195,36 +207,6 @@ private:
#endif #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<qlonglong>(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. * Returns all the torrents in JSON format.
* *
@ -257,7 +239,7 @@ QByteArray btjson::getTorrents(QString filter, QString label,
std::vector<torrent_handle>::const_iterator end = torrents.end(); std::vector<torrent_handle>::const_iterator end = torrents.end();
QTorrentFilter torrentFilter(filter, label); QTorrentFilter torrentFilter(filter, label);
for(; it != end; ++it) { for (; it != end; ++it) {
QTorrentHandle torrent = QTorrentHandle(*it); QTorrentHandle torrent = QTorrentHandle(*it);
if (torrentFilter.apply(torrent)) if (torrentFilter.apply(torrent))
@ -281,6 +263,76 @@ QByteArray btjson::getTorrents(QString filter, QString label,
return json::toJson(torrent_list); 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<torrent_handle> torrentsList = QBtSession::instance()->getTorrents();
std::vector<torrent_handle>::const_iterator it = torrentsList.begin();
std::vector<torrent_handle>::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. * Returns the trackers for a torrent in JSON format.
* *
@ -322,7 +374,7 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash)
tracker_list.append(tracker_dict); 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()); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
return QByteArray(); return QByteArray();
} }
@ -384,7 +436,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash)
const qreal ratio = QBtSession::instance()->getRealRatio(status); const qreal ratio = QBtSession::instance()->getRealRatio(status);
data[KEY_PROP_RATIO] = ratio > QBtSession::MAX_RATIO ? -1 : ratio; 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()); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
return QByteArray(); return QByteArray();
} }
@ -454,21 +506,214 @@ QByteArray btjson::getFilesForTorrent(const QString& hash)
*/ */
QByteArray btjson::getTransferInfo() QByteArray btjson::getTransferInfo()
{ {
QVariantMap info; return json::toJson(getTranserInfoMap());
}
QVariantMap getTranserInfoMap()
{
QVariantMap map;
session_status sessionStatus = QBtSession::instance()->getSessionStatus(); session_status sessionStatus = QBtSession::instance()->getSessionStatus();
session_settings sessionSettings = QBtSession::instance()->getSession()->settings(); session_settings sessionSettings = QBtSession::instance()->getSession()->settings();
info[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate; map[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate;
info[KEY_TRANSFER_DLDATA] = static_cast<qlonglong>(sessionStatus.total_payload_download); map[KEY_TRANSFER_DLDATA] = static_cast<qlonglong>(sessionStatus.total_payload_download);
info[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate; map[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate;
info[KEY_TRANSFER_UPDATA] = static_cast<qlonglong>(sessionStatus.total_payload_upload); map[KEY_TRANSFER_UPDATA] = static_cast<qlonglong>(sessionStatus.total_payload_upload);
if (sessionSettings.download_rate_limit) map[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit;
info[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit; map[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit;
if (sessionSettings.upload_rate_limit) map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes;
info[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit;
info[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes;
if (!QBtSession::instance()->getSession()->is_listening()) if (!QBtSession::instance()->getSession()->is_listening())
info[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected"; map[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected";
else else
info[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled"; map[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled";
return json::toJson(info); 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<qlonglong>(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).
// 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;
} }

2
src/webui/btjson.h

@ -33,6 +33,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QString> #include <QString>
#include <QVariant>
class QTorrentHandle; class QTorrentHandle;
@ -46,6 +47,7 @@ private:
public: public:
static QByteArray getTorrents(QString filter = "all", QString label = QString(), static QByteArray getTorrents(QString filter = "all", QString label = QString(),
QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0); 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 getTrackersForTorrent(const QString& hash);
static QByteArray getPropertiesForTorrent(const QString& hash); static QByteArray getPropertiesForTorrent(const QString& hash);
static QByteArray getFilesForTorrent(const QString& hash); static QByteArray getFilesForTorrent(const QString& hash);

10
src/webui/requesthandler.cpp

@ -48,6 +48,7 @@
#include "prefjson.h" #include "prefjson.h"
#include "qbtsession.h" #include "qbtsession.h"
#include "requesthandler.h" #include "requesthandler.h"
#include "webapplication.h"
using namespace libtorrent; using namespace libtorrent;
@ -82,6 +83,7 @@ QMap<QString, QMap<QString, RequestHandler::Action> > RequestHandler::initialize
ADD_ACTION(json, propertiesGeneral); ADD_ACTION(json, propertiesGeneral);
ADD_ACTION(json, propertiesTrackers); ADD_ACTION(json, propertiesTrackers);
ADD_ACTION(json, propertiesFiles); ADD_ACTION(json, propertiesFiles);
ADD_ACTION(sync, maindata);
ADD_ACTION(command, shutdown); ADD_ACTION(command, shutdown);
ADD_ACTION(command, download); ADD_ACTION(command, download);
ADD_ACTION(command, upload); ADD_ACTION(command, upload);
@ -261,6 +263,14 @@ void RequestHandler::action_json_propertiesFiles()
print(btjson::getFilesForTorrent(args_.front()), CONTENT_TYPE_JS); 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() void RequestHandler::action_version_api()
{ {
CHECK_URI(0); CHECK_URI(0);

1
src/webui/requesthandler.h

@ -56,6 +56,7 @@ private:
void action_json_propertiesGeneral(); void action_json_propertiesGeneral();
void action_json_propertiesTrackers(); void action_json_propertiesTrackers();
void action_json_propertiesFiles(); void action_json_propertiesFiles();
void action_sync_maindata();
void action_command_shutdown(); void action_command_shutdown();
void action_command_download(); void action_command_download();
void action_command_upload(); void action_command_upload();

50
src/webui/webapplication.h

@ -33,12 +33,14 @@
#include <QMap> #include <QMap>
#include <QHash> #include <QHash>
#include "httptypes.h" #include "httptypes.h"
#include <QVariant>
struct WebSession struct WebSession
{ {
const QString id; const QString id;
QVariantMap syncMainDataLastResponse;
WebSession(const QString& id): id(id) {} QVariantMap syncMainDataLastAcceptedResponse;
WebSession(const QString& id): id(id) {}
}; };
const QString C_SID = "SID"; // name of session id cookie const QString C_SID = "SID"; // name of session id cookie
@ -49,39 +51,39 @@ class AbstractRequestHandler;
class WebApplication: public QObject class WebApplication: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(WebApplication) Q_DISABLE_COPY(WebApplication)
public: public:
WebApplication(QObject* parent = 0); WebApplication(QObject* parent = 0);
virtual ~WebApplication(); virtual ~WebApplication();
static WebApplication* instance(); static WebApplication* instance();
bool isBanned(const AbstractRequestHandler* _this) const; bool isBanned(const AbstractRequestHandler* _this) const;
int failedAttempts(const AbstractRequestHandler *_this) const; int failedAttempts(const AbstractRequestHandler *_this) const;
void resetFailedAttempts(AbstractRequestHandler* _this); void resetFailedAttempts(AbstractRequestHandler* _this);
void increaseFailedAttempts(AbstractRequestHandler* _this); void increaseFailedAttempts(AbstractRequestHandler* _this);
bool sessionStart(AbstractRequestHandler* _this); bool sessionStart(AbstractRequestHandler* _this);
bool sessionEnd(AbstractRequestHandler* _this); bool sessionEnd(AbstractRequestHandler* _this);
bool sessionInitialize(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: private slots:
void UnbanTimerEvent(); void UnbanTimerEvent();
private: private:
QMap<QString, WebSession*> sessions_; QMap<QString, WebSession*> sessions_;
QHash<QHostAddress, int> clientFailedAttempts_; QHash<QHostAddress, int> clientFailedAttempts_;
QMap<QString, QByteArray> translatedFiles_; QMap<QString, QByteArray> translatedFiles_;
QString generateSid(); QString generateSid();
static void translateDocument(QString& data); static void translateDocument(QString& data);
static const QStringMap CONTENT_TYPE_BY_EXT; static const QStringMap CONTENT_TYPE_BY_EXT;
static QStringMap initializeContentTypeByExtMap(); static QStringMap initializeContentTypeByExtMap();
}; };
#endif // WEBAPPLICATION_H #endif // WEBAPPLICATION_H

2
src/webui/www/public/downloadlimit.html

@ -31,7 +31,7 @@
'limit': limit 'limit': limit
}, },
onComplete: function() { onComplete: function() {
window.parent.updateTransferInfo(); window.parent.updateMainData();
window.parent.closeWindows(); window.parent.closeWindows();
} }
}).send(); }).send();

171
src/webui/www/public/scripts/client.js

@ -25,8 +25,7 @@
myTable = new dynamicTable(); myTable = new dynamicTable();
var updatePropertiesPanel = function(){}; var updatePropertiesPanel = function(){};
var updateTransferInfo = function(){}; var updateMainData = function(){};
var updateTransferList = function(){};
var alternativeSpeedLimits = false; var alternativeSpeedLimits = false;
selected_filter = getLocalStorageItem('selected_filter', 'all'); selected_filter = getLocalStorageItem('selected_filter', 'all');
@ -49,7 +48,6 @@ var saveSelectedLabel = function () {
} }
} }
window.addEvent('load', function () { window.addEvent('load', function () {
var saveColumnSizes = function () { var saveColumnSizes = function () {
@ -104,7 +102,7 @@ window.addEvent('load', function () {
localStorage.setItem('selected_filter', f); localStorage.setItem('selected_filter', f);
// Reload torrents // Reload torrents
if (typeof myTable.table != 'undefined') if (typeof myTable.table != 'undefined')
updateTransferList(); updateMainData();
} }
new MochaUI.Panel({ new MochaUI.Panel({
@ -131,114 +129,99 @@ window.addEvent('load', function () {
if (!speedInTitle) if (!speedInTitle)
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
var loadTorrentsInfoTimer; var syncMainDataLastResponseId = 0;
var loadTorrentsInfo = function () { var serverState = {};
var url = new URI('json/torrents');
var syncMainDataTimer;
var syncMainData = function () {
var url = new URI('sync/maindata');
url.setData('rid', syncMainDataLastResponseId);
var request = new Request.JSON({ var request = new Request.JSON({
url : url, url : url,
noCache : true, noCache : true,
method : 'get', method : 'get',
onFailure : function () { onFailure : function () {
$('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR'); $('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR');
clearTimeout(loadTorrentsInfoTimer); clearTimeout(syncMainDataTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(2000); syncMainDataTimer = syncMainData.delay(2000);
}, },
onSuccess : function (response) { onSuccess : function (response) {
$('error_div').set('html', ''); $('error_div').set('html', '');
if (response) { if (response) {
var queueing_enabled = false; var full_update = (response['full_update'] == true);
var torrents_hashes = new Array(); if (full_update)
for (var i = 0; i < response.length; i++) { myTable.rows.erase();
torrents_hashes.push(response[i].hash); if (response['rid'])
if (response[i].priority > -1) syncMainDataLastResponseId = response['rid'];
queueing_enabled = true; if ('queueing' in response) {
myTable.updateRowData(response[i]) var queueing_enabled = response['queueing'];
} myTable.columns['priority'].force_hide = !queueing_enabled;
myTable.updateColumn('priority');
var keys = myTable.rows.getKeys(); if (queueing_enabled) {
for (var i = 0; i < keys.length; i++) { $('queueingButtons').removeClass('invisible');
if (!torrents_hashes.contains(keys[i])) $('queueingMenuItems').removeClass('invisible');
myTable.rows.erase(keys[i]); }
} else {
$('queueingButtons').addClass('invisible');
myTable.columns['priority'].force_hide = !queueing_enabled; $('queueingMenuItems').addClass('invisible');
myTable.updateColumn('priority'); }
if (queueing_enabled) {
$('queueingButtons').removeClass('invisible');
$('queueingMenuItems').removeClass('invisible');
}
else {
$('queueingButtons').addClass('invisible');
$('queueingMenuItems').addClass('invisible');
} }
if (response['torrents'])
myTable.updateTable(true); 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(); myTable.altRow();
if (response['server_state']) {
var tmp = response['server_state'];
for(var key in tmp)
serverState[key] = tmp[key];
processServerState();
}
} }
clearTimeout(loadTorrentsInfoTimer); clearTimeout(syncMainDataTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(1500); syncMainDataTimer = syncMainData.delay(1500);
} }
}).send(); }).send();
}; };
updateTransferList = function() { updateMainData = function() {
myTable.updateTable(); myTable.updateTable();
clearTimeout(loadTorrentsInfoTimer); clearTimeout(syncMainDataTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(30); syncMainDataTimer = syncMainData.delay(100);
} }
var loadTransferInfoTimer; var processServerState = function () {
var loadTransferInfo = function () { var transfer_info = "";
var url = 'json/transferInfo'; if (serverState.dl_rate_limit > 0)
var request = new Request.JSON({ transfer_info += "[" + friendlyUnit(serverState.dl_rate_limit, true) + "] ";
url : url, transfer_info += friendlyUnit(serverState.dl_info_speed, true);
noCache : true, transfer_info += " (" + friendlyUnit(serverState.dl_info_data, false) + ")"
method : 'get', $("DlInfos").set('html', transfer_info);
onFailure : function () { transfer_info = "";
$('error_div').set('html', 'QBT_TR(qBittorrent client is not reachable)QBT_TR'); if (serverState.up_rate_limit > 0)
clearTimeout(loadTransferInfoTimer); transfer_info += "[" + friendlyUnit(serverState.up_rate_limit, true) + "] ";
loadTransferInfoTimer = loadTransferInfo.delay(4000); transfer_info += friendlyUnit(serverState.up_info_speed, true)
}, transfer_info += " (" + friendlyUnit(serverState.up_info_data, false) + ")"
onSuccess : function (info) { $("UpInfos").set('html', transfer_info);
if (info) { if (speedInTitle)
var transfer_info = ""; 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));
if (info.dl_rate_limit != undefined) else
transfer_info += "[" + friendlyUnit(info.dl_rate_limit, true) + "] "; document.title = "QBT_TR(qBittorrent web User Interface)QBT_TR";
transfer_info += friendlyUnit(info.dl_info_speed, true); $('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR'.replace("%1", serverState.dht_nodes));
transfer_info += " (" + friendlyUnit(info.dl_info_data, false) + ")" if (serverState.connection_status == "connected")
$("DlInfos").set('html', transfer_info); $('connectionStatus').src = 'images/skin/connected.png';
transfer_info = ""; else if (serverState.connection_status == "firewalled")
if (info.up_rate_limit != undefined) $('connectionStatus').src = 'images/skin/firewalled.png';
transfer_info += "[" + friendlyUnit(info.up_rate_limit, true) + "] "; else
transfer_info += friendlyUnit(info.up_info_speed, true) $('connectionStatus').src = 'images/skin/disconnected.png';
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();
}; };
updateTransferInfo = function() {
clearTimeout(loadTransferInfoTimer);
loadTransferInfo();
}
// Start fetching data now
loadTransferInfo();
var updateAltSpeedIcon = function(enabled) { var updateAltSpeedIcon = function(enabled) {
if (enabled) if (enabled)
$('alternativeSpeedLimits').src = "images/slow.png"; $('alternativeSpeedLimits').src = "images/slow.png";
@ -264,7 +247,7 @@ window.addEvent('load', function () {
method: 'post', method: 'post',
onComplete: function() { onComplete: function() {
alternativeSpeedLimits = !alternativeSpeedLimits; alternativeSpeedLimits = !alternativeSpeedLimits;
updateTransferInfo(); updateMainData();
}, },
onFailure: function() { onFailure: function() {
// Restore icon in case of failure // Restore icon in case of failure
@ -278,7 +261,7 @@ window.addEvent('load', function () {
setSortedColumn = function (column) { setSortedColumn = function (column) {
myTable.setSortedColumn(column); myTable.setSortedColumn(column);
updateTransferList(); updateMainData();
}; };
$('speedInBrowserTitleBarLink').addEvent('click', function(e) { $('speedInBrowserTitleBarLink').addEvent('click', function(e) {
@ -288,7 +271,7 @@ window.addEvent('load', function () {
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '1'; $('speedInBrowserTitleBarLink').firstChild.style.opacity = '1';
else else
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '0'; $('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
updateTransferInfo(); updateMainData();
}); });
new MochaUI.Panel({ new MochaUI.Panel({
@ -304,7 +287,7 @@ window.addEvent('load', function () {
loadMethod : 'xhr', loadMethod : 'xhr',
contentURL : 'transferlist.html', contentURL : 'transferlist.html',
onContentLoaded : function () { onContentLoaded : function () {
updateTransferList(); updateMainData();
}, },
column : 'mainColumn', column : 'mainColumn',
onResize : saveColumnSizes, onResize : saveColumnSizes,

18
src/webui/www/public/scripts/mocha-init.js

@ -55,7 +55,7 @@ initializeWindows = function() {
width: 500, width: 500,
height: 300 height: 300
}); });
updateTransferList(); updateMainData();
}); });
addClickEvent('preferences', function(e) { addClickEvent('preferences', function(e) {
@ -95,7 +95,7 @@ initializeWindows = function() {
width: 600, width: 600,
height: 130 height: 130
}); });
updateTransferList(); updateMainData();
}); });
globalUploadLimitFN = function() { globalUploadLimitFN = function() {
@ -144,7 +144,7 @@ initializeWindows = function() {
hashes: h.join("|") hashes: h.join("|")
} }
}).send(); }).send();
updateTransferList(); updateMainData();
} }
}; };
@ -158,7 +158,7 @@ initializeWindows = function() {
hashes: h.join("|") hashes: h.join("|")
} }
}).send(); }).send();
updateTransferList(); updateMainData();
} }
}; };
@ -218,7 +218,7 @@ initializeWindows = function() {
width: 424, width: 424,
height: 140 height: 140
}); });
updateTransferList(); updateMainData();
} }
}; };
@ -239,7 +239,7 @@ initializeWindows = function() {
} }
}).send(); }).send();
}); });
updateTransferList(); updateMainData();
} }
}; };
@ -255,7 +255,7 @@ initializeWindows = function() {
} }
}).send(); }).send();
}); });
updateTransferList(); updateMainData();
} }
}; };
@ -288,7 +288,7 @@ initializeWindows = function() {
} }
}).send(); }).send();
}); });
updateTransferList(); updateMainData();
} }
}); });
@ -317,7 +317,7 @@ initializeWindows = function() {
hashes: h.join("|") hashes: h.join("|")
} }
}).send(); }).send();
updateTransferList(); updateMainData();
} }
} }

2
src/webui/www/public/uploadlimit.html

@ -31,7 +31,7 @@
'limit': limit 'limit': limit
}, },
onComplete: function() { onComplete: function() {
window.parent.updateTransferInfo(); window.parent.updateMainData();
window.parent.closeWindows(); window.parent.closeWindows();
} }
}).send(); }).send();

Loading…
Cancel
Save