Allow to save downloaded metadata as torrent file

This commit is contained in:
Vladimir Golovnev (Glassez) 2020-02-19 13:19:51 +03:00
parent 06ceac4cda
commit dd0cee44c1
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
8 changed files with 110 additions and 63 deletions

View File

@ -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);
}
}

View File

@ -38,15 +38,16 @@
#include <libtorrent/address.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/time.hpp>
#include <libtorrent/version.hpp>
#if (LIBTORRENT_VERSION_NUM >= 10200)
#include <libtorrent/storage_defs.hpp>
#include <libtorrent/write_resume_data.hpp>
#else
#include <libtorrent/storage.hpp>
#endif
#include <QBitArray>
@ -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);

View File

@ -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<DownloadPriority> &priorities);
void setRatioLimit(qreal limit);
void setSeedingTimeLimit(int limit);

View File

@ -32,6 +32,8 @@
#include <boost/optional.hpp>
#endif
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/error_code.hpp>
#include <QByteArray>
@ -42,6 +44,7 @@
#include <QStringList>
#include <QUrl>
#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));

View File

@ -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);

View File

@ -30,6 +30,7 @@
#include <QDebug>
#include <QDir>
#include <QFileDialog>
#include <QMenu>
#include <QPushButton>
#include <QShortcut>
@ -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()

View File

@ -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;

View File

@ -256,8 +256,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>68</height>
<width>333</width>
<height>69</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -364,33 +364,41 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QProgressBar" name="progMetaLoading">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-1</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblMetaLoading">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<layout class="QHBoxLayout" name="metadataLayout">
<item>
<widget class="QProgressBar" name="progMetaLoading">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>-1</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblMetaLoading">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonSave">
<property name="text">
<string>Save as .torrent file...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">