Browse Source

- Display trackers in torrent properties in Web UI

adaptive-webui-19844
Christophe Dumez 15 years ago
parent
commit
11a41a1652
  1. 2
      Changelog
  2. 45
      src/eventmanager.cpp
  3. 1
      src/eventmanager.h
  4. 20
      src/httpconnection.cpp
  5. 1
      src/httpconnection.h
  6. 8
      src/trackerlist.h
  7. 7
      src/webui/css/dynamicTable.css
  8. 8
      src/webui/prop-general.html
  9. 128
      src/webui/prop-trackers.html
  10. 96
      src/webui/scripts/dynamicTable.js

2
Changelog

@ -33,7 +33,7 @@
- FEATURE: Include DHT traffic in the rate limiter (libtorrent >= v0.15 only) - FEATURE: Include DHT traffic in the rate limiter (libtorrent >= v0.15 only)
- FEATURE: Support for bitcomet padding files (libtorrent >= v0.15 only) - FEATURE: Support for bitcomet padding files (libtorrent >= v0.15 only)
- FEATURE: Option to skip file checking and start seeding immediately in torrent addition dialog (Stephanos Antaris) (libtorrent >= v0.15 only) - FEATURE: Option to skip file checking and start seeding immediately in torrent addition dialog (Stephanos Antaris) (libtorrent >= v0.15 only)
- WEB UI: Removed Web UI to match new qBittorrent UI - WEB UI: Remodeled Web UI to match new qBittorrent UI (Properties and preferences available)
- WEB UI: Added internationalization support - WEB UI: Added internationalization support
- WEB UI: Reduced computation in Javascript (do this one server side instead) - WEB UI: Reduced computation in Javascript (do this one server side instead)
- COSMETIC: Merged download / upload lists - COSMETIC: Merged download / upload lists

45
src/eventmanager.cpp

@ -44,6 +44,51 @@ QList<QVariantMap> EventManager::getEventList() const {
return event_list.values(); return event_list.values();
} }
QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
QList<QVariantMap> trackersInfo;
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()) {
QHash<QString, TrackerInfos> trackers_data = BTSession->getTrackersInfo(hash);
std::vector<announce_entry> vect_trackers = h.trackers();
std::vector<announce_entry>::iterator it;
for(it = vect_trackers.begin(); it != vect_trackers.end(); it++) {
QVariantMap tracker;
QString tracker_url = misc::toQString(it->url);
tracker["url"] = tracker_url;
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed();
#ifdef LIBTORRENT_0_15
if(it->verified) {
tracker["status"] = tr("Working");
} else {
if(it->updating && it->fails == 0) {
tracker["status"] = tr("Updating...");
} else {
if(it->fails > 0) {
tracker["status"] = tr("Not working");
} else {
tracker["status"] = tr("Not contacted yet");
}
}
}
#else
if(data.verified) {
tracker["status"] = tr("Working");
} else {
if(data.fail_count > 0)
tracker["status"] = tr("Not working");
else
tracker["status"] = tr("Not contacted yet");
}
#endif
tracker["num_peers"] = QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers);
tracker["msg"] = error_message;
trackersInfo << tracker;
}
}
return trackersInfo;
}
QVariantMap EventManager::getPropGeneralInfo(QString hash) const { QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
QVariantMap data; QVariantMap data;
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);

1
src/eventmanager.h

@ -52,6 +52,7 @@ class EventManager : public QObject
EventManager(QObject *parent, Bittorrent* BTSession); EventManager(QObject *parent, Bittorrent* BTSession);
QList<QVariantMap> getEventList() const; QList<QVariantMap> getEventList() const;
QVariantMap getPropGeneralInfo(QString hash) const; QVariantMap getPropGeneralInfo(QString hash) const;
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
public slots: public slots:
void addedTorrent(QTorrentHandle& h); void addedTorrent(QTorrentHandle& h);

20
src/httpconnection.cpp

