mirror of
https://github.com/transmission/transmission
synced 2024-12-31 20:16:57 +00:00
feat: support webseeds in transmission-create (#2611)
This commit is contained in:
parent
e14c7f38e5
commit
2410ad2fa6
7 changed files with 121 additions and 11 deletions
|
@ -284,7 +284,7 @@ void MakeDialog::Impl::onResponse(int response)
|
|||
else
|
||||
{
|
||||
announce_urls.push_front(str);
|
||||
trackers.push_back(tr_tracker_info{ tier, announce_urls.front().data(), nullptr, 0 });
|
||||
trackers.push_back(tr_tracker_info{ tier, announce_urls.front().data() });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,6 +295,8 @@ void MakeDialog::Impl::onResponse(int response)
|
|||
target.c_str(),
|
||||
trackers.data(),
|
||||
trackers.size(),
|
||||
nullptr,
|
||||
0,
|
||||
useComment ? comment.c_str() : nullptr,
|
||||
isPrivate,
|
||||
useSource ? source.c_str() : nullptr);
|
||||
|
|
|
@ -234,6 +234,11 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder* builder)
|
|||
tr_free(builder->trackers[i].announce);
|
||||
}
|
||||
|
||||
for (int i = 0; i < builder->webseedCount; ++i)
|
||||
{
|
||||
tr_free(builder->webseeds[i]);
|
||||
}
|
||||
|
||||
tr_free(builder->trackers);
|
||||
tr_free(builder->outputFile);
|
||||
tr_free(builder);
|
||||
|
@ -420,7 +425,6 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder)
|
|||
{
|
||||
tr_variant top;
|
||||
|
||||
/* allow an empty set, but if URLs *are* listed, verify them. #814, #971 */
|
||||
for (int i = 0; i < builder->trackerCount && builder->result == TrMakemetaResult::OK; ++i)
|
||||
{
|
||||
if (!tr_urlIsValidTracker(builder->trackers[i].announce))
|
||||
|
@ -430,6 +434,15 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder)
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < builder->webseedCount && builder->result == TrMakemetaResult::OK; ++i)
|
||||
{
|
||||
if (!tr_urlIsValid(builder->webseeds[i]))
|
||||
{
|
||||
tr_strlcpy(builder->errfile, builder->webseeds[i], sizeof(builder->errfile));
|
||||
builder->result = TrMakemetaResult::ERR_URL;
|
||||
}
|
||||
}
|
||||
|
||||
tr_variantInitDict(&top, 6);
|
||||
|
||||
if (builder->fileCount == 0 || builder->totalSize == 0 || builder->pieceSize == 0 || builder->pieceCount == 0)
|
||||
|
@ -464,6 +477,16 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder)
|
|||
tr_variantDictAddStr(&top, TR_KEY_announce, builder->trackers[0].announce);
|
||||
}
|
||||
|
||||
if (builder->result == TrMakemetaResult::OK && builder->webseedCount > 0)
|
||||
{
|
||||
tr_variant* url_list = tr_variantDictAddList(&top, TR_KEY_url_list, builder->webseedCount);
|
||||
|
||||
for (int i = 0; i < builder->webseedCount; ++i)
|
||||
{
|
||||
tr_variantListAddStr(url_list, builder->webseeds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (builder->result == TrMakemetaResult::OK && !builder->abortFlag)
|
||||
{
|
||||
if (!tr_str_is_empty(builder->comment))
|
||||
|
@ -543,6 +566,8 @@ void tr_makeMetaInfo(
|
|||
char const* outputFile,
|
||||
tr_tracker_info const* trackers,
|
||||
int trackerCount,
|
||||
char const** webseeds,
|
||||
int webseedCount,
|
||||
char const* comment,
|
||||
bool isPrivate,
|
||||
char const* source)
|
||||
|
@ -572,6 +597,13 @@ void tr_makeMetaInfo(
|
|||
builder->trackers[i].announce = tr_strdup(trackers[i].announce);
|
||||
}
|
||||
|
||||
builder->webseedCount = webseedCount;
|
||||
builder->webseeds = tr_new0(char*, webseedCount);
|
||||
for (int i = 0; i < webseedCount; ++i)
|
||||
{
|
||||
builder->webseeds[i] = tr_strdup(webseeds[i]);
|
||||
}
|
||||
|
||||
builder->comment = tr_strdup(comment);
|
||||
builder->isPrivate = isPrivate;
|
||||
builder->source = tr_strdup(source);
|
||||
|
|
|
@ -26,8 +26,6 @@ struct tr_tracker_info
|
|||
{
|
||||
int tier;
|
||||
char* announce;
|
||||
char* scrape;
|
||||
uint32_t id; /* unique identifier used to match to a tr_tracker_stat */
|
||||
};
|
||||
|
||||
struct tr_metainfo_builder
|
||||
|
@ -53,6 +51,10 @@ struct tr_metainfo_builder
|
|||
|
||||
tr_tracker_info* trackers;
|
||||
int trackerCount;
|
||||
|
||||
char** webseeds;
|
||||
int webseedCount;
|
||||
|
||||
char* comment;
|
||||
char* outputFile;
|
||||
bool isPrivate;
|
||||
|
@ -116,9 +118,11 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder*);
|
|||
*/
|
||||
void tr_makeMetaInfo(
|
||||
tr_metainfo_builder* builder,
|
||||
char const* outputFile,
|
||||
char const* output_file,
|
||||
tr_tracker_info const* trackers,
|
||||
int trackerCount,
|
||||
int n_trackers,
|
||||
char const** webseeds,
|
||||
int n_webseeds,
|
||||
char const* comment,
|
||||
bool isPrivate,
|
||||
bool is_private,
|
||||
char const* source);
|
||||
|
|
|
@ -569,6 +569,8 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
fLocation.path.UTF8String,
|
||||
trackerInfo,
|
||||
fTrackers.count,
|
||||
nullptr,
|
||||
0,
|
||||
fCommentView.string.UTF8String,
|
||||
fPrivateCheck.state == NSControlStateValueOn,
|
||||
fSource.stringValue.UTF8String);
|
||||
|
|
|
@ -189,6 +189,8 @@ void MakeDialog::makeTorrent()
|
|||
target.toUtf8().constData(),
|
||||
trackers.empty() ? nullptr : trackers.data(),
|
||||
trackers.size(),
|
||||
nullptr,
|
||||
0,
|
||||
comment.isEmpty() ? nullptr : comment.toUtf8().constData(),
|
||||
ui_.privateCheck->isChecked(),
|
||||
source.isNull() ? nullptr : source.toUtf8().constData());
|
||||
|
|
|
@ -34,6 +34,8 @@ protected:
|
|||
tr_torrent_metainfo& metainfo,
|
||||
tr_tracker_info const* trackers,
|
||||
int const trackerCount,
|
||||
char const** webseeds,
|
||||
int const webseedCount,
|
||||
void const* payload,
|
||||
size_t const payloadSize,
|
||||
char const* comment,
|
||||
|
@ -56,7 +58,16 @@ protected:
|
|||
|
||||
// have tr_makeMetaInfo() build the .torrent file
|
||||
auto const torrent_file = tr_strvJoin(input_file, ".torrent");
|
||||
tr_makeMetaInfo(builder, torrent_file.c_str(), trackers, trackerCount, comment, isPrivate, std::string(source).c_str());
|
||||
tr_makeMetaInfo(
|
||||
builder,
|
||||
torrent_file.c_str(),
|
||||
trackers,
|
||||
trackerCount,
|
||||
webseeds,
|
||||
webseedCount,
|
||||
comment,
|
||||
isPrivate,
|
||||
std::string(source).c_str());
|
||||
EXPECT_EQ(isPrivate, builder->isPrivate);
|
||||
EXPECT_EQ(torrent_file, builder->outputFile);
|
||||
EXPECT_STREQ(comment, builder->comment);
|
||||
|
@ -78,6 +89,7 @@ protected:
|
|||
EXPECT_EQ(comment, metainfo.comment());
|
||||
EXPECT_EQ(isPrivate, metainfo.isPrivate());
|
||||
EXPECT_EQ(size_t(trackerCount), std::size(metainfo.announceList()));
|
||||
EXPECT_EQ(size_t(webseedCount), metainfo.webseedCount());
|
||||
EXPECT_EQ(tr_file_index_t{ 1 }, metainfo.fileCount());
|
||||
EXPECT_EQ(makeString(tr_sys_path_basename(input_file.data(), nullptr)), metainfo.fileSubpath(0));
|
||||
EXPECT_EQ(payloadSize, metainfo.fileSize(0));
|
||||
|
@ -89,6 +101,8 @@ protected:
|
|||
void testSingleDirectoryImpl(
|
||||
tr_tracker_info const* trackers,
|
||||
int const tracker_count,
|
||||
char const** webseeds,
|
||||
int const webseed_count,
|
||||
void const** payloads,
|
||||
size_t const* payload_sizes,
|
||||
size_t const payload_count,
|
||||
|
@ -135,7 +149,16 @@ protected:
|
|||
|
||||
// build the .torrent file
|
||||
auto torrent_file = tr_strvJoin(top, ".torrent"sv);
|
||||
tr_makeMetaInfo(builder, torrent_file.c_str(), trackers, tracker_count, comment, is_private, source);
|
||||
tr_makeMetaInfo(
|
||||
builder,
|
||||
torrent_file.c_str(),
|
||||
trackers,
|
||||
tracker_count,
|
||||
webseeds,
|
||||
webseed_count,
|
||||
comment,
|
||||
is_private,
|
||||
source);
|
||||
EXPECT_EQ(is_private, builder->isPrivate);
|
||||
EXPECT_EQ(torrent_file, builder->outputFile);
|
||||
EXPECT_STREQ(comment, builder->comment);
|
||||
|
@ -170,6 +193,8 @@ protected:
|
|||
void testSingleDirectoryRandomPayloadImpl(
|
||||
tr_tracker_info const* trackers,
|
||||
int const tracker_count,
|
||||
char const** webseeds,
|
||||
int const webseed_count,
|
||||
size_t const max_file_count,
|
||||
size_t const max_file_size,
|
||||
char const* comment,
|
||||
|
@ -193,6 +218,8 @@ protected:
|
|||
testSingleDirectoryImpl(
|
||||
trackers,
|
||||
tracker_count,
|
||||
webseeds,
|
||||
webseed_count,
|
||||
const_cast<void const**>(payloads),
|
||||
payload_sizes,
|
||||
payload_count,
|
||||
|
@ -229,6 +256,32 @@ TEST_F(MakemetaTest, singleFile)
|
|||
metainfo,
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
nullptr,
|
||||
0,
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
is_private,
|
||||
"TESTME"sv);
|
||||
}
|
||||
|
||||
TEST_F(MakemetaTest, webseed)
|
||||
{
|
||||
auto trackers = std::vector<tr_tracker_info>{};
|
||||
auto webseeds = std::vector<char const*>{};
|
||||
|
||||
webseeds.emplace_back("https://www.example.com/linux.iso");
|
||||
|
||||
auto const payload = std::string{ "Hello, World!\n" };
|
||||
char const* const comment = "This is the comment";
|
||||
bool const is_private = false;
|
||||
auto metainfo = tr_torrent_metainfo{};
|
||||
testSingleFileImpl(
|
||||
metainfo,
|
||||
std::data(trackers),
|
||||
std::size(trackers),
|
||||
std::data(webseeds),
|
||||
std::size(webseeds),
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
|
@ -255,6 +308,8 @@ TEST_F(MakemetaTest, singleFileDifferentSourceFlags)
|
|||
metainfo_foobar,
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
nullptr,
|
||||
0,
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
|
@ -266,6 +321,8 @@ TEST_F(MakemetaTest, singleFileDifferentSourceFlags)
|
|||
metainfo_testme,
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
nullptr,
|
||||
0,
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
|
@ -297,6 +354,8 @@ TEST_F(MakemetaTest, singleDirectoryRandomPayload)
|
|||
testSingleDirectoryRandomPayloadImpl(
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
nullptr,
|
||||
0,
|
||||
DefaultMaxFileCount,
|
||||
DefaultMaxFileSize,
|
||||
comment,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
|
@ -30,13 +31,14 @@ char constexpr Usage[] = "Usage: transmission-create [options] <file|directory>"
|
|||
|
||||
uint32_t constexpr KiB = 1024;
|
||||
|
||||
auto constexpr Options = std::array<tr_option, 8>{
|
||||
auto constexpr Options = std::array<tr_option, 9>{
|
||||
{ { 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, nullptr },
|
||||
{ 'r', "source", "Set the source for private trackers", "r", true, "<source>" },
|
||||
{ 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "<file>" },
|
||||
{ 's', "piecesize", "Set the piece size in KiB, overriding the preferred default", "s", true, "<KiB>" },
|
||||
{ 'c', "comment", "Add a comment", "c", true, "<comment>" },
|
||||
{ 't', "tracker", "Add a tracker's announce URL", "t", true, "<url>" },
|
||||
{ 'w', "webseed", "Add a webseed URL", "w", true, "<url>" },
|
||||
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
|
||||
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
|
||||
};
|
||||
|
@ -44,6 +46,7 @@ auto constexpr Options = std::array<tr_option, 8>{
|
|||
struct app_options
|
||||
{
|
||||
std::vector<tr_tracker_info> trackers;
|
||||
std::vector<char const*> webseeds;
|
||||
std::string outfile;
|
||||
char const* comment = nullptr;
|
||||
char const* infile = nullptr;
|
||||
|
@ -79,7 +82,11 @@ int parseCommandLine(app_options& options, int argc, char const* const* argv)
|
|||
break;
|
||||
|
||||
case 't':
|
||||
options.trackers.push_back(tr_tracker_info{ 0, const_cast<char*>(optarg), nullptr, 0 });
|
||||
options.trackers.push_back(tr_tracker_info{ 0, const_cast<char*>(optarg) });
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
options.webseeds.push_back(optarg);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
|
@ -220,6 +227,8 @@ int tr_main(int argc, char* argv[])
|
|||
options.outfile.c_str(),
|
||||
std::data(options.trackers),
|
||||
std::size(options.trackers),
|
||||
std::data(options.webseeds),
|
||||
std::size(options.webseeds),
|
||||
options.comment,
|
||||
options.is_private,
|
||||
options.source);
|
||||
|
|
Loading…
Reference in a new issue