#838 (Adding trackers to existing torrents): add a backend API for this (tr_torrentSetAnnounceList) and implement a GUI for it in the gtk+ client.
This commit is contained in:
parent
0a023367b6
commit
07c1b28e9e
285
gtk/details.c
285
gtk/details.c
|
@ -10,6 +10,7 @@
|
|||
* $Id$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/utils.h> /* tr_httpIsValidURL */
|
||||
|
||||
#include "actions.h"
|
||||
#include "details.h"
|
||||
|
@ -1078,6 +1080,15 @@ struct tracker_page
|
|||
{
|
||||
TrTorrent * gtor;
|
||||
|
||||
GtkTreeView * view;
|
||||
GtkListStore * store;
|
||||
GtkTreeSelection * sel;
|
||||
|
||||
GtkWidget * add_button;
|
||||
GtkWidget * remove_button;
|
||||
GtkWidget * save_button;
|
||||
GtkWidget * revert_button;
|
||||
|
||||
GtkWidget * last_scrape_time_lb;
|
||||
GtkWidget * last_scrape_response_lb;
|
||||
GtkWidget * next_scrape_countdown_lb;
|
||||
|
@ -1088,19 +1099,291 @@ struct tracker_page
|
|||
GtkWidget * manual_announce_countdown_lb;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TR_COL_TIER,
|
||||
TR_COL_ANNOUNCE,
|
||||
TR_N_COLS
|
||||
};
|
||||
|
||||
static void
|
||||
setTrackerChangeState( struct tracker_page * page, gboolean changed )
|
||||
{
|
||||
gtk_widget_set_sensitive( page->save_button, changed );
|
||||
gtk_widget_set_sensitive( page->revert_button, changed );
|
||||
}
|
||||
|
||||
static GtkTreeModel*
|
||||
tracker_model_new( tr_torrent * tor )
|
||||
{
|
||||
int i;
|
||||
const tr_info * inf = tr_torrentInfo( tor );
|
||||
GtkListStore * store = gtk_list_store_new( TR_N_COLS, G_TYPE_INT, G_TYPE_STRING );
|
||||
|
||||
for( i=0; i<inf->trackerCount; ++i )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
const tr_tracker_info * tinf = inf->trackers + i;
|
||||
gtk_list_store_append( store, &iter );
|
||||
gtk_list_store_set( store, &iter, TR_COL_TIER, tinf->tier + 1,
|
||||
TR_COL_ANNOUNCE, tinf->announce,
|
||||
-1 );
|
||||
}
|
||||
|
||||
gtk_tree_sortable_set_sort_column_id( GTK_TREE_SORTABLE( store ),
|
||||
TR_COL_TIER,
|
||||
GTK_SORT_ASCENDING );
|
||||
|
||||
return GTK_TREE_MODEL( store );
|
||||
}
|
||||
|
||||
static void
|
||||
onTrackerSelectionChanged( GtkTreeSelection * sel,
|
||||
gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
gboolean has_selection = gtk_tree_selection_get_selected( sel, NULL, NULL );
|
||||
gtk_widget_set_sensitive( page->remove_button, has_selection );
|
||||
}
|
||||
|
||||
static void
|
||||
onTrackerRemoveClicked( GtkButton * w UNUSED, gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreeIter iter;
|
||||
if( gtk_tree_selection_get_selected( page->sel, NULL, &iter ) ) {
|
||||
gtk_list_store_remove( page->store, &iter );
|
||||
setTrackerChangeState( page, TRUE );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
onTrackerAddClicked( GtkButton * w UNUSED, gpointer gpage )
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreePath * path;
|
||||
gtk_list_store_append( page->store, &iter );
|
||||
setTrackerChangeState( page, TRUE );
|
||||
gtk_list_store_set( page->store, &iter, TR_COL_TIER, 1,
|
||||
TR_COL_ANNOUNCE, _( "Announce URL" ),
|
||||
-1 );
|
||||
path = gtk_tree_model_get_path( GTK_TREE_MODEL( page->store ), &iter );
|
||||
gtk_tree_view_set_cursor( page->view,
|
||||
path,
|
||||
gtk_tree_view_get_column( page->view, TR_COL_ANNOUNCE ),
|
||||
TRUE );
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
|
||||
static void
|
||||
onTrackerSaveClicked( GtkButton * w UNUSED, gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreeModel * model = GTK_TREE_MODEL( page->store );
|
||||
const int n = gtk_tree_model_iter_n_children( model, NULL );
|
||||
|
||||
if( n > 0 ) /* must have at least one tracker */
|
||||
{
|
||||
int i = 0;
|
||||
GtkTreeIter iter;
|
||||
tr_tracker_info * trackers;
|
||||
|
||||
/* build the tracker list */
|
||||
trackers = g_new0( tr_tracker_info, n );
|
||||
if( gtk_tree_model_get_iter_first( model, &iter ) ) do {
|
||||
gtk_tree_model_get( model, &iter, TR_COL_TIER, &trackers[i].tier,
|
||||
TR_COL_ANNOUNCE, &trackers[i].announce,
|
||||
-1 );
|
||||
++i;
|
||||
} while( gtk_tree_model_iter_next( model, &iter ) );
|
||||
g_assert( i == n );
|
||||
|
||||
/* set the tracker list */
|
||||
tr_torrentSetAnnounceList( tr_torrent_handle( page->gtor ),
|
||||
trackers, n );
|
||||
|
||||
|
||||
setTrackerChangeState( page, FALSE );
|
||||
|
||||
/* cleanup */
|
||||
for( i=0; i<n; ++i )
|
||||
g_free( trackers[i].announce );
|
||||
g_free( trackers );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
onTrackerRevertClicked( GtkButton * w UNUSED, gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreeModel * model = tracker_model_new( tr_torrent_handle( page->gtor ) );
|
||||
gtk_tree_view_set_model( page->view, model );
|
||||
page->store = GTK_LIST_STORE( model );
|
||||
g_object_unref( G_OBJECT( model ) );
|
||||
setTrackerChangeState( page, FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
onAnnounceEdited( GtkCellRendererText * renderer UNUSED,
|
||||
gchar * path_string,
|
||||
gchar * new_text,
|
||||
gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreeModel * model = GTK_TREE_MODEL( page->store );
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath * path = gtk_tree_path_new_from_string( path_string ) ;
|
||||
if( gtk_tree_model_get_iter( model, &iter, path ) )
|
||||
{
|
||||
char * old_text;
|
||||
gtk_tree_model_get( model, &iter, TR_COL_ANNOUNCE, &old_text, -1 );
|
||||
if( tr_httpIsValidURL( new_text ) )
|
||||
{
|
||||
if( strcmp( old_text, new_text ) )
|
||||
{
|
||||
gtk_list_store_set( page->store, &iter, TR_COL_ANNOUNCE, new_text, -1 );
|
||||
setTrackerChangeState( page, TRUE );
|
||||
}
|
||||
}
|
||||
else if( !tr_httpIsValidURL( old_text ) )
|
||||
{
|
||||
/* both old and new are invalid...
|
||||
they must've typed in an invalid URL
|
||||
after hitting the "Add" button */
|
||||
onTrackerRemoveClicked( NULL, page );
|
||||
setTrackerChangeState( page, TRUE );
|
||||
}
|
||||
g_free( old_text );
|
||||
}
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
|
||||
static void
|
||||
onTierEdited( GtkCellRendererText * renderer UNUSED,
|
||||
gchar * path_string,
|
||||
gchar * new_text,
|
||||
gpointer gpage )
|
||||
{
|
||||
struct tracker_page * page = gpage;
|
||||
GtkTreeModel * model = GTK_TREE_MODEL( page->store );
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath * path;
|
||||
char * end;
|
||||
int new_tier;
|
||||
|
||||
errno = 0;
|
||||
new_tier = strtol( new_text, &end, 10 );
|
||||
if( new_tier<1 || *end || errno )
|
||||
return;
|
||||
|
||||
path = gtk_tree_path_new_from_string( path_string ) ;
|
||||
if( gtk_tree_model_get_iter( model, &iter, path ) )
|
||||
{
|
||||
int old_tier;
|
||||
gtk_tree_model_get( model, &iter, TR_COL_TIER, &old_tier, -1 );
|
||||
if( old_tier != new_tier )
|
||||
{
|
||||
gtk_list_store_set( page->store, &iter, TR_COL_TIER, new_tier, -1 );
|
||||
setTrackerChangeState( page, TRUE );
|
||||
}
|
||||
}
|
||||
gtk_tree_path_free( path );
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
tracker_page_new( TrTorrent * gtor )
|
||||
{
|
||||
GtkWidget * t;
|
||||
GtkWidget * l;
|
||||
GtkWidget * w;
|
||||
GtkWidget * h;
|
||||
GtkWidget * v;
|
||||
GtkWidget * fr;
|
||||
int row = 0;
|
||||
const char * s;
|
||||
GtkTreeModel * m;
|
||||
GtkCellRenderer * r;
|
||||
GtkTreeViewColumn * c;
|
||||
GtkTreeSelection * sel;
|
||||
struct tracker_page * page = g_new0( struct tracker_page, 1 );
|
||||
const tr_info * info = tr_torrent_info (gtor);
|
||||
|
||||
page->gtor = gtor;
|
||||
|
||||
t = hig_workarea_create( );
|
||||
hig_workarea_add_section_title( t, &row, _( "Trackers" ) );
|
||||
|
||||
h = gtk_hbox_new( FALSE, GUI_PAD );
|
||||
m = tracker_model_new( tr_torrent_handle( gtor ) );
|
||||
page->store = GTK_LIST_STORE( m );
|
||||
w = gtk_tree_view_new_with_model( m );
|
||||
page->view = GTK_TREE_VIEW( w );
|
||||
gtk_tree_view_set_enable_search( page->view, FALSE );
|
||||
r = gtk_cell_renderer_text_new( );
|
||||
g_object_set( G_OBJECT( r ),
|
||||
"editable", TRUE,
|
||||
NULL );
|
||||
g_signal_connect( r, "edited",
|
||||
G_CALLBACK( onTierEdited ), page );
|
||||
c = gtk_tree_view_column_new_with_attributes( _( "Tier" ), r,
|
||||
"text", TR_COL_TIER,
|
||||
NULL );
|
||||
gtk_tree_view_column_set_sort_column_id( c, TR_COL_TIER );
|
||||
gtk_tree_view_append_column( page->view, c );
|
||||
r = gtk_cell_renderer_text_new( );
|
||||
g_object_set( G_OBJECT( r ),
|
||||
"editable", TRUE,
|
||||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||
NULL );
|
||||
g_signal_connect( r, "edited",
|
||||
G_CALLBACK( onAnnounceEdited ), page );
|
||||
c = gtk_tree_view_column_new_with_attributes( _( "Announce URL" ), r,
|
||||
"text", TR_COL_ANNOUNCE,
|
||||
NULL );
|
||||
gtk_tree_view_column_set_sort_column_id( c, TR_COL_ANNOUNCE );
|
||||
gtk_tree_view_append_column( page->view, c );
|
||||
w = gtk_scrolled_window_new( NULL, NULL );
|
||||
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( w ),
|
||||
GTK_POLICY_NEVER,
|
||||
GTK_POLICY_AUTOMATIC );
|
||||
sel = gtk_tree_view_get_selection( page->view );
|
||||
page->sel = sel;
|
||||
g_signal_connect( sel, "changed",
|
||||
G_CALLBACK( onTrackerSelectionChanged ), page );
|
||||
gtk_container_add( GTK_CONTAINER( w ), GTK_WIDGET( page->view ) );
|
||||
gtk_widget_set_size_request( w, -1, 133 );
|
||||
fr = gtk_frame_new( NULL );
|
||||
gtk_frame_set_shadow_type( GTK_FRAME( fr ), GTK_SHADOW_IN );
|
||||
gtk_container_add( GTK_CONTAINER( fr ), w );
|
||||
gtk_box_pack_start_defaults( GTK_BOX( h ), fr );
|
||||
g_object_unref( G_OBJECT( m ) );
|
||||
|
||||
v = gtk_vbox_new( TRUE, GUI_PAD_SMALL );
|
||||
w = gtk_button_new_from_stock( GTK_STOCK_ADD );
|
||||
g_signal_connect( w, "clicked", G_CALLBACK( onTrackerAddClicked ), page );
|
||||
gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
|
||||
page->add_button = w;
|
||||
w = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
|
||||
g_signal_connect( w, "clicked", G_CALLBACK( onTrackerRemoveClicked ), page );
|
||||
gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
|
||||
page->remove_button = w;
|
||||
w = gtk_button_new_from_stock( GTK_STOCK_SAVE );
|
||||
g_signal_connect( w, "clicked", G_CALLBACK( onTrackerSaveClicked ), page );
|
||||
gtk_widget_set_sensitive( w, FALSE );
|
||||
gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
|
||||
page->save_button = w;
|
||||
w = gtk_button_new_from_stock( GTK_STOCK_REVERT_TO_SAVED );
|
||||
g_signal_connect( w, "clicked", G_CALLBACK( onTrackerRevertClicked ), page );
|
||||
gtk_widget_set_sensitive( w, FALSE );
|
||||
gtk_box_pack_start( GTK_BOX( v ), w, FALSE, FALSE, 0 );
|
||||
page->revert_button = w;
|
||||
gtk_box_pack_start( GTK_BOX( h ), v, FALSE, FALSE, 0 );
|
||||
|
||||
hig_workarea_add_wide_control( t, &row, h );
|
||||
onTrackerSelectionChanged( sel, page );
|
||||
|
||||
hig_workarea_add_section_divider( t, &row );
|
||||
hig_workarea_add_section_title( t, &row, _( "Scrape" ) );
|
||||
|
||||
s = _( "Last scrape at:" );
|
||||
|
@ -1147,8 +1430,8 @@ tracker_page_new( TrTorrent * gtor )
|
|||
page->manual_announce_countdown_lb = l;
|
||||
hig_workarea_add_row( t, &row, s, l, NULL );
|
||||
|
||||
g_object_set_data_full( G_OBJECT( t ), TRACKER_PAGE, page, g_free );
|
||||
hig_workarea_finish( t, &row );
|
||||
g_object_set_data_full( G_OBJECT( t ), TRACKER_PAGE, page, g_free );
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
@ -634,7 +634,7 @@ file_list_new( TrTorrent * gtor )
|
|||
|
||||
/* add priority column */
|
||||
rend = gtk_cell_renderer_text_new( );
|
||||
col = gtk_tree_view_column_new_with_attributes( _( "Priorities" ), rend, NULL );
|
||||
col = gtk_tree_view_column_new_with_attributes( _( "Priority" ), rend, NULL );
|
||||
gtk_tree_view_column_set_cell_data_func( col, rend, renderPriority, NULL, NULL);
|
||||
gtk_tree_view_append_column ( GTK_TREE_VIEW( view ), col);
|
||||
|
||||
|
@ -645,7 +645,7 @@ file_list_new( TrTorrent * gtor )
|
|||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_container_add( GTK_CONTAINER( scroll ), view );
|
||||
gtk_widget_set_size_request (scroll, 0u, 200u);
|
||||
gtk_widget_set_size_request (scroll, -1, 200 );
|
||||
|
||||
data = g_new0( FileData, 1 );
|
||||
|
||||
|
|
|
@ -699,7 +699,7 @@ wannaquit( void * vdata )
|
|||
gtk_table_attach_defaults( GTK_TABLE( p ), w, 1, 2, 1, 2 );
|
||||
|
||||
b = gtk_alignment_new(0.0, 1.0, 0.01, 0.01);
|
||||
w = gtk_button_new_with_label( _( "_Quit Immediately" ) );
|
||||
w = gtk_button_new_with_label( _( "_Quit Now" ) );
|
||||
gtk_button_set_image( GTK_BUTTON(w), gtk_image_new_from_stock( GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON ) );
|
||||
g_signal_connect(w, "clicked", G_CALLBACK(do_exit_cb), NULL);
|
||||
gtk_container_add(GTK_CONTAINER(b), w);
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include "ptrarray.h"
|
||||
#include "utils.h" /* tr_new(), tr_free() */
|
||||
|
||||
const tr_benc BENC_NULL = { 0, { 0 } };
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
@ -328,13 +330,13 @@ tr_bencLoad( const void * buf_in,
|
|||
****
|
||||
***/
|
||||
|
||||
tr_benc *
|
||||
tr_bencDictFind( tr_benc * val, const char * key )
|
||||
static int
|
||||
dictIndexOf( tr_benc * val, const char * key )
|
||||
{
|
||||
int len, ii;
|
||||
|
||||
if( !tr_bencIsDict( val ) )
|
||||
return NULL;
|
||||
return -1;
|
||||
|
||||
len = strlen( key );
|
||||
|
||||
|
@ -346,10 +348,17 @@ tr_bencDictFind( tr_benc * val, const char * key )
|
|||
{
|
||||
continue;
|
||||
}
|
||||
return &val->val.l.vals[ii+1];
|
||||
return ii;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
tr_benc *
|
||||
tr_bencDictFind( tr_benc * val, const char * key )
|
||||
{
|
||||
const int i = dictIndexOf( val, key );
|
||||
return i<0 ? NULL : &val->val.l.vals[i+1];
|
||||
}
|
||||
|
||||
tr_benc*
|
||||
|
@ -646,6 +655,30 @@ tr_bencDictAddRaw( tr_benc * dict, const char * key, const void * src, size_t le
|
|||
return child;
|
||||
}
|
||||
|
||||
int
|
||||
tr_bencDictRemove( tr_benc * dict, const char * key )
|
||||
{
|
||||
int i = dictIndexOf( dict, key );
|
||||
if( i >= 0 )
|
||||
{
|
||||
const int n = dict->val.l.count;
|
||||
fprintf( stderr, "i is %d... count is %d\n", i, dict->val.l.count );
|
||||
fprintf( stderr, "moving %d items from pos %d to %d\n", dict->val.l.count-(i+2), i+2, i );
|
||||
#if 0
|
||||
tr_bencFree( &dict->val.l.vals[i] );
|
||||
tr_bencFree( &dict->val.l.vals[i+1] );
|
||||
#endif
|
||||
if( i + 2 < n )
|
||||
{
|
||||
dict->val.l.vals[i] = dict->val.l.vals[n-2];
|
||||
dict->val.l.vals[i+1] = dict->val.l.vals[n-1];
|
||||
}
|
||||
dict->val.l.count -= 2;
|
||||
}
|
||||
return i >= 0; /* return true if found */
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
**** BENC WALKING
|
||||
***/
|
||||
|
@ -909,7 +942,7 @@ freeContainerBeginFunc( const tr_benc * val, void * freeme )
|
|||
void
|
||||
tr_bencFree( tr_benc * val )
|
||||
{
|
||||
if( val != NULL )
|
||||
if( val && val->type )
|
||||
{
|
||||
tr_ptrArray * freeme = tr_ptrArrayNew( );
|
||||
struct WalkFuncs walkFuncs;
|
||||
|
|
|
@ -53,6 +53,8 @@ typedef struct tr_benc
|
|||
} val;
|
||||
} tr_benc;
|
||||
|
||||
const tr_benc BENC_INIT;
|
||||
|
||||
/* backwards compatability */
|
||||
typedef tr_benc benc_val_t;
|
||||
|
||||
|
@ -108,6 +110,7 @@ tr_benc * tr_bencDictAddStr( tr_benc * dict, const char * key, const char * v
|
|||
tr_benc * tr_bencDictAddList( tr_benc * dict, const char * key, int reserveCount );
|
||||
tr_benc * tr_bencDictAddDict( tr_benc * dict, const char * key, int reserveCount );
|
||||
tr_benc * tr_bencDictAddRaw( tr_benc * dict, const char * key, const void *, size_t len );
|
||||
int tr_bencDictRemove( tr_benc * dict, const char * key );
|
||||
|
||||
char* tr_bencSave( const tr_benc * val, int * len );
|
||||
char* tr_bencSaveAsJSON( const tr_benc * top, int * len );
|
||||
|
|
|
@ -307,17 +307,18 @@ tr_torrentInitFilePieces( tr_torrent * tor )
|
|||
tr_file_index_t ff;
|
||||
tr_piece_index_t pp;
|
||||
uint64_t offset = 0;
|
||||
tr_info * inf = &tor->info;
|
||||
|
||||
assert( tor != NULL );
|
||||
assert( inf != NULL );
|
||||
|
||||
for( ff=0; ff<tor->info.fileCount; ++ff ) {
|
||||
tor->info.files[ff].offset = offset;
|
||||
offset += tor->info.files[ff].length;
|
||||
initFilePieces( &tor->info, ff );
|
||||
for( ff=0; ff<inf->fileCount; ++ff ) {
|
||||
inf->files[ff].offset = offset;
|
||||
offset += inf->files[ff].length;
|
||||
initFilePieces( inf, ff );
|
||||
}
|
||||
|
||||
for( pp=0; pp<tor->info.pieceCount; ++pp )
|
||||
tor->info.pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
|
||||
for( pp=0; pp<inf->pieceCount; ++pp )
|
||||
inf->pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1517,3 +1518,54 @@ tr_torrentGetMTimes( const tr_torrent * tor, int * setme_n )
|
|||
*setme_n = n;
|
||||
return m;
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
void
|
||||
tr_torrentSetAnnounceList( tr_torrent * tor,
|
||||
const tr_tracker_info * trackers,
|
||||
int trackerCount )
|
||||
{
|
||||
tr_benc metainfo = BENC_INIT;
|
||||
|
||||
/* save to the .torrent file */
|
||||
if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
|
||||
{
|
||||
int i;
|
||||
int prevTier = -1;
|
||||
tr_benc * tier = NULL;
|
||||
tr_benc * announceList;
|
||||
tr_info tmpInfo;
|
||||
|
||||
/* remove the old fields */
|
||||
tr_bencDictRemove( &metainfo, "announce" );
|
||||
tr_bencDictRemove( &metainfo, "announce-list" );
|
||||
|
||||
/* add the new fields */
|
||||
tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
|
||||
announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
|
||||
for( i=0; i<trackerCount; ++i ) {
|
||||
if( prevTier != trackers[i].tier ) {
|
||||
prevTier = trackers[i].tier;
|
||||
tier = tr_bencListAddList( announceList, 0 );
|
||||
}
|
||||
tr_bencListAddStr( tier, trackers[i].announce );
|
||||
}
|
||||
|
||||
/* try to parse it back again, to make sure it's good */
|
||||
memset( &tmpInfo, 0, sizeof( tr_info ) );
|
||||
if( !tr_metainfoParse( tor->handle, &tmpInfo, &metainfo ) )
|
||||
{
|
||||
/* if it's good, save it and use it */
|
||||
tr_metainfoFree( &tor->info );
|
||||
tor->info = tmpInfo;
|
||||
tr_torrentInitFilePieces( tor );
|
||||
tr_bencSaveFile( tor->info.torrent, &metainfo );
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
tr_bencFree( &metainfo );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -802,6 +802,34 @@ void tr_torrentSetDownloadDir( tr_torrent *, const char * );
|
|||
|
||||
const char * tr_torrentGetDownloadDir( const tr_torrent * );
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
typedef struct tr_tracker_info
|
||||
{
|
||||
int tier;
|
||||
char * announce;
|
||||
char * scrape;
|
||||
}
|
||||
tr_tracker_info;
|
||||
|
||||
/**
|
||||
* @brief Modify a torrent's tracker list.
|
||||
*
|
||||
* This updates the torrent in-memory and also the metainfo file
|
||||
* stored in the torrent folder in tr_sessionGetConfigDir().
|
||||
*
|
||||
* @param torrent The torrent whose tracker list is to be modified.
|
||||
* @param trackers An array of trackers, sorted by tier from first to last.
|
||||
* NOTE: only the `tier' and `announce' fields are used.
|
||||
* libtransmission derives `scrape' from `announce'.
|
||||
* @param trackerCount size of the `trackers' array.
|
||||
*/
|
||||
void tr_torrentSetAnnounceList( tr_torrent * torrent,
|
||||
const tr_tracker_info * trackers,
|
||||
int trackerCount );
|
||||
|
||||
|
||||
/**
|
||||
***
|
||||
|
@ -937,14 +965,6 @@ typedef struct tr_piece
|
|||
int8_t dnd; /* nonzero if the piece shouldn't be downloaded */
|
||||
}
|
||||
tr_piece;
|
||||
|
||||
typedef struct tr_tracker_info
|
||||
{
|
||||
int tier;
|
||||
char * announce;
|
||||
char * scrape;
|
||||
}
|
||||
tr_tracker_info;
|
||||
|
||||
struct tr_info
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue