diff --git a/src/base/bittorrent/torrentdescriptor.cpp b/src/base/bittorrent/torrentdescriptor.cpp index 42e969b0d59..0ce08b39fd0 100644 --- a/src/base/bittorrent/torrentdescriptor.cpp +++ b/src/base/bittorrent/torrentdescriptor.cpp @@ -143,6 +143,22 @@ catch (const lt::system_error &err) return nonstd::make_unexpected(QString::fromLocal8Bit(err.what())); } +nonstd::expected BitTorrent::TorrentDescriptor::saveToBuffer() const +try +{ + const lt::entry torrentEntry = lt::write_torrent_file(m_ltAddTorrentParams); + // usually torrent size should be smaller than 1 MB, + // however there are >100 MB v2/hybrid torrent files out in the wild + QByteArray buffer; + buffer.reserve(1024 * 1024); + lt::bencode(std::back_inserter(buffer), torrentEntry); + return buffer; +} +catch (const lt::system_error &err) +{ + return nonstd::make_unexpected(QString::fromLocal8Bit(err.what())); +} + BitTorrent::TorrentDescriptor::TorrentDescriptor(lt::add_torrent_params ltAddTorrentParams) : m_ltAddTorrentParams {std::move(ltAddTorrentParams)} { diff --git a/src/base/bittorrent/torrentdescriptor.h b/src/base/bittorrent/torrentdescriptor.h index 54248ae5768..b62dba945f4 100644 --- a/src/base/bittorrent/torrentdescriptor.h +++ b/src/base/bittorrent/torrentdescriptor.h @@ -69,6 +69,7 @@ namespace BitTorrent static nonstd::expected loadFromFile(const Path &path) noexcept; static nonstd::expected parse(const QString &str) noexcept; nonstd::expected saveToFile(const Path &path) const; + nonstd::expected saveToBuffer() const; const lt::add_torrent_params <AddTorrentParams() const; diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 97d371fd496..580640f9b70 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -1962,6 +1962,35 @@ void TorrentsController::parseMetadataAction() setResult(result); } +void TorrentsController::saveMetadataAction() +{ + requireParams({u"source"_s}); + + const QString sourceParam = params()[u"source"_s].trimmed(); + if (sourceParam.isEmpty()) + throw APIError(APIErrorType::BadParams, tr("Must specify URI or hash")); + + const QString source = QUrl::fromPercentEncoding(sourceParam.toLatin1()); + if (const auto iter = m_torrentSource.find(source); iter != m_torrentSource.end()) + { + const BitTorrent::InfoHash &infoHash = iter.value(); + if (isMetadataDownloaded(infoHash)) + { + const BitTorrent::TorrentDescriptor &torrentDescr = m_torrentMetadata[infoHash]; + const nonstd::expected result = torrentDescr.saveToBuffer(); + if (!result) + throw APIError(APIErrorType::Conflict, tr("Unable to export torrent metadata. Error: %1").arg(result.error())); + + setResult(result.value(), u"application/x-bittorrent"_s, (infoHash.toTorrentID().toString() + u".torrent")); + return; + } + + throw APIError(APIErrorType::Conflict, tr("Metadata is not yet available")); + } + + throw APIError(APIErrorType::NotFound); +} + void TorrentsController::onDownloadFinished(const Net::DownloadResult &result) { const QString &source = result.url; diff --git a/src/webui/api/torrentscontroller.h b/src/webui/api/torrentscontroller.h index dcceff62c8d..528d27d99f9 100644 --- a/src/webui/api/torrentscontroller.h +++ b/src/webui/api/torrentscontroller.h @@ -106,6 +106,7 @@ private slots: void setSSLParametersAction(); void fetchMetadataAction(); void parseMetadataAction(); + void saveMetadataAction(); private: void onMetadataDownloaded(const BitTorrent::TorrentInfo &info);