From 69bf31f4e9fd67dc1a652d3ab814392ae2cd9d5f Mon Sep 17 00:00:00 2001 From: Thomas Piccirello Date: Wed, 18 Sep 2024 10:18:13 -0700 Subject: [PATCH] Add WebAPI for downloading torrent metadata Signed-off-by: Thomas Piccirello --- src/base/bittorrent/torrentdescriptor.cpp | 16 +++++++++++ src/base/bittorrent/torrentdescriptor.h | 1 + src/webui/api/torrentscontroller.cpp | 34 +++++++++++++++++++++-- src/webui/api/torrentscontroller.h | 1 + 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/base/bittorrent/torrentdescriptor.cpp b/src/base/bittorrent/torrentdescriptor.cpp index 437c6557c..a8945d205 100644 --- a/src/base/bittorrent/torrentdescriptor.cpp +++ b/src/base/bittorrent/torrentdescriptor.cpp @@ -141,6 +141,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 f41140fed..74c971c8c 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 6cb903d33..6f74fee36 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -997,9 +997,9 @@ void TorrentsController::addAction() if (!filePrioritiesParam.isEmpty()) { if (urls.size() > 1) - throw APIError(APIErrorType::BadParams, tr("You cannot specify filePriorities when adding multiple torrents")); + throw APIError(APIErrorType::BadParams, tr("Cannot specify filePriorities when adding multiple torrents")); if (!torrents.isEmpty()) - throw APIError(APIErrorType::BadParams, tr("You cannot specify filePriorities when uploading torrent files")); + throw APIError(APIErrorType::BadParams, tr("Cannot specify filePriorities when uploading torrent files")); filePriorities.reserve(filePrioritiesParam.size()); for (const QString &priorityStr : filePrioritiesParam) @@ -2035,6 +2035,36 @@ 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 iter = m_torrentSourceCache.constFind(source); iter != m_torrentSourceCache.constEnd()) + infoHash = iter.value(); + else if (const auto sourceTorrentDescr = BitTorrent::TorrentDescriptor::parse(source)) + infoHash = sourceTorrentDescr.value().infoHash(); + + if (!infoHash.isValid()) + throw APIError(APIErrorType::NotFound); + + const BitTorrent::TorrentDescriptor &torrentDescr = m_torrentMetadataCache.value(infoHash); + if (!torrentDescr.info().has_value()) + throw APIError(APIErrorType::Conflict, tr("Metadata is not yet available")); + + 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")); +} + 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 bbbbc57c6..92f93055b 100644 --- a/src/webui/api/torrentscontroller.h +++ b/src/webui/api/torrentscontroller.h @@ -112,6 +112,7 @@ private slots: void setSSLParametersAction(); void fetchMetadataAction(); void parseMetadataAction(); + void saveMetadataAction(); private: void onDownloadFinished(const Net::DownloadResult &result);