mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-21 05:43:32 -07:00
Merge pull request #2191 from glassez/webui
WebUI: Implement server-side filtering, sorting and limit/offset.
This commit is contained in:
commit
3672363207
12 changed files with 1149 additions and 834 deletions
|
@ -40,6 +40,7 @@
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "qtorrenthandle.h"
|
#include "qtorrenthandle.h"
|
||||||
#include "torrentpersistentdata.h"
|
#include "torrentpersistentdata.h"
|
||||||
|
#include "qbtsession.h"
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
@ -403,7 +404,52 @@ bool QTorrentHandle::has_metadata() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QTorrentHandle::file_progress(std::vector<size_type>& fp) const {
|
void QTorrentHandle::file_progress(std::vector<size_type>& fp) const {
|
||||||
torrent_handle::file_progress(fp, torrent_handle::piece_granularity);
|
torrent_handle::file_progress(fp, torrent_handle::piece_granularity);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTorrentState QTorrentHandle::torrentState() const {
|
||||||
|
QTorrentState state = QTorrentState::Unknown;
|
||||||
|
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
||||||
|
|
||||||
|
if (is_paused(s)) {
|
||||||
|
if (has_error(s))
|
||||||
|
state = QTorrentState::Error;
|
||||||
|
else
|
||||||
|
state = is_seed(s) ? QTorrentState::PausedUploading : QTorrentState::PausedDownloading;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (QBtSession::instance()->isQueueingEnabled() && is_queued(s)) {
|
||||||
|
state = is_seed(s) ? QTorrentState::QueuedUploading : QTorrentState::QueuedDownloading;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (s.state) {
|
||||||
|
case torrent_status::finished:
|
||||||
|
case torrent_status::seeding:
|
||||||
|
state = s.upload_payload_rate > 0 ? QTorrentState::Uploading : QTorrentState::StalledUploading;
|
||||||
|
break;
|
||||||
|
case torrent_status::allocating:
|
||||||
|
case torrent_status::checking_files:
|
||||||
|
case torrent_status::queued_for_checking:
|
||||||
|
case torrent_status::checking_resume_data:
|
||||||
|
state = is_seed(s) ? QTorrentState::CheckingUploading : QTorrentState::CheckingDownloading;
|
||||||
|
break;
|
||||||
|
case torrent_status::downloading:
|
||||||
|
case torrent_status::downloading_metadata:
|
||||||
|
state = s.download_payload_rate > 0 ? QTorrentState::Downloading : QTorrentState::StalledDownloading;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qWarning("Unrecognized torrent status, should not happen!!! status was %d", this->state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
qulonglong QTorrentHandle::eta() const
|
||||||
|
{
|
||||||
|
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
||||||
|
return QBtSession::instance()->getETA(hash(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -681,3 +727,44 @@ QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsign
|
||||||
return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index)));
|
return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QTorrentState::QTorrentState(int value)
|
||||||
|
: m_value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QTorrentState::toString() const
|
||||||
|
{
|
||||||
|
switch (m_value) {
|
||||||
|
case Error:
|
||||||
|
return "error";
|
||||||
|
case Uploading:
|
||||||
|
return "uploading";
|
||||||
|
case PausedUploading:
|
||||||
|
return "pausedUP";
|
||||||
|
case QueuedUploading:
|
||||||
|
return "queuedUP";
|
||||||
|
case StalledUploading:
|
||||||
|
return "stalledUP";
|
||||||
|
case CheckingUploading:
|
||||||
|
return "checkingUP";
|
||||||
|
case Downloading:
|
||||||
|
return "downloading";
|
||||||
|
case PausedDownloading:
|
||||||
|
return "pausedDL";
|
||||||
|
case QueuedDownloading:
|
||||||
|
return "queuedDL";
|
||||||
|
case StalledDownloading:
|
||||||
|
return "stalledDL";
|
||||||
|
case CheckingDownloading:
|
||||||
|
return "checkingDL";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTorrentState::operator int() const
|
||||||
|
{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,36 @@ QT_BEGIN_NAMESPACE
|
||||||
class QStringList;
|
class QStringList;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class QTorrentState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
Unknown = -1,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
|
||||||
|
Uploading,
|
||||||
|
PausedUploading,
|
||||||
|
QueuedUploading,
|
||||||
|
StalledUploading,
|
||||||
|
CheckingUploading,
|
||||||
|
|
||||||
|
Downloading,
|
||||||
|
PausedDownloading,
|
||||||
|
QueuedDownloading,
|
||||||
|
StalledDownloading,
|
||||||
|
CheckingDownloading
|
||||||
|
};
|
||||||
|
|
||||||
|
QTorrentState(int value);
|
||||||
|
|
||||||
|
operator int() const;
|
||||||
|
QString toString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_value;
|
||||||
|
};
|
||||||
|
|
||||||
// A wrapper for torrent_handle in libtorrent
|
// A wrapper for torrent_handle in libtorrent
|
||||||
// to interact well with Qt types
|
// to interact well with Qt types
|
||||||
class QTorrentHandle : public libtorrent::torrent_handle {
|
class QTorrentHandle : public libtorrent::torrent_handle {
|
||||||
|
@ -94,6 +124,8 @@ public:
|
||||||
void downloading_pieces(libtorrent::bitfield& bf) const;
|
void downloading_pieces(libtorrent::bitfield& bf) const;
|
||||||
bool has_metadata() const;
|
bool has_metadata() const;
|
||||||
void file_progress(std::vector<libtorrent::size_type>& fp) const;
|
void file_progress(std::vector<libtorrent::size_type>& fp) const;
|
||||||
|
QTorrentState torrentState() const;
|
||||||
|
qulonglong eta() const;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Setters
|
// Setters
|
||||||
|
|
|
@ -32,10 +32,14 @@
|
||||||
#include "fs_utils.h"
|
#include "fs_utils.h"
|
||||||
#include "qbtsession.h"
|
#include "qbtsession.h"
|
||||||
#include "torrentpersistentdata.h"
|
#include "torrentpersistentdata.h"
|
||||||
|
#include "qtorrentfilter.h"
|
||||||
#include "jsonutils.h"
|
#include "jsonutils.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
#include <QMetaType>
|
||||||
|
#endif
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,32 +51,32 @@ using namespace libtorrent;
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
|
||||||
|
|
||||||
#define CACHED_VARIABLE(VARTYPE, VAR, DUR) \
|
#define CACHED_VARIABLE(VARTYPE, VAR, DUR) \
|
||||||
static VARTYPE VAR; \
|
static VARTYPE VAR; \
|
||||||
static QElapsedTimer cacheTimer; \
|
static QElapsedTimer cacheTimer; \
|
||||||
static bool initialized = false; \
|
static bool initialized = false; \
|
||||||
if (initialized && !cacheTimer.hasExpired(DUR)) \
|
if (initialized && !cacheTimer.hasExpired(DUR)) \
|
||||||
return json::toJson(VAR); \
|
return json::toJson(VAR); \
|
||||||
initialized = true; \
|
initialized = true; \
|
||||||
cacheTimer.start(); \
|
cacheTimer.start(); \
|
||||||
VAR = VARTYPE()
|
VAR = VARTYPE()
|
||||||
|
|
||||||
#define CACHED_VARIABLE_FOR_HASH(VARTYPE, VAR, DUR, HASH) \
|
#define CACHED_VARIABLE_FOR_HASH(VARTYPE, VAR, DUR, HASH) \
|
||||||
static VARTYPE VAR; \
|
static VARTYPE VAR; \
|
||||||
static QString prev_hash; \
|
static QString prev_hash; \
|
||||||
static QElapsedTimer cacheTimer; \
|
static QElapsedTimer cacheTimer; \
|
||||||
if (prev_hash == HASH && !cacheTimer.hasExpired(DUR)) \
|
if (prev_hash == HASH && !cacheTimer.hasExpired(DUR)) \
|
||||||
return json::toJson(VAR); \
|
return json::toJson(VAR); \
|
||||||
prev_hash = HASH; \
|
prev_hash = HASH; \
|
||||||
cacheTimer.start(); \
|
cacheTimer.start(); \
|
||||||
VAR = VARTYPE()
|
VAR = VARTYPE()
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// We don't support caching for Qt < 4.7 at the moment
|
// We don't support caching for Qt < 4.7 at the moment
|
||||||
#define CACHED_VARIABLE(VARTYPE, VAR, DUR) \
|
#define CACHED_VARIABLE(VARTYPE, VAR, DUR) \
|
||||||
VARTYPE VAR
|
VARTYPE VAR
|
||||||
|
|
||||||
#define CACHED_VARIABLE_FOR_HASH(VARTYPE, VAR, DUR, HASH) \
|
#define CACHED_VARIABLE_FOR_HASH(VARTYPE, VAR, DUR, HASH) \
|
||||||
VARTYPE VAR
|
VARTYPE VAR
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -132,63 +136,83 @@ static const char KEY_TRANSFER_DLDATA[] = "dl_info_data";
|
||||||
static const char KEY_TRANSFER_UPSPEED[] = "up_info_speed";
|
static const char KEY_TRANSFER_UPSPEED[] = "up_info_speed";
|
||||||
static const char KEY_TRANSFER_UPDATA[] = "up_info_data";
|
static const char KEY_TRANSFER_UPDATA[] = "up_info_data";
|
||||||
|
|
||||||
|
class QTorrentCompare
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QTorrentCompare(QString key, bool greaterThan = false)
|
||||||
|
: key_(key)
|
||||||
|
, greaterThan_(greaterThan)
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
, type_(QVariant::Invalid)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(QVariant torrent1, QVariant torrent2)
|
||||||
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
if (type_ == QVariant::Invalid)
|
||||||
|
type_ = torrent1.toMap().value(key_).type();
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
case QVariant::Int:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toInt() > torrent2.toMap().value(key_).toInt()
|
||||||
|
: torrent1.toMap().value(key_).toInt() < torrent2.toMap().value(key_).toInt();
|
||||||
|
case QVariant::LongLong:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toLongLong() > torrent2.toMap().value(key_).toLongLong()
|
||||||
|
: torrent1.toMap().value(key_).toLongLong() < torrent2.toMap().value(key_).toLongLong();
|
||||||
|
case QVariant::ULongLong:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toULongLong() > torrent2.toMap().value(key_).toULongLong()
|
||||||
|
: torrent1.toMap().value(key_).toULongLong() < torrent2.toMap().value(key_).toULongLong();
|
||||||
|
case QMetaType::Float:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toFloat() > torrent2.toMap().value(key_).toFloat()
|
||||||
|
: torrent1.toMap().value(key_).toFloat() < torrent2.toMap().value(key_).toFloat();
|
||||||
|
case QVariant::Double:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toDouble() > torrent2.toMap().value(key_).toDouble()
|
||||||
|
: torrent1.toMap().value(key_).toDouble() < torrent2.toMap().value(key_).toDouble();
|
||||||
|
default:
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_).toString() > torrent2.toMap().value(key_).toString()
|
||||||
|
: torrent1.toMap().value(key_).toString() < torrent2.toMap().value(key_).toString();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return greaterThan_ ? torrent1.toMap().value(key_) > torrent2.toMap().value(key_)
|
||||||
|
: torrent1.toMap().value(key_) < torrent2.toMap().value(key_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString key_;
|
||||||
|
bool greaterThan_;
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
QVariant::Type type_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
static QVariantMap toMap(const QTorrentHandle& h)
|
static QVariantMap toMap(const QTorrentHandle& h)
|
||||||
{
|
{
|
||||||
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
|
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
|
||||||
|
|
||||||
QVariantMap ret;
|
QVariantMap ret;
|
||||||
ret[KEY_TORRENT_HASH] = h.hash();
|
ret[KEY_TORRENT_HASH] = h.hash();
|
||||||
ret[KEY_TORRENT_NAME] = h.name();
|
ret[KEY_TORRENT_NAME] = h.name();
|
||||||
ret[KEY_TORRENT_SIZE] = static_cast<qlonglong>(status.total_wanted);
|
ret[KEY_TORRENT_SIZE] = static_cast<qlonglong>(status.total_wanted);
|
||||||
ret[KEY_TORRENT_PROGRESS] = h.progress(status);
|
ret[KEY_TORRENT_PROGRESS] = h.progress(status);
|
||||||
ret[KEY_TORRENT_DLSPEED] = status.download_payload_rate;
|
ret[KEY_TORRENT_DLSPEED] = status.download_payload_rate;
|
||||||
ret[KEY_TORRENT_UPSPEED] = status.upload_payload_rate;
|
ret[KEY_TORRENT_UPSPEED] = status.upload_payload_rate;
|
||||||
if (QBtSession::instance()->isQueueingEnabled() && h.queue_position(status) >= 0)
|
if (QBtSession::instance()->isQueueingEnabled() && h.queue_position(status) >= 0)
|
||||||
ret[KEY_TORRENT_PRIORITY] = h.queue_position(status);
|
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 > 100.) ? -1 : ratio;
|
|
||||||
qulonglong eta = 0;
|
|
||||||
QString state;
|
|
||||||
if (h.is_paused(status)) {
|
|
||||||
if (h.has_error(status))
|
|
||||||
state = "error";
|
|
||||||
else
|
else
|
||||||
state = h.is_seed(status) ? "pausedUP" : "pausedDL";
|
ret[KEY_TORRENT_PRIORITY] = -1;
|
||||||
} else {
|
ret[KEY_TORRENT_SEEDS] = status.num_seeds;
|
||||||
if (QBtSession::instance()->isQueueingEnabled() && h.is_queued(status))
|
ret[KEY_TORRENT_NUM_COMPLETE] = status.num_complete;
|
||||||
state = h.is_seed(status) ? "queuedUP" : "queuedDL";
|
ret[KEY_TORRENT_LEECHS] = status.num_peers - status.num_seeds;
|
||||||
else {
|
ret[KEY_TORRENT_NUM_INCOMPLETE] = status.num_incomplete;
|
||||||
switch (status.state) {
|
const qreal ratio = QBtSession::instance()->getRealRatio(status);
|
||||||
case torrent_status::finished:
|
ret[KEY_TORRENT_RATIO] = (ratio > 100.) ? -1 : ratio;
|
||||||
case torrent_status::seeding:
|
ret[KEY_TORRENT_STATE] = h.torrentState().toString();
|
||||||
state = status.upload_payload_rate > 0 ? "uploading" : "stalledUP";
|
ret[KEY_TORRENT_ETA] = h.eta();
|
||||||
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(status) ? "checkingUP" : "checkingDL";
|
|
||||||
break;
|
|
||||||
case torrent_status::downloading:
|
|
||||||
case torrent_status::downloading_metadata:
|
|
||||||
state = status.download_payload_rate > 0 ? "downloading" : "stalledDL";
|
|
||||||
eta = QBtSession::instance()->getETA(h.hash(), status);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qWarning("Unrecognized torrent status, should not happen!!! status was %d", h.state());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret[KEY_TORRENT_ETA] = eta ? eta : MAX_ETA;
|
|
||||||
ret[KEY_TORRENT_STATE] = state;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,16 +235,38 @@ static QVariantMap toMap(const QTorrentHandle& h)
|
||||||
* - "eta": Torrent ETA
|
* - "eta": Torrent ETA
|
||||||
* - "state": Torrent state
|
* - "state": Torrent state
|
||||||
*/
|
*/
|
||||||
QByteArray btjson::getTorrents()
|
QByteArray btjson::getTorrents(QString filter, QString label,
|
||||||
|
QString sortedColumn, bool reverse, int limit, int offset)
|
||||||
{
|
{
|
||||||
CACHED_VARIABLE(QVariantList, torrent_list, CACHE_DURATION_MS);
|
QVariantList torrent_list;
|
||||||
std::vector<torrent_handle> torrents = QBtSession::instance()->getTorrents();
|
|
||||||
std::vector<torrent_handle>::const_iterator it = torrents.begin();
|
std::vector<torrent_handle> torrents = QBtSession::instance()->getTorrents();
|
||||||
std::vector<torrent_handle>::const_iterator end = torrents.end();
|
std::vector<torrent_handle>::const_iterator it = torrents.begin();
|
||||||
for( ; it != end; ++it) {
|
std::vector<torrent_handle>::const_iterator end = torrents.end();
|
||||||
torrent_list.append(toMap(QTorrentHandle(*it)));
|
|
||||||
}
|
QTorrentFilter torrentFilter(filter, label);
|
||||||
return json::toJson(torrent_list);
|
for( ; it != end; ++it) {
|
||||||
|
QTorrentHandle torrent = QTorrentHandle(*it);
|
||||||
|
|
||||||
|
if (torrentFilter.apply(torrent))
|
||||||
|
torrent_list.append(toMap(torrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(torrent_list.begin(), torrent_list.end(), QTorrentCompare(sortedColumn, reverse));
|
||||||
|
int size = torrent_list.size();
|
||||||
|
// normalize offset
|
||||||
|
if (offset < 0)
|
||||||
|
offset = size - offset;
|
||||||
|
if ((offset >= size) || (offset < 0))
|
||||||
|
offset = 0;
|
||||||
|
// normalize limit
|
||||||
|
if (limit <= 0)
|
||||||
|
limit = -1; // unlimited
|
||||||
|
|
||||||
|
if ((limit > 0) || (offset > 0))
|
||||||
|
return json::toJson(torrent_list.mid(offset, limit));
|
||||||
|
else
|
||||||
|
return json::toJson(torrent_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,39 +281,40 @@ QByteArray btjson::getTorrents()
|
||||||
*/
|
*/
|
||||||
QByteArray btjson::getTrackersForTorrent(const QString& hash)
|
QByteArray btjson::getTrackersForTorrent(const QString& hash)
|
||||||
{
|
{
|
||||||
CACHED_VARIABLE_FOR_HASH(QVariantList, tracker_list, CACHE_DURATION_MS, hash);
|
CACHED_VARIABLE_FOR_HASH(QVariantList, tracker_list, CACHE_DURATION_MS, hash);
|
||||||
try {
|
try {
|
||||||
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
||||||
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(hash);
|
QHash<QString, TrackerInfos> trackers_data = QBtSession::instance()->getTrackersInfo(hash);
|
||||||
std::vector<announce_entry> vect_trackers = h.trackers();
|
std::vector<announce_entry> vect_trackers = h.trackers();
|
||||||
std::vector<announce_entry>::const_iterator it = vect_trackers.begin();
|
std::vector<announce_entry>::const_iterator it = vect_trackers.begin();
|
||||||
std::vector<announce_entry>::const_iterator end = vect_trackers.end();
|
std::vector<announce_entry>::const_iterator end = vect_trackers.end();
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
QVariantMap tracker_dict;
|
QVariantMap tracker_dict;
|
||||||
const QString tracker_url = misc::toQString(it->url);
|
const QString tracker_url = misc::toQString(it->url);
|
||||||
tracker_dict[KEY_TRACKER_URL] = tracker_url;
|
tracker_dict[KEY_TRACKER_URL] = tracker_url;
|
||||||
const TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
|
const TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
|
||||||
QString status;
|
QString status;
|
||||||
if (it->verified)
|
if (it->verified)
|
||||||
status = tr("Working");
|
status = tr("Working");
|
||||||
else {
|
else {
|
||||||
if (it->updating && it->fails == 0)
|
if (it->updating && it->fails == 0)
|
||||||
status = tr("Updating...");
|
status = tr("Updating...");
|
||||||
else
|
else
|
||||||
status = it->fails > 0 ? tr("Not working") : tr("Not contacted yet");
|
status = it->fails > 0 ? tr("Not working") : tr("Not contacted yet");
|
||||||
}
|
}
|
||||||
tracker_dict[KEY_TRACKER_STATUS] = status;
|
tracker_dict[KEY_TRACKER_STATUS] = status;
|
||||||
tracker_dict[KEY_TRACKER_PEERS] = static_cast<qulonglong>(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers);
|
tracker_dict[KEY_TRACKER_PEERS] = static_cast<qulonglong>(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers);
|
||||||
tracker_dict[KEY_TRACKER_MSG] = data.last_message.trimmed();
|
tracker_dict[KEY_TRACKER_MSG] = data.last_message.trimmed();
|
||||||
|
|
||||||
tracker_list.append(tracker_dict);
|
tracker_list.append(tracker_dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception& e) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
||||||
|
return QByteArray();
|
||||||
}
|
}
|
||||||
} catch(const std::exception& e) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return json::toJson(tracker_list);
|
return json::toJson(tracker_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,41 +341,42 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash)
|
||||||
*/
|
*/
|
||||||
QByteArray btjson::getPropertiesForTorrent(const QString& hash)
|
QByteArray btjson::getPropertiesForTorrent(const QString& hash)
|
||||||
{
|
{
|
||||||
CACHED_VARIABLE_FOR_HASH(QVariantMap, data, CACHE_DURATION_MS, hash);
|
CACHED_VARIABLE_FOR_HASH(QVariantMap, data, CACHE_DURATION_MS, hash);
|
||||||
try {
|
try {
|
||||||
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
||||||
|
|
||||||
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
|
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
|
||||||
if (!status.has_metadata)
|
if (!status.has_metadata)
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
|
||||||
// Save path
|
// Save path
|
||||||
QString save_path = fsutils::toNativePath(TorrentPersistentData::getSavePath(hash));
|
QString save_path = fsutils::toNativePath(TorrentPersistentData::getSavePath(hash));
|
||||||
if (save_path.isEmpty())
|
if (save_path.isEmpty())
|
||||||
save_path = fsutils::toNativePath(h.save_path());
|
save_path = fsutils::toNativePath(h.save_path());
|
||||||
data[KEY_PROP_SAVE_PATH] = save_path;
|
data[KEY_PROP_SAVE_PATH] = save_path;
|
||||||
data[KEY_PROP_CREATION_DATE] = h.creation_date_unix();
|
data[KEY_PROP_CREATION_DATE] = h.creation_date_unix();
|
||||||
data[KEY_PROP_PIECE_SIZE] = static_cast<qlonglong>(h.piece_length());
|
data[KEY_PROP_PIECE_SIZE] = static_cast<qlonglong>(h.piece_length());
|
||||||
data[KEY_PROP_COMMENT] = h.comment();
|
data[KEY_PROP_COMMENT] = h.comment();
|
||||||
data[KEY_PROP_WASTED] = static_cast<qlonglong>(status.total_failed_bytes + status.total_redundant_bytes);
|
data[KEY_PROP_WASTED] = static_cast<qlonglong>(status.total_failed_bytes + status.total_redundant_bytes);
|
||||||
data[KEY_PROP_UPLOADED] = static_cast<qlonglong>(status.all_time_upload);
|
data[KEY_PROP_UPLOADED] = static_cast<qlonglong>(status.all_time_upload);
|
||||||
data[KEY_PROP_UPLOADED_SESSION] = static_cast<qlonglong>(status.total_payload_upload);
|
data[KEY_PROP_UPLOADED_SESSION] = static_cast<qlonglong>(status.total_payload_upload);
|
||||||
data[KEY_PROP_DOWNLOADED] = static_cast<qlonglong>(status.all_time_download);
|
data[KEY_PROP_DOWNLOADED] = static_cast<qlonglong>(status.all_time_download);
|
||||||
data[KEY_PROP_DOWNLOADED_SESSION] = static_cast<qlonglong>(status.total_payload_download);
|
data[KEY_PROP_DOWNLOADED_SESSION] = static_cast<qlonglong>(status.total_payload_download);
|
||||||
data[KEY_PROP_UP_LIMIT] = h.upload_limit() <= 0 ? -1 : h.upload_limit();
|
data[KEY_PROP_UP_LIMIT] = h.upload_limit() <= 0 ? -1 : h.upload_limit();
|
||||||
data[KEY_PROP_DL_LIMIT] = h.download_limit() <= 0 ? -1 : h.download_limit();
|
data[KEY_PROP_DL_LIMIT] = h.download_limit() <= 0 ? -1 : h.download_limit();
|
||||||
data[KEY_PROP_TIME_ELAPSED] = status.active_time;
|
data[KEY_PROP_TIME_ELAPSED] = status.active_time;
|
||||||
data[KEY_PROP_SEEDING_TIME] = status.seeding_time;
|
data[KEY_PROP_SEEDING_TIME] = status.seeding_time;
|
||||||
data[KEY_PROP_CONNECT_COUNT] = status.num_connections;
|
data[KEY_PROP_CONNECT_COUNT] = status.num_connections;
|
||||||
data[KEY_PROP_CONNECT_COUNT_LIMIT] = status.connections_limit;
|
data[KEY_PROP_CONNECT_COUNT_LIMIT] = status.connections_limit;
|
||||||
const qreal ratio = QBtSession::instance()->getRealRatio(status);
|
const qreal ratio = QBtSession::instance()->getRealRatio(status);
|
||||||
data[KEY_PROP_RATIO] = ratio > 100. ? -1 : ratio;
|
data[KEY_PROP_RATIO] = ratio > 100. ? -1 : ratio;
|
||||||
} catch(const std::exception& e) {
|
}
|
||||||
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
catch(const std::exception& e) {
|
||||||
return QByteArray();
|
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
||||||
}
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
return json::toJson(data);
|
return json::toJson(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,36 +392,37 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash)
|
||||||
*/
|
*/
|
||||||
QByteArray btjson::getFilesForTorrent(const QString& hash)
|
QByteArray btjson::getFilesForTorrent(const QString& hash)
|
||||||
{
|
{
|
||||||
CACHED_VARIABLE_FOR_HASH(QVariantList, file_list, CACHE_DURATION_MS, hash);
|
CACHED_VARIABLE_FOR_HASH(QVariantList, file_list, CACHE_DURATION_MS, hash);
|
||||||
try {
|
try {
|
||||||
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
|
||||||
if (!h.has_metadata())
|
if (!h.has_metadata())
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
|
|
||||||
const std::vector<int> priorities = h.file_priorities();
|
const std::vector<int> priorities = h.file_priorities();
|
||||||
std::vector<size_type> fp;
|
std::vector<size_type> fp;
|
||||||
h.file_progress(fp);
|
h.file_progress(fp);
|
||||||
for (int i = 0; i < h.num_files(); ++i) {
|
for (int i = 0; i < h.num_files(); ++i) {
|
||||||
QVariantMap file_dict;
|
QVariantMap file_dict;
|
||||||
QString fileName = h.filename_at(i);
|
QString fileName = h.filename_at(i);
|
||||||
if (fileName.endsWith(".!qB", Qt::CaseInsensitive))
|
if (fileName.endsWith(".!qB", Qt::CaseInsensitive))
|
||||||
fileName.chop(4);
|
fileName.chop(4);
|
||||||
file_dict[KEY_FILE_NAME] = fsutils::toNativePath(fileName);
|
file_dict[KEY_FILE_NAME] = fsutils::toNativePath(fileName);
|
||||||
const size_type size = h.filesize_at(i);
|
const size_type size = h.filesize_at(i);
|
||||||
file_dict[KEY_FILE_SIZE] = static_cast<qlonglong>(size);
|
file_dict[KEY_FILE_SIZE] = static_cast<qlonglong>(size);
|
||||||
file_dict[KEY_FILE_PROGRESS] = (size > 0) ? (fp[i] / (double) size) : 1.;
|
file_dict[KEY_FILE_PROGRESS] = (size > 0) ? (fp[i] / (double) size) : 1.;
|
||||||
file_dict[KEY_FILE_PRIORITY] = priorities[i];
|
file_dict[KEY_FILE_PRIORITY] = priorities[i];
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
file_dict[KEY_FILE_IS_SEED] = h.is_seed();
|
file_dict[KEY_FILE_IS_SEED] = h.is_seed();
|
||||||
|
|
||||||
file_list.append(file_dict);
|
file_list.append(file_dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
||||||
|
return QByteArray();
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return json::toJson(file_list);
|
return json::toJson(file_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -388,11 +437,11 @@ QByteArray btjson::getFilesForTorrent(const QString& hash)
|
||||||
*/
|
*/
|
||||||
QByteArray btjson::getTransferInfo()
|
QByteArray btjson::getTransferInfo()
|
||||||
{
|
{
|
||||||
CACHED_VARIABLE(QVariantMap, info, CACHE_DURATION_MS);
|
CACHED_VARIABLE(QVariantMap, info, CACHE_DURATION_MS);
|
||||||
session_status sessionStatus = QBtSession::instance()->getSessionStatus();
|
session_status sessionStatus = QBtSession::instance()->getSessionStatus();
|
||||||
info[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate;
|
info[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate;
|
||||||
info[KEY_TRANSFER_DLDATA] = static_cast<qlonglong>(sessionStatus.total_payload_download);
|
info[KEY_TRANSFER_DLDATA] = static_cast<qlonglong>(sessionStatus.total_payload_download);
|
||||||
info[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate;
|
info[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate;
|
||||||
info[KEY_TRANSFER_UPDATA] = static_cast<qlonglong>(sessionStatus.total_payload_upload);
|
info[KEY_TRANSFER_UPDATA] = static_cast<qlonglong>(sessionStatus.total_payload_upload);
|
||||||
return json::toJson(info);
|
return json::toJson(info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,18 +34,22 @@
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class btjson {
|
class QTorrentHandle;
|
||||||
Q_DECLARE_TR_FUNCTIONS(misc)
|
|
||||||
|
class btjson
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(misc)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
btjson() {}
|
btjson() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QByteArray getTorrents();
|
static QByteArray getTorrents(QString filter = "all", QString label = QString(),
|
||||||
static QByteArray getTrackersForTorrent(const QString& hash);
|
QString sortedColumn = "name", bool reverse = false, int limit = 0, int offset = 0);
|
||||||
static QByteArray getPropertiesForTorrent(const QString& hash);
|
static QByteArray getTrackersForTorrent(const QString& hash);
|
||||||
static QByteArray getFilesForTorrent(const QString& hash);
|
static QByteArray getPropertiesForTorrent(const QString& hash);
|
||||||
static QByteArray getTransferInfo();
|
static QByteArray getFilesForTorrent(const QString& hash);
|
||||||
|
static QByteArray getTransferInfo();
|
||||||
}; // class btjson
|
}; // class btjson
|
||||||
|
|
||||||
#endif // BTJSON_H
|
#endif // BTJSON_H
|
||||||
|
|
118
src/webui/qtorrentfilter.cpp
Normal file
118
src/webui/qtorrentfilter.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "torrentpersistentdata.h"
|
||||||
|
#include "qtorrentfilter.h"
|
||||||
|
|
||||||
|
QTorrentFilter::QTorrentFilter(QString filter, QString label)
|
||||||
|
: type_(All)
|
||||||
|
, label_(label)
|
||||||
|
{
|
||||||
|
if (filter == "downloading")
|
||||||
|
type_ = Downloading;
|
||||||
|
else if (filter == "completed")
|
||||||
|
type_ = Completed;
|
||||||
|
else if (filter == "paused")
|
||||||
|
type_ = Paused;
|
||||||
|
else if (filter == "active")
|
||||||
|
type_ = Active;
|
||||||
|
else if (filter == "inactive")
|
||||||
|
type_ = Inactive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::apply(const QTorrentHandle& h) const
|
||||||
|
{
|
||||||
|
if (!torrentHasLabel(h))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
case Downloading:
|
||||||
|
return isTorrentDownloading(h);
|
||||||
|
case Completed:
|
||||||
|
return isTorrentCompleted(h);
|
||||||
|
case Paused:
|
||||||
|
return isTorrentPaused(h);
|
||||||
|
case Active:
|
||||||
|
return isTorrentActive(h);
|
||||||
|
case Inactive:
|
||||||
|
return isTorrentInactive(h);
|
||||||
|
default: // All
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::isTorrentDownloading(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
const QTorrentState state = h.torrentState();
|
||||||
|
|
||||||
|
return state == QTorrentState::Downloading
|
||||||
|
|| state == QTorrentState::StalledDownloading
|
||||||
|
|| state == QTorrentState::CheckingDownloading
|
||||||
|
|| state == QTorrentState::PausedDownloading
|
||||||
|
|| state == QTorrentState::QueuedDownloading;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::isTorrentCompleted(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
const QTorrentState state = h.torrentState();
|
||||||
|
|
||||||
|
return state == QTorrentState::Uploading
|
||||||
|
|| state == QTorrentState::StalledUploading
|
||||||
|
|| state == QTorrentState::CheckingUploading
|
||||||
|
|| state == QTorrentState::PausedUploading
|
||||||
|
|| state == QTorrentState::QueuedUploading;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::isTorrentPaused(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
const QTorrentState state = h.torrentState();
|
||||||
|
|
||||||
|
return state == QTorrentState::PausedDownloading
|
||||||
|
|| state == QTorrentState::PausedUploading;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::isTorrentActive(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
const QTorrentState state = h.torrentState();
|
||||||
|
|
||||||
|
return state == QTorrentState::Downloading
|
||||||
|
|| state == QTorrentState::Uploading;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::isTorrentInactive(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
return !isTorrentActive(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTorrentFilter::torrentHasLabel(const QTorrentHandle &h) const
|
||||||
|
{
|
||||||
|
if (label_.isNull())
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return TorrentPersistentData::getLabel(h.hash()) == label_;
|
||||||
|
}
|
63
src/webui/qtorrentfilter.h
Normal file
63
src/webui/qtorrentfilter.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTORRENTFILTER_H
|
||||||
|
#define QTORRENTFILTER_H
|
||||||
|
|
||||||
|
#include "qtorrenthandle.h"
|
||||||
|
|
||||||
|
class QTorrentFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
All,
|
||||||
|
Downloading,
|
||||||
|
Completed,
|
||||||
|
Paused,
|
||||||
|
Active,
|
||||||
|
Inactive
|
||||||
|
};
|
||||||
|
|
||||||
|
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
||||||
|
QTorrentFilter(QString filter, QString label = QString());
|
||||||
|
bool apply(const QTorrentHandle& h) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int type_;
|
||||||
|
QString label_;
|
||||||
|
|
||||||
|
bool isTorrentDownloading(const QTorrentHandle &h) const;
|
||||||
|
bool isTorrentCompleted(const QTorrentHandle &h) const;
|
||||||
|
bool isTorrentPaused(const QTorrentHandle &h) const;
|
||||||
|
bool isTorrentActive(const QTorrentHandle &h) const;
|
||||||
|
bool isTorrentInactive(const QTorrentHandle &h) const;
|
||||||
|
bool torrentHasLabel(const QTorrentHandle &h) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTORRENTFILTER_H
|
|
@ -186,9 +186,21 @@ void RequestHandler::action_public_images()
|
||||||
printFile(path);
|
printFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET params:
|
||||||
|
// - filter (string): all, downloading, completed, paused, active, inactive
|
||||||
|
// - label (string): torrent label for filtering by it (empty string means "unlabeled"; no "label" param presented means "any label")
|
||||||
|
// - sort (string): name of column for sorting by its value
|
||||||
|
// - reverse (bool): enable reverse sorting
|
||||||
|
// - limit (int): set limit number of torrents returned (if greater than 0, otherwise - unlimited)
|
||||||
|
// - offset (int): set offset (if less than 0 - offset from end)
|
||||||
void RequestHandler::action_json_torrents()
|
void RequestHandler::action_json_torrents()
|
||||||
{
|
{
|
||||||
print(btjson::getTorrents(), CONTENT_TYPE_JS);
|
const QStringMap& gets = request().gets;
|
||||||
|
|
||||||
|
print(btjson::getTorrents(
|
||||||
|
gets["filter"], gets["label"], gets["sort"], gets["reverse"] == "true",
|
||||||
|
gets["limit"].toInt(), gets["offset"].toInt()
|
||||||
|
), CONTENT_TYPE_JS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandler::action_json_preferences()
|
void RequestHandler::action_json_preferences()
|
||||||
|
|
|
@ -11,7 +11,8 @@ HEADERS += $$PWD/httpserver.h \
|
||||||
$$PWD/extra_translations.h \
|
$$PWD/extra_translations.h \
|
||||||
$$PWD/webapplication.h \
|
$$PWD/webapplication.h \
|
||||||
$$PWD/abstractrequesthandler.h \
|
$$PWD/abstractrequesthandler.h \
|
||||||
$$PWD/requesthandler.h
|
$$PWD/requesthandler.h \
|
||||||
|
$$PWD/qtorrentfilter.h
|
||||||
|
|
||||||
SOURCES += $$PWD/httpserver.cpp \
|
SOURCES += $$PWD/httpserver.cpp \
|
||||||
$$PWD/httpconnection.cpp \
|
$$PWD/httpconnection.cpp \
|
||||||
|
@ -21,7 +22,8 @@ SOURCES += $$PWD/httpserver.cpp \
|
||||||
$$PWD/prefjson.cpp \
|
$$PWD/prefjson.cpp \
|
||||||
$$PWD/webapplication.cpp \
|
$$PWD/webapplication.cpp \
|
||||||
$$PWD/abstractrequesthandler.cpp \
|
$$PWD/abstractrequesthandler.cpp \
|
||||||
$$PWD/requesthandler.cpp
|
$$PWD/requesthandler.cpp \
|
||||||
|
$$PWD/qtorrentfilter.cpp
|
||||||
|
|
||||||
# QJson JSON parser/serializer for using with Qt4
|
# QJson JSON parser/serializer for using with Qt4
|
||||||
lessThan(QT_MAJOR_VERSION, 5) {
|
lessThan(QT_MAJOR_VERSION, 5) {
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
// Remember this via Cookie
|
// Remember this via Cookie
|
||||||
var last_filter = Cookie.read('selected_filter');
|
var filter = Cookie.read('selected_filter');
|
||||||
if(!$defined(last_filter)) {
|
if(!$defined(last_filter)) {
|
||||||
last_filter = 'all';
|
filter = 'all';
|
||||||
}
|
}
|
||||||
$(last_filter+'_filter').addClass('selectedFilter');
|
$(filter+'_filter').addClass('selectedFilter');
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,252 +23,282 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
myTable = new dynamicTable();
|
myTable = new dynamicTable();
|
||||||
ajaxfn = function(){};
|
ajaxfn = function () {};
|
||||||
setSortedColumn = function(index){
|
|
||||||
myTable.setSortedColumn(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEvent('load', function(){
|
window.addEvent('load', function () {
|
||||||
|
|
||||||
var saveColumnSizes = function() {
|
var saveColumnSizes = function () {
|
||||||
var filters_width = $('Filters').getSize().x;
|
var filters_width = $('Filters').getSize().x;
|
||||||
var properties_height = $('propertiesPanel').getSize().y;
|
var properties_height = $('propertiesPanel').getSize().y;
|
||||||
localStorage.setItem('filters_width', filters_width);
|
localStorage.setItem('filters_width', filters_width);
|
||||||
localStorage.setItem('properties_height', properties_height);
|
localStorage.setItem('properties_height', properties_height);
|
||||||
}
|
|
||||||
|
|
||||||
/*MochaUI.Desktop = new MochaUI.Desktop();
|
|
||||||
MochaUI.Desktop.desktop.setStyles({
|
|
||||||
'background': '#fff',
|
|
||||||
'visibility': 'visible'
|
|
||||||
});*/
|
|
||||||
MochaUI.Desktop.initialize();
|
|
||||||
|
|
||||||
var filt_w = localStorage.getItem('filters_width');
|
|
||||||
if($defined(filt_w))
|
|
||||||
filt_w = filt_w.toInt();
|
|
||||||
else
|
|
||||||
filt_w = 120;
|
|
||||||
new MochaUI.Column({
|
|
||||||
id: 'filtersColumn',
|
|
||||||
placement: 'left',
|
|
||||||
onResize: saveColumnSizes,
|
|
||||||
width: filt_w,
|
|
||||||
resizeLimit: [100, 300]
|
|
||||||
});
|
|
||||||
new MochaUI.Column({
|
|
||||||
id: 'mainColumn',
|
|
||||||
placement: 'main',
|
|
||||||
width: null,
|
|
||||||
resizeLimit: [100, 300]
|
|
||||||
});
|
|
||||||
MochaUI.Desktop.setDesktopSize();
|
|
||||||
new MochaUI.Panel({
|
|
||||||
id: 'Filters',
|
|
||||||
title: 'Panel',
|
|
||||||
header: false,
|
|
||||||
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
||||||
loadMethod: 'xhr',
|
|
||||||
contentURL: 'filters.html',
|
|
||||||
column: 'filtersColumn',
|
|
||||||
height: 300
|
|
||||||
});
|
|
||||||
initializeWindows();
|
|
||||||
var r=0;
|
|
||||||
var waiting=false;
|
|
||||||
var waitingTrInfo = false;
|
|
||||||
|
|
||||||
var stateToImg = function(state){
|
|
||||||
if(state == "pausedUP" || state == "pausedDL") {
|
|
||||||
state = "paused";
|
|
||||||
} else {
|
|
||||||
if(state == "queuedUP" || state == "queuedDL") {
|
|
||||||
state = "queued";
|
|
||||||
} else {
|
|
||||||
if(state == "checkingUP" || state == "checkingDL") {
|
|
||||||
state = "checking";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 'images/skin/'+state+'.png';
|
|
||||||
};
|
|
||||||
var loadTransferInfo = function() {
|
|
||||||
var url = 'json/transferInfo';
|
|
||||||
if(!waitingTrInfo) {
|
|
||||||
waitingTrInfo = true;
|
|
||||||
var request = new Request.JSON({
|
|
||||||
url: url,
|
|
||||||
noCache: true,
|
|
||||||
method: 'get',
|
|
||||||
onFailure: function() {
|
|
||||||
$('error_div').set('html', '_(qBittorrent client is not reachable)');
|
|
||||||
waitingTrInfo=false;
|
|
||||||
loadTransferInfo.delay(4000);
|
|
||||||
},
|
|
||||||
onSuccess: function(info) {
|
|
||||||
if(info) {
|
|
||||||
$("DlInfos").set('html', "_(D: %1 - T: %2)".replace("%1", friendlyUnit(info.dl_info_speed, true))
|
|
||||||
.replace("%2", friendlyUnit(info.dl_info_data, false)));
|
|
||||||
$("UpInfos").set('html', "_(U: %1 - T: %2)".replace("%1", friendlyUnit(info.up_info_speed, true))
|
|
||||||
.replace("%2", friendlyUnit(info.up_info_data, false)));
|
|
||||||
if(localStorage.getItem('speed_in_browser_title_bar') == 'true')
|
|
||||||
document.title = "_(D:%1 U:%2)".replace("%1", friendlyUnit(info.dl_info_speed, true)).replace("%2", friendlyUnit(info.up_info_speed, true));
|
|
||||||
else
|
|
||||||
document.title = "_(qBittorrent web User Interface)";
|
|
||||||
waitingTrInfo=false;
|
|
||||||
loadTransferInfo.delay(3000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$('DlInfos').addEvent('click', globalDownloadLimitFN);
|
|
||||||
$('UpInfos').addEvent('click', globalUploadLimitFN);
|
|
||||||
|
|
||||||
var ajaxfn = function(){
|
/*MochaUI.Desktop = new MochaUI.Desktop();
|
||||||
var queueing_enabled = false;
|
MochaUI.Desktop.desktop.setStyles({
|
||||||
var url = 'json/torrents';
|
'background': '#fff',
|
||||||
if (!waiting){
|
'visibility': 'visible'
|
||||||
waiting=true;
|
});*/
|
||||||
var request = new Request.JSON({
|
MochaUI.Desktop.initialize();
|
||||||
url: url,
|
|
||||||
noCache: true,
|
var filt_w = localStorage.getItem('filters_width');
|
||||||
method: 'get',
|
if ($defined(filt_w))
|
||||||
onFailure: function() {
|
filt_w = filt_w.toInt();
|
||||||
$('error_div').set('html', '_(qBittorrent client is not reachable)');
|
|
||||||
waiting=false;
|
|
||||||
ajaxfn.delay(2000);
|
|
||||||
},
|
|
||||||
onSuccess: function(events) {
|
|
||||||
$('error_div').set('html', '');
|
|
||||||
if(events){
|
|
||||||
// Add new torrents or update them
|
|
||||||
torrent_hashes = myTable.getRowIds();
|
|
||||||
events_hashes = new Array();
|
|
||||||
events.each(function(event){
|
|
||||||
events_hashes[events_hashes.length] = event.hash;
|
|
||||||
var row = new Array();
|
|
||||||
var data = new Array();
|
|
||||||
row.length = 10;
|
|
||||||
row[0] = stateToImg(event.state);
|
|
||||||
row[1] = event.name;
|
|
||||||
row[2] = event.priority > -1 ? event.priority : null;
|
|
||||||
data[2] = event.priority;
|
|
||||||
row[3] = friendlyUnit(event.size, false);
|
|
||||||
data[3] = event.size;
|
|
||||||
row[4] = (event.progress*100).round(1);
|
|
||||||
if(row[4] == 100.0 && event.progress != 1.0)
|
|
||||||
row[4] = 99.9;
|
|
||||||
data[4] = event.progress;
|
|
||||||
row[5] = event.num_seeds;
|
|
||||||
if (event.num_complete != -1)
|
|
||||||
row[5] += " (" + event.num_complete + ")";
|
|
||||||
data[5] = event.num_seeds;
|
|
||||||
row[6] = event.num_leechs;
|
|
||||||
if (event.num_incomplete != -1)
|
|
||||||
row[6] += " (" + event.num_incomplete + ")";
|
|
||||||
data[6] = event.num_leechs;
|
|
||||||
row[7] = friendlyUnit(event.dlspeed, true);
|
|
||||||
data[7] = event.dlspeed;
|
|
||||||
row[8] = friendlyUnit(event.upspeed, true);
|
|
||||||
data[8] = event.upspeed;
|
|
||||||
row[9] = friendlyDuration(event.eta);
|
|
||||||
data[9] = event.eta
|
|
||||||
if(event.ratio == -1)
|
|
||||||
row[10] = "∞";
|
|
||||||
else
|
|
||||||
row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up
|
|
||||||
data[10] = event.ratio;
|
|
||||||
if(row[2] != null)
|
|
||||||
queueing_enabled = true;
|
|
||||||
if(!torrent_hashes.contains(event.hash)) {
|
|
||||||
// New unfinished torrent
|
|
||||||
torrent_hashes[torrent_hashes.length] = event.hash;
|
|
||||||
//alert("Inserting row");
|
|
||||||
myTable.insertRow(event.hash, row, data, event.state);
|
|
||||||
} else {
|
|
||||||
// Update torrent data
|
|
||||||
myTable.updateRow(event.hash, row, data, event.state);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Remove deleted torrents
|
|
||||||
torrent_hashes.each(function(hash){
|
|
||||||
if(!events_hashes.contains(hash)) {
|
|
||||||
myTable.removeRow(hash);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(queueing_enabled) {
|
|
||||||
$('queueingButtons').removeClass('invisible');
|
|
||||||
myTable.showPriority();
|
|
||||||
} else {
|
|
||||||
$('queueingButtons').addClass('invisible');
|
|
||||||
myTable.hidePriority();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
waiting=false;
|
|
||||||
ajaxfn.delay(1500);
|
|
||||||
}
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
new MochaUI.Panel({
|
|
||||||
id: 'transferList',
|
|
||||||
title: 'Panel',
|
|
||||||
header: false,
|
|
||||||
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
||||||
loadMethod: 'xhr',
|
|
||||||
contentURL: 'transferlist.html',
|
|
||||||
onContentLoaded: function() {
|
|
||||||
ajaxfn();
|
|
||||||
},
|
|
||||||
column: 'mainColumn',
|
|
||||||
onResize: saveColumnSizes,
|
|
||||||
height: null
|
|
||||||
});
|
|
||||||
var prop_h = localStorage.getItem('properties_height');
|
|
||||||
if($defined(prop_h))
|
|
||||||
prop_h = prop_h.toInt();
|
|
||||||
else
|
else
|
||||||
prop_h = Window.getSize().y / 2.;
|
filt_w = 120;
|
||||||
|
new MochaUI.Column({
|
||||||
|
id : 'filtersColumn',
|
||||||
|
placement : 'left',
|
||||||
|
onResize : saveColumnSizes,
|
||||||
|
width : filt_w,
|
||||||
|
resizeLimit : [100, 300]
|
||||||
|
});
|
||||||
|
new MochaUI.Column({
|
||||||
|
id : 'mainColumn',
|
||||||
|
placement : 'main',
|
||||||
|
width : null,
|
||||||
|
resizeLimit : [100, 300]
|
||||||
|
});
|
||||||
|
MochaUI.Desktop.setDesktopSize();
|
||||||
new MochaUI.Panel({
|
new MochaUI.Panel({
|
||||||
id: 'propertiesPanel',
|
id : 'Filters',
|
||||||
title: 'Panel',
|
title : 'Panel',
|
||||||
header: true,
|
header : false,
|
||||||
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
padding : {
|
||||||
contentURL: 'prop-general.html',
|
top : 0,
|
||||||
require: {
|
right : 0,
|
||||||
css: ['css/Tabs.css']
|
bottom : 0,
|
||||||
},
|
left : 0
|
||||||
tabsURL: 'properties.html',
|
},
|
||||||
column: 'mainColumn',
|
loadMethod : 'xhr',
|
||||||
height: prop_h
|
contentURL : 'filters.html',
|
||||||
});
|
column : 'filtersColumn',
|
||||||
//ajaxfn();
|
height : 300
|
||||||
loadTransferInfo();
|
});
|
||||||
|
initializeWindows();
|
||||||
|
var r = 0;
|
||||||
|
var waiting = false;
|
||||||
|
var waitingTrInfo = false;
|
||||||
|
|
||||||
setFilter = function(f) {
|
var stateToImg = function (state) {
|
||||||
// Visually Select the right filter
|
if (state == "pausedUP" || state == "pausedDL") {
|
||||||
$("all_filter").removeClass("selectedFilter");
|
state = "paused";
|
||||||
$("downloading_filter").removeClass("selectedFilter");
|
} else {
|
||||||
$("completed_filter").removeClass("selectedFilter");
|
if (state == "queuedUP" || state == "queuedDL") {
|
||||||
$("paused_filter").removeClass("selectedFilter");
|
state = "queued";
|
||||||
$("active_filter").removeClass("selectedFilter");
|
} else {
|
||||||
$("inactive_filter").removeClass("selectedFilter");
|
if (state == "checkingUP" || state == "checkingDL") {
|
||||||
$(f+"_filter").addClass("selectedFilter");
|
state = "checking";
|
||||||
myTable.setFilter(f);
|
}
|
||||||
ajaxfn();
|
}
|
||||||
localStorage.setItem('selected_filter', f);
|
}
|
||||||
}
|
return 'images/skin/' + state + '.png';
|
||||||
|
};
|
||||||
|
var loadTransferInfo = function () {
|
||||||
|
var url = 'json/transferInfo';
|
||||||
|
if (!waitingTrInfo) {
|
||||||
|
waitingTrInfo = true;
|
||||||
|
var request = new Request.JSON({
|
||||||
|
url : url,
|
||||||
|
noCache : true,
|
||||||
|
method : 'get',
|
||||||
|
onFailure : function () {
|
||||||
|
$('error_div').set('html', '_(qBittorrent client is not reachable)');
|
||||||
|
waitingTrInfo = false;
|
||||||
|
loadTransferInfo.delay(4000);
|
||||||
|
},
|
||||||
|
onSuccess : function (info) {
|
||||||
|
if (info) {
|
||||||
|
$("DlInfos").set('html', "_(D: %1 - T: %2)"
|
||||||
|
.replace("%1", friendlyUnit(info.dl_info_speed, true))
|
||||||
|
.replace("%2", friendlyUnit(info.dl_info_data, false)));
|
||||||
|
$("UpInfos").set('html', "_(U: %1 - T: %2)"
|
||||||
|
.replace("%1", friendlyUnit(info.up_info_speed, true))
|
||||||
|
.replace("%2", friendlyUnit(info.up_info_data, false)));
|
||||||
|
if(localStorage.getItem('speed_in_browser_title_bar') == 'true')
|
||||||
|
document.title = "_(D:%1 U:%2)".replace("%1", friendlyUnit(info.dl_info_speed, true)).replace("%2", friendlyUnit(info.up_info_speed, true));
|
||||||
|
else
|
||||||
|
document.title = "_(qBittorrent web User Interface)";
|
||||||
|
waitingTrInfo = false;
|
||||||
|
loadTransferInfo.delay(3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$('DlInfos').addEvent('click', globalDownloadLimitFN);
|
||||||
|
$('UpInfos').addEvent('click', globalUploadLimitFN);
|
||||||
|
|
||||||
|
var ajaxfn = function () {
|
||||||
|
var queueing_enabled = false;
|
||||||
|
var url = new URI('json/torrents');
|
||||||
|
url.setData('filter', filter);
|
||||||
|
url.setData('sort', myTable.table.sortedColumn);
|
||||||
|
url.setData('reverse', myTable.table.reverseSort);
|
||||||
|
if (!waiting) {
|
||||||
|
waiting = true;
|
||||||
|
var request = new Request.JSON({
|
||||||
|
url : url,
|
||||||
|
noCache : true,
|
||||||
|
method : 'get',
|
||||||
|
onFailure : function () {
|
||||||
|
$('error_div').set('html', '_(qBittorrent client is not reachable)');
|
||||||
|
waiting = false;
|
||||||
|
ajaxfn.delay(2000);
|
||||||
|
},
|
||||||
|
onSuccess : function (events) {
|
||||||
|
$('error_div').set('html', '');
|
||||||
|
if (events) {
|
||||||
|
// Add new torrents or update them
|
||||||
|
torrent_hashes = myTable.getRowIds();
|
||||||
|
events_hashes = new Array();
|
||||||
|
pos = 0;
|
||||||
|
events.each(function (event) {
|
||||||
|
events_hashes[events_hashes.length] = event.hash;
|
||||||
|
var row = new Array();
|
||||||
|
var data = new Array();
|
||||||
|
row.length = 10;
|
||||||
|
row[0] = stateToImg(event.state);
|
||||||
|
row[1] = event.name;
|
||||||
|
row[2] = event.priority > -1 ? event.priority : null;
|
||||||
|
data[2] = event.priority;
|
||||||
|
row[3] = friendlyUnit(event.size, false);
|
||||||
|
data[3] = event.size;
|
||||||
|
row[4] = (event.progress * 100).round(1);
|
||||||
|
if (row[4] == 100.0 && event.progress != 1.0)
|
||||||
|
row[4] = 99.9;
|
||||||
|
data[4] = event.progress;
|
||||||
|
row[5] = event.num_seeds;
|
||||||
|
if (event.num_complete != -1)
|
||||||
|
row[5] += " (" + event.num_complete + ")";
|
||||||
|
data[5] = event.num_seeds;
|
||||||
|
row[6] = event.num_leechs;
|
||||||
|
if (event.num_incomplete != -1)
|
||||||
|
row[6] += " (" + event.num_incomplete + ")";
|
||||||
|
data[6] = event.num_leechs;
|
||||||
|
row[7] = friendlyUnit(event.dlspeed, true);
|
||||||
|
data[7] = event.dlspeed;
|
||||||
|
row[8] = friendlyUnit(event.upspeed, true);
|
||||||
|
data[8] = event.upspeed;
|
||||||
|
row[9] = friendlyDuration(event.eta);
|
||||||
|
data[9] = event.eta;
|
||||||
|
if (event.ratio == -1)
|
||||||
|
row[10] = "∞";
|
||||||
|
else
|
||||||
|
row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up
|
||||||
|
data[10] = event.ratio;
|
||||||
|
if (row[2] != null)
|
||||||
|
queueing_enabled = true;
|
||||||
|
if (!torrent_hashes.contains(event.hash)) {
|
||||||
|
// New unfinished torrent
|
||||||
|
torrent_hashes[torrent_hashes.length] = event.hash;
|
||||||
|
//alert("Inserting row");
|
||||||
|
myTable.insertRow(event.hash, row, data, event.state, pos);
|
||||||
|
} else {
|
||||||
|
// Update torrent data
|
||||||
|
myTable.updateRow(event.hash, row, data, event.state, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos++;
|
||||||
|
});
|
||||||
|
// Remove deleted torrents
|
||||||
|
torrent_hashes.each(function (hash) {
|
||||||
|
if (!events_hashes.contains(hash)) {
|
||||||
|
myTable.removeRow(hash);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (queueing_enabled) {
|
||||||
|
$('queueingButtons').removeClass('invisible');
|
||||||
|
myTable.showPriority();
|
||||||
|
} else {
|
||||||
|
$('queueingButtons').addClass('invisible');
|
||||||
|
myTable.hidePriority();
|
||||||
|
}
|
||||||
|
|
||||||
|
myTable.altRow();
|
||||||
|
}
|
||||||
|
waiting = false;
|
||||||
|
ajaxfn.delay(1500);
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setSortedColumn = function (column) {
|
||||||
|
myTable.setSortedColumn(column);
|
||||||
|
// reload torrents
|
||||||
|
ajaxfn();
|
||||||
|
};
|
||||||
|
|
||||||
|
new MochaUI.Panel({
|
||||||
|
id : 'transferList',
|
||||||
|
title : 'Panel',
|
||||||
|
header : false,
|
||||||
|
padding : {
|
||||||
|
top : 0,
|
||||||
|
right : 0,
|
||||||
|
bottom : 0,
|
||||||
|
left : 0
|
||||||
|
},
|
||||||
|
loadMethod : 'xhr',
|
||||||
|
contentURL : 'transferlist.html',
|
||||||
|
onContentLoaded : function () {
|
||||||
|
ajaxfn();
|
||||||
|
},
|
||||||
|
column : 'mainColumn',
|
||||||
|
onResize : saveColumnSizes,
|
||||||
|
height : null
|
||||||
|
});
|
||||||
|
var prop_h = localStorage.getItem('properties_height');
|
||||||
|
if ($defined(prop_h))
|
||||||
|
prop_h = prop_h.toInt();
|
||||||
|
else
|
||||||
|
prop_h = Window.getSize().y / 2.;
|
||||||
|
new MochaUI.Panel({
|
||||||
|
id : 'propertiesPanel',
|
||||||
|
title : 'Panel',
|
||||||
|
header : true,
|
||||||
|
padding : {
|
||||||
|
top : 0,
|
||||||
|
right : 0,
|
||||||
|
bottom : 0,
|
||||||
|
left : 0
|
||||||
|
},
|
||||||
|
contentURL : 'prop-general.html',
|
||||||
|
require : {
|
||||||
|
css : ['css/Tabs.css']
|
||||||
|
},
|
||||||
|
tabsURL : 'properties.html',
|
||||||
|
column : 'mainColumn',
|
||||||
|
height : prop_h
|
||||||
|
});
|
||||||
|
//ajaxfn();
|
||||||
|
loadTransferInfo();
|
||||||
|
|
||||||
|
setFilter = function (f) {
|
||||||
|
// Visually Select the right filter
|
||||||
|
$("all_filter").removeClass("selectedFilter");
|
||||||
|
$("downloading_filter").removeClass("selectedFilter");
|
||||||
|
$("completed_filter").removeClass("selectedFilter");
|
||||||
|
$("paused_filter").removeClass("selectedFilter");
|
||||||
|
$("active_filter").removeClass("selectedFilter");
|
||||||
|
$("inactive_filter").removeClass("selectedFilter");
|
||||||
|
$(f + "_filter").addClass("selectedFilter");
|
||||||
|
filter = f;
|
||||||
|
localStorage.setItem('selected_filter', f);
|
||||||
|
// Reload torrents
|
||||||
|
ajaxfn();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function closeWindows() {
|
function closeWindows() {
|
||||||
MochaUI.closeAll();
|
MochaUI.closeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEvent('keydown', function(event){
|
window.addEvent('keydown', function (event) {
|
||||||
if (event.key == 'a' && event.control) {
|
if (event.key == 'a' && event.control) {
|
||||||
event.stop();
|
event.stop();
|
||||||
myTable.selectAll();
|
myTable.selectAll();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* all copies or substantial portions of the Software.
|
* all copies or substantial portions of the Software.
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
@ -23,372 +23,288 @@
|
||||||
|
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
|
|
||||||
Script : Dynamic Table
|
Script : Dynamic Table
|
||||||
Version : 0.5
|
Version : 0.5
|
||||||
Authors : Ishan Arora & Christophe Dumez
|
Authors : Ishan Arora & Christophe Dumez
|
||||||
Desc : Programable sortable table
|
Desc : Programable sortable table
|
||||||
Licence : Open Source MIT Licence
|
Licence : Open Source MIT Licence
|
||||||
|
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
var dynamicTable = new Class ({
|
var dynamicTable = new Class({
|
||||||
|
|
||||||
initialize: function(){
|
initialize : function () {},
|
||||||
},
|
|
||||||
|
|
||||||
setup: function(table, progressIndex, context_menu){
|
|
||||||
this.table = $(table);
|
|
||||||
this.rows = new Hash();
|
|
||||||
this.cur = new Array();
|
|
||||||
this.priority_hidden = false;
|
|
||||||
this.progressIndex = progressIndex;
|
|
||||||
this.filter = localStorage.getItem('selected_filter');
|
|
||||||
if(!$defined(this.filter)) {
|
|
||||||
this.filter = 'all';
|
|
||||||
}
|
|
||||||
this.context_menu = context_menu;
|
|
||||||
this.table.sortedIndex = 1; // Default is NAME
|
|
||||||
this.table.reverseSort = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
sortfunction: function(tr1, tr2) {
|
|
||||||
var i = tr2.getParent().sortedIndex;
|
|
||||||
var reverseSort = tr2.getParent().reverseSort;
|
|
||||||
switch(i) {
|
|
||||||
case 1: // Name
|
|
||||||
if(!reverseSort)
|
|
||||||
return tr1.getElements('td')[i].get('html').localeCompare(tr2.getElements('td')[i].get('html'));
|
|
||||||
else
|
|
||||||
return tr2.getElements('td')[i].get('html').localeCompare(tr1.getElements('td')[i].get('html'));
|
|
||||||
case 2: // Prio
|
|
||||||
case 3: // Size
|
|
||||||
case 4: // Progress
|
|
||||||
case 5: // Seeds
|
|
||||||
case 6: // Peers
|
|
||||||
case 7: // Up Speed
|
|
||||||
case 8: // Down Speed
|
|
||||||
case 9: // ETA
|
|
||||||
default: // Ratio
|
|
||||||
if(!reverseSort)
|
|
||||||
return (tr1.getElements('td')[i].get('data-raw') - tr2.getElements('td')[i].get('data-raw'));
|
|
||||||
else
|
|
||||||
return (tr2.getElements('td')[i].get('data-raw') - tr1.getElements('td')[i].get('data-raw'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateSort: function() {
|
|
||||||
var trs = this.table.getChildren('tr');
|
|
||||||
trs.sort(this.sortfunction);
|
|
||||||
this.table.adopt(trs);
|
|
||||||
},
|
|
||||||
|
|
||||||
setSortedColumn: function(index) {
|
|
||||||
if(index != this.table.sortedIndex) {
|
|
||||||
this.table.sortedIndex = index;
|
|
||||||
this.table.reverseSort = false;
|
|
||||||
} else {
|
|
||||||
// Toggle sort order
|
|
||||||
this.table.reverseSort = !this.table.reverseSort;
|
|
||||||
}
|
|
||||||
this.updateSort();
|
|
||||||
this.altRow();
|
|
||||||
},
|
|
||||||
|
|
||||||
getCurrentTorrentHash: function() {
|
|
||||||
if(this.cur.length > 0)
|
|
||||||
return this.cur[0];
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
|
|
||||||
altRow: function()
|
setup : function (table, progressIndex, context_menu) {
|
||||||
{
|
this.table = $(table);
|
||||||
var trs = this.table.getElements('tr');
|
this.rows = new Hash();
|
||||||
trs.each(function(el,i){
|
this.cur = new Array();
|
||||||
if(i % 2){
|
this.priority_hidden = false;
|
||||||
el.addClass('alt');
|
this.progressIndex = progressIndex;
|
||||||
}else{
|
this.context_menu = context_menu;
|
||||||
el.removeClass('alt');
|
this.table.sortedColumn = 'name'; // Default is NAME
|
||||||
}
|
this.table.reverseSort = false;
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
hidePriority: function(){
|
|
||||||
if(this.priority_hidden) return;
|
|
||||||
$('prioHeader').addClass('invisible');
|
|
||||||
var trs = this.table.getElements('tr');
|
|
||||||
trs.each(function(tr,i){
|
|
||||||
var tds = tr.getElements('td');
|
|
||||||
tds[2].addClass('invisible');
|
|
||||||
}.bind(this));
|
|
||||||
this.priority_hidden = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
setFilter: function(f) {
|
|
||||||
this.filter = f;
|
|
||||||
},
|
|
||||||
|
|
||||||
showPriority: function(){
|
|
||||||
if(!this.priority_hidden) return;
|
|
||||||
$('prioHeader').removeClass('invisible');
|
|
||||||
var trs = this.table.getElements('tr');
|
|
||||||
trs.each(function(tr,i){
|
|
||||||
var tds = tr.getElements('td');
|
|
||||||
tds[2].removeClass('invisible');
|
|
||||||
}.bind(this));
|
|
||||||
this.priority_hidden = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
applyFilterOnRow: function(tr, status) {
|
|
||||||
switch(this.filter) {
|
|
||||||
case 'all':
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
break;
|
|
||||||
case 'downloading':
|
|
||||||
if(status == "downloading" || status == "stalledDL" || status == "checkingDL" || status == "pausedDL" || status == "queuedDL") {
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
} else {
|
|
||||||
tr.addClass("invisible");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'completed':
|
|
||||||
if(status == "uploading" || status == "stalledUP" || status == "checkingUP" || status == "pausedUP" || status == "queuedUP") {
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
} else {
|
|
||||||
tr.addClass("invisible");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'paused':
|
|
||||||
if(status == "pausedDL" || status == "pausedUP") {
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
} else {
|
|
||||||
tr.addClass("invisible");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'active':
|
|
||||||
if(status == "downloading" || status == "uploading") {
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
} else {
|
|
||||||
tr.addClass("invisible");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'inactive':
|
|
||||||
if(status != "downloading" && status != "uploading") {
|
|
||||||
tr.removeClass("invisible");
|
|
||||||
} else {
|
|
||||||
tr.addClass("invisible");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !tr.hasClass('invisible');
|
|
||||||
},
|
|
||||||
|
|
||||||
insertRow: function(id, row, data, status){
|
setSortedColumn : function (column) {
|
||||||
if(this.rows.has(id)) {
|
if (column != this.table.sortedColumn) {
|
||||||
return;
|
this.table.sortedColumn = column;
|
||||||
}
|
this.table.reverseSort = false;
|
||||||
var tr = new Element('tr');
|
} else {
|
||||||
tr.addClass("menu-target");
|
// Toggle sort order
|
||||||
this.rows.set(id, tr);
|
this.table.reverseSort = !this.table.reverseSort;
|
||||||
for(var i=0; i<row.length; i++)
|
}
|
||||||
{
|
},
|
||||||
var td = new Element('td');
|
|
||||||
if(i==this.progressIndex) {
|
|
||||||
td.adopt(new ProgressBar(row[i].toFloat(), {'id': 'pb_'+id, 'width':80}));
|
|
||||||
if (typeof data[i] != 'undefined')
|
|
||||||
td.set('data-raw', data[i])
|
|
||||||
} else {
|
|
||||||
if(i==0) {
|
|
||||||
td.adopt(new Element('img', {'src':row[i], 'class': 'statusIcon'}));
|
|
||||||
} else {
|
|
||||||
if(i==2) {
|
|
||||||
// Priority
|
|
||||||
if(this.priority_hidden)
|
|
||||||
td.addClass('invisible');
|
|
||||||
}
|
|
||||||
td.set('html', row[i]);
|
|
||||||
if (typeof data[i] != 'undefined')
|
|
||||||
td.set('data-raw', data[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
td.injectInside(tr);
|
|
||||||
};
|
|
||||||
|
|
||||||
tr.addEvent('mouseover', function(e){
|
getCurrentTorrentHash : function () {
|
||||||
tr.addClass('over');
|
if (this.cur.length > 0)
|
||||||
}.bind(this));
|
return this.cur[0];
|
||||||
tr.addEvent('mouseout', function(e){
|
return '';
|
||||||
tr.removeClass('over');
|
},
|
||||||
}.bind(this));
|
|
||||||
tr.addEvent('contextmenu', function(e) {
|
|
||||||
if(!this.cur.contains(id)) {
|
|
||||||
// Remove selected style from previous ones
|
|
||||||
for(i=0; i<this.cur.length; i++) {
|
|
||||||
if(this.rows.has(this.cur[i])) {
|
|
||||||
var temptr = this.rows.get(this.cur[i]);
|
|
||||||
temptr.removeClass('selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.cur.empty();
|
|
||||||
this.cur[this.cur.length] = id;
|
|
||||||
temptr = this.rows.get(id);
|
|
||||||
temptr.addClass("selected");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}.bind(this));
|
|
||||||
tr.addEvent('click', function(e){
|
|
||||||
e.stop();
|
|
||||||
if(e.control) {
|
|
||||||
// CTRL key was pressed
|
|
||||||
if(this.cur.contains(id)) {
|
|
||||||
// remove it
|
|
||||||
this.cur.erase(id);
|
|
||||||
// Remove selected style
|
|
||||||
if(this.rows.has(id)) {
|
|
||||||
temptr = this.rows.get(id);
|
|
||||||
temptr.removeClass('selected');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.cur[this.cur.length] = id;
|
|
||||||
// Add selected style
|
|
||||||
if(this.rows.has(id)) {
|
|
||||||
temptr = this.rows.get(id);
|
|
||||||
temptr.addClass('selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(e.shift && this.cur.length == 1) {
|
|
||||||
// Shift key was pressed
|
|
||||||
var first_id = this.cur[0];
|
|
||||||
var first_tr = this.rows.get(first_id);
|
|
||||||
var last_id = id;
|
|
||||||
var last_tr = this.rows.get(last_id);
|
|
||||||
var all_trs = this.table.getChildren('tr');
|
|
||||||
var index_first_tr = all_trs.indexOf(first_tr);
|
|
||||||
var index_last_tr = all_trs.indexOf(last_tr);
|
|
||||||
var trs_to_select = all_trs.filter(function(item, index){
|
|
||||||
if(index_first_tr < index_last_tr)
|
|
||||||
return (index > index_first_tr) && (index <= index_last_tr);
|
|
||||||
else
|
|
||||||
return (index < index_first_tr) && (index >= index_last_tr);
|
|
||||||
});
|
|
||||||
trs_to_select.each(function(item, index){
|
|
||||||
// Add to selection
|
|
||||||
this.cur[this.cur.length] = this.getRowId(item);
|
|
||||||
// Select it visually
|
|
||||||
item.addClass('selected');
|
|
||||||
}.bind(this));
|
|
||||||
} else {
|
|
||||||
// Simple selection
|
|
||||||
// Remove selected style from previous ones
|
|
||||||
for(i=0; i<this.cur.length; i++) {
|
|
||||||
if(this.rows.has(this.cur[i])) {
|
|
||||||
var temptr = this.rows.get(this.cur[i]);
|
|
||||||
temptr.removeClass('selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.cur.empty();
|
|
||||||
// Add selected style to new one
|
|
||||||
if(this.rows.has(id)) {
|
|
||||||
temptr = this.rows.get(id);
|
|
||||||
temptr.addClass('selected');
|
|
||||||
}
|
|
||||||
this.cur[0] = id;
|
|
||||||
// TODO: Warn Properties panel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}.bind(this));
|
|
||||||
// Apply filter
|
|
||||||
this.applyFilterOnRow(tr, status);
|
|
||||||
// Insert
|
|
||||||
var trs = this.table.getChildren('tr');
|
|
||||||
var i=0;
|
|
||||||
while(i<trs.length && this.sortfunction(tr, trs[i]) > 0) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if(i==trs.length) {
|
|
||||||
tr.inject(this.table);
|
|
||||||
} else {
|
|
||||||
tr.inject(trs[i], 'before');
|
|
||||||
}
|
|
||||||
//tr.injectInside(this.table);
|
|
||||||
this.altRow();
|
|
||||||
// Update context menu
|
|
||||||
this.context_menu.addTarget(tr);
|
|
||||||
},
|
|
||||||
|
|
||||||
selectAll: function() {
|
|
||||||
this.cur.empty();
|
|
||||||
this.rows.each(function(tr, id){
|
|
||||||
this.cur[this.cur.length] = id;
|
|
||||||
if(!tr.hasClass('selected')) {
|
|
||||||
tr.addClass('selected');
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateRow: function(id, row, data, status){
|
altRow : function () {
|
||||||
if(!this.rows.has(id)) {
|
var trs = this.table.getElements('tr');
|
||||||
return false;
|
trs.each(function (el, i) {
|
||||||
}
|
if (i % 2) {
|
||||||
var tr = this.rows.get(id);
|
el.addClass('alt');
|
||||||
// Apply filter
|
} else {
|
||||||
if(this.applyFilterOnRow(tr, status)) {
|
el.removeClass('alt');
|
||||||
var tds = tr.getElements('td');
|
}
|
||||||
for(var i=0; i<row.length; i++) {
|
}.bind(this));
|
||||||
if(i==1) continue; // Do not refresh name
|
},
|
||||||
if(i==this.progressIndex) {
|
|
||||||
$('pb_'+id).setValue(row[i]);
|
|
||||||
} else {
|
|
||||||
if(i==0) {
|
|
||||||
tds[i].getChildren('img')[0].set('src', row[i]);
|
|
||||||
} else {
|
|
||||||
tds[i].set('html', row[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof data[i] != 'undefined')
|
|
||||||
tds[i].set('data-raw', data[i])
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Row was hidden, check if it was selected
|
|
||||||
// and unselect it if it was
|
|
||||||
if(this.cur.contains(id)) {
|
|
||||||
// Remove from selection
|
|
||||||
this.cur.erase(id);
|
|
||||||
// Remove selected style
|
|
||||||
tr.removeClass('selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeRow: function(id){
|
hidePriority : function () {
|
||||||
if(this.cur.contains(id))
|
if (this.priority_hidden)
|
||||||
{
|
return;
|
||||||
this.cur.erase(id);
|
$('prioHeader').addClass('invisible');
|
||||||
}
|
var trs = this.table.getElements('tr');
|
||||||
if(this.rows.has(id)) {
|
trs.each(function (tr, i) {
|
||||||
var tr = this.rows.get(id);
|
var tds = tr.getElements('td');
|
||||||
tr.dispose();
|
tds[2].addClass('invisible');
|
||||||
this.altRow();
|
}.bind(this));
|
||||||
this.rows.erase(id);
|
this.priority_hidden = true;
|
||||||
return true;
|
},
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
selectedIds: function(){
|
showPriority : function () {
|
||||||
return this.cur;
|
if (!this.priority_hidden)
|
||||||
},
|
return;
|
||||||
|
$('prioHeader').removeClass('invisible');
|
||||||
getRowId: function(tr){
|
var trs = this.table.getElements('tr');
|
||||||
return this.rows.keyOf(tr);
|
trs.each(function (tr, i) {
|
||||||
},
|
var tds = tr.getElements('td');
|
||||||
|
tds[2].removeClass('invisible');
|
||||||
getRowIds: function(){
|
}.bind(this));
|
||||||
return this.rows.getKeys();
|
this.priority_hidden = false;
|
||||||
}
|
},
|
||||||
|
|
||||||
});
|
insertRow : function (id, row, data, status, pos) {
|
||||||
|
if (this.rows.has(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tr = new Element('tr');
|
||||||
|
tr.addClass("menu-target");
|
||||||
|
this.rows.set(id, tr);
|
||||||
|
for (var i = 0; i < row.length; i++) {
|
||||||
|
var td = new Element('td');
|
||||||
|
if (i == this.progressIndex) {
|
||||||
|
td.adopt(new ProgressBar(row[i].toFloat(), {
|
||||||
|
'id' : 'pb_' + id,
|
||||||
|
'width' : 80
|
||||||
|
}));
|
||||||
|
if (typeof data[i] != 'undefined')
|
||||||
|
td.set('data-raw', data[i])
|
||||||
|
} else {
|
||||||
|
if (i == 0) {
|
||||||
|
td.adopt(new Element('img', {
|
||||||
|
'src' : row[i],
|
||||||
|
'class' : 'statusIcon'
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
if (i == 2) {
|
||||||
|
// Priority
|
||||||
|
if (this.priority_hidden)
|
||||||
|
td.addClass('invisible');
|
||||||
|
}
|
||||||
|
td.set('html', row[i]);
|
||||||
|
if (typeof data[i] != 'undefined')
|
||||||
|
td.set('data-raw', data[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td.injectInside(tr);
|
||||||
|
};
|
||||||
|
|
||||||
|
tr.addEvent('mouseover', function (e) {
|
||||||
|
tr.addClass('over');
|
||||||
|
}.bind(this));
|
||||||
|
tr.addEvent('mouseout', function (e) {
|
||||||
|
tr.removeClass('over');
|
||||||
|
}.bind(this));
|
||||||
|
tr.addEvent('contextmenu', function (e) {
|
||||||
|
if (!this.cur.contains(id)) {
|
||||||
|
// Remove selected style from previous ones
|
||||||
|
for (i = 0; i < this.cur.length; i++) {
|
||||||
|
if (this.rows.has(this.cur[i])) {
|
||||||
|
var temptr = this.rows.get(this.cur[i]);
|
||||||
|
temptr.removeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cur.empty();
|
||||||
|
this.cur[this.cur.length] = id;
|
||||||
|
temptr = this.rows.get(id);
|
||||||
|
temptr.addClass("selected");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}.bind(this));
|
||||||
|
tr.addEvent('click', function (e) {
|
||||||
|
e.stop();
|
||||||
|
if (e.control) {
|
||||||
|
// CTRL key was pressed
|
||||||
|
if (this.cur.contains(id)) {
|
||||||
|
// remove it
|
||||||
|
this.cur.erase(id);
|
||||||
|
// Remove selected style
|
||||||
|
if (this.rows.has(id)) {
|
||||||
|
temptr = this.rows.get(id);
|
||||||
|
temptr.removeClass('selected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.cur[this.cur.length] = id;
|
||||||
|
// Add selected style
|
||||||
|
if (this.rows.has(id)) {
|
||||||
|
temptr = this.rows.get(id);
|
||||||
|
temptr.addClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (e.shift && this.cur.length == 1) {
|
||||||
|
// Shift key was pressed
|
||||||
|
var first_id = this.cur[0];
|
||||||
|
var first_tr = this.rows.get(first_id);
|
||||||
|
var last_id = id;
|
||||||
|
var last_tr = this.rows.get(last_id);
|
||||||
|
var all_trs = this.table.getChildren('tr');
|
||||||
|
var index_first_tr = all_trs.indexOf(first_tr);
|
||||||
|
var index_last_tr = all_trs.indexOf(last_tr);
|
||||||
|
var trs_to_select = all_trs.filter(function (item, index) {
|
||||||
|
if (index_first_tr < index_last_tr)
|
||||||
|
return (index > index_first_tr) && (index <= index_last_tr);
|
||||||
|
else
|
||||||
|
return (index < index_first_tr) && (index >= index_last_tr);
|
||||||
|
});
|
||||||
|
trs_to_select.each(function (item, index) {
|
||||||
|
// Add to selection
|
||||||
|
this.cur[this.cur.length] = this.getRowId(item);
|
||||||
|
// Select it visually
|
||||||
|
item.addClass('selected');
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
// Simple selection
|
||||||
|
// Remove selected style from previous ones
|
||||||
|
for (i = 0; i < this.cur.length; i++) {
|
||||||
|
if (this.rows.has(this.cur[i])) {
|
||||||
|
var temptr = this.rows.get(this.cur[i]);
|
||||||
|
temptr.removeClass('selected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cur.empty();
|
||||||
|
// Add selected style to new one
|
||||||
|
if (this.rows.has(id)) {
|
||||||
|
temptr = this.rows.get(id);
|
||||||
|
temptr.addClass('selected');
|
||||||
|
}
|
||||||
|
this.cur[0] = id;
|
||||||
|
// TODO: Warn Properties panel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
var trs = this.table.getChildren('tr');
|
||||||
|
if (pos >= trs.length) {
|
||||||
|
tr.inject(this.table);
|
||||||
|
} else {
|
||||||
|
tr.inject(trs[pos], 'before');
|
||||||
|
}
|
||||||
|
//tr.injectInside(this.table);
|
||||||
|
// Update context menu
|
||||||
|
this.context_menu.addTarget(tr);
|
||||||
|
},
|
||||||
|
|
||||||
|
selectAll : function () {
|
||||||
|
this.cur.empty();
|
||||||
|
this.rows.each(function (tr, id) {
|
||||||
|
this.cur[this.cur.length] = id;
|
||||||
|
if (!tr.hasClass('selected')) {
|
||||||
|
tr.addClass('selected');
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRow : function (id, row, data, status, newpos) {
|
||||||
|
if (!this.rows.has(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tr = this.rows.get(id);
|
||||||
|
var tds = tr.getElements('td');
|
||||||
|
for (var i = 0; i < row.length; i++) {
|
||||||
|
if (i == 1)
|
||||||
|
continue; // Do not refresh name
|
||||||
|
if (i == this.progressIndex) {
|
||||||
|
$('pb_' + id).setValue(row[i]);
|
||||||
|
} else {
|
||||||
|
if (i == 0) {
|
||||||
|
tds[i].getChildren('img')[0].set('src', row[i]);
|
||||||
|
} else {
|
||||||
|
tds[i].set('html', row[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof data[i] != 'undefined')
|
||||||
|
tds[i].set('data-raw', data[i])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prevent freezing of the backlight.
|
||||||
|
tr.removeClass('over');
|
||||||
|
|
||||||
|
// Move to 'newpos'
|
||||||
|
var trs = this.table.getChildren('tr');
|
||||||
|
if (newpos >= trs.length) {
|
||||||
|
tr.inject(this.table);
|
||||||
|
} else {
|
||||||
|
tr.inject(trs[newpos], 'before');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeRow : function (id) {
|
||||||
|
if (this.cur.contains(id)) {
|
||||||
|
this.cur.erase(id);
|
||||||
|
}
|
||||||
|
if (this.rows.has(id)) {
|
||||||
|
var tr = this.rows.get(id);
|
||||||
|
tr.dispose();
|
||||||
|
this.altRow();
|
||||||
|
this.rows.erase(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedIds : function () {
|
||||||
|
return this.cur;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowId : function (tr) {
|
||||||
|
return this.rows.keyOf(tr);
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowIds : function () {
|
||||||
|
return this.rows.getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
//dynamicTable.implement(new Options);
|
//dynamicTable.implement(new Options);
|
||||||
//dynamicTable.implement(new Events);
|
//dynamicTable.implement(new Events);
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 0"></th>
|
<th style="width: 0"></th>
|
||||||
<th onClick="setSortedColumn(1);" style="cursor: pointer;">_(Name)</th>
|
<th onClick="setSortedColumn('name');" style="cursor: pointer;">_(Name)</th>
|
||||||
<th id='prioHeader' onClick="setSortedColumn(2);" style="cursor: pointer;">#</th>
|
<th id='prioHeader' onClick="setSortedColumn('priority');" style="cursor: pointer;">#</th>
|
||||||
<th onClick="setSortedColumn(3);" style="cursor: pointer;">_(Size)</th>
|
<th onClick="setSortedColumn('size');" style="cursor: pointer;">_(Size)</th>
|
||||||
<th style="width: 90px;cursor: pointer;" onClick="setSortedColumn(4);">_(Done)</th>
|
<th style="width: 90px;cursor: pointer;" onClick="setSortedColumn('progress');">_(Done)</th>
|
||||||
<th onClick="setSortedColumn(5);" style="cursor: pointer;">_(Seeds)</th>
|
<th onClick="setSortedColumn('num_seeds');" style="cursor: pointer;">_(Seeds)</th>
|
||||||
<th onClick="setSortedColumn(6);" style="cursor: pointer;">_(Peers)</th>
|
<th onClick="setSortedColumn('num_leechs');" style="cursor: pointer;">_(Peers)</th>
|
||||||
<th onClick="setSortedColumn(7);" style="cursor: pointer;">_(Down Speed)</th>
|
<th onClick="setSortedColumn('dlspeed');" style="cursor: pointer;">_(Down Speed)</th>
|
||||||
<th onClick="setSortedColumn(8);" style="cursor: pointer;">_(Up Speed)</th>
|
<th onClick="setSortedColumn('upspeed');" style="cursor: pointer;">_(Up Speed)</th>
|
||||||
<th onClick="setSortedColumn(9);" style="cursor: pointer;">_(ETA)</th>
|
<th onClick="setSortedColumn('eta');" style="cursor: pointer;">_(ETA)</th>
|
||||||
<th onClick="setSortedColumn(10);" style="cursor: pointer;">_(Ratio)</th>
|
<th onClick="setSortedColumn('ratio');" style="cursor: pointer;">_(Ratio)</th>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -20,48 +20,50 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
//create a context menu
|
//create a context menu
|
||||||
var context_menu = new ContextMenu({
|
var context_menu = new ContextMenu({
|
||||||
targets: '.menu-target',
|
targets : '.menu-target',
|
||||||
menu: 'contextmenu',
|
menu : 'contextmenu',
|
||||||
actions: {
|
actions : {
|
||||||
Delete: function(element,ref) {
|
Delete : function (element, ref) {
|
||||||
deleteFN();
|
deleteFN();
|
||||||
},
|
},
|
||||||
DeleteHD: function(element,ref) {
|
DeleteHD : function (element, ref) {
|
||||||
deleteHDFN();
|
deleteHDFN();
|
||||||
},
|
},
|
||||||
Start: function(element, ref) {
|
Start : function (element, ref) {
|
||||||
startFN();
|
startFN();
|
||||||
},
|
},
|
||||||
Pause: function(element, ref) {
|
Pause : function (element, ref) {
|
||||||
pauseFN();
|
pauseFN();
|
||||||
},
|
},
|
||||||
prioTop: function(element, ref) {
|
prioTop : function (element, ref) {
|
||||||
setPriorityFN('topPrio');
|
setPriorityFN('topPrio');
|
||||||
},
|
},
|
||||||
prioUp: function(element, ref) {
|
prioUp : function (element, ref) {
|
||||||
setPriorityFN('increasePrio');
|
setPriorityFN('increasePrio');
|
||||||
},
|
},
|
||||||
prioDown: function(element, ref) {
|
prioDown : function (element, ref) {
|
||||||
setPriorityFN('decreasePrio');
|
setPriorityFN('decreasePrio');
|
||||||
},
|
},
|
||||||
prioBottom: function(element, ref) {
|
prioBottom : function (element, ref) {
|
||||||
setPriorityFN('bottomPrio');
|
setPriorityFN('bottomPrio');
|
||||||
},
|
},
|
||||||
ForceRecheck: function(element, ref) {
|
ForceRecheck : function (element, ref) {
|
||||||
recheckFN();
|
recheckFN();
|
||||||
},
|
},
|
||||||
UploadLimit: function(element, red) {
|
UploadLimit : function (element, red) {
|
||||||
uploadLimitFN();
|
uploadLimitFN();
|
||||||
},
|
},
|
||||||
DownloadLimit: function(element, red) {
|
DownloadLimit : function (element, red) {
|
||||||
downloadLimitFN();
|
downloadLimitFN();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
offsets: { x:-15, y:2 }
|
offsets : {
|
||||||
});
|
x : -15,
|
||||||
|
y : 2
|
||||||
myTable.setup('myTable', 4, context_menu);
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
myTable.setup('myTable', 4, context_menu);
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue