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 9adc963d27d..771e447d127 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -1895,6 +1895,44 @@ 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()); + + BitTorrent::InfoHash infoHash; + if (const auto sourceTorrentDescr = BitTorrent::TorrentDescriptor::parse(source)) + infoHash = sourceTorrentDescr.value().infoHash(); + else if (const auto cachedInfoHash = m_torrentSourceCache.get(source)) + infoHash = cachedInfoHash.value(); + + if (infoHash != BitTorrent::InfoHash {}) + { + // metadata may be cached from previous request + if (const auto torrentDescr = m_torrentMetadataCache.get(infoHash)) + { + const nonstd::expected result = torrentDescr.value().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")); + } + else + { + throw APIError(APIErrorType::Conflict, tr("Metadata is not yet available")); + } + } + else + { + 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 61a24ef6194..48adda94dda 100644 --- a/src/webui/api/torrentscontroller.h +++ b/src/webui/api/torrentscontroller.h @@ -108,6 +108,7 @@ private slots: void setSSLParametersAction(); void fetchMetadataAction(); void parseMetadataAction(); + void saveMetadataAction(); private: void onDownloadFinished(const Net::DownloadResult &result);