mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-23 09:47:08 +03:00
Merge pull request #12033 from glassez/save-torrent
Allow to save downloaded metadata as torrent file
This commit is contained in:
commit
4d2943a782
8 changed files with 110 additions and 63 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in a new issue