(trunk) #1787: add support for seeding ratio limiting in libtransmission

This commit is contained in:
Charles Kerr 2009-02-13 18:23:56 +00:00
parent a079c843dc
commit 3f9a1d090b
22 changed files with 576 additions and 31 deletions

View File

@ -85,6 +85,11 @@ static tr_option opts[] =
{ 930, "peers", "Set the current torrent(s)' maximum number of peers each", "pr", 1, "<max>" },
{ 931, "global-peers", "Set the global maximum number of peers", "gpr", 1, "<max>" },
{ 'R', "remove-and-delete", "Remove the current torrent(s) and delete local data", NULL, 0, NULL },
{ 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
{ 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL },
{ 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL },
{ 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
{ 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL },
{ 's', "start", "Start the current torrent(s)", "s", 0, NULL },
{ 'S', "stop", "Stop the current torrent(s)", "S", 0, NULL },
{ 't', "torrent", "Set the current torrent(s)", "t", 1, "<torrent>" },
@ -533,6 +538,36 @@ readargs( int argc,
tr_bencListAddStr( fields, "peers" );
break;
case 950:
tr_bencDictAddStr( &top, "method", "torrent-set" );
tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_SINGLE );
addIdArg( args, id );
break;
case 951:
tr_bencDictAddStr( &top, "method", "torrent-set" );
tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_GLOBAL );
addIdArg( args, id );
break;
case 952:
tr_bencDictAddStr( &top, "method", "torrent-set" );
tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_UNLIMITED );
addIdArg( args, id );
break;
case 953:
tr_bencDictAddStr( &top, "method", "session-set" );
tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
tr_bencDictAddInt( args, "ratio-limit-enabled", 1 );
break;
case 954:
tr_bencDictAddStr( &top, "method", "session-set" );
tr_bencDictAddInt( args, "ratio-limit-enabled", 0 );
break;
case TR_OPT_ERR:
fprintf( stderr, "invalid option\n" );
showUsage( );

View File

@ -19,6 +19,8 @@ and
.Op Fl g Ar files
.Op Fl gpr Ar peers
.Op Fl G Ar files
.Op Fl gsr Ar ratio
.Op Fl GSR
.Op Fl h
.Op Fl i
.Op Fl l
@ -33,6 +35,9 @@ and
.Op Fl r
.Op Fl R
.Op Fl s | S
.Op Fl sr Ar ratio
.Op Fl SR
.Op Fl srd
.Op Fl si
.Op Fl t Ar all | Ar id | Ar hash
.Op Fl u Ar number | Fl U
@ -93,6 +98,13 @@ such as "-g1,3-5" to add files #1, #3, #4, and #5 to the download list.
.It Fl G Fl -no-get Ar all | file-index | files
Mark file(s) for not downloading.
.It Fl gsr Fl -global-seedratio Ar ratio
All torrents, unless overridden by a per-torrent setting, should seed until a specific
.Ar ratio
.It Fl GSR Fl -no-global-seedratio
All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio
.It Fl h Fl -help
Print command-line option descriptions.
@ -155,6 +167,16 @@ Remove the current torrent(s). This does not delete the downloaded data.
.It Fl -remove-and-delete
Remove the current torrent(s) and delete their downloaded data.
.It Fl sr Fl -seedratio Ar ratio
Let the current torrent(s) seed until a specific
.Ar ratio
.It Fl SR Fl -no-seedratio
Let the current torrent(s) seed regardless of ratio
.It Fl srd Fl -seedratio-default
Let the current torrent(s) use the global seedratio settings
.It Fl s Fl -start
Start the current torrent(s)

View File

@ -200,6 +200,30 @@ pref_int_set_default( const char * key,
pref_int_set( key, value );
}
double
pref_double_get( const char * key )
{
double d = 0.0;
tr_bencDictFindDouble( getPrefs( ), key, &d );
return d;
}
void
pref_double_set( const char * key,
double value )
{
tr_bencDictAddDouble( getPrefs( ), key, value );
}
void
pref_double_set_default( const char * key,
double value )
{
if ( !tr_bencDictFind( getPrefs( ), key ) )
pref_double_set( key, value );
}
/***
****
***/

View File

@ -32,6 +32,10 @@ int64_t pref_int_get ( const char * key );
void pref_int_set ( const char * key, int64_t value );
void pref_int_set_default ( const char * key, int64_t value );
double pref_double_get ( const char * key );
void pref_double_set ( const char * key, double value );
void pref_double_set_default( const char * key, double value );
gboolean pref_flag_get ( const char * key );
void pref_flag_set ( const char * key, gboolean value );
void pref_flag_set_default ( const char * key, gboolean value );

View File

