mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-23 21:14:33 +00:00
Merge pull request #9375 from Piccirello/webui-trackers
Add additional tracker info to WebUI
This commit is contained in:
commit
1c525d9c19
@ -44,10 +44,10 @@ namespace BitTorrent
|
|||||||
public:
|
public:
|
||||||
enum Status
|
enum Status
|
||||||
{
|
{
|
||||||
NotContacted,
|
NotContacted = 1,
|
||||||
Working,
|
Working = 2,
|
||||||
Updating,
|
Updating = 3,
|
||||||
NotWorking
|
NotWorking = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
TrackerEntry(const QString &url);
|
TrackerEntry(const QString &url);
|
||||||
|
@ -97,23 +97,23 @@ TrackerListWidget::TrackerListWidget(PropertiesWidget *properties)
|
|||||||
insertTopLevelItem(2, m_LSDItem);
|
insertTopLevelItem(2, m_LSDItem);
|
||||||
setRowColor(2, QColor("grey"));
|
setRowColor(2, QColor("grey"));
|
||||||
// Set static items alignment
|
// Set static items alignment
|
||||||
m_DHTItem->setTextAlignment(COL_RECEIVED, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_PEXItem->setTextAlignment(COL_RECEIVED, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_LSDItem->setTextAlignment(COL_RECEIVED, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_DHTItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_PEXItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_LSDItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
m_DHTItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
m_DHTItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
m_PEXItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
m_PEXItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
m_LSDItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
m_LSDItem->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_DHTItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_PEXItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_LSDItem->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_DHTItem->setTextAlignment(COL_LEECHES, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_PEXItem->setTextAlignment(COL_LEECHES, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
m_LSDItem->setTextAlignment(COL_LEECHES, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
m_DHTItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
m_DHTItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
m_PEXItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
m_PEXItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
m_LSDItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
m_LSDItem->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
// Set header alignment
|
// Set header alignment
|
||||||
headerItem()->setTextAlignment(COL_TIER, (Qt::AlignRight | Qt::AlignVCenter));
|
headerItem()->setTextAlignment(COL_TIER, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
headerItem()->setTextAlignment(COL_RECEIVED, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
headerItem()->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
headerItem()->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
headerItem()->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
headerItem()->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
headerItem()->setTextAlignment(COL_LEECHES, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
headerItem()->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
headerItem()->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
// Set hotkeys
|
// Set hotkeys
|
||||||
m_editHotkey = new QShortcut(Qt::Key_F2, this, nullptr, nullptr, Qt::WidgetShortcut);
|
m_editHotkey = new QShortcut(Qt::Key_F2, this, nullptr, nullptr, Qt::WidgetShortcut);
|
||||||
@ -245,15 +245,15 @@ void TrackerListWidget::clear()
|
|||||||
m_trackerItems.clear();
|
m_trackerItems.clear();
|
||||||
m_DHTItem->setText(COL_STATUS, "");
|
m_DHTItem->setText(COL_STATUS, "");
|
||||||
m_DHTItem->setText(COL_SEEDS, "");
|
m_DHTItem->setText(COL_SEEDS, "");
|
||||||
m_DHTItem->setText(COL_PEERS, "");
|
m_DHTItem->setText(COL_LEECHES, "");
|
||||||
m_DHTItem->setText(COL_MSG, "");
|
m_DHTItem->setText(COL_MSG, "");
|
||||||
m_PEXItem->setText(COL_STATUS, "");
|
m_PEXItem->setText(COL_STATUS, "");
|
||||||
m_PEXItem->setText(COL_SEEDS, "");
|
m_PEXItem->setText(COL_SEEDS, "");
|
||||||
m_PEXItem->setText(COL_PEERS, "");
|
m_PEXItem->setText(COL_LEECHES, "");
|
||||||
m_PEXItem->setText(COL_MSG, "");
|
m_PEXItem->setText(COL_MSG, "");
|
||||||
m_LSDItem->setText(COL_STATUS, "");
|
m_LSDItem->setText(COL_STATUS, "");
|
||||||
m_LSDItem->setText(COL_SEEDS, "");
|
m_LSDItem->setText(COL_SEEDS, "");
|
||||||
m_LSDItem->setText(COL_PEERS, "");
|
m_LSDItem->setText(COL_LEECHES, "");
|
||||||
m_LSDItem->setText(COL_MSG, "");
|
m_LSDItem->setText(COL_MSG, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,11 +314,11 @@ void TrackerListWidget::loadStickyItems(BitTorrent::TorrentHandle *const torrent
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_DHTItem->setText(COL_SEEDS, QString::number(seedsDHT));
|
m_DHTItem->setText(COL_SEEDS, QString::number(seedsDHT));
|
||||||
m_DHTItem->setText(COL_PEERS, QString::number(peersDHT));
|
m_DHTItem->setText(COL_LEECHES, QString::number(peersDHT));
|
||||||
m_PEXItem->setText(COL_SEEDS, QString::number(seedsPeX));
|
m_PEXItem->setText(COL_SEEDS, QString::number(seedsPeX));
|
||||||
m_PEXItem->setText(COL_PEERS, QString::number(peersPeX));
|
m_PEXItem->setText(COL_LEECHES, QString::number(peersPeX));
|
||||||
m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
|
m_LSDItem->setText(COL_SEEDS, QString::number(seedsLSD));
|
||||||
m_LSDItem->setText(COL_PEERS, QString::number(peersLSD));
|
m_LSDItem->setText(COL_LEECHES, QString::number(peersLSD));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackerListWidget::loadTrackers()
|
void TrackerListWidget::loadTrackers()
|
||||||
@ -366,21 +366,15 @@ void TrackerListWidget::loadTrackers()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
item->setText(COL_RECEIVED, QString::number(data.numPeers));
|
item->setText(COL_PEERS, QString::number(data.numPeers));
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
|
||||||
item->setText(COL_SEEDS, (entry.nativeEntry().scrape_complete > -1) ? QString::number(entry.nativeEntry().scrape_complete) : tr("N/A"));
|
item->setText(COL_SEEDS, (entry.nativeEntry().scrape_complete > -1) ? QString::number(entry.nativeEntry().scrape_complete) : tr("N/A"));
|
||||||
item->setText(COL_PEERS, (entry.nativeEntry().scrape_incomplete > -1) ? QString::number(entry.nativeEntry().scrape_incomplete) : tr("N/A"));
|
item->setText(COL_LEECHES, (entry.nativeEntry().scrape_incomplete > -1) ? QString::number(entry.nativeEntry().scrape_incomplete) : tr("N/A"));
|
||||||
item->setText(COL_DOWNLOADED, (entry.nativeEntry().scrape_downloaded > -1) ? QString::number(entry.nativeEntry().scrape_downloaded) : tr("N/A"));
|
item->setText(COL_DOWNLOADED, (entry.nativeEntry().scrape_downloaded > -1) ? QString::number(entry.nativeEntry().scrape_downloaded) : tr("N/A"));
|
||||||
#else
|
|
||||||
item->setText(COL_SEEDS, tr("N/A"));
|
|
||||||
item->setText(COL_PEERS, tr("N/A"));
|
|
||||||
item->setText(COL_DOWNLOADED, tr("N/A"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
item->setTextAlignment(COL_TIER, (Qt::AlignRight | Qt::AlignVCenter));
|
item->setTextAlignment(COL_TIER, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
item->setTextAlignment(COL_RECEIVED, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
item->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
|
||||||
item->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
item->setTextAlignment(COL_PEERS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
item->setTextAlignment(COL_SEEDS, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
|
item->setTextAlignment(COL_LEECHES, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
item->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
item->setTextAlignment(COL_DOWNLOADED, (Qt::AlignRight | Qt::AlignVCenter));
|
||||||
}
|
}
|
||||||
// Remove old trackers
|
// Remove old trackers
|
||||||
@ -534,9 +528,9 @@ void TrackerListWidget::showTrackerListMenu(QPoint)
|
|||||||
QAction *delAct = nullptr;
|
QAction *delAct = nullptr;
|
||||||
QAction *editAct = nullptr;
|
QAction *editAct = nullptr;
|
||||||
if (!getSelectedTrackerItems().isEmpty()) {
|
if (!getSelectedTrackerItems().isEmpty()) {
|
||||||
|
editAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"),tr("Edit tracker URL..."));
|
||||||
delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
|
delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove tracker"));
|
||||||
copyAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker URL"));
|
copyAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker URL"));
|
||||||
editAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL"));
|
|
||||||
}
|
}
|
||||||
QAction *reannounceSelAct = nullptr;
|
QAction *reannounceSelAct = nullptr;
|
||||||
QAction *reannounceAllAct = nullptr;
|
QAction *reannounceAllAct = nullptr;
|
||||||
@ -592,9 +586,9 @@ QStringList TrackerListWidget::headerLabels()
|
|||||||
"#"
|
"#"
|
||||||
, tr("URL")
|
, tr("URL")
|
||||||
, tr("Status")
|
, tr("Status")
|
||||||
, tr("Received")
|
|
||||||
, tr("Seeds")
|
|
||||||
, tr("Peers")
|
, tr("Peers")
|
||||||
|
, tr("Seeds")
|
||||||
|
, tr("Leeches")
|
||||||
, tr("Downloaded")
|
, tr("Downloaded")
|
||||||
, tr("Message")
|
, tr("Message")
|
||||||
};
|
};
|
||||||
|
@ -54,9 +54,9 @@ public:
|
|||||||
COL_TIER,
|
COL_TIER,
|
||||||
COL_URL,
|
COL_URL,
|
||||||
COL_STATUS,
|
COL_STATUS,
|
||||||
COL_RECEIVED,
|
|
||||||
COL_SEEDS,
|
|
||||||
COL_PEERS,
|
COL_PEERS,
|
||||||
|
COL_SEEDS,
|
||||||
|
COL_LEECHES,
|
||||||
COL_DOWNLOADED,
|
COL_DOWNLOADED,
|
||||||
COL_MSG,
|
COL_MSG,
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/bittorrent/filepriority.h"
|
#include "base/bittorrent/filepriority.h"
|
||||||
|
#include "base/bittorrent/peerinfo.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrenthandle.h"
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
#include "base/bittorrent/torrentinfo.h"
|
#include "base/bittorrent/torrentinfo.h"
|
||||||
@ -56,8 +57,12 @@
|
|||||||
// Tracker keys
|
// Tracker keys
|
||||||
const char KEY_TRACKER_URL[] = "url";
|
const char KEY_TRACKER_URL[] = "url";
|
||||||
const char KEY_TRACKER_STATUS[] = "status";
|
const char KEY_TRACKER_STATUS[] = "status";
|
||||||
|
const char KEY_TRACKER_TIER[] = "tier";
|
||||||
const char KEY_TRACKER_MSG[] = "msg";
|
const char KEY_TRACKER_MSG[] = "msg";
|
||||||
const char KEY_TRACKER_PEERS[] = "num_peers";
|
const char KEY_TRACKER_PEERS_COUNT[] = "num_peers";
|
||||||
|
const char KEY_TRACKER_SEEDS_COUNT[] = "num_seeds";
|
||||||
|
const char KEY_TRACKER_LEECHES_COUNT[] = "num_leeches";
|
||||||
|
const char KEY_TRACKER_DOWNLOADED_COUNT[] = "num_downloaded";
|
||||||
|
|
||||||
// Web seed keys
|
// Web seed keys
|
||||||
const char KEY_WEBSEED_URL[] = "url";
|
const char KEY_WEBSEED_URL[] = "url";
|
||||||
@ -114,17 +119,83 @@ namespace
|
|||||||
void applyToTorrents(const QStringList &hashes, const std::function<void (BitTorrent::TorrentHandle *torrent)> &func)
|
void applyToTorrents(const QStringList &hashes, const std::function<void (BitTorrent::TorrentHandle *torrent)> &func)
|
||||||
{
|
{
|
||||||
if ((hashes.size() == 1) && (hashes[0] == QLatin1String("all"))) {
|
if ((hashes.size() == 1) && (hashes[0] == QLatin1String("all"))) {
|
||||||
for (BitTorrent::TorrentHandle *torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
for (BitTorrent::TorrentHandle *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
||||||
func(torrent);
|
func(torrent);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (const QString &hash : hashes) {
|
for (const QString &hash : hashes) {
|
||||||
BitTorrent::TorrentHandle *torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
if (torrent)
|
if (torrent)
|
||||||
func(torrent);
|
func(torrent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantList getStickyTrackers(const BitTorrent::TorrentHandle *const torrent)
|
||||||
|
{
|
||||||
|
uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0;
|
||||||
|
for (const BitTorrent::PeerInfo &peer : torrent->peers()) {
|
||||||
|
if (peer.isConnecting()) continue;
|
||||||
|
|
||||||
|
if (peer.isSeed()) {
|
||||||
|
if (peer.fromDHT())
|
||||||
|
++seedsDHT;
|
||||||
|
if (peer.fromPeX())
|
||||||
|
++seedsPeX;
|
||||||
|
if (peer.fromLSD())
|
||||||
|
++seedsLSD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (peer.fromDHT())
|
||||||
|
++leechesDHT;
|
||||||
|
if (peer.fromPeX())
|
||||||
|
++leechesPeX;
|
||||||
|
if (peer.fromLSD())
|
||||||
|
++leechesLSD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const int working = static_cast<int>(BitTorrent::TrackerEntry::Working);
|
||||||
|
const int disabled = 0;
|
||||||
|
|
||||||
|
const QString privateMsg {QCoreApplication::translate("TrackerListWidget", "This torrent is private")};
|
||||||
|
const bool isTorrentPrivate = torrent->isPrivate();
|
||||||
|
|
||||||
|
const QVariantMap dht {
|
||||||
|
{KEY_TRACKER_URL, "** [DHT] **"},
|
||||||
|
{KEY_TRACKER_TIER, ""},
|
||||||
|
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
|
||||||
|
{KEY_TRACKER_STATUS, ((BitTorrent::Session::instance()->isDHTEnabled() && !isTorrentPrivate) ? working : disabled)},
|
||||||
|
{KEY_TRACKER_PEERS_COUNT, 0},
|
||||||
|
{KEY_TRACKER_DOWNLOADED_COUNT, 0},
|
||||||
|
{KEY_TRACKER_SEEDS_COUNT, seedsDHT},
|
||||||
|
{KEY_TRACKER_LEECHES_COUNT, leechesDHT}
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariantMap pex {
|
||||||
|
{KEY_TRACKER_URL, "** [PeX] **"},
|
||||||
|
{KEY_TRACKER_TIER, ""},
|
||||||
|
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
|
||||||
|
{KEY_TRACKER_STATUS, ((BitTorrent::Session::instance()->isPeXEnabled() && !isTorrentPrivate) ? working : disabled)},
|
||||||
|
{KEY_TRACKER_PEERS_COUNT, 0},
|
||||||
|
{KEY_TRACKER_DOWNLOADED_COUNT, 0},
|
||||||
|
{KEY_TRACKER_SEEDS_COUNT, seedsPeX},
|
||||||
|
{KEY_TRACKER_LEECHES_COUNT, leechesPeX}
|
||||||
|
};
|
||||||
|
|
||||||
|
const QVariantMap lsd {
|
||||||
|
{KEY_TRACKER_URL, "** [LSD] **"},
|
||||||
|
{KEY_TRACKER_TIER, ""},
|
||||||
|
{KEY_TRACKER_MSG, (isTorrentPrivate ? privateMsg : "")},
|
||||||
|
{KEY_TRACKER_STATUS, ((BitTorrent::Session::instance()->isLSDEnabled() && !isTorrentPrivate) ? working : disabled)},
|
||||||
|
{KEY_TRACKER_PEERS_COUNT, 0},
|
||||||
|
{KEY_TRACKER_DOWNLOADED_COUNT, 0},
|
||||||
|
{KEY_TRACKER_SEEDS_COUNT, seedsLSD},
|
||||||
|
{KEY_TRACKER_LEECHES_COUNT, leechesLSD}
|
||||||
|
};
|
||||||
|
|
||||||
|
return QVariantList {dht, pex, lsd};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns all the torrents in JSON format.
|
// Returns all the torrents in JSON format.
|
||||||
@ -295,39 +366,37 @@ void TorrentsController::propertiesAction()
|
|||||||
// The dictionary keys are:
|
// The dictionary keys are:
|
||||||
// - "url": Tracker URL
|
// - "url": Tracker URL
|
||||||
// - "status": Tracker status
|
// - "status": Tracker status
|
||||||
// - "num_peers": Tracker peer count
|
// - "tier": Tracker tier
|
||||||
|
// - "num_peers": Number of peers this torrent is currently connected to
|
||||||
|
// - "num_seeds": Number of peers that have the whole file
|
||||||
|
// - "num_leeches": Number of peers that are still downloading
|
||||||
|
// - "num_downloaded": Tracker downloaded count
|
||||||
// - "msg": Tracker message (last)
|
// - "msg": Tracker message (last)
|
||||||
void TorrentsController::trackersAction()
|
void TorrentsController::trackersAction()
|
||||||
{
|
{
|
||||||
checkParams({"hash"});
|
checkParams({"hash"});
|
||||||
|
|
||||||
const QString hash {params()["hash"]};
|
const QString hash {params()["hash"]};
|
||||||
QVariantList trackerList;
|
const BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
|
||||||
if (!torrent)
|
if (!torrent)
|
||||||
throw APIError(APIErrorType::NotFound);
|
throw APIError(APIErrorType::NotFound);
|
||||||
|
|
||||||
|
QVariantList trackerList = getStickyTrackers(torrent);
|
||||||
|
|
||||||
QHash<QString, BitTorrent::TrackerInfo> trackersData = torrent->trackerInfos();
|
QHash<QString, BitTorrent::TrackerInfo> trackersData = torrent->trackerInfos();
|
||||||
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) {
|
for (const BitTorrent::TrackerEntry &tracker : asConst(torrent->trackers())) {
|
||||||
QVariantMap trackerDict;
|
|
||||||
trackerDict[KEY_TRACKER_URL] = tracker.url();
|
|
||||||
const BitTorrent::TrackerInfo data = trackersData.value(tracker.url());
|
const BitTorrent::TrackerInfo data = trackersData.value(tracker.url());
|
||||||
QString status;
|
|
||||||
switch (tracker.status()) {
|
|
||||||
case BitTorrent::TrackerEntry::NotContacted:
|
|
||||||
status = tr("Not contacted yet"); break;
|
|
||||||
case BitTorrent::TrackerEntry::Updating:
|
|
||||||
status = tr("Updating..."); break;
|
|
||||||
case BitTorrent::TrackerEntry::Working:
|
|
||||||
status = tr("Working"); break;
|
|
||||||
case BitTorrent::TrackerEntry::NotWorking:
|
|
||||||
status = tr("Not working"); break;
|
|
||||||
}
|
|
||||||
trackerDict[KEY_TRACKER_STATUS] = status;
|
|
||||||
trackerDict[KEY_TRACKER_PEERS] = data.numPeers;
|
|
||||||
trackerDict[KEY_TRACKER_MSG] = data.lastMessage.trimmed();
|
|
||||||
|
|
||||||
trackerList.append(trackerDict);
|
trackerList << QVariantMap {
|
||||||
|
{KEY_TRACKER_URL, tracker.url()},
|
||||||
|
{KEY_TRACKER_TIER, tracker.tier()},
|
||||||
|
{KEY_TRACKER_STATUS, static_cast<int>(tracker.status())},
|
||||||
|
{KEY_TRACKER_PEERS_COUNT, data.numPeers},
|
||||||
|
{KEY_TRACKER_MSG, data.lastMessage.trimmed()},
|
||||||
|
{KEY_TRACKER_SEEDS_COUNT, tracker.nativeEntry().scrape_complete},
|
||||||
|
{KEY_TRACKER_LEECHES_COUNT, tracker.nativeEntry().scrape_incomplete},
|
||||||
|
{KEY_TRACKER_DOWNLOADED_COUNT, tracker.nativeEntry().scrape_downloaded}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setResult(QJsonArray::fromVariantList(trackerList));
|
setResult(QJsonArray::fromVariantList(trackerList));
|
||||||
@ -532,15 +601,82 @@ void TorrentsController::addTrackersAction()
|
|||||||
const QString hash = params()["hash"];
|
const QString hash = params()["hash"];
|
||||||
|
|
||||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
if (torrent) {
|
if (!torrent)
|
||||||
QList<BitTorrent::TrackerEntry> trackers;
|
throw APIError(APIErrorType::NotFound);
|
||||||
for (QString url : asConst(params()["urls"].split('\n'))) {
|
|
||||||
url = url.trimmed();
|
QList<BitTorrent::TrackerEntry> trackers;
|
||||||
if (!url.isEmpty())
|
for (const QString &urlStr : asConst(params()["urls"].split('\n'))) {
|
||||||
trackers << url;
|
const QUrl url {urlStr.trimmed()};
|
||||||
}
|
if (url.isValid())
|
||||||
torrent->addTrackers(trackers);
|
trackers << url.toString();
|
||||||
}
|
}
|
||||||
|
torrent->addTrackers(trackers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentsController::editTrackerAction()
|
||||||
|
{
|
||||||
|
checkParams({"hash", "origUrl", "newUrl"});
|
||||||
|
|
||||||
|
const QString hash = params()["hash"];
|
||||||
|
const QString origUrl = params()["origUrl"];
|
||||||
|
const QString newUrl = params()["newUrl"];
|
||||||
|
|
||||||
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
|
if (!torrent)
|
||||||
|
throw APIError(APIErrorType::NotFound);
|
||||||
|
|
||||||
|
const QUrl origTrackerUrl(origUrl);
|
||||||
|
const QUrl newTrackerUrl(newUrl);
|
||||||
|
if (origTrackerUrl == newTrackerUrl)
|
||||||
|
return;
|
||||||
|
if (!newTrackerUrl.isValid())
|
||||||
|
throw APIError(APIErrorType::BadParams, "New tracker URL is invalid");
|
||||||
|
|
||||||
|
QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||||
|
bool match = false;
|
||||||
|
for (BitTorrent::TrackerEntry &tracker : trackers) {
|
||||||
|
const QUrl trackerUrl(tracker.url());
|
||||||
|
if (trackerUrl == newTrackerUrl)
|
||||||
|
throw APIError(APIErrorType::Conflict, "New tracker URL already exists");
|
||||||
|
if (trackerUrl == origTrackerUrl) {
|
||||||
|
match = true;
|
||||||
|
BitTorrent::TrackerEntry newTracker(newTrackerUrl.toString());
|
||||||
|
newTracker.setTier(tracker.tier());
|
||||||
|
tracker = newTracker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match)
|
||||||
|
throw APIError(APIErrorType::Conflict, "Tracker not found");
|
||||||
|
|
||||||
|
torrent->replaceTrackers(trackers);
|
||||||
|
if (!torrent->isPaused())
|
||||||
|
torrent->forceReannounce();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentsController::removeTrackersAction()
|
||||||
|
{
|
||||||
|
checkParams({"hash", "urls"});
|
||||||
|
|
||||||
|
const QString hash = params()["hash"];
|
||||||
|
const QStringList urls = params()["urls"].split('|');
|
||||||
|
|
||||||
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
|
if (!torrent)
|
||||||
|
throw APIError(APIErrorType::NotFound);
|
||||||
|
|
||||||
|
QList<BitTorrent::TrackerEntry> remainingTrackers;
|
||||||
|
const QList<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||||
|
for (const BitTorrent::TrackerEntry &entry : trackers) {
|
||||||
|
if (!urls.contains(entry.url()))
|
||||||
|
remainingTrackers.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingTrackers.size() == trackers.size())
|
||||||
|
throw APIError(APIErrorType::Conflict, "No trackers were removed");
|
||||||
|
|
||||||
|
torrent->replaceTrackers(remainingTrackers);
|
||||||
|
if (!torrent->isPaused())
|
||||||
|
torrent->forceReannounce();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::pauseAction()
|
void TorrentsController::pauseAction()
|
||||||
@ -548,7 +684,7 @@ void TorrentsController::pauseAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes = params()["hashes"].split('|');
|
const QStringList hashes = params()["hashes"].split('|');
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->pause(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->pause(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::resumeAction()
|
void TorrentsController::resumeAction()
|
||||||
@ -556,7 +692,7 @@ void TorrentsController::resumeAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes = params()["hashes"].split('|');
|
const QStringList hashes = params()["hashes"].split('|');
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->resume(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->resume(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::filePrioAction()
|
void TorrentsController::filePrioAction()
|
||||||
@ -606,7 +742,7 @@ void TorrentsController::uploadLimitAction()
|
|||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
for (const QString &hash : hashes) {
|
for (const QString &hash : hashes) {
|
||||||
int limit = -1;
|
int limit = -1;
|
||||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
const BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
if (torrent)
|
if (torrent)
|
||||||
limit = torrent->uploadLimit();
|
limit = torrent->uploadLimit();
|
||||||
map[hash] = limit;
|
map[hash] = limit;
|
||||||
@ -623,7 +759,7 @@ void TorrentsController::downloadLimitAction()
|
|||||||
QVariantMap map;
|
QVariantMap map;
|
||||||
for (const QString &hash : hashes) {
|
for (const QString &hash : hashes) {
|
||||||
int limit = -1;
|
int limit = -1;
|
||||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
const BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
if (torrent)
|
if (torrent)
|
||||||
limit = torrent->downloadLimit();
|
limit = torrent->downloadLimit();
|
||||||
map[hash] = limit;
|
map[hash] = limit;
|
||||||
@ -641,7 +777,7 @@ void TorrentsController::setUploadLimitAction()
|
|||||||
limit = -1;
|
limit = -1;
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [limit](BitTorrent::TorrentHandle *torrent) { torrent->setUploadLimit(limit); });
|
applyToTorrents(hashes, [limit](BitTorrent::TorrentHandle *const torrent) { torrent->setUploadLimit(limit); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::setDownloadLimitAction()
|
void TorrentsController::setDownloadLimitAction()
|
||||||
@ -653,7 +789,7 @@ void TorrentsController::setDownloadLimitAction()
|
|||||||
limit = -1;
|
limit = -1;
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [limit](BitTorrent::TorrentHandle *torrent) { torrent->setDownloadLimit(limit); });
|
applyToTorrents(hashes, [limit](BitTorrent::TorrentHandle *const torrent) { torrent->setDownloadLimit(limit); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::setShareLimitsAction()
|
void TorrentsController::setShareLimitsAction()
|
||||||
@ -664,7 +800,7 @@ void TorrentsController::setShareLimitsAction()
|
|||||||
const qlonglong seedingTimeLimit = params()["seedingTimeLimit"].toLongLong();
|
const qlonglong seedingTimeLimit = params()["seedingTimeLimit"].toLongLong();
|
||||||
const QStringList hashes = params()["hashes"].split('|');
|
const QStringList hashes = params()["hashes"].split('|');
|
||||||
|
|
||||||
applyToTorrents(hashes, [ratioLimit, seedingTimeLimit](BitTorrent::TorrentHandle *torrent)
|
applyToTorrents(hashes, [ratioLimit, seedingTimeLimit](BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
torrent->setRatioLimit(ratioLimit);
|
torrent->setRatioLimit(ratioLimit);
|
||||||
torrent->setSeedingTimeLimit(seedingTimeLimit);
|
torrent->setSeedingTimeLimit(seedingTimeLimit);
|
||||||
@ -676,7 +812,7 @@ void TorrentsController::toggleSequentialDownloadAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->toggleSequentialDownload(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->toggleSequentialDownload(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::toggleFirstLastPiecePrioAction()
|
void TorrentsController::toggleFirstLastPiecePrioAction()
|
||||||
@ -684,7 +820,7 @@ void TorrentsController::toggleFirstLastPiecePrioAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->toggleFirstLastPiecePriority(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->toggleFirstLastPiecePriority(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::setSuperSeedingAction()
|
void TorrentsController::setSuperSeedingAction()
|
||||||
@ -693,7 +829,7 @@ void TorrentsController::setSuperSeedingAction()
|
|||||||
|
|
||||||
const bool value {parseBool(params()["value"], false)};
|
const bool value {parseBool(params()["value"], false)};
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *torrent) { torrent->setSuperSeeding(value); });
|
applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *const torrent) { torrent->setSuperSeeding(value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::setForceStartAction()
|
void TorrentsController::setForceStartAction()
|
||||||
@ -702,7 +838,7 @@ void TorrentsController::setForceStartAction()
|
|||||||
|
|
||||||
const bool value {parseBool(params()["value"], false)};
|
const bool value {parseBool(params()["value"], false)};
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *torrent) { torrent->resume(value); });
|
applyToTorrents(hashes, [value](BitTorrent::TorrentHandle *const torrent) { torrent->resume(value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::deleteAction()
|
void TorrentsController::deleteAction()
|
||||||
@ -711,7 +847,7 @@ void TorrentsController::deleteAction()
|
|||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
const bool deleteFiles {parseBool(params()["deleteFiles"], false)};
|
const bool deleteFiles {parseBool(params()["deleteFiles"], false)};
|
||||||
applyToTorrents(hashes, [deleteFiles](BitTorrent::TorrentHandle *torrent)
|
applyToTorrents(hashes, [deleteFiles](BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
BitTorrent::Session::instance()->deleteTorrent(torrent->hash(), deleteFiles);
|
BitTorrent::Session::instance()->deleteTorrent(torrent->hash(), deleteFiles);
|
||||||
});
|
});
|
||||||
@ -779,7 +915,7 @@ void TorrentsController::setLocationAction()
|
|||||||
if (!QFileInfo(newLocation).isWritable())
|
if (!QFileInfo(newLocation).isWritable())
|
||||||
throw APIError(APIErrorType::AccessDenied, tr("Cannot write to directory"));
|
throw APIError(APIErrorType::AccessDenied, tr("Cannot write to directory"));
|
||||||
|
|
||||||
applyToTorrents(hashes, [newLocation](BitTorrent::TorrentHandle *torrent)
|
applyToTorrents(hashes, [newLocation](BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
LogMsg(tr("WebUI Set location: moving \"%1\", from \"%2\" to \"%3\"")
|
LogMsg(tr("WebUI Set location: moving \"%1\", from \"%2\" to \"%3\"")
|
||||||
.arg(torrent->name(), Utils::Fs::toNativePath(torrent->savePath()), Utils::Fs::toNativePath(newLocation)));
|
.arg(torrent->name(), Utils::Fs::toNativePath(torrent->savePath()), Utils::Fs::toNativePath(newLocation)));
|
||||||
@ -812,7 +948,7 @@ void TorrentsController::setAutoManagementAction()
|
|||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
const bool isEnabled {parseBool(params()["enable"], false)};
|
const bool isEnabled {parseBool(params()["enable"], false)};
|
||||||
|
|
||||||
applyToTorrents(hashes, [isEnabled](BitTorrent::TorrentHandle *torrent)
|
applyToTorrents(hashes, [isEnabled](BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
torrent->setAutoTMMEnabled(isEnabled);
|
torrent->setAutoTMMEnabled(isEnabled);
|
||||||
});
|
});
|
||||||
@ -823,7 +959,7 @@ void TorrentsController::recheckAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->forceRecheck(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->forceRecheck(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::reannounceAction()
|
void TorrentsController::reannounceAction()
|
||||||
@ -831,7 +967,7 @@ void TorrentsController::reannounceAction()
|
|||||||
checkParams({"hashes"});
|
checkParams({"hashes"});
|
||||||
|
|
||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *torrent) { torrent->forceReannounce(); });
|
applyToTorrents(hashes, [](BitTorrent::TorrentHandle *const torrent) { torrent->forceReannounce(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentsController::setCategoryAction()
|
void TorrentsController::setCategoryAction()
|
||||||
@ -841,7 +977,7 @@ void TorrentsController::setCategoryAction()
|
|||||||
const QStringList hashes {params()["hashes"].split('|')};
|
const QStringList hashes {params()["hashes"].split('|')};
|
||||||
const QString category {params()["category"].trimmed()};
|
const QString category {params()["category"].trimmed()};
|
||||||
|
|
||||||
applyToTorrents(hashes, [category](BitTorrent::TorrentHandle *torrent)
|
applyToTorrents(hashes, [category](BitTorrent::TorrentHandle *const torrent)
|
||||||
{
|
{
|
||||||
if (!torrent->setCategory(category))
|
if (!torrent->setCategory(category))
|
||||||
throw APIError(APIErrorType::Conflict, tr("Incorrect category name"));
|
throw APIError(APIErrorType::Conflict, tr("Incorrect category name"));
|
||||||
|
@ -59,6 +59,8 @@ private slots:
|
|||||||
void addAction();
|
void addAction();
|
||||||
void deleteAction();
|
void deleteAction();
|
||||||
void addTrackersAction();
|
void addTrackersAction();
|
||||||
|
void editTrackerAction();
|
||||||
|
void removeTrackersAction();
|
||||||
void filePrioAction();
|
void filePrioAction();
|
||||||
void uploadLimitAction();
|
void uploadLimitAction();
|
||||||
void downloadLimitAction();
|
void downloadLimitAction();
|
||||||
|
@ -43,9 +43,9 @@
|
|||||||
#include "base/utils/net.h"
|
#include "base/utils/net.h"
|
||||||
#include "base/utils/version.h"
|
#include "base/utils/version.h"
|
||||||
|
|
||||||
constexpr Utils::Version<int, 3, 2> API_VERSION {2, 1, 1};
|
constexpr Utils::Version<int, 3, 2> API_VERSION {2, 2, 0};
|
||||||
constexpr int COMPAT_API_VERSION = 22;
|
constexpr int COMPAT_API_VERSION = 23;
|
||||||
constexpr int COMPAT_API_VERSION_MIN = 21;
|
constexpr int COMPAT_API_VERSION_MIN = 23;
|
||||||
|
|
||||||
class APIController;
|
class APIController;
|
||||||
class WebApplication;
|
class WebApplication;
|
||||||
|
@ -9,6 +9,17 @@
|
|||||||
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.addEvent('domready', function() {
|
window.addEvent('domready', function() {
|
||||||
|
var setLocationKeyboardEvents = new Keyboard({
|
||||||
|
defaultEventType: 'keydown',
|
||||||
|
events: {
|
||||||
|
'enter': function(event) {
|
||||||
|
$('addTrackersButton').click();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setLocationKeyboardEvents.activate();
|
||||||
|
|
||||||
$('trackersUrls').focus();
|
$('trackersUrls').focus();
|
||||||
$('addTrackersButton').addEvent('click', function(e) {
|
$('addTrackersButton').addEvent('click', function(e) {
|
||||||
new Event(e).stop();
|
new Event(e).stop();
|
||||||
|
@ -450,12 +450,6 @@ td.generalLabel {
|
|||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#addTrackersPlus {
|
|
||||||
width: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unselectable {
|
.unselectable {
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
65
src/webui/www/private/edittracker.html
Normal file
65
src/webui/www/private/edittracker.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="${LANG}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>QBT_TR(Tracker editing)QBT_TR[CONTEXT=TrackerListWidget]</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
||||||
|
<script src="scripts/lib/mootools-1.2-core-yc.js"></script>
|
||||||
|
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.addEvent('domready', function() {
|
||||||
|
var setLocationKeyboardEvents = new Keyboard({
|
||||||
|
defaultEventType: 'keydown',
|
||||||
|
events: {
|
||||||
|
'enter': function(event) {
|
||||||
|
$('editTrackerButton').click();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setLocationKeyboardEvents.activate();
|
||||||
|
|
||||||
|
var currentUrl = new URI().getData('url');
|
||||||
|
if (!currentUrl)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var decodedUrl = decodeURIComponent(currentUrl);
|
||||||
|
$('trackerUrl').value = decodedUrl;
|
||||||
|
$('trackerUrl').focus();
|
||||||
|
|
||||||
|
$('editTrackerButton').addEvent('click', function(e) {
|
||||||
|
new Event(e).stop();
|
||||||
|
var hash = new URI().getData('hash');
|
||||||
|
new Request({
|
||||||
|
url: 'api/v2/torrents/editTracker',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
hash: hash,
|
||||||
|
origUrl: decodedUrl,
|
||||||
|
newUrl: $('trackerUrl').value
|
||||||
|
},
|
||||||
|
onComplete: function() {
|
||||||
|
window.parent.closeWindows();
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<br/>
|
||||||
|
<h2 class="vcenter">QBT_TR(Tracker URL:)QBT_TR[CONTEXT=TrackerListWidget]</h2>
|
||||||
|
<div style="text-align: center; padding-top: 10px;">
|
||||||
|
<input id="trackerUrl" style="width: 90%;" />
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<input type="button" value="QBT_TR(Edit)QBT_TR[CONTEXT=HttpServer]" id="editTrackerButton" />
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -158,6 +158,12 @@
|
|||||||
<li><a href="#PauseTorrentsByCategory"><img src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
<li><a href="#PauseTorrentsByCategory"><img src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Pause torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
||||||
<li><a href="#DeleteTorrentsByCategory"><img src="images/qbt-theme/edit-delete.svg" alt="QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
<li><a href="#DeleteTorrentsByCategory"><img src="images/qbt-theme/edit-delete.svg" alt="QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]"/> QBT_TR(Delete torrents)QBT_TR[CONTEXT=CategoryFilterWidget]</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul id="torrentTrackersMenu" class="contextMenu">
|
||||||
|
<li><a href="#AddTracker"><img src="images/qbt-theme/list-add.svg" alt="QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Add a new tracker...)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||||
|
<li class="separator"><a href="#EditTracker"><img src="images/qbt-theme/document-edit.svg" alt="QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Edit tracker URL...)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||||
|
<li><a href="#RemoveTracker"><img src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Remove tracker)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Remove tracker)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||||
|
<li><a href="#CopyTrackerUrl" id="CopyTrackerUrl"><img src="images/qbt-theme/edit-copy.svg" alt="QBT_TR(Copy tracker URL)QBT_TR[CONTEXT=TrackerListWidget]"/> QBT_TR(Copy tracker URL)QBT_TR[CONTEXT=TrackerListWidget]</a></li>
|
||||||
|
</ul>
|
||||||
<div id="desktopFooterWrapper">
|
<div id="desktopFooterWrapper">
|
||||||
<div id="desktopFooter">
|
<div id="desktopFooter">
|
||||||
<span id="error_div"></span>
|
<span id="error_div"></span>
|
||||||
|
@ -84,10 +84,14 @@
|
|||||||
<table class="dynamicTable" style="width: 100%">
|
<table class="dynamicTable" style="width: 100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 30%;">QBT_TR(URL)QBT_TR[CONTEXT=TrackerListWidget] <img src="images/qbt-theme/list-add.svg" id="addTrackersPlus" alt="Add Trackers" /></th>
|
<th style="width: 5%;">QBT_TR(#)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
|
<th style="width: 30%;">QBT_TR(URL)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
<th style="width: 10%;">QBT_TR(Status)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
<th style="width: 10%;">QBT_TR(Status)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
<th style="width: 10%;">QBT_TR(Peers)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
<th style="width: 5%;">QBT_TR(Peers)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
<th style="width: 50%;">QBT_TR(Message)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
<th style="width: 5%;">QBT_TR(Seeds)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
|
<th style="width: 5%;">QBT_TR(Leeches)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
|
<th style="width: 5%;">QBT_TR(Downloaded)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
|
<th style="width: 35%;">QBT_TR(Message)QBT_TR[CONTEXT=TrackerListWidget]</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="trackersTable"></tbody>
|
<tbody id="trackersTable"></tbody>
|
||||||
|
@ -2,9 +2,10 @@ var trackersDynTable = new Class({
|
|||||||
|
|
||||||
initialize: function() {},
|
initialize: function() {},
|
||||||
|
|
||||||
setup: function(table) {
|
setup: function(table, contextMenu) {
|
||||||
this.table = $(table);
|
this.table = $(table);
|
||||||
this.rows = new Hash();
|
this.rows = new Hash();
|
||||||
|
this.contextMenu = contextMenu;
|
||||||
},
|
},
|
||||||
|
|
||||||
removeRow: function(url) {
|
removeRow: function(url) {
|
||||||
@ -32,7 +33,7 @@ var trackersDynTable = new Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
insertRow: function(row) {
|
insertRow: function(row) {
|
||||||
var url = row[0];
|
var url = row[1];
|
||||||
if (this.rows.has(url)) {
|
if (this.rows.has(url)) {
|
||||||
var tableRow = this.rows.get(url);
|
var tableRow = this.rows.get(url);
|
||||||
this.updateRow(tableRow, row);
|
this.updateRow(tableRow, row);
|
||||||
@ -46,11 +47,13 @@ var trackersDynTable = new Class({
|
|||||||
td.set('html', row[i]);
|
td.set('html', row[i]);
|
||||||
td.injectInside(tr);
|
td.injectInside(tr);
|
||||||
}
|
}
|
||||||
|
this.contextMenu.addTarget(tr);
|
||||||
tr.injectInside(this.table);
|
tr.injectInside(this.table);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var current_hash = "";
|
var current_hash = "";
|
||||||
|
var selectedTracker = "";
|
||||||
|
|
||||||
var loadTrackersDataTimer;
|
var loadTrackersDataTimer;
|
||||||
var loadTrackersData = function() {
|
var loadTrackersData = function() {
|
||||||
@ -61,13 +64,13 @@ var loadTrackersData = function() {
|
|||||||
}
|
}
|
||||||
var new_hash = torrentsTable.getCurrentTorrentHash();
|
var new_hash = torrentsTable.getCurrentTorrentHash();
|
||||||
if (new_hash === "") {
|
if (new_hash === "") {
|
||||||
tTable.removeAllRows();
|
torrentTrackersTable.removeAllRows();
|
||||||
clearTimeout(loadTrackersDataTimer);
|
clearTimeout(loadTrackersDataTimer);
|
||||||
loadTrackersDataTimer = loadTrackersData.delay(10000);
|
loadTrackersDataTimer = loadTrackersData.delay(10000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (new_hash != current_hash) {
|
if (new_hash != current_hash) {
|
||||||
tTable.removeAllRows();
|
torrentTrackersTable.removeAllRows();
|
||||||
current_hash = new_hash;
|
current_hash = new_hash;
|
||||||
}
|
}
|
||||||
var url = new URI('api/v2/torrents/trackers?hash=' + current_hash);
|
var url = new URI('api/v2/torrents/trackers?hash=' + current_hash);
|
||||||
@ -82,21 +85,44 @@ var loadTrackersData = function() {
|
|||||||
},
|
},
|
||||||
onSuccess: function(trackers) {
|
onSuccess: function(trackers) {
|
||||||
$('error_div').set('html', '');
|
$('error_div').set('html', '');
|
||||||
|
torrentTrackersTable.removeAllRows();
|
||||||
|
|
||||||
if (trackers) {
|
if (trackers) {
|
||||||
// Update Trackers data
|
// Update Trackers data
|
||||||
trackers.each(function(tracker) {
|
trackers.each(function(tracker) {
|
||||||
var row = [];
|
var status;
|
||||||
row.length = 4;
|
switch (tracker.status) {
|
||||||
row[0] = escapeHtml(tracker.url);
|
case 0:
|
||||||
row[1] = tracker.status;
|
status = "QBT_TR(Disabled)QBT_TR[CONTEXT=TrackerListWidget]";
|
||||||
row[2] = tracker.num_peers;
|
break;
|
||||||
row[3] = escapeHtml(tracker.msg);
|
case 1:
|
||||||
tTable.insertRow(row);
|
status = "QBT_TR(Not contacted yet)QBT_TR[CONTEXT=TrackerListWidget]";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
status = "QBT_TR(Working)QBT_TR[CONTEXT=TrackerListWidget]";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
status = "QBT_TR(Updating...)QBT_TR[CONTEXT=TrackerListWidget]";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
status = "QBT_TR(Not working)QBT_TR[CONTEXT=TrackerListWidget]";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var row = [
|
||||||
|
tracker.tier,
|
||||||
|
escapeHtml(tracker.url),
|
||||||
|
status,
|
||||||
|
tracker.num_peers,
|
||||||
|
(tracker.num_seeds >= 0) ? tracker.num_seeds : "QBT_TR(N/A)QBT_TR[CONTEXT=TrackerListWidget]",
|
||||||
|
(tracker.num_leeches >= 0) ? tracker.num_leeches : "QBT_TR(N/A)QBT_TR[CONTEXT=TrackerListWidget]",
|
||||||
|
(tracker.num_downloaded >= 0) ? tracker.num_downloaded : "QBT_TR(N/A)QBT_TR[CONTEXT=TrackerListWidget]",
|
||||||
|
escapeHtml(tracker.msg)
|
||||||
|
];
|
||||||
|
|
||||||
|
torrentTrackersTable.insertRow(row);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
tTable.removeAllRows();
|
|
||||||
}
|
|
||||||
clearTimeout(loadTrackersDataTimer);
|
clearTimeout(loadTrackersDataTimer);
|
||||||
loadTrackersDataTimer = loadTrackersData.delay(10000);
|
loadTrackersDataTimer = loadTrackersData.delay(10000);
|
||||||
}
|
}
|
||||||
@ -108,11 +134,42 @@ var updateTrackersData = function() {
|
|||||||
loadTrackersData();
|
loadTrackersData();
|
||||||
};
|
};
|
||||||
|
|
||||||
tTable = new trackersDynTable();
|
var torrentTrackersContextMenu = new ContextMenu({
|
||||||
tTable.setup($('trackersTable'));
|
targets: '.torrentTrackersMenuTarget',
|
||||||
|
menu: 'torrentTrackersMenu',
|
||||||
|
actions: {
|
||||||
|
AddTracker: function(element, ref) {
|
||||||
|
addTrackerFN();
|
||||||
|
},
|
||||||
|
EditTracker: function(element, ref) {
|
||||||
|
editTrackerFN(element);
|
||||||
|
},
|
||||||
|
RemoveTracker: function(element, ref) {
|
||||||
|
removeTrackerFN(element);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
offsets: {
|
||||||
|
x: -15,
|
||||||
|
y: 2
|
||||||
|
},
|
||||||
|
onShow: function() {
|
||||||
|
var element = this.options.element;
|
||||||
|
selectedTracker = element;
|
||||||
|
if (element.childNodes[1].innerText.indexOf("** [") === 0) {
|
||||||
|
this.hideItem('EditTracker');
|
||||||
|
this.hideItem('RemoveTracker');
|
||||||
|
this.hideItem('CopyTrackerUrl');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.showItem('EditTracker');
|
||||||
|
this.showItem('RemoveTracker');
|
||||||
|
this.showItem('CopyTrackerUrl');
|
||||||
|
}
|
||||||
|
this.options.element.firstChild.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add trackers code
|
var addTrackerFN = function() {
|
||||||
$('addTrackersPlus').addEvent('click', function addTrackerDlg() {
|
|
||||||
if (current_hash.length === 0) return;
|
if (current_hash.length === 0) return;
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: 'trackersPage',
|
id: 'trackersPage',
|
||||||
@ -126,6 +183,62 @@ $('addTrackersPlus').addEvent('click', function addTrackerDlg() {
|
|||||||
paddingVertical: 0,
|
paddingVertical: 0,
|
||||||
paddingHorizontal: 0,
|
paddingHorizontal: 0,
|
||||||
width: 500,
|
width: 500,
|
||||||
height: 250
|
height: 250,
|
||||||
|
onCloseComplete: function() {
|
||||||
|
updateTrackersData();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var editTrackerFN = function(element) {
|
||||||
|
if (current_hash.length === 0) return;
|
||||||
|
|
||||||
|
var trackerUrl = encodeURIComponent(element.childNodes[1].innerText);
|
||||||
|
new MochaUI.Window({
|
||||||
|
id: 'trackersPage',
|
||||||
|
title: "QBT_TR(Tracker editing)QBT_TR[CONTEXT=TrackerListWidget]",
|
||||||
|
loadMethod: 'iframe',
|
||||||
|
contentURL: 'edittracker.html?hash=' + current_hash + '&url=' + trackerUrl,
|
||||||
|
scrollbars: true,
|
||||||
|
resizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
closable: true,
|
||||||
|
paddingVertical: 0,
|
||||||
|
paddingHorizontal: 0,
|
||||||
|
width: 500,
|
||||||
|
height: 150,
|
||||||
|
onCloseComplete: function() {
|
||||||
|
updateTrackersData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeTrackerFN = function(element) {
|
||||||
|
if (current_hash.length === 0) return;
|
||||||
|
|
||||||
|
var trackerUrl = element.childNodes[1].innerText;
|
||||||
|
new Request({
|
||||||
|
url: 'api/v2/torrents/removeTrackers',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
hash: current_hash,
|
||||||
|
urls: trackerUrl
|
||||||
|
},
|
||||||
|
onSuccess: function() {
|
||||||
|
updateTrackersData();
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
torrentTrackersTable = new trackersDynTable();
|
||||||
|
torrentTrackersTable.setup($('trackersTable'), torrentTrackersContextMenu);
|
||||||
|
|
||||||
|
new ClipboardJS('#CopyTrackerUrl', {
|
||||||
|
text: function(trigger) {
|
||||||
|
if (selectedTracker) {
|
||||||
|
var url = selectedTracker.childNodes[1].innerText;
|
||||||
|
selectedTracker = "";
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<file>private/css/Window.css</file>
|
<file>private/css/Window.css</file>
|
||||||
<file>private/download.html</file>
|
<file>private/download.html</file>
|
||||||
<file>private/downloadlimit.html</file>
|
<file>private/downloadlimit.html</file>
|
||||||
|
<file>private/edittracker.html</file>
|
||||||
<file>private/filters.html</file>
|
<file>private/filters.html</file>
|
||||||
<file>private/index.html</file>
|
<file>private/index.html</file>
|
||||||
<file>private/installsearchplugin.html</file>
|
<file>private/installsearchplugin.html</file>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user