Add support for creating torrents with a source flag (#443)
* Add support for creating torrents with a source flag * Add the source flag functionality for Mac OSX * Source flag should be a part of the info dictionary * Address review comments * Rename "sourceFlag" to "source" since "Flag" is usually reserved for booleans. * Free the "source" pointer in tr_metainfoFree. * Add information about transmission-create argument to its manpage. * Replace all occurences of "sourceFlag" with "source" and use "Source tag" in UI * Settle on just "Source" in UI * The last usage of "flag" hopefully bites the dust! ;-) * Add a missing free for the source in tr_metainfoFree * Add a "source" field to the torrent-get RPC method * uncrustify * Test for torrents having different infohashes due to different source flags. This is the whole point of this feature, so it makes sense to test it. * case is important * try to incorporate the macosx xml changes
This commit is contained in:
parent
bf41e1487a
commit
77b11232f2
|
@ -89,6 +89,8 @@ private:
|
|||
Gtk::CheckButton* comment_check_ = nullptr;
|
||||
Gtk::Entry* comment_entry_ = nullptr;
|
||||
Gtk::CheckButton* private_check_ = nullptr;
|
||||
Gtk::CheckButton* source_check_ = nullptr;
|
||||
Gtk::Entry* source_entry_ = nullptr;
|
||||
std::unique_ptr<MakeProgressDialog> progress_dialog_;
|
||||
Glib::RefPtr<Gtk::TextBuffer> announce_text_buffer_;
|
||||
std::unique_ptr<tr_metainfo_builder, void (*)(tr_metainfo_builder*)> builder_ = { nullptr, nullptr };
|
||||
|
@ -252,6 +254,8 @@ void MakeDialog::Impl::onResponse(int response)
|
|||
auto const comment = comment_entry_->get_text();
|
||||
bool const isPrivate = private_check_->get_active();
|
||||
bool const useComment = comment_check_->get_active();
|
||||
bool const useSource = source_check_->get_active();
|
||||
auto const source = source_entry_->get_text();
|
||||
|
||||
/* destination file */
|
||||
auto const dir = destination_chooser_->get_filename();
|
||||
|
@ -288,7 +292,8 @@ void MakeDialog::Impl::onResponse(int response)
|
|||
trackers.data(),
|
||||
trackers.size(),
|
||||
useComment ? comment.c_str() : nullptr,
|
||||
isPrivate);
|
||||
isPrivate,
|
||||
useSource ? source.c_str() : nullptr);
|
||||
}
|
||||
}
|
||||
else if (response == Gtk::RESPONSE_CLOSE)
|
||||
|
@ -495,6 +500,13 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<TrCore> const& core)
|
|||
comment_check_->signal_toggled().connect([this]() { onSourceToggled(comment_check_, comment_entry_); });
|
||||
t->add_row_w(row, *comment_check_, *comment_entry_);
|
||||
|
||||
source_check_ = Gtk::make_managed<Gtk::CheckButton>(_("_Source:"), true);
|
||||
source_check_->set_active(false);
|
||||
source_entry_ = Gtk::make_managed<Gtk::Entry>();
|
||||
source_entry_->set_sensitive(false);
|
||||
source_check_->signal_toggled().connect([this]() { onSourceToggled(source_check_, source_entry_); });
|
||||
t->add_row_w(row, *source_check_, *source_entry_);
|
||||
|
||||
private_check_ = t->add_wide_checkbutton(row, _("_Private torrent"), false);
|
||||
|
||||
gtr_dialog_set_content(dialog_, *t);
|
||||
|
|
|
@ -234,6 +234,7 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder* builder)
|
|||
tr_free(builder->files);
|
||||
tr_free(builder->top);
|
||||
tr_free(builder->comment);
|
||||
tr_free(builder->source);
|
||||
|
||||
for (int i = 0; i < builder->trackerCount; ++i)
|
||||
{
|
||||
|
@ -428,6 +429,10 @@ static void makeInfoDict(tr_variant* dict, tr_metainfo_builder* builder)
|
|||
}
|
||||
|
||||
tr_variantDictAddInt(dict, TR_KEY_private, builder->isPrivate ? 1 : 0);
|
||||
if (builder->source != nullptr)
|
||||
{
|
||||
tr_variantDictAddStr(dict, TR_KEY_source, builder->source);
|
||||
}
|
||||
}
|
||||
|
||||
static void tr_realMakeMetaInfo(tr_metainfo_builder* builder)
|
||||
|
@ -569,7 +574,8 @@ void tr_makeMetaInfo(
|
|||
tr_tracker_info const* trackers,
|
||||
int trackerCount,
|
||||
char const* comment,
|
||||
bool isPrivate)
|
||||
bool isPrivate,
|
||||
char const* source)
|
||||
{
|
||||
tr_lock* lock;
|
||||
|
||||
|
@ -581,6 +587,7 @@ void tr_makeMetaInfo(
|
|||
|
||||
tr_free(builder->trackers);
|
||||
tr_free(builder->comment);
|
||||
tr_free(builder->source);
|
||||
tr_free(builder->outputFile);
|
||||
|
||||
/* initialize the builder variables */
|
||||
|
@ -599,6 +606,7 @@ void tr_makeMetaInfo(
|
|||
|
||||
builder->comment = tr_strdup(comment);
|
||||
builder->isPrivate = isPrivate;
|
||||
builder->source = tr_strdup(source);
|
||||
|
||||
if (!tr_str_is_empty(outputFile))
|
||||
{
|
||||
|
|
|
@ -52,6 +52,7 @@ struct tr_metainfo_builder
|
|||
char* comment;
|
||||
char* outputFile;
|
||||
bool isPrivate;
|
||||
char* source;
|
||||
|
||||
/**
|
||||
*** These are set inside tr_makeMetaInfo() so the client
|
||||
|
@ -115,4 +116,5 @@ void tr_makeMetaInfo(
|
|||
tr_tracker_info const* trackers,
|
||||
int trackerCount,
|
||||
char const* comment,
|
||||
bool isPrivate);
|
||||
bool isPrivate,
|
||||
char const* source);
|
||||
|
|
|
@ -683,6 +683,19 @@ static char const* tr_metainfoParseImpl(
|
|||
|
||||
inf->isPrivate = i != 0;
|
||||
|
||||
/* source */
|
||||
len = 0;
|
||||
if (!tr_variantDictFindStr(infoDict, TR_KEY_source, &str, &len))
|
||||
{
|
||||
if (!tr_variantDictFindStr(meta, TR_KEY_source, &str, &len))
|
||||
{
|
||||
str = "";
|
||||
}
|
||||
}
|
||||
|
||||
tr_free(inf->source);
|
||||
inf->source = tr_utf8clean(std::string_view{ str, len });
|
||||
|
||||
/* piece length */
|
||||
if (!isMagnet)
|
||||
{
|
||||
|
@ -788,6 +801,7 @@ void tr_metainfoFree(tr_info* inf)
|
|||
tr_free(inf->files);
|
||||
tr_free(inf->comment);
|
||||
tr_free(inf->creator);
|
||||
tr_free(inf->source);
|
||||
tr_free(inf->torrent);
|
||||
tr_free(inf->originalName);
|
||||
tr_free(inf->name);
|
||||
|
|
|
@ -24,7 +24,7 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
|
||||
auto constexpr my_static = std::array<std::string_view, 391>{ ""sv,
|
||||
auto constexpr my_static = std::array<std::string_view, 392>{ ""sv,
|
||||
"activeTorrentCount"sv,
|
||||
"activity-date"sv,
|
||||
"activityDate"sv,
|
||||
|
@ -345,6 +345,7 @@ auto constexpr my_static = std::array<std::string_view, 391>{ ""sv,
|
|||
"sizeWhenDone"sv,
|
||||
"sort-mode"sv,
|
||||
"sort-reversed"sv,
|
||||
"source"sv,
|
||||
"speed"sv,
|
||||
"speed-Bps"sv,
|
||||
"speed-bytes"sv,
|
||||
|
|
|
@ -346,6 +346,7 @@ enum
|
|||
TR_KEY_sizeWhenDone,
|
||||
TR_KEY_sort_mode,
|
||||
TR_KEY_sort_reversed,
|
||||
TR_KEY_source,
|
||||
TR_KEY_speed,
|
||||
TR_KEY_speed_Bps,
|
||||
TR_KEY_speed_bytes,
|
||||
|
|
|
@ -777,6 +777,10 @@ static void initField(
|
|||
tr_variantInitInt(initme, st->sizeWhenDone);
|
||||
break;
|
||||
|
||||
case TR_KEY_source:
|
||||
tr_variantDictAddStr(initme, key, inf->source);
|
||||
break;
|
||||
|
||||
case TR_KEY_startDate:
|
||||
tr_variantInitInt(initme, st->startDate);
|
||||
break;
|
||||
|
|
|
@ -1624,6 +1624,10 @@ struct tr_info
|
|||
|
||||
char* comment;
|
||||
char* creator;
|
||||
|
||||
/* torrent's source. empty if not set. */
|
||||
char* source;
|
||||
|
||||
tr_file* files;
|
||||
tr_piece* pieces;
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
IBOutlet NSTextView* fCommentView;
|
||||
IBOutlet NSButton* fPrivateCheck;
|
||||
IBOutlet NSButton* fOpenCheck;
|
||||
IBOutlet NSTextField* fSource;
|
||||
|
||||
IBOutlet NSView* fProgressView;
|
||||
IBOutlet NSProgressIndicator* fProgressIndicator;
|
||||
|
|
|
@ -206,6 +206,11 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
fPrivateCheck.state = [fDefaults boolForKey:@"CreatorPrivate"] ? NSOnState : NSOffState;
|
||||
}
|
||||
|
||||
if ([fDefaults objectForKey:@"CreatorSource"])
|
||||
{
|
||||
fSource.stringValue = [fDefaults stringForKey:@"CreatorSource"];
|
||||
}
|
||||
|
||||
fOpenCheck.state = [fDefaults boolForKey:@"CreatorOpen"] ? NSOnState : NSOffState;
|
||||
}
|
||||
|
||||
|
@ -241,6 +246,7 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
[state encodeObject:fTrackers forKey:@"TRCreatorTrackers"];
|
||||
[state encodeInteger:fOpenCheck.state forKey:@"TRCreatorOpenCheck"];
|
||||
[state encodeInteger:fPrivateCheck.state forKey:@"TRCreatorPrivateCheck"];
|
||||
[state encodeObject:fSource.stringValue forKey:@"TRCreatorSource"];
|
||||
[state encodeObject:fCommentView.string forKey:@"TRCreatorPrivateComment"];
|
||||
}
|
||||
|
||||
|
@ -254,6 +260,7 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
|
||||
fOpenCheck.state = [coder decodeIntegerForKey:@"TRCreatorOpenCheck"];
|
||||
fPrivateCheck.state = [coder decodeIntegerForKey:@"TRCreatorPrivateCheck"];
|
||||
fSource.stringValue = [coder decodeObjectForKey:@"TRCreatorSource"];
|
||||
fCommentView.string = [coder decodeObjectForKey:@"TRCreatorPrivateComment"];
|
||||
}
|
||||
|
||||
|
@ -570,6 +577,7 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
//store values
|
||||
[fDefaults setObject:fTrackers forKey:@"CreatorTrackers"];
|
||||
[fDefaults setBool:fPrivateCheck.state == NSOnState forKey:@"CreatorPrivate"];
|
||||
[fDefaults setObject: [fSource stringValue] forKey: @"CreatorSource"];
|
||||
[fDefaults setBool:fOpenCheck.state == NSOnState forKey:@"CreatorOpen"];
|
||||
fOpenWhenCreated = fOpenCheck.state == NSOnState; //need this since the check box might not exist, and value in prefs might have changed from another creator window
|
||||
[fDefaults setURL:fLocation.URLByDeletingLastPathComponent forKey:@"CreatorLocationURL"];
|
||||
|
@ -583,7 +591,8 @@ NSMutableSet* creatorWindowControllerSet = nil;
|
|||
trackerInfo,
|
||||
fTrackers.count,
|
||||
fCommentView.string.UTF8String,
|
||||
fPrivateCheck.state == NSOnState);
|
||||
fPrivateCheck.state == NSOnState,
|
||||
fSource.stringValue.UTF8String);
|
||||
tr_free(trackerInfo);
|
||||
|
||||
fTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkProgress) userInfo:nil repeats:YES];
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<outlet property="fPrivateCheck" destination="22" id="33"/>
|
||||
<outlet property="fProgressIndicator" destination="57" id="61"/>
|
||||
<outlet property="fProgressView" destination="56" id="60"/>
|
||||
<outlet property="fSource" destination="R2K-fl-edv" id="bqE-sb-RJZ"/>
|
||||
<outlet property="fStatusField" destination="10" id="34"/>
|
||||
<outlet property="fTrackerAddRemoveControl" destination="103" id="105"/>
|
||||
<outlet property="fTrackerTable" destination="92" id="99"/>
|
||||
|
@ -262,6 +263,24 @@ Gw
|
|||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" misplaced="YES" id="R2K-fl-edv">
|
||||
<rect key="frame" x="103" y="120" width="585" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="sNv-mo-m3a">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" allowsCharacterPickerTouchBarItem="YES" id="faa-JK-W9Q">
|
||||
<rect key="frame" x="21" y="123" width="77" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Source:" id="f1g-Kk-weU">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
|
|
|
@ -176,6 +176,14 @@ void MakeDialog::makeTorrent()
|
|||
comment = ui_.commentEdit->text();
|
||||
}
|
||||
|
||||
// source
|
||||
QString source;
|
||||
|
||||
if (ui_.sourceCheck->isChecked())
|
||||
{
|
||||
source = ui_.sourceEdit->text();
|
||||
}
|
||||
|
||||
// start making the torrent
|
||||
tr_makeMetaInfo(
|
||||
builder_.get(),
|
||||
|
@ -183,7 +191,8 @@ void MakeDialog::makeTorrent()
|
|||
trackers.isEmpty() ? nullptr : trackers.data(),
|
||||
trackers.size(),
|
||||
comment.isEmpty() ? nullptr : comment.toUtf8().constData(),
|
||||
ui_.privateCheck->isChecked());
|
||||
ui_.privateCheck->isChecked(),
|
||||
source.isNull() ? nullptr : source.toUtf8().constData());
|
||||
|
||||
// pop up the dialog
|
||||
auto* dialog = new MakeProgressDialog(session_, *builder_, this);
|
||||
|
|
|
@ -157,7 +157,21 @@ To add another primary URL, add it after a blank line.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="sourceCheck">
|
||||
<property name="text">
|
||||
<string>&Source:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="sourceEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="privateCheck">
|
||||
<property name="text">
|
||||
<string>&Private torrent</string>
|
||||
|
@ -235,5 +249,21 @@ To add another primary URL, add it after a blank line.</string>
|
|||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>sourceCheck</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>sourceEdit</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>75</x>
|
||||
<y>347</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>342</x>
|
||||
<y>347</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
|
@ -29,15 +29,15 @@ class MakemetaTest : public SandboxedTest
|
|||
{
|
||||
protected:
|
||||
void testSingleFileImpl(
|
||||
tr_info& inf,
|
||||
tr_tracker_info const* trackers,
|
||||
int const trackerCount,
|
||||
void const* payload,
|
||||
size_t const payloadSize,
|
||||
char const* comment,
|
||||
bool isPrivate)
|
||||
bool isPrivate,
|
||||
char const* source)
|
||||
{
|
||||
// char* sandbox;
|
||||
auto inf = tr_info{};
|
||||
|
||||
// create a single input file
|
||||
auto input_file = makeString(tr_buildPath(sandboxDir().data(), "test.XXXXXX", nullptr));
|
||||
|
@ -54,10 +54,11 @@ protected:
|
|||
|
||||
// have tr_makeMetaInfo() build the .torrent file
|
||||
auto* torrent_file = tr_strdup_printf("%s.torrent", input_file.data());
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, trackerCount, comment, isPrivate);
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, trackerCount, comment, isPrivate, source);
|
||||
EXPECT_EQ(isPrivate, builder->isPrivate);
|
||||
EXPECT_STREQ(torrent_file, builder->outputFile);
|
||||
EXPECT_STREQ(comment, builder->comment);
|
||||
EXPECT_STREQ(source, builder->source);
|
||||
EXPECT_EQ(trackerCount, builder->trackerCount);
|
||||
|
||||
while (!builder->isDone)
|
||||
|
@ -84,7 +85,6 @@ protected:
|
|||
// cleanup
|
||||
tr_free(torrent_file);
|
||||
tr_ctorFree(ctor);
|
||||
tr_metainfoFree(&inf);
|
||||
tr_metaInfoBuilderFree(builder);
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,8 @@ protected:
|
|||
size_t const* payload_sizes,
|
||||
size_t const payload_count,
|
||||
char const* comment,
|
||||
bool const is_private)
|
||||
bool const is_private,
|
||||
char const* source)
|
||||
{
|
||||
// create the top temp directory
|
||||
auto* top = tr_buildPath(sandboxDir().data(), "folder.XXXXXX", nullptr);
|
||||
|
@ -136,10 +137,11 @@ protected:
|
|||
|
||||
// build the .torrent file
|
||||
auto* torrent_file = tr_strdup_printf("%s.torrent", top);
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, tracker_count, comment, is_private);
|
||||
tr_makeMetaInfo(builder, torrent_file, trackers, tracker_count, comment, is_private, source);
|
||||
EXPECT_EQ(is_private, builder->isPrivate);
|
||||
EXPECT_STREQ(torrent_file, builder->outputFile);
|
||||
EXPECT_STREQ(comment, builder->comment);
|
||||
EXPECT_STREQ(source, builder->source);
|
||||
EXPECT_EQ(tracker_count, builder->trackerCount);
|
||||
auto test = [&builder]()
|
||||
{
|
||||
|
@ -161,6 +163,7 @@ protected:
|
|||
EXPECT_STREQ(tmpstr, inf.name);
|
||||
tr_free(tmpstr);
|
||||
EXPECT_STREQ(comment, inf.comment);
|
||||
EXPECT_STREQ(source, inf.source);
|
||||
EXPECT_EQ(payload_count, inf.fileCount);
|
||||
EXPECT_EQ(is_private, inf.isPrivate);
|
||||
EXPECT_EQ(builder->isFolder, inf.isFolder);
|
||||
|
@ -181,7 +184,8 @@ protected:
|
|||
size_t const max_file_count,
|
||||
size_t const max_file_size,
|
||||
char const* comment,
|
||||
bool const is_private)
|
||||
bool const is_private,
|
||||
char const* source)
|
||||
{
|
||||
// build random payloads
|
||||
size_t payload_count = 1 + tr_rand_int_weak(max_file_count);
|
||||
|
@ -204,7 +208,8 @@ protected:
|
|||
payload_sizes,
|
||||
payload_count,
|
||||
comment,
|
||||
is_private);
|
||||
is_private,
|
||||
source);
|
||||
|
||||
// cleanup
|
||||
for (size_t i = 0; i < payload_count; i++)
|
||||
|
@ -230,7 +235,52 @@ TEST_F(MakemetaTest, singleFile)
|
|||
auto const payload = std::string{ "Hello, World!\n" };
|
||||
char const* const comment = "This is the comment";
|
||||
bool const is_private = false;
|
||||
testSingleFileImpl(trackers.data(), tracker_count, payload.data(), payload.size(), comment, is_private);
|
||||
char const* const source = "TESTME";
|
||||
tr_info inf{};
|
||||
testSingleFileImpl(inf, trackers.data(), tracker_count, payload.data(), payload.size(), comment, is_private, source);
|
||||
tr_metainfoFree(&inf);
|
||||
}
|
||||
|
||||
TEST_F(MakemetaTest, singleFileDifferentSourceFlags)
|
||||
{
|
||||
auto trackers = std::array<tr_tracker_info, 16>{};
|
||||
auto tracker_count = int{};
|
||||
trackers[tracker_count].tier = tracker_count;
|
||||
trackers[tracker_count].announce = const_cast<char*>("udp://tracker.openbittorrent.com:80");
|
||||
++tracker_count;
|
||||
trackers[tracker_count].tier = tracker_count;
|
||||
trackers[tracker_count].announce = const_cast<char*>("udp://tracker.publicbt.com:80");
|
||||
++tracker_count;
|
||||
auto const payload = std::string{ "Hello, World!\n" };
|
||||
char const* const comment = "This is the comment";
|
||||
bool const is_private = false;
|
||||
|
||||
tr_info inf_foobar{};
|
||||
testSingleFileImpl(
|
||||
inf_foobar,
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
is_private,
|
||||
"FOOBAR");
|
||||
|
||||
tr_info inf_testme{};
|
||||
testSingleFileImpl(
|
||||
inf_testme,
|
||||
trackers.data(),
|
||||
tracker_count,
|
||||
payload.data(),
|
||||
payload.size(),
|
||||
comment,
|
||||
is_private,
|
||||
"TESTME");
|
||||
|
||||
EXPECT_TRUE(std::memcmp(inf_foobar.hash, inf_testme.hash, sizeof(inf_foobar.hash)) != 0);
|
||||
|
||||
tr_metainfoFree(&inf_foobar);
|
||||
tr_metainfoFree(&inf_testme);
|
||||
}
|
||||
|
||||
TEST_F(MakemetaTest, singleDirectoryRandomPayload)
|
||||
|
@ -248,6 +298,7 @@ TEST_F(MakemetaTest, singleDirectoryRandomPayload)
|
|||
++tracker_count;
|
||||
char const* const comment = "This is the comment";
|
||||
bool const is_private = false;
|
||||
char const* const source = "TESTME";
|
||||
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
{
|
||||
|
@ -257,7 +308,8 @@ TEST_F(MakemetaTest, singleDirectoryRandomPayload)
|
|||
DefaultMaxFileCount,
|
||||
DefaultMaxFileSize,
|
||||
comment,
|
||||
is_private);
|
||||
is_private,
|
||||
source);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,11 @@ static char const* comment = nullptr;
|
|||
static char const* outfile = nullptr;
|
||||
static char const* infile = nullptr;
|
||||
static uint32_t piecesize_kib = 0;
|
||||
static char const* source = NULL;
|
||||
|
||||
static tr_option options[] = {
|
||||
{ '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 how many KiB each piece should be, overriding the preferred default", "s", true, "<size in KiB>" },
|
||||
{ 'c', "comment", "Add a comment", "c", true, "<comment>" },
|
||||
|
@ -97,6 +99,10 @@ static int parseCommandLine(int argc, char const* const* argv)
|
|||
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
source = optarg;
|
||||
break;
|
||||
|
||||
case TR_OPT_UNK:
|
||||
infile = optarg;
|
||||
break;
|
||||
|
@ -212,7 +218,7 @@ int tr_main(int argc, char* argv[])
|
|||
b->pieceCount,
|
||||
tr_formatter_size_B(buf, b->pieceSize, sizeof(buf)));
|
||||
|
||||
tr_makeMetaInfo(b, outfile, trackers, trackerCount, comment, isPrivate);
|
||||
tr_makeMetaInfo(b, outfile, trackers, trackerCount, comment, isPrivate, source);
|
||||
|
||||
uint32_t last = UINT32_MAX;
|
||||
while (!b->isDone)
|
||||
|
|
|
@ -762,6 +762,7 @@ static tr_quark const details_keys[] = {
|
|||
TR_KEY_seedRatioMode,
|
||||
TR_KEY_seedRatioLimit,
|
||||
TR_KEY_sizeWhenDone,
|
||||
TR_KEY_source,
|
||||
TR_KEY_startDate,
|
||||
TR_KEY_status,
|
||||
TR_KEY_totalSize,
|
||||
|
@ -1173,6 +1174,11 @@ static void printDetails(tr_variant* top)
|
|||
printf(" Creator: %s\n", str);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStr(t, TR_KEY_source, &str, NULL) && str != NULL && *str != '\0')
|
||||
{
|
||||
printf(" Source: %s\n", str);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(t, TR_KEY_pieceCount, &i))
|
||||
{
|
||||
printf(" Piece Count: %" PRId64 "\n", i);
|
||||
|
|
|
@ -30,6 +30,8 @@ Flag the torrent as intended for use on private trackers.
|
|||
Add a comment to the torrent file.
|
||||
.It Fl s Fl -piecesize
|
||||
Set how many KiB each piece should be, overriding the preferred default
|
||||
.It Fl r Fl -source
|
||||
Set the torrent's source for private trackers
|
||||
.It Fl t Fl -tracker
|
||||
Add a tracker's
|
||||
.Ar announce URL
|
||||
|
|
Loading…
Reference in New Issue