@ -30,6 +30,13 @@
#include "util.h"
#define UPDATE_INTERVAL_SECONDS 2
typedef struct
{
gpointer gtor;
TrCore * core;
guint handler;
} ResponseData;
/****
***** PIECES VIEW
@ -1170,6 +1177,19 @@ dl_speed_toggled_cb( GtkToggleButton *tb,
speed_toggled_cb( tb, gtor, TR_DOWN );
}
#define RATIO_MODE_KEY "ratio-mode"
static void
ratio_mode_changed_cb( GtkToggleButton * tb, gpointer gtor )
{
if( gtk_toggle_button_get_active( tb ) )
{
tr_torrent * tor = tr_torrent_handle( gtor );
const int mode = GPOINTER_TO_INT( g_object_get_data( G_OBJECT( tb ), RATIO_MODE_KEY ) );
tr_torrentSetRatioMode( tor, mode );
}
}
static void
sensitize_from_check_cb( GtkToggleButton *toggle,
gpointer w )
@ -1203,6 +1223,16 @@ dl_speed_spun_cb( GtkSpinButton *spin,
setSpeedLimit( spin, gtor, TR_DOWN );
}
static void
ratio_spun_cb( GtkSpinButton *spin,
gpointer gtor )
{
tr_torrent * tor = tr_torrent_handle ( gtor );
float ratio = gtk_spin_button_get_value ( spin );
tr_torrentSetRatioLimit( tor, ratio );
}
static void
max_peers_spun_cb( GtkSpinButton * spin,
gpointer gtor )
@ -1212,13 +1242,45 @@ max_peers_spun_cb( GtkSpinButton * spin,
tr_torrentSetPeerLimit( tr_torrent_handle( gtor ), n );
}
static char*
get_global_ratio_radiobutton_string( void )
{
char * s;
const gboolean b = pref_flag_get( TR_PREFS_KEY_RATIO_ENABLED );
const double d = pref_double_get( TR_PREFS_KEY_RATIO );
if( b )
s = g_strdup_printf( _( "Use _Global setting (currently: stop seeding when a torrent's ratio reaches %.2f)" ), d );
else
s = g_strdup( _( "Use _Global setting (currently: seed regardless of ratio)" ) );
return s;
}
static void
prefsChanged( TrCore * core UNUSED, const char * key, gpointer rb )
{
if( !strcmp( key, TR_PREFS_KEY_RATIO_ENABLED ) || !strcmp( key, TR_PREFS_KEY_RATIO ) )
{
char * s = get_global_ratio_radiobutton_string( );
gtk_button_set_label( GTK_BUTTON( rb ), s );
g_free( s );
}
}
static GtkWidget*
options_page_new( TrTorrent * gtor )
options_page_new( ResponseData * data )
{
uint16_t maxConnectedPeers;
int i, row;
double d;
gboolean b;
GtkWidget * t, *w, *tb;
char * s;
GSList * group;
GtkWidget * t, *w, *tb, *h;
tr_ratiolimit mode;
TrCore * core = data->core;
TrTorrent * gtor = data->gtor;
tr_torrent * tor = tr_torrent_handle ( gtor );
row = 0;
@ -1263,6 +1325,44 @@ options_page_new( TrTorrent * gtor )
sensitize_from_check_cb ( GTK_TOGGLE_BUTTON( tb ), w );
hig_workarea_add_row_w ( t, &row, tb, w, NULL );
hig_workarea_add_section_divider ( t, &row );
hig_workarea_add_section_title ( t, &row, _( "Seed-Until Ratio" ) );
group = NULL;
mode = tr_torrentGetRatioMode( tor );
s = get_global_ratio_radiobutton_string( );
w = gtk_radio_button_new_with_mnemonic( group, s );
data->handler = g_signal_connect( core, "prefs-changed", G_CALLBACK( prefsChanged ), w );
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON( w ), mode == TR_RATIOLIMIT_GLOBAL);
hig_workarea_add_wide_control( t, &row, w );
g_free( s );
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_GLOBAL ) );
g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), gtor );
w = gtk_radio_button_new_with_mnemonic( group, _( "Seed _regardless of ratio" ) );
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON( w ), mode == TR_RATIOLIMIT_UNLIMITED);
hig_workarea_add_wide_control( t, &row, w );
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_UNLIMITED ) );
g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), gtor );
h = gtk_hbox_new ( FALSE, GUI_PAD );
w = gtk_radio_button_new_with_mnemonic( group, _( "_Stop seeding when a torrent's ratio reaches" ) );
g_object_set_data( G_OBJECT( w ), RATIO_MODE_KEY, GINT_TO_POINTER( TR_RATIOLIMIT_SINGLE ) );
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON( w ), mode == TR_RATIOLIMIT_SINGLE);
g_signal_connect( w, "toggled", G_CALLBACK( ratio_mode_changed_cb ), gtor );
group = gtk_radio_button_get_group( GTK_RADIO_BUTTON( w ) );
gtk_box_pack_start ( GTK_BOX( h ), w, FALSE, FALSE, 0 );
d = tr_torrentGetRatioLimit( tor );
w = gtk_spin_button_new_with_range( 0.5, INT_MAX, .05 );
gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 2 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), d );
g_signal_connect ( w, "value-changed", G_CALLBACK( ratio_spun_cb ), gtor );
gtk_box_pack_start ( GTK_BOX( h ), w, FALSE, FALSE, 0 );
hig_workarea_add_wide_control( t, &row, h );
hig_workarea_add_section_divider ( t, &row );
hig_workarea_add_section_title ( t, &row, _( "Peer Connections" ) );
@ -1458,10 +1558,17 @@ remove_tag( gpointer tag )
static void
response_cb( GtkDialog * dialog,
int response UNUSED,
gpointer gtor )
gpointer data )
{
g_object_weak_unref ( G_OBJECT( gtor ), torrent_destroyed, dialog );
ResponseData *rd = data;
TrCore * core = rd->core;
gulong handler = rd-> handler;
g_signal_handler_disconnect( core, handler );
g_object_weak_unref ( G_OBJECT( rd->gtor ), torrent_destroyed, dialog );
gtk_widget_destroy ( GTK_WIDGET( dialog ) );
g_free ( rd );
}
static gboolean
@ -1477,6 +1584,7 @@ periodic_refresh( gpointer data )
GtkWidget*
torrent_inspector_new( GtkWindow * parent,
TrCore * core,
TrTorrent * gtor )
{
guint tag;
@ -1484,14 +1592,18 @@ torrent_inspector_new( GtkWindow * parent,
tr_torrent * tor = tr_torrent_handle ( gtor );
char title[512];
const tr_info * info = tr_torrent_info ( gtor );
ResponseData * rd;
/* create the dialog */
rd = g_new0(ResponseData, 1);
rd->gtor = gtor;
rd->core = core;
g_snprintf( title, sizeof( title ), _( "%s Properties" ), info->name );
d = gtk_dialog_new_with_buttons ( title, parent, 0,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
NULL );
gtk_window_set_role ( GTK_WINDOW( d ), "tr-info" );
g_signal_connect ( d, "response", G_CALLBACK ( response_cb ), gtor );
g_signal_connect ( d, "response", G_CALLBACK ( response_cb ), rd );
gtk_dialog_set_has_separator( GTK_DIALOG( d ), FALSE );
gtk_container_set_border_width( GTK_CONTAINER( d ), GUI_PAD );
g_object_weak_ref ( G_OBJECT( gtor ), torrent_destroyed, d );
@ -1526,10 +1638,10 @@ torrent_inspector_new( GtkWindow * parent,
gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w,
gtk_label_new ( _( "Files" ) ) );
w = options_page_new ( gtor );
w = options_page_new ( rd );
g_object_set_data ( G_OBJECT( d ), "options-top", w );
gtk_notebook_append_page ( GTK_NOTEBOOK( n ), w,
gtk_label_new ( _( "Options" ) ) );
gtk_label_new ( _( "Options" ) ) );
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( d )->vbox ), n, TRUE, TRUE, 0 );

