diff --git a/Changelog b/Changelog index 4d3bb61a3..a134fc279 100644 --- a/Changelog +++ b/Changelog @@ -27,6 +27,7 @@ - FEATURE: Improved a lot downloading from urls (using libcommoncpp2 instead of libcurl) - FEATURE: A search request can now be terminated by another - FEATURE: User is now warned when fast resume data was rejected + - FEATURE: Url seeds are now displayed in torrent properties and are editable - I18N: Added Hungarian translation - BUGFIX: Progress of paused torrents is now correct on restart - BUGFIX: Progress column gets sorted on restart it is was during last execution diff --git a/TODO b/TODO index 73c4415f1..fff9b8f12 100644 --- a/TODO +++ b/TODO @@ -31,7 +31,6 @@ // in v1.0.0 (partial) - WIP - Check storage st creation + hasher in torrent creation - test IPv6 support (How? Who uses IPv6?) -- Display Url seeds in torrent properties and allow to edit them - Sorting in Download Status column should be smarter than just an alphabetical sort - Allow to scan multiple directories? - Fix all (or almost all) opened bugs in bug tracker @@ -43,6 +42,7 @@ - Windows port (Chris - Peerkoel) - Translations update - Optimize and cleanup code + - Improve trackers edition code - Wait for some bug fixes in libtorrent : - upload/download limit per torrent - double free or corruption on exit diff --git a/src/GUI.cpp b/src/GUI.cpp index e8762a6d7..50929426a 100644 --- a/src/GUI.cpp +++ b/src/GUI.cpp @@ -147,6 +147,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent){ connect(BTSession, SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString))); connect(BTSession, SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); connect(BTSession, SIGNAL(aboutToDownloadFromUrl(QString)), this, SLOT(displayDownloadingUrlInfos(QString))); + connect(BTSession, SIGNAL(urlSeedProblem(QString, QString)), this, SLOT(addUrlSeedError(QString, QString))); // creating options options = new options_imp(this); connect(options, SIGNAL(status_changed(QString, bool)), this, SLOT(OptionsSaved(QString, bool))); @@ -324,7 +325,11 @@ void GUI::setInfoBar(QString info, QString color){ } void GUI::addFastResumeRejectedAlert(QString name){ - setInfoBar(tr("Fast resume data was rejected for torrent %1, checking again...").arg(name)); + setInfoBar(tr("Fast resume data was rejected for torrent %1, checking again...").arg(name), "red"); +} + +void GUI::addUrlSeedError(QString url, QString msg){ + setInfoBar(tr("Url seed lookup failed for url: %1, message: %2").arg(url).arg(msg), "red"); } void GUI::balloonClicked(){ diff --git a/src/GUI.h b/src/GUI.h index da4bef538..b24a3d0f5 100644 --- a/src/GUI.h +++ b/src/GUI.h @@ -190,6 +190,7 @@ class GUI : public QMainWindow, private Ui::MainWindow{ void setTabText(int index, QString text); void updateFileSize(QString hash); void sortProgressColumnDelayed(); + void addUrlSeedError(QString url, QString msg); protected: void closeEvent(QCloseEvent *); diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index d0fac8226..8e60052db 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -163,6 +163,7 @@ void bittorrent::deleteTorrent(QString hash, bool permanent){ torrentBackup.remove(hash+".trackers"); torrentBackup.remove(hash+".speedLimits"); torrentBackup.remove(hash+".ratio"); + torrentBackup.remove(hash+".urlseeds"); // Remove it from ETAs hash tables ETAstats.take(hash); ETAs.take(hash); @@ -213,6 +214,22 @@ void bittorrent::resumeTorrent(QString hash){ } } +void bittorrent::loadWebSeeds(QString fileHash){ + QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".urlseeds"); + if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return; + QByteArray urlseeds_lines = urlseeds_file.readAll(); + urlseeds_file.close(); + QList url_seeds = urlseeds_lines.split('\n'); + QByteArray url_seed; + torrent_handle h = getTorrentHandle(fileHash); + torrent_info torrentInfo = h.get_torrent_info(); + foreach(url_seed, url_seeds){ + if(!url_seed.isEmpty()) + qDebug("Loading custom url seed: %s", (const char*)url_seed.data()); + torrentInfo.add_url_seed(misc::toString((const char*)url_seed.data())); + } +} + // Add a torrent to the bittorrent session void bittorrent::addTorrent(QString path, bool fromScanDir, bool onStartup, QString from_url){ torrent_handle h; @@ -311,6 +328,8 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, bool onStartup, QStr qDebug("Torrent hash is " + hash.toUtf8()); // Load filtered files loadFilesPriorities(h); + // Load custom url seeds + loadWebSeeds(hash); // Load speed limit from hard drive loadTorrentSpeedLimits(hash); // Load ratio data @@ -894,6 +913,9 @@ void bittorrent::readAlerts(){ else if (fastresume_rejected_alert* p = dynamic_cast(a.get())){ emit fastResumeDataRejected(QString(p->handle.name().c_str())); } + else if (url_seed_alert* p = dynamic_cast(a.get())){ + emit urlSeedProblem(QString(p->url.c_str()), QString(p->msg().c_str())); + } a = s->pop_alert(); } } @@ -951,6 +973,8 @@ void bittorrent::reloadTorrent(const torrent_handle &h){ loadFilesPriorities(new_h); // Load speed limit from hard drive loadTorrentSpeedLimits(fileHash); + // Load custom url seeds + loadWebSeeds(fileHash); // Load ratio data loadDownloadUploadForTorrent(fileHash); // Pause torrent if it was paused last time diff --git a/src/bittorrent.h b/src/bittorrent.h index 70b87b679..82870522c 100644 --- a/src/bittorrent.h +++ b/src/bittorrent.h @@ -106,6 +106,7 @@ class bittorrent : public QObject{ void saveDownloadUploadForTorrent(QString hash); void loadDownloadUploadForTorrent(QString hash); void HandleDownloadFailure(QString url, QString reason); + void loadWebSeeds(QString fileHash); // Session configuration - Setters void setListeningPortsRange(std::pair ports); void setMaxConnections(int maxConnec); @@ -147,6 +148,7 @@ class bittorrent : public QObject{ void peerBlocked(QString); void downloadFromUrlFailure(QString url, QString reason); void fastResumeDataRejected(QString name); + void urlSeedProblem(QString url, QString msg); }; diff --git a/src/properties.ui b/src/properties.ui index 5152e9f6b..e83798d0f 100644 --- a/src/properties.ui +++ b/src/properties.ui @@ -13,28 +13,46 @@ Torrent Properties - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + 0 - + Main infos - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -81,28 +99,55 @@ Torrent infos - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -189,12 +234,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -276,9 +330,7 @@ - - 0 - 0 + 0 0 @@ -304,28 +356,55 @@ Current session - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -402,12 +481,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -476,23 +564,30 @@ - + Trackers - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 0 + 0 0 @@ -518,20 +613,38 @@ Tracker - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -552,18 +665,25 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + - - 0 - 0 + 0 0 @@ -572,12 +692,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -648,12 +777,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -724,12 +862,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -759,12 +906,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + @@ -822,17 +978,118 @@ - + + + Url seeds + + + + + + + 75 + true + + + + The following web seeds are available for this torrent: + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 24 + 24 + + + + + + + + + + + + 24 + 24 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + Torrent content - - 9 - 6 + + 9 + + + 9 + + + 9 + + + 9 + @@ -906,12 +1163,21 @@ - - 0 - 6 + + 0 + + + 0 + + + 0 + + + 0 + diff --git a/src/properties_imp.cpp b/src/properties_imp.cpp index ee14a0284..0c6144037 100644 --- a/src/properties_imp.cpp +++ b/src/properties_imp.cpp @@ -41,6 +41,8 @@ properties::properties(QWidget *parent, bittorrent *BTSession, torrent_handle &h removeTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png"))); lowerTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/downarrow.png"))); riseTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/uparrow.png"))); + addWS_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png"))); + deleteWS_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png"))); setAttribute(Qt::WA_DeleteOnClose); // Set Properties list model PropListModel = new QStandardItemModel(0,4); @@ -61,6 +63,8 @@ properties::properties(QWidget *parent, bittorrent *BTSession, torrent_handle &h connect(actionNormal, SIGNAL(triggered()), this, SLOT(normalSelection())); connect(actionHigh, SIGNAL(triggered()), this, SLOT(highSelection())); connect(actionMaximum, SIGNAL(triggered()), this, SLOT(maximumSelection())); + connect(addWS_button, SIGNAL(clicked()), this, SLOT(askWebSeed())); + connect(deleteWS_button, SIGNAL(clicked()), this, SLOT(deleteSelectedUrlSeeds())); // get Infos from torrent handle fileHash = QString(misc::toString(h.info_hash()).c_str()); torrent_status torrentStatus = h.status(); @@ -112,6 +116,9 @@ properties::properties(QWidget *parent, bittorrent *BTSession, torrent_handle &h PropListModel->setData(PropListModel->index(row, PROGRESS), QVariant((double)fp[i])); } loadPiecesPriorities(); + // List web seeds + loadWebSeedsFromFile(); + loadWebSeeds(); // Incremental download if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".incremental")){ incrementalDownload->setChecked(true); @@ -130,6 +137,27 @@ properties::~properties(){ delete PropListModel; } +void properties::loadWebSeeds(){ + QString url_seed; + torrent_info torrentInfo = h.get_torrent_info(); + std::vector url_seeds = torrentInfo.url_seeds(); + unsigned int nbSeeds = url_seeds.size(); + // Clear url seeds list + listWebSeeds->clear(); + // Add hard coded url seeds + for(unsigned int i=0; iaddItem(url_seed); + } + // Add manually added url seeds + foreach(url_seed, manualUrlSeeds){ + listWebSeeds->addItem(url_seed); + qDebug("Added custom url seed to list: %s", (const char*)url_seed.toUtf8()); + } +} + void properties::loadPiecesPriorities(){ torrent_info torrentInfo = h.get_torrent_info(); unsigned int nbFiles = torrentInfo.num_files(); @@ -238,6 +266,28 @@ void properties::loadTrackers(){ } } +void properties::askWebSeed(){ + bool ok; + // Ask user for a new url seed + QString url_seed = QInputDialog::getText(this, tr("New url seed"), + tr("New url seed:"), QLineEdit::Normal, + "http://www.", &ok); + if(!ok) return; + torrent_info torrentInfo = h.get_torrent_info(); + qDebug("Adding %s web seed", (const char*)url_seed.toUtf8()); + if(manualUrlSeeds.indexOf(url_seed) != -1) { + QMessageBox::warning(this, tr("qBittorrent"), + tr("This url seed is already in the list."), + QMessageBox::Ok); + return; + } + manualUrlSeeds << url_seed; + torrentInfo.add_url_seed(url_seed.toStdString()); + saveWebSeeds(); + // Refresh the seeds list + loadWebSeeds(); +} + // Ask the user for a new tracker // and add it to the download list // if it is not already in it @@ -259,18 +309,44 @@ void properties::askForTracker(){ loadTrackers(); } +void properties::deleteSelectedUrlSeeds(){ + QList selectedItems; + selectedItems = listWebSeeds->selectedItems(); + QListWidgetItem *item; + bool error = false; + foreach(item, selectedItems){ + QString url_seed = item->text(); + int index = manualUrlSeeds.indexOf(url_seed); + if(index != -1){ + manualUrlSeeds.removeAt(index); + qDebug("Removed an url seeds from manualUrlSeeds list"); + }else{ + error = true; + } + } + // Save them to disk + saveWebSeeds(); + // Refresh list + loadWebSeeds(); + if(error){ + QMessageBox::warning(this, tr("qBittorrent"), + tr("Hard-coded url seeds cannot be deleted."), + QMessageBox::Ok); + } +} + void properties::deleteSelectedTrackers(){ std::vector trackers = h.trackers(); QList selectedItems; selectedItems = trackersURLS->selectedItems(); QListWidgetItem *item; - unsigned int nbTrackers = trackers.size(); - if(nbTrackers == (unsigned int) selectedItems.size()){ - QMessageBox::warning(this, tr("qBittorrent"), - tr("Trackers list can't be empty."), - QMessageBox::Ok); - return; - } + unsigned int nbTrackers = trackers.size(); + if(nbTrackers == (unsigned int) selectedItems.size()){ + QMessageBox::warning(this, tr("qBittorrent"), + tr("Trackers list can't be empty."), + QMessageBox::Ok); + return; + } foreach(item, selectedItems){ QString url = item->text(); for(unsigned int i=0; i url_seeds = urlseeds_lines.split('\n'); + manualUrlSeeds.clear(); + QByteArray url_seed; + foreach(url_seed, url_seeds){ + if(!url_seed.isEmpty()) + manualUrlSeeds << url_seed; + } +} + +void properties::saveWebSeeds(){ + QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+fileHash+".urlseeds"); + if(!urlseeds_file.open(QIODevice::WriteOnly | QIODevice::Text)){ + std::cerr << "Error: Could not save url seeds\n"; + return; + } + QString url_seed; + foreach(url_seed, manualUrlSeeds){ + urlseeds_file.write(QByteArray((const char*)(url_seed+"\n").toUtf8())); + } + urlseeds_file.close(); + qDebug("url seeds were saved"); +} + void properties::savePiecesPriorities(){ if(!changedFilteredfiles) return; qDebug("Saving pieces priorities"); diff --git a/src/properties_imp.h b/src/properties_imp.h index 4767da661..c8fcd022f 100644 --- a/src/properties_imp.h +++ b/src/properties_imp.h @@ -43,6 +43,7 @@ class properties : public QDialog, private Ui::properties{ bool has_filtered_files; bool changedFilteredfiles; bittorrent *BTSession; + QStringList manualUrlSeeds; protected slots: void on_okButton_clicked(); @@ -62,6 +63,11 @@ class properties : public QDialog, private Ui::properties{ void normalSelection(); void highSelection(); void maximumSelection(); + void loadWebSeeds(); + void askWebSeed(); + void saveWebSeeds(); + void loadWebSeedsFromFile(); + void deleteSelectedUrlSeeds(); signals: void filteredFilesChanged(QString fileHash);