@ -102,7 +102,7 @@ void HttpConnection::write()
} }
QString HttpConnection::translateDocument(QString data) { QString HttpConnection::translateDocument(QString data) {
std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg"}; std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList"};
int i=0; int i=0;
bool found = false; bool found = false;
do { do {
@ -117,7 +117,7 @@ QString HttpConnection::translateDocument(QString data) {
do { do {
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().data(), 0, QCoreApplication::UnicodeUTF8, 1); translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().data(), 0, QCoreApplication::UnicodeUTF8, 1);
++context_index; ++context_index;
}while(translation == word && context_index < 7); }while(translation == word && context_index < 8);
//qDebug("Translation is %s", translation.toUtf8().data()); //qDebug("Translation is %s", translation.toUtf8().data());
data = data.replace(i, regex.matchedLength(), translation); data = data.replace(i, regex.matchedLength(), translation);
i += translation.length(); i += translation.length();
@ -162,6 +162,12 @@ void HttpConnection::respond()
respondGenPropertiesJson(hash); respondGenPropertiesJson(hash);
return; return;
} }
if(list[1] == "propertiesTrackers") {
qDebug("Web UI asked for trackers");
QString hash = list[2];
respondTrackersPropertiesJson(hash);
return;
}
} }
} }
if (list[0] == "command") if (list[0] == "command")
@ -226,6 +232,16 @@ void HttpConnection::respondGenPropertiesJson(QString hash) {
write(); write();
} }
void HttpConnection::respondTrackersPropertiesJson(QString hash) {
EventManager* manager = parent->eventManager();
QString string = json::toJson(manager->getPropTrackersInfo(hash));
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondCommand(QString command) void HttpConnection::respondCommand(QString command)
{ {
if(command == "download") if(command == "download")

1
src/httpconnection.h

@ -57,6 +57,7 @@ class HttpConnection : public QObject
virtual void respond(); virtual void respond();
void respondJson(); void respondJson();
void respondGenPropertiesJson(QString hash); void respondGenPropertiesJson(QString hash);
void respondTrackersPropertiesJson(QString hash);
void respondCommand(QString command); void respondCommand(QString command);
void respondNotFound(); void respondNotFound();
void processDownloadedFile(QString, QString); void processDownloadedFile(QString, QString);

8
src/trackerlist.h

@ -174,7 +174,7 @@ public slots:
std::vector<announce_entry>::iterator it; std::vector<announce_entry>::iterator it;
for(it = trackers.begin(); it != trackers.end(); it++) { for(it = trackers.begin(); it != trackers.end(); it++) {
QStringList item_list; QStringList item_list;
QString tracker_url = misc::toQString((*it).url); QString tracker_url = misc::toQString(it->url);
QTreeWidgetItem *item = tracker_items.value(tracker_url, 0); QTreeWidgetItem *item = tracker_items.value(tracker_url, 0);
if(!item) { if(!item) {
item = new QTreeWidgetItem(); item = new QTreeWidgetItem();
@ -187,13 +187,13 @@ public slots:
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed(); QString error_message = data.last_message.trimmed();
#ifdef LIBTORRENT_0_15 #ifdef LIBTORRENT_0_15
if((*it).verified) { if(it->verified) {
item->setText(COL_STATUS, tr("Working")); item->setText(COL_STATUS, tr("Working"));
} else { } else {
if((*it).updating && (*it).fails == 0) { if(it->updating && it->fails == 0) {
item->setText(COL_STATUS, tr("Updating...")); item->setText(COL_STATUS, tr("Updating..."));
} else { } else {
if((*it).fails > 0) { if(it->fails > 0) {
item->setText(COL_STATUS, tr("Not working")); item->setText(COL_STATUS, tr("Not working"));
} else { } else {
item->setText(COL_STATUS, tr("Not contacted yet")); item->setText(COL_STATUS, tr("Not contacted yet"));

7
src/webui/css/dynamicTable.css

@ -7,35 +7,42 @@
**************************************************************/ **************************************************************/
#properties #trackers table,
#transferList table { #transferList table {
border: 1px solid #ccc; border: 1px solid #ccc;
width: 100%; width: 100%;
} }
#properties #trackers th,
#transferList th { #transferList th {
background-color: #eee; background-color: #eee;
padding: 4px; padding: 4px;
} }
#properties #trackers tr,
#transferList tr { #transferList tr {
background-color: #fff; background-color: #fff;
padding: 4px; padding: 4px;
} }
#properties #trackers tr.alt,
#transferList tr.alt { #transferList tr.alt {
background-color: #eee; background-color: #eee;
padding: 4px; padding: 4px;
} }
#properties #trackers td,
#transferList td { #transferList td {
padding: 0 2px; padding: 0 2px;
} }
#properties #trackers tr.selected,
#transferList tr.selected { #transferList tr.selected {
background-color: #354158; background-color: #354158;
color: #fff; color: #fff;
} }
#properties #trackers tr.over,
#transferList tr.over { #transferList tr.over {
background-color: #ee6600; background-color: #ee6600;
color: #fff; color: #fff;

8
src/webui/prop-general.html

@ -51,10 +51,14 @@ dynamic information: total_downloaded, total_uploaded, total_wasted, up_limit, d
} }
var loadData = function() { var loadData = function() {
if(!$defined($('torrent_hash'))) {
// Tab changed
return;
}
var current_hash = myTable.getCurrentTorrentHash(); var current_hash = myTable.getCurrentTorrentHash();
if(current_hash == "") { if(current_hash == "") {
clearData(); clearData();
loadData.delay(2000); loadData.delay(1500);
return; return;
} }
// Display hash // Display hash
@ -89,7 +93,7 @@ dynamic information: total_downloaded, total_uploaded, total_wasted, up_limit, d
clearData(); clearData();
} }
waiting=false; waiting=false;
loadData.delay(2000); loadData.delay(1500);
} }
}).send(); }).send();
} }

