From dd0cee44c16d0c4caa6c775ed067ceaf44e81a50 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 19 Feb 2020 13:19:51 +0300 Subject: [PATCH] Allow to save downloaded metadata as torrent file --- src/base/bittorrent/session.cpp | 23 ++++++---- src/base/bittorrent/torrenthandle.cpp | 28 ++---------- src/base/bittorrent/torrenthandle.h | 1 - src/base/bittorrent/torrentinfo.cpp | 24 ++++++++++ src/base/bittorrent/torrentinfo.h | 1 + src/gui/addnewtorrentdialog.cpp | 29 ++++++++++++ src/gui/addnewtorrentdialog.h | 1 + src/gui/addnewtorrentdialog.ui | 66 +++++++++++++++------------ 8 files changed, 110 insertions(+), 63 deletions(-) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index f1db4eb92..a8ad282fb 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -3831,13 +3831,18 @@ void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent) torrent->saveResumeData(); // Save metadata - const QDir resumeDataDir(m_resumeFolderPath); - QString torrentFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash())); - if (torrent->saveTorrentFile(torrentFile)) { + const QDir resumeDataDir {m_resumeFolderPath}; + const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; + try { + torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName)); // Copy the torrent file to the export folder if (!torrentExportDirectory().isEmpty()) exportTorrentFile(torrent); } + catch (const RuntimeError &err) { + LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2") + .arg(torrentFileName, err.message()), Log::CRITICAL); + } emit torrentMetadataLoaded(torrent); } @@ -4352,15 +4357,17 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) // The following is useless for newly added magnet if (!fromMagnetUri) { // Backup torrent file - const QDir resumeDataDir(m_resumeFolderPath); - const QString newFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash())); - if (torrent->saveTorrentFile(newFile)) { + const QDir resumeDataDir {m_resumeFolderPath}; + const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())}; + try { + torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName)); // Copy the torrent file to the export folder if (!torrentExportDirectory().isEmpty()) exportTorrentFile(torrent); } - else { - LogMsg(tr("Couldn't save '%1.torrent'").arg(torrent->hash()), Log::CRITICAL); + catch (const RuntimeError &err) { + LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2") + .arg(torrentFileName, err.message()), Log::CRITICAL); } } diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index ebde146af..daf7d2ba5 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -38,15 +38,16 @@ #include #include -#include -#include #include #include #include #include #if (LIBTORRENT_VERSION_NUM >= 10200) +#include #include +#else +#include #endif #include @@ -1533,29 +1534,6 @@ void TorrentHandle::renameFile(const int index, const QString &name) m_nativeHandle.rename_file(LTFileIndex {index}, Utils::Fs::toNativePath(name).toStdString()); } -bool TorrentHandle::saveTorrentFile(const QString &path) -{ - if (!m_torrentInfo.isValid()) return false; -#if (LIBTORRENT_VERSION_NUM < 10200) - const lt::create_torrent torrentCreator = lt::create_torrent(*(m_torrentInfo.nativeInfo()), true); -#else - const lt::create_torrent torrentCreator = lt::create_torrent(*(m_torrentInfo.nativeInfo())); -#endif - const lt::entry torrentEntry = torrentCreator.generate(); - - QByteArray out; - out.reserve(1024 * 1024); // most torrent file sizes are under 1 MB - lt::bencode(std::back_inserter(out), torrentEntry); - if (out.isEmpty()) - return false; - - QFile torrentFile(path); - if (torrentFile.open(QIODevice::WriteOnly)) - return (torrentFile.write(out) == out.size()); - - return false; -} - void TorrentHandle::handleStateUpdate(const lt::torrent_status &nativeStatus) { updateStatus(nativeStatus); diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 4a4def445..8c8d813cc 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -319,7 +319,6 @@ namespace BitTorrent void forceDHTAnnounce(); void forceRecheck(); void renameFile(int index, const QString &name); - bool saveTorrentFile(const QString &path); void prioritizeFiles(const QVector &priorities); void setRatioLimit(qreal limit); void setSeedingTimeLimit(int limit); diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp index 1e403b9ec..4838a80a5 100644 --- a/src/base/bittorrent/torrentinfo.cpp +++ b/src/base/bittorrent/torrentinfo.cpp @@ -32,6 +32,8 @@ #include #endif +#include +#include #include #include @@ -42,6 +44,7 @@ #include #include +#include "base/exceptions.h" #include "base/global.h" #include "base/utils/fs.h" #include "base/utils/misc.h" @@ -151,6 +154,27 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc return load(data, error); } +void TorrentInfo::saveToFile(const QString &path) const +{ + if (!isValid()) + throw RuntimeError {tr("Invalid metadata.")}; + +#if (LIBTORRENT_VERSION_NUM < 10200) + const lt::create_torrent torrentCreator = lt::create_torrent(*(nativeInfo()), true); +#else + const lt::create_torrent torrentCreator = lt::create_torrent(*(nativeInfo())); +#endif + const lt::entry torrentEntry = torrentCreator.generate(); + + QByteArray out; + out.reserve(1024 * 1024); // most torrent file sizes are under 1 MB + lt::bencode(std::back_inserter(out), torrentEntry); + + QFile torrentFile{path}; + if (!torrentFile.open(QIODevice::WriteOnly) || (torrentFile.write(out) != out.size())) + throw RuntimeError {torrentFile.errorString()}; +} + bool TorrentInfo::isValid() const { return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0)); diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h index b94461324..6d140007b 100644 --- a/src/base/bittorrent/torrentinfo.h +++ b/src/base/bittorrent/torrentinfo.h @@ -66,6 +66,7 @@ namespace BitTorrent static TorrentInfo load(const QByteArray &data, QString *error = nullptr) noexcept; static TorrentInfo loadFromFile(const QString &path, QString *error = nullptr) noexcept; + void saveToFile(const QString &path) const; TorrentInfo &operator=(const TorrentInfo &other); diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 55fa6f98c..dae5d960e 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include "base/bittorrent/magneturi.h" #include "base/bittorrent/session.h" #include "base/bittorrent/torrenthandle.h" +#include "base/exceptions.h" #include "base/global.h" #include "base/net/downloadmanager.h" #include "base/settingsstorage.h" @@ -95,6 +97,8 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP m_ui->lblMetaLoading->setVisible(false); m_ui->progMetaLoading->setVisible(false); + m_ui->buttonSave->setVisible(false); + connect(m_ui->buttonSave, &QPushButton::clicked, this, &AddNewTorrentDialog::saveTorrentFile); m_ui->savePath->setMode(FileSystemPathEdit::Mode::DirectorySave); m_ui->savePath->setDialogCaption(tr("Choose save path")); @@ -437,6 +441,30 @@ void AddNewTorrentDialog::setSavePath(const QString &newPath) onSavePathChanged(newPath); } +void AddNewTorrentDialog::saveTorrentFile() +{ + Q_ASSERT(m_hasMetadata); + + const QString torrentFileExtension {C_TORRENT_FILE_EXTENSION}; + const QString filter {QString{"Torrent file (*%1)"}.arg(torrentFileExtension)}; + + QString path = QFileDialog::getSaveFileName( + this, tr("Save as torrent file") + , QDir::home().absoluteFilePath(m_torrentInfo.name() + torrentFileExtension) + , filter); + if (path.isEmpty()) return; + + if (!path.endsWith(torrentFileExtension, Qt::CaseInsensitive)) + path += torrentFileExtension; + + try { + m_torrentInfo.saveToFile(path); + } + catch (const RuntimeError &err) { + QMessageBox::critical(this, tr("I/O Error"), err.message()); + } +} + void AddNewTorrentDialog::populateSavePathComboBox() { m_ui->savePath->clear(); @@ -590,6 +618,7 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co m_ui->lblMetaLoading->setVisible(true); m_ui->lblMetaLoading->setText(labelText); m_ui->progMetaLoading->setVisible(visibleIndicator); + m_ui->buttonSave->setVisible(!visibleIndicator); } void AddNewTorrentDialog::setupTreeview() diff --git a/src/gui/addnewtorrentdialog.h b/src/gui/addnewtorrentdialog.h index 00ddb9a9d..6f9b18fa5 100644 --- a/src/gui/addnewtorrentdialog.h +++ b/src/gui/addnewtorrentdialog.h @@ -104,6 +104,7 @@ private: void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {}); void setupTreeview(); void setSavePath(const QString &newPath); + void saveTorrentFile(); void showEvent(QShowEvent *event) override; diff --git a/src/gui/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui index 23140e37d..181f16410 100644 --- a/src/gui/addnewtorrentdialog.ui +++ b/src/gui/addnewtorrentdialog.ui @@ -256,8 +256,8 @@ 0 0 - 421 - 68 + 333 + 69 @@ -364,33 +364,41 @@ - - - - 0 - 0 - - - - 0 - - - -1 - - - false - - - - - - - - 0 - 0 - - - + + + + + + 0 + 0 + + + + 0 + + + -1 + + + false + + + + + + + + + + + + + + Save as .torrent file... + + + +