diff --git a/Changelog b/Changelog index 95b7e4050..77a9a97ee 100644 --- a/Changelog +++ b/Changelog @@ -23,6 +23,7 @@ - FEATURE: Seeds and Peers columns are now sortable - FEATURE: Torrents can be rechecked from Web UI (Stephanos Antaris) - FEATURE: New peers can manually be added to the torrents + - FEATURE: Support per-peer rate limiting - COSMETIC: Merged download / upload lists - COSMETIC: Torrents can be filtered based on their status - COSMETIC: Torrent properties are now displayed in main window diff --git a/src/TransferListWidget.cpp b/src/TransferListWidget.cpp index a4f66776b..1105099e3 100644 --- a/src/TransferListWidget.cpp +++ b/src/TransferListWidget.cpp @@ -709,9 +709,9 @@ void TransferListWidget::displayListMenu(const QPoint&) { connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); QAction actionPreview_file(QIcon(QString::fromUtf8(":/Icons/skin/preview.png")), tr("Preview file"), 0); connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); - QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Set upload limit"), 0); + QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Limit upload rate"), 0); connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); - QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png")), tr("Set download limit"), 0); + QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png")), tr("Limit download rate"), 0); connect(&actionSet_download_limit, SIGNAL(triggered()), this, SLOT(setDlLimitSelectedTorrents())); QAction actionDelete_Permanently(QIcon(QString::fromUtf8(":/Icons/skin/delete_perm.png")), tr("Delete Permanently"), 0); connect(&actionDelete_Permanently, SIGNAL(triggered()), this, SLOT(deletePermSelectedTorrents())); diff --git a/src/peerlistwidget.cpp b/src/peerlistwidget.cpp index 8bc978ce6..b7729c8ef 100644 --- a/src/peerlistwidget.cpp +++ b/src/peerlistwidget.cpp @@ -35,6 +35,7 @@ #include "propertieswidget.h" #include "geoip.h" #include "peeraddition.h" +#include "speedlimitdlg.h" #include #include #include @@ -113,9 +114,23 @@ void PeerListWidget::showPeerListMenu(QPoint) { QMenu menu; QTorrentHandle h = properties->getCurrentTorrent(); if(!h.is_valid()) return; + QModelIndexList selectedIndexes = selectionModel()->selectedRows(); + QStringList selectedPeerIPs; + foreach(const QModelIndex &index, selectedIndexes) { + QString IP = proxyModel->data(index).toString(); + selectedPeerIPs << IP; + } + // Add Peer Action QAction *addPeerAct = 0; if(!h.is_queued() && !h.is_checking()) { - addPeerAct = menu.addAction(QIcon(":/Icons/oxygen/add_peer.png"), "Add a new peer"); + addPeerAct = menu.addAction(QIcon(":/Icons/oxygen/add_peer.png"), tr("Add a new peer")); + } + // Per Peer Speed limiting actions + QAction *upLimitAct = 0; + QAction *dlLimitAct = 0; + if(!selectedPeerIPs.isEmpty()) { + upLimitAct = menu.addAction(QIcon(":/Icons/skin/seeding.png"), tr("Limit upload rate")); + dlLimitAct = menu.addAction(QIcon(":/Icons/skin/downloading.png"), tr("Limit download rate")); } QAction *act = menu.exec(QCursor::pos()); if(act == addPeerAct) { @@ -132,11 +147,63 @@ void PeerListWidget::showPeerListMenu(QPoint) { } return; } + if(act == upLimitAct) { + limitUpRateSelectedPeers(selectedPeerIPs); + return; + } + if(act == dlLimitAct) { + limitDlRateSelectedPeers(selectedPeerIPs); + return; + } } +void PeerListWidget::limitUpRateSelectedPeers(QStringList peer_ips) { + QTorrentHandle h = properties->getCurrentTorrent(); + if(!h.is_valid()) return; + bool ok=false; + long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Upload rate limiting"), -1); + if(!ok) return; + foreach(const QString &ip, peer_ips) { + boost::asio::ip::tcp::endpoint ep = peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint()); + if(ep != boost::asio::ip::tcp::endpoint()) { + qDebug("Settings Upload limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data()); + try { + h.set_peer_upload_limit(ep, limit); + }catch(std::exception) { + std::cerr << "Impossible to apply upload limit to peer" << std::endl; + } + } else { + qDebug("The selected peer no longer exists..."); + } + } +} + +void PeerListWidget::limitDlRateSelectedPeers(QStringList peer_ips) { + QTorrentHandle h = properties->getCurrentTorrent(); + if(!h.is_valid()) return; + bool ok=false; + long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Download rate limiting"), -1); + if(!ok) return; + foreach(const QString &ip, peer_ips) { + boost::asio::ip::tcp::endpoint ep = peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint()); + if(ep != boost::asio::ip::tcp::endpoint()) { + qDebug("Settings Download limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data()); + try { + h.set_peer_download_limit(ep, limit); + }catch(std::exception) { + std::cerr << "Impossible to apply download limit to peer" << std::endl; + } + } else { + qDebug("The selected peer no longer exists..."); + } + } +} + + void PeerListWidget::clear() { qDebug("clearing peer list"); peerItems.clear(); + peerEndpoints.clear(); missingFlags.clear(); int nbrows = listModel->rowCount(); if(nbrows > 0) { @@ -184,6 +251,7 @@ void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_reso } else { // Add new peer peerItems[peer_ip] = addPeer(peer_ip, peer); + peerEndpoints[peer_ip] = peer.ip; } } // Delete peers that are gone @@ -191,6 +259,7 @@ void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_reso while(it.hasNext()) { QString ip = it.next(); missingFlags.remove(ip); + peerEndpoints.remove(ip); QStandardItem *item = peerItems.take(ip); listModel->removeRow(item->row()); } diff --git a/src/peerlistwidget.h b/src/peerlistwidget.h index fa0b245ec..35b9a0049 100644 --- a/src/peerlistwidget.h +++ b/src/peerlistwidget.h @@ -53,6 +53,7 @@ private: PeerListDelegate *listDelegate; QSortFilterProxyModel * proxyModel; QHash peerItems; + QHash peerEndpoints; QSet missingFlags; QPointer resolver; PropertiesWidget* properties; @@ -75,6 +76,8 @@ protected slots: void loadSettings(); void saveSettings() const; void showPeerListMenu(QPoint); + void limitUpRateSelectedPeers(QStringList peer_ips); + void limitDlRateSelectedPeers(QStringList peer_ips); }; #endif // PEERLISTWIDGET_H diff --git a/src/qtorrenthandle.cpp b/src/qtorrenthandle.cpp index 253a251a8..9ef7f8214 100644 --- a/src/qtorrenthandle.cpp +++ b/src/qtorrenthandle.cpp @@ -508,6 +508,16 @@ void QTorrentHandle::connect_peer(asio::ip::tcp::endpoint const& adr, int source h.connect_peer(adr, source); } +void QTorrentHandle::set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const { + Q_ASSERT(h.is_valid()); + h.set_peer_upload_limit(ip, limit); +} + +void QTorrentHandle::set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const { + Q_ASSERT(h.is_valid()); + h.set_peer_download_limit(ip, limit); +} + // // Operators // diff --git a/src/qtorrenthandle.h b/src/qtorrenthandle.h index 9a9bb0cb1..e3e22fd52 100644 --- a/src/qtorrenthandle.h +++ b/src/qtorrenthandle.h @@ -147,6 +147,8 @@ class QTorrentHandle { void super_seeding(bool on) const; void resolve_countries(bool r); void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const; + void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const; + void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const; // // Operators