128
src/webui/prop-trackers.html

@ -1 +1,127 @@
CONTENT <span id="trackers">
<table class="torrentTable" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th>_(URL)</th>
<th>_(Status)</th>
<th>_(Peers)</th>
<th>_(Message)</th>
</tr>
</thead>
<tbody id="trackersTable"></tbody>
</table>
</span>
<script type="text/javascript">
var trackersDynTable = new Class ({
initialize: function(){
},
setup: function(table){
this.table = $(table);
this.rows = new Hash();
},
removeRow: function(url){
if(this.rows.has(url)) {
var tr = this.rows.get(url);
tr.dispose();
this.rows.erase(url);
return true;
}
return false;
},
removeAllRows: function() {
this.rows.each(function(tr, url) {
this.removeRow(url);
}.bind(this));
},
updateRow: function(tr, row){
var tds = tr.getElements('td');
for(var i=0; i<row.length; i++) {
tds[i].set('html', row[i]);
}
return true;
},
insertRow: function(row) {
var url = row[0];
if(this.rows.has(url)) {
var tr = this.rows.get(url);
this.updateRow(tr, row);
return;
}
//this.removeRow(id);
var tr = new Element('tr');
this.rows.set(url, tr);
for(var i=0; i<row.length; i++)
{
var td = new Element('td');
td.set('html', row[i]);
td.injectInside(tr);
}
tr.injectInside(this.table);
},
});
var waitingTrackers=false;
var current_hash = "";
var loadTrackersData = function() {
if(!$defined($('trackersTable'))) {
// Tab changed
return;
}
var new_hash = myTable.getCurrentTorrentHash();
if(new_hash == "") {
tTable.removeAllRows();
loadTrackersData.delay(1500);
return;
}
if(new_hash != current_hash) {
tTable.removeAllRows();
current_hash = new_hash;
}
var url = 'json/propertiesTrackers/'+current_hash;
if (!waitingTrackers) {
waitingTrackers=true;
var request = new Request.JSON({
url: url,
method: 'get',
onFailure: function() {
$('error_div').set('html', 'qBittorrent client is not reachable');
waitingTrackers=false;
loadTrackersData.delay(2000);
},
onSuccess: function(trackers) {
$('error_div').set('html', '');
if(trackers){
// Update Trackers data
trackers.each(function(tracker){
var row = new Array();
row.length = 4;
row[0] = tracker.url;
row[1] = tracker.status;
row[2] = tracker.num_peers;
row[3] = tracker.msg;
tTable.insertRow(row);
});
} else {
tTable.removeAllRows();
}
waitingTrackers=false;
loadTrackersData.delay(1500);
}
}).send();
}
}
tTable = new trackersDynTable();
tTable.setup($('trackersTable'));
// Initial loading
loadTrackersData();
</script>

96
src/webui/scripts/dynamicTable.js