View File

@ -14,9 +14,11 @@
#define GTK_TORRENT_INSPECTOR_H
#include <gtk/gtk.h>
#include "tr-core.h"
#include "tr-torrent.h"
GtkWidget* torrent_inspector_new( GtkWindow * parent,
TrCore * core,
TrTorrent * tor );
#endif /* TG_PREFS_H */

View File

@ -1082,6 +1082,16 @@ prefschanged( TrCore * core UNUSED,
const int limit = pref_int_get( key );
tr_sessionSetSpeedLimit( tr, TR_UP, limit );
}
else if( !strcmp( key, TR_PREFS_KEY_RATIO_ENABLED ) )
{
const gboolean b = pref_flag_get( key );
tr_sessionSetRatioLimited( tr, b );
}
else if( !strcmp( key, TR_PREFS_KEY_RATIO ) )
{
const double limit = pref_double_get( key );
tr_sessionSetRatioLimit( tr, limit );
}
else if( !strncmp( key, "sched-", 6 ) )
{
updateScheduledLimits( tr );
@ -1316,7 +1326,7 @@ showInfoForeach( GtkTreeModel * model,
gtk_window_present( GTK_WINDOW( w ) );
else
{
w = torrent_inspector_new( GTK_WINDOW( data->wind ), tor );
w = torrent_inspector_new( GTK_WINDOW( data->wind ), data->core, tor );
gtk_widget_show( w );
g_hash_table_insert( data->tor2details, (gpointer)hashString, w );
g_hash_table_insert( data->details2tor, w, (gpointer)hashString );

View File

@ -1270,3 +1270,17 @@ tr_core_set_pref_int( TrCore * self,
}
}
void
tr_core_set_pref_double( TrCore * self,
const char * key,
double newval )
{
const double oldval = pref_double_get( key );
if( oldval != newval )
{
pref_double_set( key, newval );
commitPrefsChange( self, key );
}
}

View File

@ -204,6 +204,11 @@ void tr_core_set_pref_int( TrCore * self,
const char * key,
int val );
/* Set a double preference value, save the prefs file, and emit the
"prefs-changed" signal */
void tr_core_set_pref_double( TrCore * self,
const char * key,
double val );
/**
***

View File

@ -145,6 +145,7 @@ struct spin_idle_data
{
gpointer core;
GTimer * last_change;
gboolean isDouble;
};
static void
@ -168,9 +169,17 @@ spun_cb_idle( gpointer spin )
{
/* update the core */
const char * key = g_object_get_data( o, PREF_KEY );
const int value = gtk_spin_button_get_value_as_int(
GTK_SPIN_BUTTON( spin ) );
tr_core_set_pref_int( TR_CORE( data->core ), key, value );
if (data->isDouble)
{
const double value = gtk_spin_button_get_value( GTK_SPIN_BUTTON( spin ) );
tr_core_set_pref_double( TR_CORE( data->core ), key, value );
}
else
{
const int value = gtk_spin_button_get_value_as_int(
GTK_SPIN_BUTTON( spin ) );
tr_core_set_pref_int( TR_CORE( data->core ), key, value );
}
/* cleanup */
g_object_set_data( o, IDLE_DATA, NULL );
@ -183,7 +192,8 @@ spun_cb_idle( gpointer spin )
static void
spun_cb( GtkSpinButton * w,
gpointer core )
gpointer core,
gboolean isDouble )
{
/* user may be spinning through many values, so let's hold off
for a moment to keep from flooding the core with changes */
@ -195,6 +205,7 @@ spun_cb( GtkSpinButton * w,
data = g_new( struct spin_idle_data, 1 );
data->core = core;
data->last_change = g_timer_new( );
data->isDouble = isDouble;
g_object_set_data_full( o, IDLE_DATA, data, spin_idle_data_free );
g_object_ref( G_OBJECT( o ) );
g_timeout_add( 100, spun_cb_idle, w );
@ -202,6 +213,20 @@ spun_cb( GtkSpinButton * w,
g_timer_start( data->last_change );
}
static void
spun_cb_int( GtkSpinButton * w,
gpointer core )
{
spun_cb( w, core, FALSE );
}
static void
spun_cb_double( GtkSpinButton * w,
gpointer core )
{
spun_cb( w, core, TRUE );
}
static GtkWidget*
new_spin_button( const char * key,
gpointer core,
@ -215,7 +240,24 @@ new_spin_button( const char * key,
key ), g_free );
gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 0 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), pref_int_get( key ) );
g_signal_connect( w, "value-changed", G_CALLBACK( spun_cb ), core );
g_signal_connect( w, "value-changed", G_CALLBACK( spun_cb_int ), core );
return w;
}
static GtkWidget*
new_spin_button_double( const char * key,
gpointer core,
double low,
double high,
double step )
{
GtkWidget * w = gtk_spin_button_new_with_range( low, high, step );
g_object_set_data_full( G_OBJECT( w ), PREF_KEY, g_strdup(
key ), g_free );
gtk_spin_button_set_digits( GTK_SPIN_BUTTON( w ), 2 );
gtk_spin_button_set_value( GTK_SPIN_BUTTON( w ), pref_double_get( key ) );
g_signal_connect( w, "value-changed", G_CALLBACK( spun_cb_double ), core );
return w;
}
@ -1120,6 +1162,13 @@ bandwidthPage( GObject * core )
g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
hig_workarea_add_row_w( t, &row, w, w2, NULL );
s = _( "_Stop seeding when a torrent's ratio reaches:" );
w = new_check_button( s, TR_PREFS_KEY_RATIO_ENABLED, core );
w2 = new_spin_button_double( TR_PREFS_KEY_RATIO, core, .5, INT_MAX, .05 );
gtk_widget_set_sensitive( GTK_WIDGET( w2 ), pref_flag_get( TR_PREFS_KEY_RATIO_ENABLED ) );
g_signal_connect( w, "toggled", G_CALLBACK( target_cb ), w2 );
hig_workarea_add_row_w( t, &row, w, w2, NULL );
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Scheduled Limits" ) );

