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();
|
torrent->saveResumeData();
|
||||||
|
|
||||||
// Save metadata
|
// Save metadata
|
||||||
const QDir resumeDataDir(m_resumeFolderPath);
|
const QDir resumeDataDir {m_resumeFolderPath};
|
||||||
QString torrentFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
|
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())};
|
||||||
if (torrent->saveTorrentFile(torrentFile)) {
|
try {
|
||||||
|
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
|
||||||
// Copy the torrent file to the export folder
|
// Copy the torrent file to the export folder
|
||||||
if (!torrentExportDirectory().isEmpty())
|
if (!torrentExportDirectory().isEmpty())
|
||||||
exportTorrentFile(torrent);
|
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);
|
emit torrentMetadataLoaded(torrent);
|
||||||
}
|
}
|
||||||
|
@ -4352,15 +4357,17 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
|
||||||
// The following is useless for newly added magnet
|
// The following is useless for newly added magnet
|
||||||
if (!fromMagnetUri) {
|
if (!fromMagnetUri) {
|
||||||
// Backup torrent file
|
// Backup torrent file
|
||||||
const QDir resumeDataDir(m_resumeFolderPath);
|
const QDir resumeDataDir {m_resumeFolderPath};
|
||||||
const QString newFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
|
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->hash())};
|
||||||
if (torrent->saveTorrentFile(newFile)) {
|
try {
|
||||||
|
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
|
||||||
// Copy the torrent file to the export folder
|
// Copy the torrent file to the export folder
|
||||||
if (!torrentExportDirectory().isEmpty())
|
if (!torrentExportDirectory().isEmpty())
|
||||||
exportTorrentFile(torrent);
|
exportTorrentFile(torrent);
|
||||||
}
|
}
|
||||||
else {
|
catch (const RuntimeError &err) {
|
||||||
LogMsg(tr("Couldn't save '%1.torrent'").arg(torrent->hash()), Log::CRITICAL);
|
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/address.hpp>
|
||||||
#include <libtorrent/alert_types.hpp>
|
#include <libtorrent/alert_types.hpp>
|
||||||
#include <libtorrent/bencode.hpp>
|
|
||||||
#include <libtorrent/create_torrent.hpp>
|
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
#include <libtorrent/time.hpp>
|
#include <libtorrent/time.hpp>
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
#if (LIBTORRENT_VERSION_NUM >= 10200)
|
#if (LIBTORRENT_VERSION_NUM >= 10200)
|
||||||
|
#include <libtorrent/storage_defs.hpp>
|
||||||
#include <libtorrent/write_resume_data.hpp>
|
#include <libtorrent/write_resume_data.hpp>
|
||||||
|
#else
|
||||||
|
#include <libtorrent/storage.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QBitArray>
|
#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());
|
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)
|
void TorrentHandle::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||||
{
|
{
|
||||||
updateStatus(nativeStatus);
|
updateStatus(nativeStatus);
|
||||||
|
|
|
@ -319,7 +319,6 @@ namespace BitTorrent
|
||||||
void forceDHTAnnounce();
|
void forceDHTAnnounce();
|
||||||
void forceRecheck();
|
void forceRecheck();
|
||||||
void renameFile(int index, const QString &name);
|
void renameFile(int index, const QString &name);
|
||||||
bool saveTorrentFile(const QString &path);
|
|
||||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities);
|
void prioritizeFiles(const QVector<DownloadPriority> &priorities);
|
||||||
void setRatioLimit(qreal limit);
|
void setRatioLimit(qreal limit);
|
||||||
void setSeedingTimeLimit(int limit);
|
void setSeedingTimeLimit(int limit);
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <libtorrent/bencode.hpp>
|
||||||
|
#include <libtorrent/create_torrent.hpp>
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
@ -42,6 +44,7 @@
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
|
@ -151,6 +154,27 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
|
||||||
return load(data, error);
|
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
|
bool TorrentInfo::isValid() const
|
||||||
{
|
{
|
||||||
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
|
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 load(const QByteArray &data, QString *error = nullptr) noexcept;
|
||||||
static TorrentInfo loadFromFile(const QString &path, 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);
|
TorrentInfo &operator=(const TorrentInfo &other);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QFileDialog>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
#include "base/bittorrent/magneturi.h"
|
#include "base/bittorrent/magneturi.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrenthandle.h"
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/net/downloadmanager.h"
|
#include "base/net/downloadmanager.h"
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
|
@ -95,6 +97,8 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
|
||||||
|
|
||||||
m_ui->lblMetaLoading->setVisible(false);
|
m_ui->lblMetaLoading->setVisible(false);
|
||||||
m_ui->progMetaLoading->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->setMode(FileSystemPathEdit::Mode::DirectorySave);
|
||||||
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
m_ui->savePath->setDialogCaption(tr("Choose save path"));
|
||||||
|
@ -437,6 +441,30 @@ void AddNewTorrentDialog::setSavePath(const QString &newPath)
|
||||||
onSavePathChanged(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()
|
void AddNewTorrentDialog::populateSavePathComboBox()
|
||||||
{
|
{
|
||||||
m_ui->savePath->clear();
|
m_ui->savePath->clear();
|
||||||
|
@ -590,6 +618,7 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
|
||||||
m_ui->lblMetaLoading->setVisible(true);
|
m_ui->lblMetaLoading->setVisible(true);
|
||||||
m_ui->lblMetaLoading->setText(labelText);
|
m_ui->lblMetaLoading->setText(labelText);
|
||||||
m_ui->progMetaLoading->setVisible(visibleIndicator);
|
m_ui->progMetaLoading->setVisible(visibleIndicator);
|
||||||
|
m_ui->buttonSave->setVisible(!visibleIndicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddNewTorrentDialog::setupTreeview()
|
void AddNewTorrentDialog::setupTreeview()
|
||||||
|
|
|
@ -104,6 +104,7 @@ private:
|
||||||
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
|
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = {});
|
||||||
void setupTreeview();
|
void setupTreeview();
|
||||||
void setSavePath(const QString &newPath);
|
void setSavePath(const QString &newPath);
|
||||||
|
void saveTorrentFile();
|
||||||
|
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
|
|
@ -256,8 +256,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>421</width>
|
<width>333</width>
|
||||||
<height>68</height>
|
<height>69</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
@ -364,33 +364,41 @@
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QProgressBar" name="progMetaLoading">
|
<layout class="QHBoxLayout" name="metadataLayout">
|
||||||
<property name="sizePolicy">
|
<item>
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
<widget class="QProgressBar" name="progMetaLoading">
|
||||||
<horstretch>0</horstretch>
|
<property name="sizePolicy">
|
||||||
<verstretch>0</verstretch>
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
</sizepolicy>
|
<horstretch>0</horstretch>
|
||||||
</property>
|
<verstretch>0</verstretch>
|
||||||
<property name="maximum">
|
</sizepolicy>
|
||||||
<number>0</number>
|
</property>
|
||||||
</property>
|
<property name="maximum">
|
||||||
<property name="value">
|
<number>0</number>
|
||||||
<number>-1</number>
|
</property>
|
||||||
</property>
|
<property name="value">
|
||||||
<property name="textVisible">
|
<number>-1</number>
|
||||||
<bool>false</bool>
|
</property>
|
||||||
</property>
|
<property name="textVisible">
|
||||||
</widget>
|
<bool>false</bool>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
</widget>
|
||||||
<widget class="QLabel" name="lblMetaLoading">
|
</item>
|
||||||
<property name="sizePolicy">
|
<item>
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<widget class="QLabel" name="lblMetaLoading">
|
||||||
<horstretch>0</horstretch>
|
<property name="text">
|
||||||
<verstretch>0</verstretch>
|
<string/>
|
||||||
</sizepolicy>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="buttonSave">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save as .torrent file...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
|
Loading…
Reference in a new issue