mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-10 23:07:59 +00:00
Merge pull request #16886 from Chocobo1/export
Add "Export .torrent" action
This commit is contained in:
commit
eba5a48abd
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
const int TorrentIDTypeId = qRegisterMetaType<BitTorrent::TorrentID>();
|
const int TorrentIDTypeId = qRegisterMetaType<BitTorrent::TorrentID>();
|
||||||
|
|
||||||
BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash)
|
BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash)
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
#include <QHash>
|
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
|
||||||
#include "base/digest32.h"
|
#include "base/digest32.h"
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
#include "base/tagset.h"
|
#include "base/tagset.h"
|
||||||
#include "abstractfilestorage.h"
|
#include "abstractfilestorage.h"
|
||||||
@ -300,6 +301,7 @@ namespace BitTorrent
|
|||||||
virtual void clearPeers() = 0;
|
virtual void clearPeers() = 0;
|
||||||
|
|
||||||
virtual QString createMagnetURI() const = 0;
|
virtual QString createMagnetURI() const = 0;
|
||||||
|
virtual nonstd::expected<void, QString> exportToFile(const Path &path) const = 0;
|
||||||
|
|
||||||
TorrentID id() const;
|
TorrentID id() const;
|
||||||
bool isResumed() const;
|
bool isResumed() const;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include <libtorrent/address.hpp>
|
#include <libtorrent/address.hpp>
|
||||||
#include <libtorrent/alert_types.hpp>
|
#include <libtorrent/alert_types.hpp>
|
||||||
|
#include <libtorrent/create_torrent.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#include <libtorrent/storage_defs.hpp>
|
#include <libtorrent/storage_defs.hpp>
|
||||||
@ -53,6 +54,7 @@
|
|||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/utils/io.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "downloadpriority.h"
|
#include "downloadpriority.h"
|
||||||
@ -2232,6 +2234,37 @@ QString TorrentImpl::createMagnetURI() const
|
|||||||
return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle));
|
return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) const
|
||||||
|
{
|
||||||
|
if (!hasMetadata())
|
||||||
|
return nonstd::make_unexpected(tr("Missing metadata"));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
|
const std::shared_ptr<lt::torrent_info> completeTorrentInfo = m_nativeHandle.torrent_file_with_hashes();
|
||||||
|
const std::shared_ptr<lt::torrent_info> torrentInfo = {completeTorrentInfo ? completeTorrentInfo : info().nativeInfo()};
|
||||||
|
#else
|
||||||
|
const std::shared_ptr<lt::torrent_info> torrentInfo = info().nativeInfo();
|
||||||
|
#endif
|
||||||
|
auto creator = lt::create_torrent(*torrentInfo);
|
||||||
|
|
||||||
|
for (const TrackerEntry &entry : asConst(trackers()))
|
||||||
|
creator.add_tracker(entry.url.toStdString(), entry.tier);
|
||||||
|
|
||||||
|
const lt::entry torrentEntry = creator.generate();
|
||||||
|
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, torrentEntry);
|
||||||
|
if (!result)
|
||||||
|
return result.get_unexpected();
|
||||||
|
}
|
||||||
|
catch (const lt::system_error &err)
|
||||||
|
{
|
||||||
|
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
||||||
{
|
{
|
||||||
if (!hasMetadata()) return;
|
if (!hasMetadata()) return;
|
||||||
|
@ -227,6 +227,7 @@ namespace BitTorrent
|
|||||||
void clearPeers() override;
|
void clearPeers() override;
|
||||||
|
|
||||||
QString createMagnetURI() const override;
|
QString createMagnetURI() const override;
|
||||||
|
nonstd::expected<void, QString> exportToFile(const Path &path) const;
|
||||||
|
|
||||||
bool needSaveResumeData() const;
|
bool needSaveResumeData() const;
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/3rdparty/expected.hpp"
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include "base/bittorrent/trackerentry.h"
|
#include "base/bittorrent/trackerentry.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/path.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/torrentfilter.h"
|
#include "base/torrentfilter.h"
|
||||||
#include "base/utils/compare.h"
|
#include "base/utils/compare.h"
|
||||||
@ -757,6 +758,57 @@ void TransferListWidget::editTorrentTrackers()
|
|||||||
trackerDialog->open();
|
trackerDialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TransferListWidget::exportTorrent()
|
||||||
|
{
|
||||||
|
if (getSelectedTorrents().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto fileDialog = new QFileDialog(this, tr("Choose folder to save exported .torrent files"));
|
||||||
|
fileDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
fileDialog->setFileMode(QFileDialog::Directory);
|
||||||
|
fileDialog->setOptions(QFileDialog::ShowDirsOnly);
|
||||||
|
connect(fileDialog, &QFileDialog::fileSelected, this, [this](const QString &dir)
|
||||||
|
{
|
||||||
|
const QVector<BitTorrent::Torrent *> torrents = getSelectedTorrents();
|
||||||
|
if (torrents.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Path savePath {dir};
|
||||||
|
if (!savePath.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QString errorMsg = tr("Export .torrent file failed. Torrent: \"%1\". Save path: \"%2\". Reason: \"%3\"");
|
||||||
|
|
||||||
|
bool hasError = false;
|
||||||
|
for (const BitTorrent::Torrent *torrent : torrents)
|
||||||
|
{
|
||||||
|
const Path filePath = savePath / Path(torrent->name() + u".torrent");
|
||||||
|
if (filePath.exists())
|
||||||
|
{
|
||||||
|
LogMsg(errorMsg.arg(torrent->name(), filePath.toString(), tr("A file with the same name already exists")) , Log::WARNING);
|
||||||
|
hasError = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonstd::expected<void, QString> result = torrent->exportToFile(filePath);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LogMsg(errorMsg.arg(torrent->name(), filePath.toString(), result.error()) , Log::WARNING);
|
||||||
|
hasError = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasError)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Export .torrent file error")
|
||||||
|
, tr("Errors occured when exporting .torrent files. Check execution log for details."));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fileDialog->open();
|
||||||
|
}
|
||||||
|
|
||||||
void TransferListWidget::confirmRemoveAllTagsForSelection()
|
void TransferListWidget::confirmRemoveAllTagsForSelection()
|
||||||
{
|
{
|
||||||
QMessageBox::StandardButton response = QMessageBox::question(
|
QMessageBox::StandardButton response = QMessageBox::question(
|
||||||
@ -906,6 +958,8 @@ void TransferListWidget::displayListMenu()
|
|||||||
connect(actionAutoTMM, &QAction::triggered, this, &TransferListWidget::setSelectedAutoTMMEnabled);
|
connect(actionAutoTMM, &QAction::triggered, this, &TransferListWidget::setSelectedAutoTMMEnabled);
|
||||||
auto *actionEditTracker = new QAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Edit trackers..."), listMenu);
|
auto *actionEditTracker = new QAction(UIThemeManager::instance()->getIcon(u"edit-rename"_qs), tr("Edit trackers..."), listMenu);
|
||||||
connect(actionEditTracker, &QAction::triggered, this, &TransferListWidget::editTorrentTrackers);
|
connect(actionEditTracker, &QAction::triggered, this, &TransferListWidget::editTorrentTrackers);
|
||||||
|
auto *actionExportTorrent = new QAction(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Export .torrent..."), listMenu);
|
||||||
|
connect(actionExportTorrent, &QAction::triggered, this, &TransferListWidget::exportTorrent);
|
||||||
// End of actions
|
// End of actions
|
||||||
|
|
||||||
// Enable/disable pause/start action given the DL state
|
// Enable/disable pause/start action given the DL state
|
||||||
@ -1161,8 +1215,7 @@ void TransferListWidget::displayListMenu()
|
|||||||
queueMenu->addAction(actionBottomQueuePos);
|
queueMenu->addAction(actionBottomQueuePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu *copySubMenu = listMenu->addMenu(
|
QMenu *copySubMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy"));
|
||||||
UIThemeManager::instance()->getIcon(u"edit-copy"_qs), tr("Copy"));
|
|
||||||
copySubMenu->addAction(actionCopyName);
|
copySubMenu->addAction(actionCopyName);
|
||||||
copySubMenu->addAction(actionCopyHash1);
|
copySubMenu->addAction(actionCopyHash1);
|
||||||
actionCopyHash1->setEnabled(hasInfohashV1);
|
actionCopyHash1->setEnabled(hasInfohashV1);
|
||||||
@ -1171,6 +1224,9 @@ void TransferListWidget::displayListMenu()
|
|||||||
copySubMenu->addAction(actionCopyMagnetLink);
|
copySubMenu->addAction(actionCopyMagnetLink);
|
||||||
copySubMenu->addAction(actionCopyID);
|
copySubMenu->addAction(actionCopyID);
|
||||||
|
|
||||||
|
actionExportTorrent->setToolTip(u"Exported torrent is not necessarily the same as the imported"_qs);
|
||||||
|
listMenu->addAction(actionExportTorrent);
|
||||||
|
|
||||||
listMenu->popup(QCursor::pos());
|
listMenu->popup(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@
|
|||||||
#include <QTreeView>
|
#include <QTreeView>
|
||||||
|
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
#include "base/path.h"
|
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
class Path;
|
||||||
class TransferListModel;
|
class TransferListModel;
|
||||||
class TransferListSortModel;
|
class TransferListSortModel;
|
||||||
|
|
||||||
@ -124,6 +124,7 @@ private:
|
|||||||
QVector<BitTorrent::Torrent *> getSelectedTorrents() const;
|
QVector<BitTorrent::Torrent *> getSelectedTorrents() const;
|
||||||
void askAddTagsForSelection();
|
void askAddTagsForSelection();
|
||||||
void editTorrentTrackers();
|
void editTorrentTrackers();
|
||||||
|
void exportTorrent();
|
||||||
void confirmRemoveAllTagsForSelection();
|
void confirmRemoveAllTagsForSelection();
|
||||||
QStringList askTagsForSelection(const QString &dialogTitle);
|
QStringList askTagsForSelection(const QString &dialogTitle);
|
||||||
void applyToSelectedTorrents(const std::function<void (BitTorrent::Torrent *const)> &fn);
|
void applyToSelectedTorrents(const std::function<void (BitTorrent::Torrent *const)> &fn);
|
||||||
|
Loading…
Reference in New Issue
Block a user