View File

@ -86,8 +86,8 @@ struct tr_datatype
static void
didWriteWrapper( tr_peerIo * io, size_t bytes_transferred )
{
while( bytes_transferred )
{
while( bytes_transferred && tr_isPeerIo( io ) )
{
struct tr_datatype * next = __tr_list_entry( io->outbuf_datatypes.next, struct tr_datatype, head );
const size_t payload = MIN( next->length, bytes_transferred );
const size_t overhead = getPacketOverhead( payload );
@ -99,13 +99,16 @@ didWriteWrapper( tr_peerIo * io, size_t bytes_transferred )
if( io->didWrite )
io->didWrite( io, payload, next->isPieceData, io->userData );
bytes_transferred -= payload;
next->length -= payload;
if( !next->length ) {
__tr_list_remove( io->outbuf_datatypes.next );
tr_free( next );
}
if( tr_isPeerIo( io ) )
{
bytes_transferred -= payload;
next->length -= payload;
if( !next->length ) {
__tr_list_remove( io->outbuf_datatypes.next );
tr_free( next );
}
}
}
}

View File

@ -932,6 +932,15 @@ peerSuggestedPiece( Torrent * t UNUSED,
#endif
}
static void
fireRatioLimitHit( tr_torrent * tor )
{
assert( tr_isTorrent( tor ) );
if( tor->ratio_limit_hit_func )
tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
}
static void
peerCallbackFunc( void * vpeer, void * vevent, void * vt )
{
@ -963,6 +972,7 @@ peerCallbackFunc( void * vpeer, void * vevent, void * vt )
{
const time_t now = time( NULL );
tr_torrent * tor = t->tor;
double seedRatio;
tor->activityDate = now;
@ -980,6 +990,17 @@ peerCallbackFunc( void * vpeer, void * vevent, void * vt )
a->piece_data_time = now;
}
/* if we're seeding and we've reached our seed ratio limit, stop the torrent */
if( tr_torrentIsSeed( tor ) && tr_torrentGetSeedRatio( tor, &seedRatio ) ) {
double up = (double)tor->uploadedCur + (double)tor->uploadedPrev;
double down = (double)tor->downloadedCur + (double)tor->downloadedPrev;
double ratio = tr_getRatio( up, down );
if( ratio >= seedRatio ) {
tr_torrentStop( tor );
fireRatioLimitHit( tor );
}
}
break;
}

View File

@ -1596,7 +1596,9 @@ didWrite( tr_peerIo * io UNUSED, size_t bytesWritten, int wasPieceData, void * v
{
tr_peermsgs * msgs = vmsgs;
firePeerGotData( msgs, bytesWritten, wasPieceData );
peerPulse( msgs );
if ( tr_isPeerIo( io ) && io->userData )
peerPulse( msgs );
}
static ReadState

View File

@ -39,12 +39,15 @@
#define KEY_PRIORITY "priority"
#define KEY_PROGRESS "progress"
#define KEY_SPEEDLIMIT "speed-limit"
#define KEY_RATIOLIMIT "ratio-limit"
#define KEY_UPLOADED "uploaded"
#define KEY_SPEEDLIMIT_DOWN_SPEED "down-speed"
#define KEY_SPEEDLIMIT_DOWN_MODE "down-mode"
#define KEY_SPEEDLIMIT_UP_SPEED "up-speed"
#define KEY_SPEEDLIMIT_UP_MODE "up-mode"
#define KEY_RATIOLIMIT_RATIO "ratio-limit"
#define KEY_RATIOLIMIT_MODE "ratio-mode"
#define KEY_PROGRESS_MTIMES "mtimes"
#define KEY_PROGRESS_BITFIELD "bitfield"
@ -255,6 +258,18 @@ saveSpeedLimits( tr_benc * dict,
tr_torrentGetSpeedMode( tor, TR_UP ) );
}
static void
saveRatioLimits( tr_benc * dict,
const tr_torrent * tor )
{
tr_benc * d = tr_bencDictAddDict( dict, KEY_RATIOLIMIT, 4 );
tr_bencDictAddDouble( d, KEY_RATIOLIMIT_RATIO,
tr_torrentGetRatioLimit( tor ) );
tr_bencDictAddInt( d, KEY_RATIOLIMIT_MODE,
tr_torrentGetRatioMode( tor ) );
}
static uint64_t
loadSpeedLimits( tr_benc * dict,
tr_torrent * tor )
@ -279,6 +294,26 @@ loadSpeedLimits( tr_benc * dict,
return ret;
}
static uint64_t
loadRatioLimits( tr_benc * dict,
tr_torrent * tor )
{
uint64_t ret = 0;
tr_benc * d;
if( tr_bencDictFindDict( dict, KEY_RATIOLIMIT, &d ) )
{
int64_t i;
double dratio;
if( tr_bencDictFindDouble( d, KEY_RATIOLIMIT_RATIO, &dratio ) )
tr_torrentSetRatioLimit( tor, dratio );
if( tr_bencDictFindInt( d, KEY_RATIOLIMIT_MODE, &i ) )
tr_torrentSetRatioMode( tor, i );
ret = TR_FR_RATIOLIMIT;
}
return ret;
}
/***
****
***/
@ -435,6 +470,7 @@ tr_torrentSaveResume( const tr_torrent * tor )
saveDND( &top, tor );
saveProgress( &top, tor );
saveSpeedLimits( &top, tor );
saveRatioLimits( &top, tor );
filename = getResumeFilename( tor );
tr_bencSaveFile( filename, &top );
@ -553,6 +589,9 @@ loadFromFile( tr_torrent * tor,
if( fieldsToLoad & TR_FR_SPEEDLIMIT )
fieldsLoaded |= loadSpeedLimits( &top, tor );
if( fieldsToLoad & TR_FR_RATIOLIMIT )
fieldsLoaded |= loadRatioLimits( &top, tor );
tr_bencFree( &top );
tr_free( filename );

View File

@ -32,7 +32,8 @@ enum
TR_FR_MAX_PEERS = ( 1 << 10 ),
TR_FR_ADDED_DATE = ( 1 << 11 ),
TR_FR_DONE_DATE = ( 1 << 12 ),
TR_FR_ACTIVITY_DATE = ( 1 << 13 )
TR_FR_ACTIVITY_DATE = ( 1 << 13 ),
TR_FR_RATIOLIMIT = ( 1 << 14 )
};
/**

View File

@ -603,6 +603,7 @@ torrentSet( tr_session * session,
for( i = 0; i < torrentCount; ++i )
{
int64_t tmp;
double d;
tr_benc * files;
tr_torrent * tor = torrents[i];
@ -629,7 +630,10 @@ torrentSet( tr_session * session,
if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &tmp ) )
tr_torrentSetSpeedMode( tor, TR_UP, tmp ? TR_SPEEDLIMIT_SINGLE
: TR_SPEEDLIMIT_GLOBAL );
if( tr_bencDictFindDouble( args_in, "ratio-limit", &d ) )
tr_torrentSetRatioLimit( tor, d );
if( tr_bencDictFindInt( args_in, "ratio-limit-mode", &tmp ) )
tr_torrentSetRatioMode( tor, tmp );
notify( session, TR_RPC_TORRENT_CHANGED, tor );
}
@ -786,6 +790,7 @@ sessionSet( tr_session * session,
struct tr_rpc_idle_data * idle_data )
{
int64_t i;
double d;
const char * str;
assert( idle_data == NULL );
@ -808,6 +813,10 @@ sessionSet( tr_session * session,
tr_sessionSetSpeedLimit( session, TR_UP, i );
if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &i ) )
tr_sessionSetSpeedLimitEnabled( session, TR_UP, i );
if( tr_bencDictFindDouble( args_in, "ratio-limit", &d ) )
tr_sessionSetRatioLimit( session, d );
if( tr_bencDictFindInt( args_in, "ratio-limit-enabled", &i ) )
tr_sessionSetRatioLimited( session, i );
if( tr_bencDictFindStr( args_in, "encryption", &str ) )
{
if( !strcmp( str, "required" ) )
@ -892,6 +901,8 @@ sessionGet( tr_session * session,
tr_bencDictAddInt( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_UP ) );
tr_bencDictAddInt( d, "speed-limit-down", tr_sessionGetSpeedLimit( session, TR_DOWN ) );
tr_bencDictAddInt( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_DOWN ) );
tr_bencDictAddDouble( d, "ratio-limit", tr_sessionGetRatioLimit( session ) );
tr_bencDictAddInt( d, "ratio-limit-enabled", tr_sessionIsRatioLimited( session ) );
tr_bencDictAddStr( d, "version", LONG_VERSION_STRING );
switch( tr_sessionGetEncryption( session ) ) {
case TR_CLEAR_PREFERRED: str = "tolerated"; break;

View File

@ -245,6 +245,8 @@ tr_sessionGetDefaultSettings( tr_benc * d )
tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_PORT, 80 );
tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_TYPE, TR_PROXY_HTTP );
tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_USERNAME, "" );
tr_bencDictAddDouble( d, TR_PREFS_KEY_RATIO, 2.0 );
tr_bencDictAddInt( d, TR_PREFS_KEY_RATIO_ENABLED, FALSE );
tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE );
tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_ENABLED, TRUE );
tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_PASSWORD, "" );
@ -291,6 +293,8 @@ tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_PORT, s->proxyPort );
tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_TYPE, s->proxyType );
tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_USERNAME, s->proxyUsername );
tr_bencDictAddDouble( d, TR_PREFS_KEY_RATIO, s->desiredRatio );
tr_bencDictAddInt( d, TR_PREFS_KEY_RATIO_ENABLED, s->isRatioLimited );
tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) );
tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_ENABLED, tr_sessionIsRPCEnabled( s ) );
tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_PASSWORD, freeme[n++] = tr_sessionGetRPCPassword( s ) );
@ -408,6 +412,7 @@ tr_sessionInitImpl( void * vdata )
{
int64_t i;
int64_t j;
double d;
tr_bool found;
const char * str;
tr_benc settings;
@ -555,6 +560,12 @@ tr_sessionInitImpl( void * vdata )
tr_sessionSetSpeedLimit( session, TR_DOWN, i );
tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, j );
found = tr_bencDictFindDouble( &settings, TR_PREFS_KEY_RATIO, &d )
&& tr_bencDictFindInt( &settings, TR_PREFS_KEY_RATIO_ENABLED, &j );
assert( found );
tr_sessionSetRatioLimit( session, d );
tr_sessionSetRatioLimited( session, j );
/* initialize the blocklist */
filename = tr_buildPath( session->configDir, "blocklists", NULL );
tr_mkdirp( filename, 0777 );
@ -737,6 +748,15 @@ tr_sessionSetSpeedLimitEnabled( tr_session * session,
updateBandwidth( session, dir );
}
void
tr_sessionSetRatioLimited( tr_session * session,
tr_bool isLimited )
{
assert( tr_isSession( session ) );
session->isRatioLimited = isLimited;
}
void
tr_sessionSetSpeedLimit( tr_session * session,
tr_direction dir,
@ -749,6 +769,15 @@ tr_sessionSetSpeedLimit( tr_session * session,
updateBandwidth( session, dir );
}
void
tr_sessionSetRatioLimit( tr_session * session,
double desiredRatio )
{
assert( tr_isSession( session ) );
session->desiredRatio = desiredRatio;
}
tr_bool
tr_sessionIsSpeedLimitEnabled( const tr_session * session,
tr_direction dir )
@ -759,6 +788,14 @@ tr_sessionIsSpeedLimitEnabled( const tr_session * session,
return session->isSpeedLimited[dir];
}
tr_bool
tr_sessionIsRatioLimited( const tr_session * session )
{
assert( tr_isSession( session ) );
return session->isRatioLimited;
}
int
tr_sessionGetSpeedLimit( const tr_session * session,
tr_direction dir )
@ -769,6 +806,14 @@ tr_sessionGetSpeedLimit( const tr_session * session,
return session->speedLimit[dir];
}
double
tr_sessionGetRatioLimit( const tr_session * session )
{
assert( tr_isSession( session ) );
return session->desiredRatio;
}
/***
****
***/

View File

@ -66,6 +66,7 @@ struct tr_session
tr_bool isClosed;
tr_bool isWaiting;
tr_bool useLazyBitfield;
tr_bool isRatioLimited;
tr_bool isSpeedLimited[2];
int speedLimit[2];
@ -128,7 +129,9 @@ struct tr_session
int so_rcvbuf;
/* monitors the "global pool" speeds */
struct tr_bandwidth * bandwidth;
struct tr_bandwidth * bandwidth;
double desiredRatio;
};
const char * tr_sessionFindTorrentFile( const tr_session * session,
@ -141,7 +144,6 @@ void tr_sessionSetTorrentFile( tr_session * session,
tr_bool tr_sessionIsAddressBlocked( const tr_session * session,
const struct tr_address * addr );
void tr_globalLock( tr_session * );
void tr_globalUnlock( tr_session * );

View File

@ -145,9 +145,46 @@ int
tr_torrentGetSpeedLimit( const tr_torrent * tor,
tr_direction dir )
{
assert( tr_isTorrent( tor ) );
return tr_bandwidthGetDesiredSpeed( tor->bandwidth, dir );
}
void
tr_torrentSetRatioMode( tr_torrent * tor,
tr_ratiolimit mode )
{
assert( tr_isTorrent( tor ) );
assert( mode==TR_RATIOLIMIT_GLOBAL || mode==TR_RATIOLIMIT_SINGLE || mode==TR_RATIOLIMIT_UNLIMITED );
tor->ratioLimitMode = mode;
}
tr_ratiolimit
tr_torrentGetRatioMode( const tr_torrent * tor )
{
assert( tr_isTorrent( tor ) );
return tor->ratioLimitMode;
}
void
tr_torrentSetRatioLimit( tr_torrent * tor,
double desiredRatio )
{
assert( tr_isTorrent( tor ) );
tor->desiredRatio = desiredRatio;
}
double
tr_torrentGetRatioLimit( const tr_torrent * tor )
{
assert( tr_isTorrent( tor ) );
return tor->desiredRatio;
}
tr_bool
tr_torrentIsPieceTransferAllowed( const tr_torrent * tor,
tr_direction direction )
@ -177,6 +214,33 @@ tr_torrentIsPieceTransferAllowed( const tr_torrent * tor,
return isEnabled;
}
tr_bool
tr_torrentGetSeedRatio( const tr_torrent * tor, double * ratio )
{
double r = 0;
tr_bool isLimited;
switch( tr_torrentGetRatioMode( tor ) )
{
case TR_RATIOLIMIT_SINGLE:
isLimited = TRUE;
r = tr_torrentGetRatioLimit( tor );
break;
case TR_RATIOLIMIT_GLOBAL:
if(( isLimited = tr_sessionIsRatioLimited( tor->session )))
r = tr_sessionGetRatioLimit( tor->session );
break;
case TR_RATIOLIMIT_UNLIMITED:
isLimited = FALSE;
break;
}
*ratio = r;
return isLimited;
}
/***
****
***/
@ -525,8 +589,14 @@ torrentRealInit( tr_session * session,
tr_torrentSetSpeedLimit( tor, TR_UP,
tr_sessionGetSpeedLimit( tor->session, TR_UP ) );
tr_torrentSetSpeedLimit( tor, TR_DOWN,
tr_sessionGetSpeedLimit( tor->session,
TR_DOWN ) );
tr_sessionGetSpeedLimit( tor->session, TR_DOWN ) );
}
if( !( loaded & TR_FR_RATIOLIMIT ) )
{
tr_torrentSetRatioMode( tor, tr_sessionIsRatioLimited( tor-> session )
? TR_RATIOLIMIT_GLOBAL : TR_RATIOLIMIT_UNLIMITED );
tr_torrentSetRatioLimit( tor, tr_sessionGetRatioLimit( tor->session ) );
}
tor->completeness = tr_cpGetStatus( &tor->completion );
@ -1296,12 +1366,29 @@ tr_torrentSetCompletenessCallback( tr_torrent * tor,
tor->completeness_func_user_data = user_data;
}
void
tr_torrentSetRatioLimitHitCallback( tr_torrent * tor,
tr_torrent_ratio_limit_hit_func func,
void * user_data )
{
assert( tr_isTorrent( tor ) );
tor->ratio_limit_hit_func = func;
tor->ratio_limit_hit_func_user_data = user_data;
}
void
tr_torrentClearCompletenessCallback( tr_torrent * torrent )
{
tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
}
void
tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
{
tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
}
void
tr_torrentRecheckCompleteness( tr_torrent * tor )
{

View File

@ -70,6 +70,9 @@ tr_torrent* tr_torrentFindFromObfuscatedHash( tr_session * session,
tr_bool tr_torrentIsPieceTransferAllowed( const tr_torrent * torrent,
tr_direction direction );
tr_bool tr_torrentGetSeedRatio( const tr_torrent * tor, double * ratio );
#define tr_block( a, b ) _tr_block( tor, a, b )
tr_block_index_t _tr_block( const tr_torrent * tor,
@ -185,6 +188,9 @@ struct tr_torrent
tr_torrent_completeness_func * completeness_func;
void * completeness_func_user_data;
tr_torrent_ratio_limit_hit_func * ratio_limit_hit_func;
void * ratio_limit_hit_func_user_data;
tr_bool isRunning;
tr_bool isDeleting;
@ -202,6 +208,9 @@ struct tr_torrent
struct tr_bandwidth * bandwidth;
struct tr_torrent_peers * torrentPeers;
double desiredRatio;
tr_ratiolimit ratioLimitMode;
};
/* get the index of this piece's first block */

View File

@ -29,11 +29,11 @@ extern int tr_optind;
typedef struct tr_option
{
int val; /* the value to return from tr_getopt() */
int val; /* the value to return from tr_getopt() */
const char * longName; /* --long-form */
const char * description; /* option's description for tr_getopt_usage() */
const char * shortName; /* short form */
int has_arg; /* 0 for no argument, 1 for argument */
int has_arg; /* 0 for no argument, 1 for argument */
const char * argName; /* argument's description for tr_getopt_usage() */
}
tr_option;

View File

@ -176,6 +176,8 @@ static TR_INLINE tr_bool tr_isEncryptionMode( tr_encryption_mode m )
#define TR_PREFS_KEY_PROXY "proxy"
#define TR_PREFS_KEY_PROXY_TYPE "proxy-type"
#define TR_PREFS_KEY_PROXY_USERNAME "proxy-auth-username"
#define TR_PREFS_KEY_RATIO "ratio-limit"
#define TR_PREFS_KEY_RATIO_ENABLED "ratio-limit-enabled"
#define TR_PREFS_KEY_RPC_AUTH_REQUIRED "rpc-authentication-required"
#define TR_PREFS_KEY_RPC_ENABLED "rpc-enabled"
#define TR_PREFS_KEY_RPC_PASSWORD "rpc-password"
@ -560,6 +562,16 @@ void tr_sessionSetSpeedLimit ( tr_session * session,
int tr_sessionGetSpeedLimit ( const tr_session * session,
tr_direction direction );
void tr_sessionSetRatioLimited ( tr_session * session,
tr_bool isEnabled );
tr_bool tr_sessionIsRatioLimited ( const tr_session * session);
void tr_sessionSetRatioLimit ( tr_session * session,
double desiredRatio);
double tr_sessionGetRatioLimit ( const tr_session * session);
double tr_sessionGetRawSpeed ( const tr_session * session,
tr_direction direction );
@ -866,6 +878,14 @@ typedef enum
}
tr_speedlimit;
typedef enum
{
TR_RATIOLIMIT_GLOBAL = 0, /* follow the global settings */
TR_RATIOLIMIT_SINGLE = 1, /* override the global settings, seeding until a certain ratio */
TR_RATIOLIMIT_UNLIMITED = 2 /* override the global settings, seeding regardless of ratio */
}
tr_ratiolimit;
void tr_torrentSetSpeedMode( tr_torrent * tor,
tr_direction up_or_down,
tr_speedlimit mode );
@ -880,6 +900,16 @@ void tr_torrentSetSpeedLimit( tr_torrent * tor,
int tr_torrentGetSpeedLimit( const tr_torrent * tor,
tr_direction direction );
void tr_torrentSetRatioMode( tr_torrent * tor,
tr_ratiolimit mode );
tr_ratiolimit tr_torrentGetRatioMode( const tr_torrent * tor );
void tr_torrentSetRatioLimit( tr_torrent * tor,
double ratio );
double tr_torrentGetRatioLimit( const tr_torrent * tor );
/****
***** Peer Limits
****/
@ -994,6 +1024,9 @@ typedef void ( tr_torrent_completeness_func )( tr_torrent * torrent,
tr_completeness completeness,
void * user_data );
typedef void ( tr_torrent_ratio_limit_hit_func )( tr_torrent * torrent,
void * user_data );
/**
* Register to be notified whenever a torrent's "completeness"
* changes. This will be called, for example, when a torrent
@ -1015,6 +1048,21 @@ void tr_torrentSetCompletenessCallback(
void tr_torrentClearCompletenessCallback( tr_torrent * torrent );
/**
* Register to be notified whenever a torrent's ratio limit
* has been hit. This will be called when the torrent's
* ul/dl ratio has met or exceeded the designated ratio limit.
*
* Has the same restrictions as tr_torrentSetCompletenessCallback
*/
void tr_torrentSetRatioLimitHitCallback(
tr_torrent * torrent,
tr_torrent_ratio_limit_hit_func func,
void * user_data );
void tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent );
/**
* MANUAL ANNOUNCE
*