diff --git a/src/misc.cpp b/src/misc.cpp index 9fef8f92f..9cef9bb6d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -236,15 +236,20 @@ int misc::pythonVersion() { // use Binary prefix standards from IEC 60027-2 // see http://en.wikipedia.org/wiki/Kilobyte // value must be given in bytes -QString misc::friendlyUnit(qreal val) { +QString misc::friendlyUnit(qreal val, bool is_speed) { if (val < 0) return tr("Unknown", "Unknown (size)"); int i = 0; while(val >= 1024. && i++<6) val /= 1024.; + QString ret; if (i == 0) - return QString::number((long)val) + " " + tr(units[0].source, units[0].comment); - return QString::number(val, 'f', 1) + " " + tr(units[i].source, units[i].comment); + ret = QString::number((long)val) + " " + tr(units[0].source, units[0].comment); + else + ret = QString::number(val, 'f', 1) + " " + tr(units[i].source, units[i].comment); + if (is_speed) + ret += tr("/s", "per second"); + return ret; } bool misc::isPreviewable(QString extension) { diff --git a/src/misc.h b/src/misc.h index 7f4284050..098b1bebb 100644 --- a/src/misc.h +++ b/src/misc.h @@ -97,7 +97,7 @@ public: // use Binary prefix standards from IEC 60027-2 // see http://en.wikipedia.org/wiki/Kilobyte // value must be given in bytes - static QString friendlyUnit(qreal val); + static QString friendlyUnit(qreal val, bool is_speed = false); static bool isPreviewable(QString extension); static QString magnetUriToName(QString magnet_uri); static QString magnetUriToHash(QString magnet_uri); diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 4a91d146e..3d14f2c68 100644 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -682,7 +682,7 @@ void QBtSession::initWebUi() { httpServer->close(); } } else { - httpServer = new HttpServer(3000, this); + httpServer = new HttpServer(this); } #ifndef QT_NO_OPENSSL diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp new file mode 100644 index 000000000..8e328fa0e --- /dev/null +++ b/src/webui/btjson.cpp @@ -0,0 +1,333 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#include "btjson.h" +#include "jsondict.h" +#include "jsonlist.h" +#include "misc.h" +#include "qbtsession.h" +#include "torrentpersistentdata.h" + +#include + +using namespace libtorrent; + +#define CACHED_VARIABLE(VARTYPE, VAR, DUR) \ + static VARTYPE VAR; \ + static QElapsedTimer cacheTimer; \ + static bool initialized = false; \ + if (initialized && !cacheTimer.hasExpired(DUR)) \ + return VAR.toString(); \ + initialized = true; \ + cacheTimer.start(); \ + VAR.clear() + +// Numerical constants +static const int CACHE_DURATION_MS = 1500; // 1500ms + +// Torrent keys +static const char KEY_TORRENT_HASH[] = "hash"; +static const char KEY_TORRENT_NAME[] = "name"; +static const char KEY_TORRENT_SIZE[] = "size"; +static const char KEY_TORRENT_PROGRESS[] = "progress"; +static const char KEY_TORRENT_DLSPEED[] = "dlspeed"; +static const char KEY_TORRENT_UPSPEED[] = "upspeed"; +static const char KEY_TORRENT_PRIORITY[] = "priority"; +static const char KEY_TORRENT_SEEDS[] = "num_seeds"; +static const char KEY_TORRENT_LEECHS[] = "num_leechs"; +static const char KEY_TORRENT_RATIO[] = "ratio"; +static const char KEY_TORRENT_ETA[] = "eta"; +static const char KEY_TORRENT_STATE[] = "state"; + +// Tracker keys +static const char KEY_TRACKER_URL[] = "url"; +static const char KEY_TRACKER_STATUS[] = "status"; +static const char KEY_TRACKER_MSG[] = "msg"; +static const char KEY_TRACKER_PEERS[] = "num_peers"; + +// Torrent keys (Properties) +static const char KEY_PROP_SAVE_PATH[] = "save_path"; +static const char KEY_PROP_CREATION_DATE[] = "creation_date"; +static const char KEY_PROP_PIECE_SIZE[] = "piece_size"; +static const char KEY_PROP_COMMENT[] = "comment"; +static const char KEY_PROP_WASTED[] = "total_wasted"; +static const char KEY_PROP_DOWNLOADED[] = "total_uploaded"; +static const char KEY_PROP_UPLOADED[] = "total_downloaded"; +static const char KEY_PROP_UP_LIMIT[] = "up_limit"; +static const char KEY_PROP_DL_LIMIT[] = "dl_limit"; +static const char KEY_PROP_TIME_ELAPSED[] = "time_elapsed"; +static const char KEY_PROP_CONNECT_COUNT[] = "nb_connections"; +static const char KEY_PROP_RATIO[] = "share_ratio"; + +// File keys +static const char KEY_FILE_NAME[] = "name"; +static const char KEY_FILE_SIZE[] = "size"; +static const char KEY_FILE_PROGRESS[] = "progress"; +static const char KEY_FILE_PRIORITY[] = "priority"; +static const char KEY_FILE_IS_SEED[] = "is_seed"; + +static JsonDict toJson(const QTorrentHandle& h) +{ + JsonDict ret; + ret.add(KEY_TORRENT_HASH, h.hash()); + ret.add(KEY_TORRENT_NAME, h.name()); + ret.add(KEY_TORRENT_SIZE, misc::friendlyUnit(h.actual_size())); // FIXME: Should pass as Number, not formatted String (for sorting). + ret.add(KEY_TORRENT_PROGRESS, (double)h.progress()); + ret.add(KEY_TORRENT_DLSPEED, misc::friendlyUnit(h.download_payload_rate(), true)); // FIXME: Should be passed as a Number + ret.add(KEY_TORRENT_UPSPEED, misc::friendlyUnit(h.upload_payload_rate(), true)); // FIXME: Should be passed as a Number + if (QBtSession::instance()->isQueueingEnabled() && h.queue_position() >= 0) + ret.add(KEY_TORRENT_PRIORITY, QString::number(h.queue_position())); + else + ret.add(KEY_TORRENT_PRIORITY, "*"); + QString seeds = QString::number(h.num_seeds()); + if (h.num_complete() > 0) + seeds += " ("+QString::number(h.num_complete())+")"; + ret.add(KEY_TORRENT_SEEDS, seeds); + QString leechs = QString::number(h.num_peers() - h.num_seeds()); + if (h.num_incomplete() > 0) + leechs += " ("+QString::number(h.num_incomplete())+")"; + ret.add(KEY_TORRENT_LEECHS, leechs); + const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); + ret.add(KEY_TORRENT_RATIO, (ratio > 100.) ? QString::fromUtf8("∞") : QString::number(ratio, 'f', 1)); + QString eta; + QString state; + if (h.is_paused()) { + if (h.has_error()) + state = "error"; + else + state = h.is_seed() ? "pausedUP" : "pausedDL"; + } else { + if (QBtSession::instance()->isQueueingEnabled() && h.is_queued()) + state = h.is_seed() ? "queuedUP" : "queuedDL"; + else { + switch (h.state()) { + case torrent_status::finished: + case torrent_status::seeding: + state = h.upload_payload_rate() > 0 ? "uploading" : "stalledUP"; + break; + case torrent_status::allocating: + case torrent_status::checking_files: + case torrent_status::queued_for_checking: + case torrent_status::checking_resume_data: + state = h.is_seed() ? "checkingUP" : "checkingDL"; + break; + case torrent_status::downloading: + case torrent_status::downloading_metadata: + state = h.download_payload_rate() > 0 ? "downloading" : "stalledDL"; + eta = misc::userFriendlyDuration(QBtSession::instance()->getETA(h.hash())); + break; + default: + qWarning("Unrecognized torrent status, should not happen!!! status was %d", h.state()); + } + } + } + ret.add(KEY_TORRENT_ETA, eta.isEmpty() ? QString::fromUtf8("∞") : eta); + ret.add(KEY_TORRENT_STATE, state); + + return ret; +} + +/** + * Returns all the torrents in JSON format. + * + * The return value is a JSON-formatted list of dictionaries. + * The dictionary keys are: + * - "hash": Torrent hash + * - "name": Torrent name + * - "size": Torrent size + * - "progress: Torrent progress + * - "dlspeed": Torrent download speed + * - "upspeed": Torrent upload speed + * - "priority": Torrent priority ('*' if queuing is disabled) + * - "num_seeds": Torrent seed count + * - "num_leechs": Torrent leecher count + * - "ratio": Torrent share ratio + * - "eta": Torrent ETA + * - "state": Torrent state + */ +QString btjson::getTorrents() +{ + CACHED_VARIABLE(JsonList, torrent_list, CACHE_DURATION_MS); + std::vector torrents = QBtSession::instance()->getTorrents(); + std::vector::const_iterator it = torrents.begin(); + std::vector::const_iterator end = torrents.end(); + for( ; it != end; ++it) { + torrent_list.append(toJson(QTorrentHandle(*it))); + } + return torrent_list.toString(); +} + +/** + * Returns the trackers for a torrent in JSON format. + * + * The return value is a JSON-formatted list of dictionaries. + * The dictionary keys are: + * - "url": Tracker URL + * - "status": Tracker status + * - "num_peers": Tracker peer count + * - "msg": Tracker message (last) + */ +QString btjson::getTrackersForTorrent(const QString& hash) +{ + CACHED_VARIABLE(JsonList, tracker_list, CACHE_DURATION_MS); + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + QHash trackers_data = QBtSession::instance()->getTrackersInfo(hash); + std::vector vect_trackers = h.trackers(); + std::vector::const_iterator it = vect_trackers.begin(); + std::vector::const_iterator end = vect_trackers.end(); + for (; it != end; ++it) { + JsonDict tracker_dict; + const QString tracker_url = misc::toQString(it->url); + tracker_dict.add(KEY_TRACKER_URL, tracker_url); + const TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); + QString status; + if (it->verified) + status = tr("Working"); + else { + if (it->updating && it->fails == 0) + status = tr("Updating..."); + else + status = it->fails > 0 ? tr("Not working") : tr("Not contacted yet"); + } + tracker_dict.add(KEY_TRACKER_STATUS, status); + tracker_dict.add(KEY_TRACKER_PEERS, QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers)); + tracker_dict.add(KEY_TRACKER_MSG, data.last_message.trimmed()); + + tracker_list.append(tracker_dict); + } + } catch(const std::exception&) { + qWarning() << "getTrackersForTorrent() called with invalid torrent"; + return QString(); + } + + return tracker_list.toString(); +} + +/** + * Returns the properties for a torrent in JSON format. + * + * The return value is a JSON-formatted dictionary. + * The dictionary keys are: + * - "save_path": Torrent save path + * - "creation_date": Torrent creation date + * - "piece_size": Torrent piece size + * - "comment": Torrent comment + * - "total_wasted": Total data wasted for torrent + * - "total_uploaded": Total data uploaded for torrent + * - "total_downloaded": Total data uploaded for torrent + * - "up_limit": Torrent upload limit + * - "dl_limit": Torrent download limit + * - "time_elapsed": Torrent elapsed time + * - "nb_connections": Torrent connection count + * - "share_ratio": Torrent share ratio + */ +QString btjson::getPropertiesForTorrent(const QString& hash) +{ + CACHED_VARIABLE(JsonDict, data, CACHE_DURATION_MS); + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (!h.has_metadata()) + return QString(); + + // Save path + QString save_path = TorrentPersistentData::getSavePath(hash); + if (save_path.isEmpty()) + save_path = h.save_path(); + data.add(KEY_PROP_SAVE_PATH, save_path); + data.add(KEY_PROP_CREATION_DATE, h.creation_date()); + data.add(KEY_PROP_PIECE_SIZE, misc::friendlyUnit(h.piece_length())); + data.add(KEY_PROP_COMMENT, h.comment()); + data.add(KEY_PROP_WASTED, misc::friendlyUnit(h.total_failed_bytes() + h.total_redundant_bytes())); + data.add(KEY_PROP_UPLOADED, QString(misc::friendlyUnit(h.all_time_upload()) + " (" + misc::friendlyUnit(h.total_payload_upload()) + " " + tr("this session") + ")")); + data.add(KEY_PROP_DOWNLOADED, QString(misc::friendlyUnit(h.all_time_download()) + " (" + misc::friendlyUnit(h.total_payload_download()) + " " + tr("this session") + ")")); + if (h.upload_limit() <= 0) + data.add(KEY_PROP_UPLOADED, QString::fromUtf8("∞")); + else + data.add(KEY_PROP_UPLOADED, misc::friendlyUnit(h.upload_limit(), true)); + if (h.download_limit() <= 0) + data.add(KEY_PROP_DOWNLOADED, QString::fromUtf8("∞")); + else + data.add(KEY_PROP_DOWNLOADED, misc::friendlyUnit(h.download_limit(), true)); + QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); + if (h.is_seed()) + elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; + data.add(KEY_PROP_TIME_ELAPSED, elapsed_txt); + data.add(KEY_PROP_CONNECT_COUNT, QString(QString::number(h.num_connections()) + " (" + tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit())) + ")")); + const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); + data.add(KEY_PROP_RATIO, ratio > 100. ? QString::fromUtf8("∞") : QString::number(ratio, 'f', 1)); + } catch(const std::exception&) { + return QString(); + } + + return data.toString(); +} + +/** + * Returns the files in a torrent in JSON format. + * + * The return value is a JSON-formatted list of dictionaries. + * The dictionary keys are: + * - "name": File name + * - "size": File size + * - "progress": File progress + * - "priority": File priority + * - "is_seed": Flag indicating if torrent is seeding/complete + */ +QString btjson::getFilesForTorrent(const QString& hash) +{ + CACHED_VARIABLE(JsonList, file_list, CACHE_DURATION_MS); + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + if (!h.has_metadata()) + return QString(); + + const std::vector priorities = h.file_priorities(); + std::vector fp; + h.file_progress(fp); + for (int i = 0; i < h.num_files(); ++i) { + JsonDict file_dict; + file_dict.add(KEY_FILE_NAME, h.filename_at(i)); + const size_type size = h.filesize_at(i); + file_dict.add(KEY_FILE_SIZE, misc::friendlyUnit(size)); + file_dict.add(KEY_FILE_PROGRESS, (size > 0) ? (fp[i] / (double) size) : 1.); + file_dict.add(KEY_FILE_PRIORITY, priorities[i]); + if (i == 0) + file_dict.add(KEY_FILE_IS_SEED, h.is_seed()); + + file_list.append(file_dict); + } + } catch (const std::exception&) { + return QString(); + } + + return file_list.toString(); +} diff --git a/src/webui/btjson.h b/src/webui/btjson.h new file mode 100644 index 000000000..96b410826 --- /dev/null +++ b/src/webui/btjson.h @@ -0,0 +1,50 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#ifndef BTJSON_H +#define BTJSON_H + +#include +#include + +class btjson { + Q_DECLARE_TR_FUNCTIONS(misc) + +private: + btjson() {} + +public: + static QString getTorrents(); + static QString getTrackersForTorrent(const QString& hash); + static QString getPropertiesForTorrent(const QString& hash); + static QString getFilesForTorrent(const QString& hash); +}; // class btjson + +#endif // BTJSON_H diff --git a/src/webui/eventmanager.cpp b/src/webui/eventmanager.cpp index 99011d903..25abf0bde 100644 --- a/src/webui/eventmanager.cpp +++ b/src/webui/eventmanager.cpp @@ -51,68 +51,6 @@ EventManager::EventManager(QObject *parent) { } -QList EventManager::getEventList() const { - return event_list.values(); -} - -QList EventManager::getPropTrackersInfo(QString hash) const { - QList trackersInfo; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) { - QHash trackers_data = QBtSession::instance()->getTrackersInfo(hash); - std::vector vect_trackers = h.trackers(); - std::vector::iterator it; - for (it = vect_trackers.begin(); it != vect_trackers.end(); it++) { - QVariantMap tracker; - QString tracker_url = misc::toQString(it->url); - tracker["url"] = tracker_url; - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - QString error_message = data.last_message.trimmed(); - if (it->verified) { - tracker["status"] = tr("Working"); - } else { - if (it->updating && it->fails == 0) { - tracker["status"] = tr("Updating..."); - } else { - if (it->fails > 0) { - tracker["status"] = tr("Not working"); - } else { - tracker["status"] = tr("Not contacted yet"); - } - } - } - tracker["num_peers"] = QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers); - tracker["msg"] = error_message; - trackersInfo << tracker; - } - } - return trackersInfo; -} - -QList EventManager::getPropFilesInfo(QString hash) const { - QList files; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (!h.is_valid() || !h.has_metadata()) return files; - std::vector priorities = h.file_priorities(); - std::vector fp; - h.file_progress(fp); - for (int i=0; i 0) - file["progress"] = fp[i]/(double)size; - else - file["progress"] = 1.; // Empty file... - file["priority"] = priorities[i]; - if (i == 0) - file["is_seed"] = h.is_seed(); - files << file; - } - return files; -} - void EventManager::setGlobalPreferences(QVariantMap m) { // UI Preferences pref; @@ -408,140 +346,3 @@ QVariantMap EventManager::getGlobalPreferences() const { data["dyndns_domain"] = pref.getDynDomainName(); return data; } - -QVariantMap EventManager::getPropGeneralInfo(QString hash) const { - QVariantMap data; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - // Save path - QString p = TorrentPersistentData::getSavePath(hash); - if (p.isEmpty()) p = h.save_path(); - data["save_path"] = p; - // Creation date - data["creation_date"] = h.creation_date(); - // Piece size - data["piece_size"] = misc::friendlyUnit(h.piece_length()); - // Comment - data["comment"] = h.comment(); - data["total_wasted"] = QVariant(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes())); - data["total_uploaded"] = QVariant(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")"); - data["total_downloaded"] = QVariant(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")"); - if (h.upload_limit() <= 0) - data["up_limit"] = QString::fromUtf8("∞"); - else - data["up_limit"] = QVariant(misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)")); - if (h.download_limit() <= 0) - data["dl_limit"] = QString::fromUtf8("∞"); - else - data["dl_limit"] = QVariant(misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)")); - QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); - if (h.is_seed()) { - elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; - } - data["time_elapsed"] = elapsed_txt; - data["nb_connections"] = QVariant(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")"); - // Update ratio info - qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); - if (ratio > 100.) - data["share_ratio"] = QString::fromUtf8("∞"); - else - data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1)); - } - return data; -} - -void EventManager::addedTorrent(const QTorrentHandle& h) -{ - modifiedTorrent(h); -} - -void EventManager::deletedTorrent(QString hash) -{ - event_list.remove(hash); -} - -void EventManager::modifiedTorrent(const QTorrentHandle& h) -{ - QString hash = h.hash(); - QVariantMap event; - event["eta"] = QVariant(QString::fromUtf8("∞")); - if (h.is_paused()) { - if (h.has_error()) { - event["state"] = QVariant("error"); - } else { - if (h.is_seed()) - event["state"] = QVariant("pausedUP"); - else - event["state"] = QVariant("pausedDL"); - } - } else { - if (QBtSession::instance()->isQueueingEnabled() && h.is_queued()) { - if (h.is_seed()) - event["state"] = QVariant("queuedUP"); - else - event["state"] = QVariant("queuedDL"); - } else { - switch(h.state()) - { - case torrent_status::finished: - case torrent_status::seeding: - if (h.upload_payload_rate() > 0) { - event["state"] = QVariant("uploading"); - } else { - event["state"] = QVariant("stalledUP"); - } - break; - case torrent_status::allocating: - case torrent_status::checking_files: - case torrent_status::queued_for_checking: - case torrent_status::checking_resume_data: - if (h.is_seed()) { - event["state"] = QVariant("checkingUP"); - } else { - event["state"] = QVariant("checkingDL"); - } - break; - case torrent_status::downloading: - case torrent_status::downloading_metadata: - if (h.download_payload_rate() > 0) - event["state"] = QVariant("downloading"); - else - event["state"] = QVariant("stalledDL"); - event["eta"] = misc::userFriendlyDuration(QBtSession::instance()->getETA(hash)); - break; - default: - qDebug("No status, should not happen!!! status is %d", h.state()); - event["state"] = QVariant(); - } - } - } - event["name"] = QVariant(h.name()); - event["size"] = QVariant(misc::friendlyUnit(h.actual_size())); - event["progress"] = QVariant((double)h.progress()); - event["dlspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.download_payload_rate()))); - if (QBtSession::instance()->isQueueingEnabled()) { - if (h.queue_position() >= 0) - event["priority"] = QVariant(QString::number(h.queue_position())); - else - event["priority"] = "*"; - } else { - event["priority"] = "*"; - } - event["upspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.upload_payload_rate()))); - QString seeds = QString::number(h.num_seeds()); - if (h.num_complete() > 0) - seeds += " ("+QString::number(h.num_complete())+")"; - event["num_seeds"] = QVariant(seeds); - QString leechs = QString::number(h.num_peers()-h.num_seeds()); - if (h.num_incomplete() > 0) - leechs += " ("+QString::number(h.num_incomplete())+")"; - event["num_leechs"] = QVariant(leechs); - event["seed"] = QVariant(h.is_seed()); - qreal ratio = QBtSession::instance()->getRealRatio(hash); - if (ratio > 100.) - event["ratio"] = QString::fromUtf8("∞"); - else - event["ratio"] = QVariant(QString::number(ratio, 'f', 1)); - event["hash"] = QVariant(hash); - event_list[hash] = event; -} diff --git a/src/webui/eventmanager.h b/src/webui/eventmanager.h index 1124f31dc..f6bb7924b 100644 --- a/src/webui/eventmanager.h +++ b/src/webui/eventmanager.h @@ -41,28 +41,17 @@ class EventManager : public QObject Q_OBJECT Q_DISABLE_COPY(EventManager) -private: - QHash event_list; - protected: void update(QVariantMap event); public: EventManager(QObject *parent); - QList getEventList() const; - QVariantMap getPropGeneralInfo(QString hash) const; - QList getPropTrackersInfo(QString hash) const; QList getPropFilesInfo(QString hash) const; QVariantMap getGlobalPreferences() const; void setGlobalPreferences(QVariantMap m); signals: void localeChanged(const QString &locale); - -public slots: - void addedTorrent(const QTorrentHandle& h); - void deletedTorrent(QString hash); - void modifiedTorrent(const QTorrentHandle& h); }; #endif diff --git a/src/webui/httpconnection.cpp b/src/webui/httpconnection.cpp index 03a4f9c2d..508184d6e 100644 --- a/src/webui/httpconnection.cpp +++ b/src/webui/httpconnection.cpp @@ -34,6 +34,7 @@ #include "eventmanager.h" #include "preferences.h" #include "json.h" +#include "btjson.h" #include "qbtsession.h" #include "misc.h" #ifndef DISABLE_GUI @@ -242,8 +243,8 @@ void HttpConnection::respond() { if (list.size() >= 2) { if (list[0] == "json") { - if (list[1] == "events") { - respondJson(); + if (list[1] == "torrents") { + respondTorrentsJson(); return; } if (list.size() > 2) { @@ -337,39 +338,31 @@ void HttpConnection::respondNotFound() { write(); } -void HttpConnection::respondJson() { - EventManager* manager = m_httpserver->eventManager(); - QString string = json::toJson(manager->getEventList()); +void HttpConnection::respondTorrentsJson() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); - m_generator.setMessage(string); + m_generator.setMessage(btjson::getTorrents()); write(); } void HttpConnection::respondGenPropertiesJson(const QString& hash) { - EventManager* manager = m_httpserver->eventManager(); - QString string = json::toJson(manager->getPropGeneralInfo(hash)); m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); - m_generator.setMessage(string); + m_generator.setMessage(btjson::getPropertiesForTorrent(hash)); write(); } void HttpConnection::respondTrackersPropertiesJson(const QString& hash) { - EventManager* manager = m_httpserver->eventManager(); - QString string = json::toJson(manager->getPropTrackersInfo(hash)); m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); - m_generator.setMessage(string); + m_generator.setMessage(btjson::getTrackersForTorrent(hash)); write(); } void HttpConnection::respondFilesPropertiesJson(const QString& hash) { - EventManager* manager = m_httpserver->eventManager(); - QString string = json::toJson(manager->getPropFilesInfo(hash)); m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); - m_generator.setMessage(string); + m_generator.setMessage(btjson::getFilesForTorrent(hash)); write(); } diff --git a/src/webui/httpconnection.h b/src/webui/httpconnection.h index 62d6992cd..970768617 100644 --- a/src/webui/httpconnection.h +++ b/src/webui/httpconnection.h @@ -55,7 +55,7 @@ public: protected slots: void write(); void respond(); - void respondJson(); + void respondTorrentsJson(); void respondGenPropertiesJson(const QString& hash); void respondTrackersPropertiesJson(const QString& hash); void respondFilesPropertiesJson(const QString& hash); diff --git a/src/webui/httpserver.cpp b/src/webui/httpserver.cpp index 303decfc5..b9346916c 100644 --- a/src/webui/httpserver.cpp +++ b/src/webui/httpserver.cpp @@ -89,7 +89,7 @@ void HttpServer::resetNbFailedAttemptsForIp(const QString& ip) { m_clientFailedAttempts.remove(ip); } -HttpServer::HttpServer(int msec, QObject* parent) : QTcpServer(parent), +HttpServer::HttpServer(QObject* parent) : QTcpServer(parent), m_eventManager(new EventManager(this)) { const Preferences pref; @@ -109,23 +109,6 @@ HttpServer::HttpServer(int msec, QObject* parent) : QTcpServer(parent), } #endif - // Add torrents - std::vector torrents = QBtSession::instance()->getTorrents(); - std::vector::iterator torrentIT; - for (torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (h.is_valid()) - m_eventManager->addedTorrent(h); - } - - //connect QBtSession::instance() to manager - connect(QBtSession::instance(), SIGNAL(addedTorrent(QTorrentHandle)), m_eventManager, SLOT(addedTorrent(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(deletedTorrent(QString)), m_eventManager, SLOT(deletedTorrent(QString))); - - //set timer - connect(&m_timer, SIGNAL(timeout()), SLOT(onTimer())); - m_timer.start(msec); - // Additional translations for Web UI QString a = tr("File"); a = tr("Edit"); @@ -214,16 +197,6 @@ void HttpServer::handleNewConnection(QTcpSocket *socket) connect(connection, SIGNAL(resumeAllTorrents()), QBtSession::instance(), SLOT(resumeAllTorrents())); } -void HttpServer::onTimer() { - std::vector torrents = QBtSession::instance()->getTorrents(); - std::vector::iterator torrentIT; - for (torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (h.is_valid()) - m_eventManager->modifiedTorrent(h); - } -} - QString HttpServer::generateNonce() const { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(QTime::currentTime().toString("hhmmsszzz").toLocal8Bit()); diff --git a/src/webui/httpserver.h b/src/webui/httpserver.h index 96323949f..fdd51a6ee 100644 --- a/src/webui/httpserver.h +++ b/src/webui/httpserver.h @@ -58,7 +58,7 @@ class HttpServer : public QTcpServer { Q_DISABLE_COPY(HttpServer) public: - HttpServer(int msec, QObject* parent = 0); + HttpServer(QObject* parent = 0); ~HttpServer(); void setAuthorization(const QString& username, const QString& password_sha1); bool isAuthorized(const QByteArray& auth, const QString& method) const; @@ -80,7 +80,6 @@ private: void incomingConnection(int socketDescriptor); private slots: - void onTimer(); void UnbanTimerEvent(); void onLocaleChanged(const QString &locale); @@ -90,8 +89,7 @@ private: private: QByteArray m_username; QByteArray m_passwordSha1; - EventManager *m_eventManager; - QTimer m_timer; + EventManager *m_eventManager; // TODO: Remove QHash m_clientFailedAttempts; bool m_localAuthEnabled; bool m_needsTranslation; diff --git a/src/webui/json.cpp b/src/webui/json.cpp new file mode 100644 index 000000000..90b1d2b29 --- /dev/null +++ b/src/webui/json.cpp @@ -0,0 +1,181 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2006-2012 Ishan Arora and Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#include "json.h" + +#include +#include + +QString json::toJson(const QVariant& v) { + if (v.isNull()) + return "null"; + switch(v.type()) + { + case QVariant::Bool: + case QVariant::Double: + case QVariant::Int: + case QVariant::LongLong: + case QVariant::UInt: + case QVariant::ULongLong: + return v.value(); + case QVariant::StringList: + case QVariant::List: { + QStringList strList; + foreach (const QVariant &var, v.toList()) { + strList << toJson(var); + } + return "["+strList.join(",")+"]"; + } + case QVariant::String: { + QString s = v.value(); + QString result = "\""; + for (int i=0; i& v) { + QStringList res; + foreach (QVariantMap m, v) { + QStringList vlist; + QVariantMap::ConstIterator it; + for (it = m.constBegin(); it != m.constEnd(); it++) { + vlist << toJson(it.key())+":"+toJson(it.value()); + } + res << "{"+vlist.join(",")+"}"; + } + return "["+res.join(",")+"]"; +} diff --git a/src/webui/json.h b/src/webui/json.h index 153a57077..d9a74c217 100644 --- a/src/webui/json.h +++ b/src/webui/json.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2006 Ishan Arora and Christophe Dumez + * Copyright (C) 2006-2012 Ishan Arora and Christophe Dumez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -36,150 +36,11 @@ namespace json { - QString toJson(const QVariant& v) { - if (v.isNull()) - return "null"; - switch(v.type()) - { - case QVariant::Bool: - case QVariant::Double: - case QVariant::Int: - case QVariant::LongLong: - case QVariant::UInt: - case QVariant::ULongLong: - return v.value(); - case QVariant::StringList: - case QVariant::List: { - QStringList strList; - foreach (const QVariant &var, v.toList()) { - strList << toJson(var); - } - return "["+strList.join(",")+"]"; - } - case QVariant::String: { - QString s = v.value(); - QString result = "\""; - for (int i=0; i& v); // TODO: Remove + QVariantMap fromJson(const QString& json); - QString toJson(const QVariantMap& m) { - QStringList vlist; - QVariantMap::ConstIterator it; - for (it = m.constBegin(); it != m.constEnd(); it++) { - vlist << toJson(it.key())+":"+toJson(it.value()); - } - return "{"+vlist.join(",")+"}"; - } - - QVariantMap fromJson(const QString& json) { - qDebug("JSON is %s", qPrintable(json)); - QVariantMap m; - if (json.startsWith("{") && json.endsWith("}")) { - QStringList couples; - QString tmp = ""; - bool in_list = false; - foreach (const QChar &c, json.mid(1, json.length()-2)) { - if (c == ',' && !in_list) { - couples << tmp; - tmp = ""; - } else { - if (c == '[') - in_list = true; - else if (c == ']') - in_list = false; - tmp += c; - } - } - if (!tmp.isEmpty()) couples << tmp; - - foreach (const QString &couple, couples) { - QStringList parts = couple.split(":"); - if (parts.size() != 2) continue; - QString key = parts.first(); - if (key.startsWith("\"") && key.endsWith("\"")) { - key = key.mid(1, key.length()-2); - } - QString value_str = parts.last(); - QVariant value; - if (value_str.startsWith("[") && value_str.endsWith("]")) { - value_str = value_str.mid(1, value_str.length()-2); - QStringList list_elems = value_str.split(",", QString::SkipEmptyParts); - QVariantList varlist; - foreach (const QString &list_val, list_elems) { - if (list_val.startsWith("\"") && list_val.endsWith("\"")) { - varlist << list_val.mid(1, list_val.length()-2).replace("\\n", "\n"); - } else { - varlist << list_val.toInt(); - } - } - value = varlist; - } else { - if (value_str.startsWith("\"") && value_str.endsWith("\"")) { - value_str = value_str.mid(1, value_str.length()-2).replace("\\n", "\n"); - value = value_str; - } else { - if (value_str.compare("false", Qt::CaseInsensitive) == 0) - value = false; - else if (value_str.compare("true", Qt::CaseInsensitive) == 0) - value = true; - else - value = value_str.toInt(); - } - } - m.insert(key, value); - qDebug("%s:%s", key.toLocal8Bit().data(), value_str.toLocal8Bit().data()); - } - } - return m; - } - - QString toJson(const QList& v) { - QStringList res; - foreach (QVariantMap m, v) { - QStringList vlist; - QVariantMap::ConstIterator it; - for (it = m.constBegin(); it != m.constEnd(); it++) { - vlist << toJson(it.key())+":"+toJson(it.value()); - } - res << "{"+vlist.join(",")+"}"; - } - return "["+res.join(",")+"]"; - } -} +} // namespace json #endif diff --git a/src/webui/jsondict.cpp b/src/webui/jsondict.cpp new file mode 100644 index 000000000..d57f4f912 --- /dev/null +++ b/src/webui/jsondict.cpp @@ -0,0 +1,58 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#include "jsondict.h" +#include "json.h" + +JsonDict::JsonDict(): m_dirty(false) +{ +} + +void JsonDict::clear() +{ + m_items.clear(); + m_dirty = true; +} + +void JsonDict::add(const QString& key, const QVariant& value) +{ + m_items.append("\"" + key + "\":" + json::toJson(value)); + m_dirty = true; +} + +const QString& JsonDict::toString() const +{ + static QString str; + if (m_dirty) { + str = "{" + m_items.join(",") + "}"; + m_dirty = false; + } + return str; +} diff --git a/src/webui/jsondict.h b/src/webui/jsondict.h new file mode 100644 index 000000000..ba41af150 --- /dev/null +++ b/src/webui/jsondict.h @@ -0,0 +1,49 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#ifndef JSONDICT_H +#define JSONDICT_H + +#include + +class JsonDict +{ +public: + JsonDict(); + void clear(); + void add(const QString& key, const QVariant& value); + const QString& toString() const; + +private: + mutable bool m_dirty; + QStringList m_items; +}; + +#endif // JSONDICT_H diff --git a/src/webui/jsonlist.cpp b/src/webui/jsonlist.cpp new file mode 100644 index 000000000..4e6d2681f --- /dev/null +++ b/src/webui/jsonlist.cpp @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#include "jsonlist.h" +#include "json.h" + +JsonList::JsonList() : m_dirty(false) +{ +} + +const QString& JsonList::toString() const +{ + static QString str; + if (m_dirty) { + str = "[" + m_items.join(",") + "]"; + m_dirty = false; + } + return str; +} + +void JsonList::clear() +{ + m_items.clear(); + m_dirty = true; +} + +void JsonList::append(const QVariant& val) +{ + m_items.append(json::toJson(val)); + m_dirty = true; +} + +void JsonList::append(const JsonDict& dict) +{ + m_items.append(dict.toString()); + m_dirty = true; +} diff --git a/src/webui/jsonlist.h b/src/webui/jsonlist.h new file mode 100644 index 000000000..d480349e6 --- /dev/null +++ b/src/webui/jsonlist.h @@ -0,0 +1,51 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2012, Christophe Dumez + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#ifndef JSONLIST_H +#define JSONLIST_H + +#include +#include "jsondict.h" + +class JsonList +{ +public: + JsonList(); + const QString& toString() const; + void clear(); + void append(const QVariant& val); + void append(const JsonDict& dict); + +private: + mutable bool m_dirty; + QStringList m_items; +}; + +#endif // JSONLIST_H diff --git a/src/webui/scripts/client.js b/src/webui/scripts/client.js index 3e118a082..b97f65f77 100644 --- a/src/webui/scripts/client.js +++ b/src/webui/scripts/client.js @@ -232,7 +232,7 @@ window.addEvent('load', function(){ var ajaxfn = function(){ var queueing_enabled = false; - var url = 'json/events'; + var url = 'json/torrents'; if (!waiting){ waiting=true; var request = new Request.JSON({ diff --git a/src/webui/webui.pri b/src/webui/webui.pri index da7eea482..914232860 100644 --- a/src/webui/webui.pri +++ b/src/webui/webui.pri @@ -5,12 +5,19 @@ HEADERS += $$PWD/httpserver.h \ $$PWD/httprequestparser.h \ $$PWD/httpresponsegenerator.h \ $$PWD/eventmanager.h \ - $$PWD/json.h + $$PWD/json.h \ + $$PWD/jsonlist.h \ + $$PWD/jsondict.h \ + $$PWD/btjson.h SOURCES += $$PWD/httpserver.cpp \ $$PWD/httpconnection.cpp \ $$PWD/httprequestparser.cpp \ $$PWD/httpresponsegenerator.cpp \ - $$PWD/eventmanager.cpp + $$PWD/eventmanager.cpp \ + $$PWD/jsonlist.cpp \ + $$PWD/jsondict.cpp \ + $$PWD/btjson.cpp \ + $$PWD/json.cpp -RESOURCES += $$PWD/webui.qrc \ No newline at end of file +RESOURCES += $$PWD/webui.qrc