@ -38,12 +38,11 @@ var dynamicTable = new Class ({
setup: function(table, progressIndex){ setup: function(table, progressIndex){
this.table = $(table); this.table = $(table);
this.rows = new Object(); this.rows = new Hash();
this.cur = new Array(); this.cur = new Array();
this.priority_hidden = false; this.priority_hidden = false;
this.progressIndex = progressIndex; this.progressIndex = progressIndex;
this.filter = 'all'; this.filter = 'all';
this.current_hash = '';
}, },
getCurrentTorrentHash: function() { getCurrentTorrentHash: function() {
@ -127,12 +126,11 @@ var dynamicTable = new Class ({
}, },
insertRow: function(id, row, status){ insertRow: function(id, row, status){
var tr = this.rows[id]; if(this.rows.has(id)) {
if($defined(tr)) return;
return; }
//this.removeRow(id);
var tr = new Element('tr'); var tr = new Element('tr');
this.rows[id] = tr; this.rows.set(id, tr);
for(var i=0; i<row.length; i++) for(var i=0; i<row.length; i++)
{ {
var td = new Element('td'); var td = new Element('td');
@ -158,15 +156,15 @@ var dynamicTable = new Class ({
// remove it // remove it
this.cur.erase(id); this.cur.erase(id);
// Remove selected style // Remove selected style
temptr = this.rows[id]; if(this.rows.has(id)) {
if(temptr){ temptr = this.rows.get(id);
temptr.removeClass('selected'); temptr.removeClass('selected');
} }
} else { } else {
this.cur[this.cur.length] = id; this.cur[this.cur.length] = id;
// Add selected style // Add selected style
temptr = this.rows[id]; if(this.rows.has(id)) {
if(temptr){ temptr = this.rows.get(id);
temptr.addClass('selected'); temptr.addClass('selected');
} }
} }
@ -186,8 +184,8 @@ var dynamicTable = new Class ({
curID = ids[i]; curID = ids[i];
this.cur[this.cur.length] = curID; this.cur[this.cur.length] = curID;
// Add selected style // Add selected style
temptr = this.rows[curID]; if(this.rows.has(curID)) {
if(temptr){ temptr = this.rows.get(curID);
temptr.addClass('selected'); temptr.addClass('selected');
} }
} }
@ -195,15 +193,15 @@ var dynamicTable = new Class ({
// Simple selection // Simple selection
// Remove selected style from previous ones // Remove selected style from previous ones
for(i=0; i<this.cur.length; i++) { for(i=0; i<this.cur.length; i++) {
var temptr = this.rows[this.cur[i]]; if(this.rows.has(this.cur[i])) {
if(temptr){ var temptr = this.rows.get(this.cur[i]);
temptr.removeClass('selected'); temptr.removeClass('selected');
} }
} }
this.cur.empty(); this.cur.empty();
// Add selected style to new one // Add selected style to new one
temptr = this.rows[id]; if(this.rows.has(id)) {
if(temptr){ temptr = this.rows.get(id);
temptr.addClass('selected'); temptr.addClass('selected');
} }
this.cur[0] = id; this.cur[0] = id;
@ -212,44 +210,40 @@ var dynamicTable = new Class ({
} }
return false; return false;
}.bind(this)); }.bind(this));
// Apply filter
this.applyFilterOnRow(tr, status);
// Insert
tr.injectInside(this.table); tr.injectInside(this.table);
this.altRow(); this.altRow();
// Apply filter
//this.applyFilterOnRow(tr, status);
}, },
selectAll: function() { selectAll: function() {
this.cur.empty(); this.cur.empty();
for (var id in this.rows) { this.rows.each(function(tr, id){
this.cur[this.cur.length] = id; this.cur[this.cur.length] = id;
temptr = this.rows[id]; if(!tr.hasClass('selected')) {
if(temptr){ tr.addClass('selected');
if(!temptr.hasClass('selected')) {
temptr.addClass('selected');
}
} }
} });
}, },
updateRow: function(id, row, status){ updateRow: function(id, row, status){
var tr = this.rows[id]; if(!this.rows.has(id)) {
if($defined(tr)) return false;
{
// Apply filter
this.applyFilterOnRow(tr, status);
var tds = tr.getElements('td');
for(var i=0; i<row.length; i++) {
if(i==this.progressIndex) {
tds[i].set('html', '');
tds[i].adopt(new ProgressBar(row[i].toFloat(), {width:80}));
} else {
tds[i].set('html', row[i]);
}
};
return true;
} }
return false; var tr = this.rows.get(id);
// Apply filter
this.applyFilterOnRow(tr, status);
var tds = tr.getElements('td');
for(var i=0; i<row.length; i++) {
if(i==this.progressIndex) {
tds[i].set('html', '');
tds[i].adopt(new ProgressBar(row[i].toFloat(), {width:80}));
} else {
tds[i].set('html', row[i]);
}
};
return true;
}, },
removeRow: function(id){ removeRow: function(id){
@ -257,11 +251,11 @@ var dynamicTable = new Class ({
{ {
this.cur.erase(id); this.cur.erase(id);
} }
var tr = this.rows[id]; if(this.rows.has(id)) {
if($defined(tr)) var tr = this.rows.get(id);
{
tr.dispose(); tr.dispose();
this.altRow(); this.altRow();
this.rows.erase(id);
return true; return true;
} }
return false; return false;
@ -274,10 +268,10 @@ var dynamicTable = new Class ({
getRowIds: function(){ getRowIds: function(){
var ids = new Array(); var ids = new Array();
var i = 0; var i = 0;
for (var id in this.rows) { this.rows.each(function(tr, id) {
ids[i] = id; ids[i] = id;
i++ i++;
} }.bind(this));
return ids; return ids;
} }
}); });

Loading…
Cancel
Save