(trunk) #671 "torrent queuing" -- Preliminary implementation. Covers libtransmission; GTK+ and Qt clients, and rudimentary web client support.

This commit is contained in:
Jordan Lee 2011-08-01 22:24:24 +00:00
parent 56525f4b80
commit 61174b007e
38 changed files with 1477 additions and 459 deletions

View File

@ -65,6 +65,7 @@
Method name | libtransmission function
---------------------+-------------------------------------------------
"torrent-start" | tr_torrentStart
"torrent-start-now" | tr_torrentStartNow
"torrent-stop" | tr_torrentStop
"torrent-verify" | tr_torrentVerify
"torrent-reannounce" | tr_torrentManualUpdate ("ask tracker for more peers")
@ -98,6 +99,7 @@
"priority-high" | array indices of high-priority file(s)
"priority-low" | array indices of low-priority file(s)
"priority-normal" | array indices of normal-priority file(s)
"queuePosition" | number position of this torrent in its queue [0...n)
"seedIdleLimit" | number torrent-level number of minutes of seeding inactivity
"seedIdleMode" | number which seeding inactivity to use. See tr_inactvelimit
"seedRatioLimit" | double torrent-level seeding ratio
@ -179,6 +181,7 @@
pieceCount | number | tr_info
pieceSize | number | tr_info
priorities | array (see below) | n/a
queuePosition | number | tr_stat
rateDownload (B/s) | number | tr_stat
rateUpload (B/s) | number | tr_stat
recheckProgress | double | tr_stat
@ -423,6 +426,8 @@
"config-dir" | string | location of transmission's configuration directory
"download-dir" | string | default path to download torrents
"download-dir-free-space" | number | number of free bytes available in download-dir, or -1 if it can't be calculated
"download-queue-size" | number | max number of torrents to download at once (see download-queue-enabled)
"download-queue-enabled" | boolean | if true, limit how many torrents can be downloaded at once
"dht-enabled" | boolean | true means allow dht in public torrents
"encryption" | string | "required", "preferred", "tolerated"
"idle-seeding-limit" | number | torrents we're seeding will be stopped if they're idle for this long
@ -436,6 +441,7 @@
"peer-port" | number | port number
"peer-port-random-on-start" | boolean | true means pick a random peer port on launch
"port-forwarding-enabled" | boolean | true means enabled
"queue-stalled-minutes" | number | torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size
"rename-partial-files" | boolean | true means append ".part" to incomplete files
"rpc-version" | number | the current RPC API version
"rpc-version-minimum" | number | the minimum RPC API version supported
@ -443,6 +449,8 @@
"script-torrent-done-enabled" | boolean | whether or not to call the "done" script
"seedRatioLimit" | double | the default seed ratio for torrents to use
"seedRatioLimited" | boolean | true if seedRatioLimit is honored by default
"seed-queue-size" | number | max number of torrents to uploaded at once (see seed-queue-enabled)
"seed-queue-enabled" | boolean | if true, limit how many torrents can be uploaded at once
"speed-limit-down" | number | max global download speed (KBps)
"speed-limit-down-enabled" | boolean | true means enabled
"speed-limit-up" | number | max global upload speed (KBps)
@ -536,10 +544,27 @@
This method tells the transmission session to shut down.
Method-name: "session-close"
Method name: "session-close"
Request arguments: none
Response arguments: none
4.6. Queue Movement Requests
Method name | libtransmission function
---------------------+-------------------------------------------------
"queue-move-top" | tr_torrentQueueMoveTop()
"queue-move-up" | tr_torrentQueueMoveUp()
"queue-move-down" | tr_torrentQueueMoveDown()
"queue-move-bottom" | tr_torrentQueueMoveBottom()
Request arguments:
string | value type & description
------------+----------------------------------------------------------
"ids" | array torrent list, as described in 3.1.
Response arguments: none
5.0. Protocol Versions
The following changes have been made to the RPC interface:
@ -662,3 +687,17 @@
13 | 2.30 | yes | session-get | new arg "isUTP" to the "peers" list
| | yes | torrent-add | new arg "cookies"
| | NO | torrent-get | removed arg "peersKnown"
------+---------+-----------+----------------+-------------------------------
14 | 2.40 | NO | torrent-get | values of "status" field changed
| | yes | torrent-get | new arg "queuePosition"
| | yes | torrent-set | new arg "queuePosition"
| | yes | session-set | new arg "download-queue-size"
| | yes | session-set | new arg "download-queue-enabled"
| | yes | session-set | new arg "seed-queue-size"
| | yes | session-set | new arg "seed-queue-enabled"
| | yes | session-set | new arg "queue-stalled-minutes"
| | yes | | new method "queue-move-top"
| | yes | | new method "queue-move-up"
| | yes | | new method "queue-move-down"
| | yes | | new method "queue-move-bottom"
| | yes | | new method "torrent-start-now"

View File

@ -100,17 +100,19 @@ static GtkActionEntry entries[] =
{ "torrent-menu", NULL, N_( "_Torrent" ), NULL, NULL, NULL },
{ "view-menu", NULL, N_( "_View" ), NULL, NULL, NULL },
{ "sort-menu", NULL, N_( "_Sort Torrents By" ), NULL, NULL, NULL },
{ "queue-menu", NULL, N_( "_Queue" ), NULL, NULL, NULL },
{ "edit-menu", NULL, N_( "_Edit" ), NULL, NULL, NULL },
{ "help-menu", NULL, N_( "_Help" ), NULL, NULL, NULL },
{ "copy-magnet-link-to-clipboard", GTK_STOCK_COPY, N_("Copy _Magnet Link to Clipboard" ), "", NULL, G_CALLBACK( action_cb ) },
{ "open-torrent-from-url", GTK_STOCK_OPEN, N_("Open _URL..." ), "<control>U", N_( "Open URL..." ), G_CALLBACK( action_cb ) },
{ "open-torrent-toolbar", GTK_STOCK_OPEN, NULL, NULL, N_( "Open a torrent" ), G_CALLBACK( action_cb ) },
{ "open-torrent-menu", GTK_STOCK_OPEN, NULL, NULL, N_( "Open a torrent" ), G_CALLBACK( action_cb ) },
{ "start-torrent", GTK_STOCK_MEDIA_PLAY, N_( "_Start" ), "<control>S", N_( "Start torrent" ), G_CALLBACK( action_cb ) },
{ "torrent-start", GTK_STOCK_MEDIA_PLAY, N_( "_Start" ), "<control>S", N_( "Start torrent" ), G_CALLBACK( action_cb ) },
{ "torrent-start-now", GTK_STOCK_MEDIA_PLAY, N_( "Start _Now" ), "<shift><control>S", N_( "Start torrent now" ), G_CALLBACK( action_cb ) },
{ "show-stats", NULL, N_( "_Statistics" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "donate", NULL, N_( "_Donate" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "verify-torrent", NULL, N_( "_Verify Local Data" ), "<control>V", NULL, G_CALLBACK( action_cb ) },
{ "pause-torrent", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause" ), "<control>P", N_( "Pause torrent" ), G_CALLBACK( action_cb ) },
{ "torrent-verify", NULL, N_( "_Verify Local Data" ), "<control>V", NULL, G_CALLBACK( action_cb ) },
{ "torrent-stop", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause" ), "<control>P", N_( "Pause torrent" ), G_CALLBACK( action_cb ) },
{ "pause-all-torrents", GTK_STOCK_MEDIA_PAUSE, N_( "_Pause All" ), NULL, N_( "Pause all torrents" ), G_CALLBACK( action_cb ) },
{ "start-all-torrents", GTK_STOCK_MEDIA_PLAY, N_( "_Start All" ), NULL, N_( "Start all torrents" ), G_CALLBACK( action_cb ) },
{ "relocate-torrent", NULL, N_("Set _Location..." ), NULL, NULL, G_CALLBACK( action_cb ) },
@ -125,7 +127,11 @@ static GtkActionEntry entries[] =
{ "open-torrent-folder", GTK_STOCK_OPEN, N_( "Open Fold_er" ), "<control>E", NULL, G_CALLBACK( action_cb ) },
{ "show-about-dialog", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK( action_cb ) },
{ "help", GTK_STOCK_HELP, N_( "_Contents" ), "F1", NULL, G_CALLBACK( action_cb ) },
{ "update-tracker", GTK_STOCK_NETWORK, N_( "Ask Tracker for _More Peers" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "torrent-reannounce", GTK_STOCK_NETWORK, N_( "Ask Tracker for _More Peers" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "queue-move-top", GTK_STOCK_GOTO_TOP, N_( "Move to _Top" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "queue-move-up", GTK_STOCK_GO_UP, N_( "Move _Up" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "queue-move-down", GTK_STOCK_GO_DOWN, N_( "Move _Down" ), NULL, NULL, G_CALLBACK( action_cb ) },
{ "queue-move-bottom", GTK_STOCK_GOTO_BOTTOM, N_( "Move to _Bottom" ), NULL, NULL, G_CALLBACK( action_cb ) }
};
typedef struct

View File

@ -557,11 +557,13 @@ activityString( int activity, bool finished )
{
switch( activity )
{
case TR_STATUS_CHECK_WAIT: return _( "Waiting to verify local data" );
case TR_STATUS_CHECK: return _( "Verifying local data" );
case TR_STATUS_DOWNLOAD: return _( "Downloading" );
case TR_STATUS_SEED: return _( "Seeding" );
case TR_STATUS_STOPPED: return finished ? _( "Finished" ) : _( "Paused" );
case TR_STATUS_CHECK_WAIT: return _( "Waiting to verify local data" );
case TR_STATUS_CHECK: return _( "Verifying local data" );
case TR_STATUS_DOWNLOAD_WAIT: return _( "Queued to download" );
case TR_STATUS_DOWNLOAD: return _( "Downloading" );
case TR_STATUS_SEED_WAIT: return _( "Queued to seed" );
case TR_STATUS_SEED: return _( "Seeding" );
case TR_STATUS_STOPPED: return finished ? _( "Finished" ) : _( "Paused" );
}
return "";

View File

@ -581,7 +581,6 @@ enum
ACTIVITY_FILTER_ACTIVE,
ACTIVITY_FILTER_PAUSED,
ACTIVITY_FILTER_FINISHED,
ACTIVITY_FILTER_QUEUED,
ACTIVITY_FILTER_VERIFYING,
ACTIVITY_FILTER_ERROR,
ACTIVITY_FILTER_SEPARATOR
@ -612,10 +611,12 @@ test_torrent_activity( tr_torrent * tor, int type )
switch( type )
{
case ACTIVITY_FILTER_DOWNLOADING:
return st->activity == TR_STATUS_DOWNLOAD;
return ( st->activity == TR_STATUS_DOWNLOAD )
|| ( st->activity == TR_STATUS_DOWNLOAD_WAIT );
case ACTIVITY_FILTER_SEEDING:
return st->activity == TR_STATUS_SEED;
return ( st->activity == TR_STATUS_SEED )
|| ( st->activity == TR_STATUS_SEED_WAIT );
case ACTIVITY_FILTER_ACTIVE:
return ( st->peersSendingToUs > 0 )
@ -629,11 +630,9 @@ test_torrent_activity( tr_torrent * tor, int type )
case ACTIVITY_FILTER_FINISHED:
return st->finished == TRUE;
case ACTIVITY_FILTER_QUEUED:
return st->activity == TR_STATUS_CHECK_WAIT;
case ACTIVITY_FILTER_VERIFYING:
return ( st->activity == TR_STATUS_CHECK ) || ( st->activity == TR_STATUS_CHECK_WAIT );
return ( st->activity == TR_STATUS_CHECK )
|| ( st->activity == TR_STATUS_CHECK_WAIT );
case ACTIVITY_FILTER_ERROR:
return st->error != 0;
@ -700,7 +699,6 @@ activity_filter_model_new( GtkTreeModel * tmodel )
{ ACTIVITY_FILTER_SEEDING, N_( "Seeding" ), GTK_STOCK_GO_UP },
{ ACTIVITY_FILTER_PAUSED, N_( "Paused" ), GTK_STOCK_MEDIA_PAUSE },
{ ACTIVITY_FILTER_FINISHED, N_( "Finished" ), NULL },
{ ACTIVITY_FILTER_QUEUED, N_( "Queued" ), NULL },
{ ACTIVITY_FILTER_VERIFYING, N_( "Verifying" ), GTK_STOCK_REFRESH },
{ ACTIVITY_FILTER_ERROR, N_( "Error" ), GTK_STOCK_DIALOG_ERROR }
};

View File

@ -248,7 +248,8 @@ struct counts_data
{
int total_count;
int active_count;
int inactive_count;
int queued_count;
int stopped_count;
};
static void
@ -256,14 +257,18 @@ get_selected_torrent_counts_foreach( GtkTreeModel * model, GtkTreePath * path UN
GtkTreeIter * iter, gpointer user_data )
{
int activity = 0;
int queuePosition = -1;
struct counts_data * counts = user_data;
++counts->total_count;
gtk_tree_model_get( model, iter, MC_ACTIVITY, &activity, -1 );
if( activity == TR_STATUS_STOPPED )
++counts->inactive_count;
if( activity == TR_STATUS_DOWNLOAD_WAIT )
++counts->queued_count;
if( ( activity == TR_STATUS_STOPPED ) && ( queuePosition < 0 ) )
++counts->stopped_count;
else
++counts->active_count;
}
@ -271,9 +276,10 @@ get_selected_torrent_counts_foreach( GtkTreeModel * model, GtkTreePath * path UN
static void
get_selected_torrent_counts( struct cbdata * data, struct counts_data * counts )
{
counts->active_count = 0;
counts->inactive_count = 0;
counts->total_count = 0;
counts->active_count = 0;
counts->queued_count = 0;
counts->stopped_count = 0;
gtk_tree_selection_selected_foreach( data->sel, get_selected_torrent_counts_foreach, counts );
}
@ -296,6 +302,9 @@ refresh_actions( gpointer gdata )
const size_t total = gtr_core_get_torrent_count( data->core );
const size_t active = gtr_core_get_active_torrent_count( data->core );
const int torrent_count = gtk_tree_model_iter_n_children( gtr_core_model( data->core ), NULL );
const tr_session * session = gtr_core_session( data->core );
const bool queue_enabled = tr_sessionGetQueueEnabled( session, TR_DOWN )
|| tr_sessionGetQueueEnabled( session, TR_UP );
gtr_action_set_sensitive( "select-all", torrent_count != 0 );
gtr_action_set_sensitive( "deselect-all", torrent_count != 0 );
@ -303,19 +312,24 @@ refresh_actions( gpointer gdata )
gtr_action_set_sensitive( "start-all-torrents", active != total );
get_selected_torrent_counts( data, &sel_counts );
gtr_action_set_sensitive( "pause-torrent", sel_counts.active_count != 0 );
gtr_action_set_sensitive( "start-torrent", sel_counts.inactive_count != 0 );
gtr_action_set_sensitive( "torrent-stop", ( sel_counts.active_count + sel_counts.queued_count ) > 0 );
gtr_action_set_sensitive( "torrent-start", ( sel_counts.stopped_count ) > 0 );
gtr_action_set_sensitive( "torrent-start-now", ( sel_counts.stopped_count + sel_counts.queued_count ) > 0 );
gtr_action_set_sensitive( "torrent-verify", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "remove-torrent", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "delete-torrent", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "verify-torrent", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "relocate-torrent", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "show-torrent-properties", sel_counts.total_count != 0 );
gtr_action_set_sensitive( "open-torrent-folder", sel_counts.total_count == 1 );
gtr_action_set_sensitive( "copy-magnet-link-to-clipboard", sel_counts.total_count == 1 );
gtr_action_set_sensitive( "queue-move-top", queue_enabled && ( sel_counts.queued_count > 0 ) );
gtr_action_set_sensitive( "queue-move-up", queue_enabled && ( sel_counts.queued_count > 0 ) );
gtr_action_set_sensitive( "queue-move-down", queue_enabled && ( sel_counts.queued_count > 0 ) );
gtr_action_set_sensitive( "queue-move-bottom", queue_enabled && ( sel_counts.queued_count > 0 ) );
canUpdate = 0;
gtk_tree_selection_selected_foreach( data->sel, count_updatable_foreach, &canUpdate );
gtr_action_set_sensitive( "update-tracker", canUpdate != 0 );
gtr_action_set_sensitive( "torrent-reannounce", canUpdate != 0 );
data->refresh_actions_tag = 0;
return FALSE;
@ -1286,6 +1300,14 @@ on_prefs_changed( TrCore * core UNUSED, const char * key, gpointer data )
{
tr_sessionSetIncompleteFileNamingEnabled( tr, gtr_pref_flag_get( key ) );
}
else if( !strcmp( key, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE ) )
{
tr_sessionSetQueueSize( tr, TR_DOWN, gtr_pref_int_get( key ) );
}
else if( !strcmp( key, TR_PREFS_KEY_QUEUE_STALLED_MINUTES ) )
{
tr_sessionSetQueueStalledMinutes( tr, gtr_pref_int_get( key ) );
}
else if( !strcmp( key, TR_PREFS_KEY_DHT_ENABLED ) )
{
tr_sessionSetDHTEnabled( tr, gtr_pref_flag_get( key ) );
@ -1661,21 +1683,17 @@ gtr_actions_handler( const char * action_name, gpointer user_data )
gtk_widget_show( w );
}
}
else if( !strcmp( action_name, "start-torrent" ) )
else if( !strcmp( action_name, "torrent-start" )
|| !strcmp( action_name, "torrent-start-now" )
|| !strcmp( action_name, "torrent-stop" )
|| !strcmp( action_name, "torrent-reannounce" )
|| !strcmp( action_name, "torrent-verify" )
|| !strcmp( action_name, "queue-move-top" )
|| !strcmp( action_name, "queue-move-up" )
|| !strcmp( action_name, "queue-move-down" )
|| !strcmp( action_name, "queue-move-bottom" ) )
{
changed |= call_rpc_for_selected_torrents( data, "torrent-start" );
}
else if( !strcmp( action_name, "pause-torrent" ) )
{
changed |= call_rpc_for_selected_torrents( data, "torrent-stop" );
}
else if( !strcmp( action_name, "verify-torrent" ) )
{
changed |= call_rpc_for_selected_torrents( data, "torrent-verify" );
}
else if( !strcmp( action_name, "update-tracker" ) )
{
changed |= call_rpc_for_selected_torrents( data, "torrent-reannounce" );
changed |= call_rpc_for_selected_torrents( data, action_name );
}
else if( !strcmp( action_name, "open-torrent-folder" ) )
{

View File

@ -226,6 +226,14 @@ getShortStatusString( GString * gstr,
tr_truncd( st->recheckProgress * 100.0, 1 ) );
break;
case TR_STATUS_DOWNLOAD_WAIT:
g_string_append_printf( gstr, _( "Download queue #%d" ), st->queuePosition + 1 );
break;
case TR_STATUS_SEED_WAIT:
g_string_append_printf( gstr, _( "Seed queue #%d" ), st->queuePosition + 1 );
break;
case TR_STATUS_DOWNLOAD:
case TR_STATUS_SEED:
{
@ -253,10 +261,6 @@ getStatusString( GString * gstr,
const double uploadSpeed_KBps,
const double downloadSpeed_KBps )
{
const int isActive = st->activity != TR_STATUS_STOPPED;
const int isChecking = st->activity == TR_STATUS_CHECK
|| st->activity == TR_STATUS_CHECK_WAIT;
if( st->error )
{
const char * fmt[] = { NULL, N_( "Tracker gave a warning: \"%s\"" ),
@ -269,6 +273,8 @@ getStatusString( GString * gstr,
case TR_STATUS_STOPPED:
case TR_STATUS_CHECK_WAIT:
case TR_STATUS_CHECK:
case TR_STATUS_DOWNLOAD_WAIT:
case TR_STATUS_SEED_WAIT:
{
getShortStatusString( gstr, tor, st, uploadSpeed_KBps, downloadSpeed_KBps );
break;
@ -307,7 +313,11 @@ getStatusString( GString * gstr,
break;
}
if( isActive && !isChecking )
if( ( st->activity != TR_STATUS_CHECK_WAIT ) &&
( st->activity != TR_STATUS_CHECK ) &&
( st->activity != TR_STATUS_DOWNLOAD_WAIT ) &&
( st->activity != TR_STATUS_SEED_WAIT ) &&
( st->activity != TR_STATUS_STOPPED ) )
{
char buf[256];
getShortTransferString( tor, st, uploadSpeed_KBps, downloadSpeed_KBps, buf, sizeof( buf ) );
@ -630,7 +640,7 @@ render_compact( TorrentCellRenderer * cell,
struct TorrentCellRendererPrivate * p = cell->priv;
const tr_torrent * tor = p->tor;
const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
const gboolean active = st->activity != TR_STATUS_STOPPED;
const gboolean active = ( st->activity != TR_STATUS_STOPPED ) && ( st->activity != TR_STATUS_DOWNLOAD_WAIT ) && ( st->activity != TR_STATUS_SEED_WAIT );
const double percentDone = get_percent_done( tor, st, &seed );
const gboolean sensitive = active || st->error;
GString * gstr_stat = p->gstr1;
@ -673,7 +683,8 @@ render_compact( TorrentCellRenderer * cell,
g_object_set( p->icon_renderer, "pixbuf", icon, "sensitive", sensitive, NULL );
gtr_cell_renderer_render( p->icon_renderer, window, widget, &icon_area, flags );
g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL, "sensitive", sensitive,
g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", NULL,
"sensitive", sensitive || ( st->queuePosition >= 0 ),
#if GTK_CHECK_VERSION( 3,0,0 )
"inverted", seed,
#elif GTK_CHECK_VERSION( 2,6,0 )
@ -683,7 +694,7 @@ render_compact( TorrentCellRenderer * cell,
gtr_cell_renderer_render( p->progress_renderer, window, widget, &prog_area, flags );
g_object_set( p->text_renderer, "text", gstr_stat->str, "scale", SMALL_SCALE, "ellipsize", PANGO_ELLIPSIZE_END, FOREGROUND_COLOR_KEY, &text_color, NULL );
gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
g_object_set( p->text_renderer, "text", name, "scale", 1.0, NULL );
g_object_set( p->text_renderer, "text", name, "scale", 1.0, FOREGROUND_COLOR_KEY, &text_color, NULL );
gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
/* cleanup */
@ -715,7 +726,7 @@ render_full( TorrentCellRenderer * cell,
const tr_torrent * tor = p->tor;
const tr_stat * st = tr_torrentStatCached( (tr_torrent*)tor );
const tr_info * inf = tr_torrentInfo( tor );
const gboolean active = st->activity != TR_STATUS_STOPPED;
const gboolean active = ( st->activity != TR_STATUS_STOPPED ) && ( st->activity != TR_STATUS_DOWNLOAD_WAIT ) && ( st->activity != TR_STATUS_SEED_WAIT );
const double percentDone = get_percent_done( tor, st, &seed );
const gboolean sensitive = active || st->error;
GString * gstr_prog = p->gstr1;
@ -793,7 +804,7 @@ render_full( TorrentCellRenderer * cell,
gtr_cell_renderer_render( p->text_renderer, window, widget, &name_area, flags );
g_object_set( p->text_renderer, "text", gstr_prog->str, "scale", SMALL_SCALE, "weight", PANGO_WEIGHT_NORMAL, NULL );
gtr_cell_renderer_render( p->text_renderer, window, widget, &prog_area, flags );
g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", sensitive,
g_object_set( p->progress_renderer, "value", (int)(percentDone*100.0), "text", "", "sensitive", ( sensitive || st->queuePosition >= 0 ),
#if GTK_CHECK_VERSION( 3,0,0 )
"inverted", seed,
#elif GTK_CHECK_VERSION( 2,6,0 )
@ -801,7 +812,7 @@ render_full( TorrentCellRenderer * cell,
#endif
NULL );
gtr_cell_renderer_render( p->progress_renderer, window, widget, &prct_area, flags );
g_object_set( p->text_renderer, "text", gstr_stat->str, NULL );
g_object_set( p->text_renderer, "text", gstr_stat->str, FOREGROUND_COLOR_KEY, &text_color, NULL );
gtr_cell_renderer_render( p->text_renderer, window, widget, &stat_area, flags );
/* cleanup */

View File

@ -247,6 +247,7 @@ core_init( GTypeInstance * instance, gpointer g_class UNUSED )
G_TYPE_INT, /* tr_stat.activity */
G_TYPE_UCHAR, /* tr_stat.finished */
G_TYPE_CHAR, /* tr_priority_t */
G_TYPE_INT, /* tr_stat.queuePosition */
G_TYPE_UINT, /* build_torrent_trackers_hash() */
G_TYPE_INT, /* MC_ERROR */
G_TYPE_INT }; /* MC_ACTIVE_PEER_COUNT */
@ -457,6 +458,15 @@ compare_by_name( GtkTreeModel * m, GtkTreeIter * a, GtkTreeIter * b, gpointer us
return tr_strcmp0( ca, cb );
}
static int
compare_by_queue( const tr_stat * a, const tr_stat * b )
{
const bool a_is_queued = a->queuePosition >= 0;
const bool b_is_queued = b->queuePosition >= 0;
if( a_is_queued != b_is_queued ) return a_is_queued ? -1 : 1;
return b->queuePosition - a->queuePosition;
}
static int
compare_by_ratio( GtkTreeModel* m, GtkTreeIter * a, GtkTreeIter * b, gpointer user_data )
{
@ -470,6 +480,7 @@ compare_by_ratio( GtkTreeModel* m, GtkTreeIter * a, GtkTreeIter * b, gpointer us
sb = tr_torrentStatCached( tb );
if( !ret ) ret = compare_ratio( sa->ratio, sb->ratio );
if( !ret ) ret = compare_by_queue( sa, sb );
if( !ret ) ret = compare_by_name( m, a, b, user_data );
return ret;
}
@ -495,6 +506,7 @@ compare_by_activity( GtkTreeModel * m, GtkTreeIter * a, GtkTreeIter * b, gpointe
if( !ret ) ret = compare_double( aUp+aDown, bUp+bDown );
if( !ret ) ret = compare_uint64( sa->uploadedEver, sb->uploadedEver );
if( !ret ) ret = compare_by_queue( sa, sb );
if( !ret ) ret = compare_by_name( m, a, b, user_data );
return ret;
}
@ -569,11 +581,13 @@ compare_by_state( GtkTreeModel * m, GtkTreeIter * a, GtkTreeIter * b, gpointer u
{
int ret = 0;
int sa, sb;
tr_torrent *ta, *tb;
gtk_tree_model_get( m, a, MC_ACTIVITY, &sa, -1 );
gtk_tree_model_get( m, b, MC_ACTIVITY, &sb, -1 );
gtk_tree_model_get( m, a, MC_ACTIVITY, &sa, MC_TORRENT, &ta, -1 );
gtk_tree_model_get( m, b, MC_ACTIVITY, &sb, MC_TORRENT, &tb, -1 );
if( !ret ) ret = compare_int( sa, sb );
if( !ret ) ret = compare_by_queue( tr_torrentStatCached( ta ), tr_torrentStatCached( tb ) );
if( !ret ) ret = compare_by_progress( m, a, b, u );
return ret;
}
@ -1026,6 +1040,7 @@ gtr_core_add_torrent( TrCore * core, tr_torrent * tor, gboolean do_notify )
MC_ACTIVITY, st->activity,
MC_FINISHED, st->finished,
MC_PRIORITY, tr_torrentGetPriority( tor ),
MC_QUEUE_POSITION, st->queuePosition,
MC_TRACKERS, trackers_hash,
-1 );
@ -1412,6 +1427,7 @@ update_foreach( GtkTreeModel * model, GtkTreeIter * iter )
int oldActivePeerCount, newActivePeerCount;
int oldError, newError;
bool oldFinished, newFinished;
int oldQueuePosition, newQueuePosition;
tr_priority_t oldPriority, newPriority;
unsigned int oldTrackers, newTrackers;
double oldUpSpeed, newUpSpeed;
@ -1430,6 +1446,7 @@ update_foreach( GtkTreeModel * model, GtkTreeIter * iter )
MC_ACTIVITY, &oldActivity,
MC_FINISHED, &oldFinished,
MC_PRIORITY, &oldPriority,
MC_QUEUE_POSITION, &oldQueuePosition,
MC_TRACKERS, &oldTrackers,
MC_SPEED_UP, &oldUpSpeed,
MC_RECHECK_PROGRESS, &oldRecheckProgress,
@ -1442,6 +1459,7 @@ update_foreach( GtkTreeModel * model, GtkTreeIter * iter )
newActivity = st->activity;
newFinished = st->finished;
newPriority = tr_torrentGetPriority( tor );
newQueuePosition = st->queuePosition;
newTrackers = build_torrent_trackers_hash( tor );
newUpSpeed = st->pieceUploadSpeed_KBps;
newDownSpeed = st->pieceDownloadSpeed_KBps;
@ -1455,6 +1473,7 @@ update_foreach( GtkTreeModel * model, GtkTreeIter * iter )
|| ( newActivity != oldActivity )
|| ( newFinished != oldFinished )
|| ( newPriority != oldPriority )
|| ( newQueuePosition != oldQueuePosition )
|| ( newError != oldError )
|| ( newActivePeerCount != oldActivePeerCount )
|| ( newTrackers != oldTrackers )
@ -1469,6 +1488,7 @@ update_foreach( GtkTreeModel * model, GtkTreeIter * iter )
MC_ACTIVITY, newActivity,
MC_FINISHED, newFinished,
MC_PRIORITY, newPriority,
MC_QUEUE_POSITION, newQueuePosition,
MC_TRACKERS, newTrackers,
MC_SPEED_UP, newUpSpeed,
MC_SPEED_DOWN, newDownSpeed,

View File

@ -193,6 +193,7 @@ enum
MC_ACTIVITY,
MC_FINISHED,
MC_PRIORITY,
MC_QUEUE_POSITION,
MC_TRACKERS,
/* tr_stat.error

View File

@ -252,6 +252,62 @@ target_cb( GtkWidget * tb, gpointer target )
gtk_widget_set_sensitive( GTK_WIDGET( target ), b );
}
/****
***** Download Tab
****/
static GtkWidget*
downloadPage( GObject * core )
{
GtkWidget * t;
GtkWidget * w;
GtkWidget * l;
const char * s;
int row = 0;
t = hig_workarea_create( );
hig_workarea_add_section_title( t, &row, _( "Location" ) );
w = new_path_chooser_button( TR_PREFS_KEY_DOWNLOAD_DIR, core );
hig_workarea_add_row( t, &row, _( "Save to _Location:" ), w, NULL );
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Queue" ) );
s = _( "Maximum active _downloads:" );
w = new_spin_button( TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, core, 0, INT_MAX, 1 );
hig_workarea_add_row( t, &row, s, w, NULL );
s = _( "E_xempt torrents if idle for N minutes:" );
w = new_spin_button( TR_PREFS_KEY_QUEUE_STALLED_MINUTES, core, 1, INT_MAX, 1 );
hig_workarea_add_row( t, &row, s, w, NULL );
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Incomplete" ) );
s = _( "Append \"._part\" to incomplete files' names" );
w = new_check_button( s, TR_PREFS_KEY_RENAME_PARTIAL_FILES, core );
hig_workarea_add_wide_control( t, &row, w );
s = _( "Keep _incomplete torrents in:" );
l = new_check_button( s, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, core );
w = new_path_chooser_button( TR_PREFS_KEY_INCOMPLETE_DIR, core );
gtk_widget_set_sensitive( GTK_WIDGET( w ), gtr_pref_flag_get( TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED ) );
g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
hig_workarea_add_row_w( t, &row, l, w, NULL );
s = _( "Call _script when torrent is completed:" );
l = new_check_button( s, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, core );
w = new_file_chooser_button( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, core );
gtk_widget_set_sensitive( GTK_WIDGET( w ), gtr_pref_flag_get( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED ) );
g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
hig_workarea_add_row_w( t, &row, l, w, NULL );
hig_workarea_finish( t, &row );
return t;
}
/****
***** Torrent Tab
****/
@ -291,30 +347,6 @@ torrentPage( GObject * core )
hig_workarea_add_row_w( t, &row, l, w, NULL );
#endif
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Downloading" ) );
s = _( "Append \"._part\" to incomplete files' names" );
w = new_check_button( s, TR_PREFS_KEY_RENAME_PARTIAL_FILES, core );
hig_workarea_add_wide_control( t, &row, w );
w = new_path_chooser_button( TR_PREFS_KEY_DOWNLOAD_DIR, core );
hig_workarea_add_row( t, &row, _( "Save to _Location:" ), w, NULL );
s = _( "Keep _incomplete torrents in:" );
l = new_check_button( s, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, core );
w = new_path_chooser_button( TR_PREFS_KEY_INCOMPLETE_DIR, core );
gtk_widget_set_sensitive( GTK_WIDGET( w ), gtr_pref_flag_get( TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED ) );
g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
hig_workarea_add_row_w( t, &row, l, w, NULL );
s = _( "Call scrip_t when torrent is completed:" );
l = new_check_button( s, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, core );
w = new_file_chooser_button( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, core );
gtk_widget_set_sensitive( GTK_WIDGET( w ), gtr_pref_flag_get( TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED ) );
g_signal_connect( l, "toggled", G_CALLBACK( target_cb ), w );
hig_workarea_add_row_w( t, &row, l, w, NULL );
hig_workarea_add_section_divider( t, &row );
hig_workarea_add_section_title( t, &row, _( "Seeding" ) );
@ -1251,6 +1283,9 @@ gtr_prefs_dialog_new( GtkWindow * parent, GObject * core )
gtk_notebook_append_page( GTK_NOTEBOOK( n ),
torrentPage( core ),
gtk_label_new ( _( "Torrents" ) ) );
gtk_notebook_append_page( GTK_NOTEBOOK( n ),
downloadPage( core ),
gtk_label_new ( _( "Downloading" ) ) );
gtk_notebook_append_page( GTK_NOTEBOOK( n ),
bandwidthPage( core ),
gtk_label_new ( _( "Speed" ) ) );

View File

@ -596,7 +596,7 @@ gtr_window_new( GtkUIManager * ui_mgr, TrCore * core )
/* main menu */
mainmenu = gtr_action_get_widget( "/main-window-menu" );
w = gtr_action_get_widget( "/main-window-menu/torrent-menu/update-tracker" );
w = gtr_action_get_widget( "/main-window-menu/torrent-menu/torrent-reannounce" );
#if GTK_CHECK_VERSION( 2, 12, 0 )
g_signal_connect( w, "query-tooltip",
G_CALLBACK( onAskTrackerQueryTooltip ), p );

View File

@ -21,13 +21,20 @@ static const char * fallback_ui_file =
" <menuitem action='show-torrent-properties'/>\n"
" <menuitem action='open-torrent-folder'/>\n"
" <separator/>\n"
" <menuitem action='start-torrent'/>\n"
" <menuitem action='update-tracker'/>\n"
" <menuitem action='pause-torrent'/>\n"
" <menuitem action='copy-magnet-link-to-clipboard'/>\n"
" <menuitem action='torrent-start'/>\n"
" <menuitem action='torrent-start-now'/>\n"
" <menuitem action='torrent-reannounce'/>\n"
" <menu action='queue-menu'>\n"
" <menuitem action='queue-move-top'/>\n"
" <menuitem action='queue-move-up'/>\n"
" <menuitem action='queue-move-down'/>\n"
" <menuitem action='queue-move-bottom'/>\n"
" </menu>\n"
" <menuitem action='torrent-stop'/>\n"
" <separator/>\n"
" <menuitem action='relocate-torrent'/>\n"
" <menuitem action='verify-torrent'/>\n"
" <menuitem action='torrent-verify'/>\n"
" <menuitem action='copy-magnet-link-to-clipboard'/>\n"
" <separator/>\n"
" <menuitem action='remove-torrent'/>\n"
" <menuitem action='delete-torrent'/>\n"
@ -63,8 +70,8 @@ static const char * fallback_ui_file =
"\n"
" <toolbar name='main-window-toolbar'>\n"
" <toolitem action='open-torrent-toolbar'/>\n"
" <toolitem action='start-torrent'/>\n"
" <toolitem action='pause-torrent'/>\n"
" <toolitem action='torrent-start'/>\n"
" <toolitem action='torrent-stop'/>\n"
" <toolitem action='remove-torrent'/>\n"
" <separator/>\n"
" <toolitem action='show-torrent-properties'/>\n"
@ -87,13 +94,20 @@ static const char * fallback_ui_file =
" <menuitem action='sort-reversed'/>\n"
" </menu>\n"
" <separator/>\n"
" <menuitem action='start-torrent'/>\n"
" <menuitem action='update-tracker'/>\n"
" <menuitem action='pause-torrent'/>\n"
" <menuitem action='copy-magnet-link-to-clipboard'/>\n"
" <menuitem action='torrent-start'/>\n"
" <menuitem action='torrent-start-now'/>\n"
" <menuitem action='torrent-reannounce'/>\n"
" <menu action='queue-menu'>\n"
" <menuitem action='queue-move-top'/>\n"
" <menuitem action='queue-move-up'/>\n"
" <menuitem action='queue-move-down'/>\n"
" <menuitem action='queue-move-bottom'/>\n"
" </menu>\n"
" <menuitem action='torrent-stop'/>\n"
" <separator/>\n"
" <menuitem action='verify-torrent'/>\n"
" <menuitem action='relocate-torrent'/>\n"
" <menuitem action='torrent-verify'/>\n"
" <menuitem action='copy-magnet-link-to-clipboard'/>\n"
" <separator/>\n"
" <menuitem action='remove-torrent'/>\n"
" <menuitem action='delete-torrent'/>\n"

View File

@ -530,12 +530,15 @@ torrentNew( tr_peerMgr * manager, tr_torrent * tor )
return t;
}
static void ensureMgrTimersExist( struct tr_peerMgr * m );
tr_peerMgr*
tr_peerMgrNew( tr_session * session )
{
tr_peerMgr * m = tr_new0( tr_peerMgr, 1 );
m->session = session;
m->incomingHandshakes = TR_PTR_ARRAY_INIT;
ensureMgrTimersExist( m );
return m;
}
@ -3557,23 +3560,42 @@ pumpAllPeers( tr_peerMgr * mgr )
}
}
static void
queuePulse( tr_session * session, tr_direction dir )
{
assert( tr_isSession( session ) );
assert( tr_isDirection( dir ) );
if( tr_sessionGetQueueEnabled( session, dir ) )
{
int i;
const int n = tr_sessionCountQueueFreeSlots( session, dir );
for( i=0; i<n; i++ ) {
tr_torrent * tor = tr_sessionGetNextQueuedTorrent( session, dir );
if( tor != NULL )
tr_torrentStartNow( tor );
}
}
}
static void
bandwidthPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
{
tr_torrent * tor;
tr_peerMgr * mgr = vmgr;
tr_session * session = mgr->session;
managerLock( mgr );
/* FIXME: this next line probably isn't necessary... */
pumpAllPeers( mgr );
/* allocate bandwidth to the peers */
tr_bandwidthAllocate( &mgr->session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
tr_bandwidthAllocate( &mgr->session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
tr_bandwidthAllocate( &session->bandwidth, TR_UP, BANDWIDTH_PERIOD_MSEC );
tr_bandwidthAllocate( &session->bandwidth, TR_DOWN, BANDWIDTH_PERIOD_MSEC );
/* torrent upkeep */
tor = NULL;
while(( tor = tr_torrentNext( mgr->session, tor )))
while(( tor = tr_torrentNext( session, tor )))
{
/* possibly stop torrents that have seeded enough */
tr_torrentCheckSeedLimit( tor );
@ -3590,6 +3612,10 @@ bandwidthPulse( int foo UNUSED, short bar UNUSED, void * vmgr )
tr_torrentStop( tor );
}
/* pump the queues */
queuePulse( session, TR_UP );
queuePulse( session, TR_DOWN );
reconnectPulse( 0, 0, mgr );
tr_timerAddMsec( mgr->bandwidthTimer, BANDWIDTH_PERIOD_MSEC );

View File

@ -186,6 +186,70 @@ getTorrents( tr_session * session,
return torrents;
}
static void
notifyBatchChange( tr_session * session, tr_torrent ** torrents, int n )
{
int i;
for( i=0; i<n; ++i )
notify( session, TR_RPC_TORRENT_CHANGED, torrents[i] );
}
static const char*
queueMoveTop( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveTop( torrents, n );
notifyBatchChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveUp( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveUp( torrents, n );
notifyBatchChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveDown( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveDown( torrents, n );
notifyBatchChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveBottom( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveBottom( torrents, n );
notifyBatchChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
torrentStart( tr_session * session,
tr_benc * args_in,
@ -210,6 +274,30 @@ torrentStart( tr_session * session,
return NULL;
}
static const char*
torrentStartNow( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
if( !tor->isRunning )
{
tr_torrentStartNow( tor );
notify( session, TR_RPC_TORRENT_STARTED, tor );
}
}
tr_free( torrents );
return NULL;
}
static const char*
torrentStop( tr_session * session,
tr_benc * args_in,
@ -225,7 +313,7 @@ torrentStop( tr_session * session,
{
tr_torrent * tor = torrents[i];
if( tor->isRunning )
if( tor->isRunning || ( tor->queuePosition >= 0 ) )
{
tor->isStopping = true;
notify( session, TR_RPC_TORRENT_STOPPED, tor );
@ -578,6 +666,8 @@ addField( const tr_torrent * const tor,
for( i = 0; i < inf->fileCount; ++i )
tr_bencListAddInt( p, inf->files[i].priority );
}
else if( tr_streq( key, keylen, "queuePosition" ) )
tr_bencDictAddInt( d, key, st->queuePosition );
else if( tr_streq( key, keylen, "rateDownload" ) )
tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
else if( tr_streq( key, keylen, "rateUpload" ) )
@ -1017,6 +1107,8 @@ torrentSet( tr_session * session,
tr_torrentSetRatioLimit( tor, d );
if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
tr_torrentSetRatioMode( tor, tmp );
if( tr_bencDictFindInt( args_in, "queuePosition", &tmp ) )
tr_torrentSetQueuePosition( tor, tmp );
if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
errmsg = addTrackerUrls( tor, trackers );
if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
@ -1465,6 +1557,10 @@ sessionSet( tr_session * session,
tr_blocklistSetURL( session, str );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
tr_sessionSetDownloadDir( session, str );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled ( session, TR_DOWN, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_DOWN, i );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
tr_sessionSetIncompleteDir( session, str );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
@ -1499,6 +1595,10 @@ sessionSet( tr_session * session,
tr_sessionSetIdleLimited( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
tr_sessionSetPaused( session, !boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SEED_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled ( session, TR_UP, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_SEED_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_UP, i );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
tr_sessionSetTorrentDoneScript( session, str );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
@ -1597,6 +1697,8 @@ sessionGet( tr_session * s,
tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_DOWN ) );
tr_bencDictAddInt ( d, "download-dir-free-space", tr_sessionGetDownloadDirFreeSpace( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
@ -1616,6 +1718,8 @@ sessionGet( tr_session * s,
tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SEED_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_SEED_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
@ -1676,9 +1780,14 @@ methods[] =
{ "torrent-set", true, torrentSet },
{ "torrent-set-location", true, torrentSetLocation },
{ "torrent-start", true, torrentStart },
{ "torrent-start-now", true, torrentStartNow },
{ "torrent-stop", true, torrentStop },
{ "torrent-verify", true, torrentVerify },
{ "torrent-reannounce", true, torrentReannounce }
{ "torrent-reannounce", true, torrentReannounce },
{ "queue-move-top", true, queueMoveTop },
{ "queue-move-up", true, queueMoveUp },
{ "queue-move-down", true, queueMoveDown },
{ "queue-move-bottom", true, queueMoveBottom }
};
static void

View File

@ -304,62 +304,67 @@ tr_sessionGetDefaultSettings( tr_benc * d )
assert( tr_bencIsDict( d ) );
tr_bencDictReserve( d, 60 );
tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, "http://www.example.com/blocklist" );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, DEFAULT_CACHE_SIZE_MB );
tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_getDefaultDownloadDir( ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, 100 );
tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION, TR_DEFAULT_ENCRYPTION );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, 30 );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_getDefaultDownloadDir( ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, atoi( TR_DEFAULT_PEER_PORT_STR ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, 49152 );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, 65535 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS, TR_DEFAULT_PEER_SOCKET_TOS_STR );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, true );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION, TR_PREALLOCATE_SPARSE );
tr_bencDictAddBool( d, TR_PREFS_KEY_PREFETCH_ENABLED, DEFAULT_PREFETCH_ENABLED );
tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, 2.0 );
tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, false );
tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, "0.0.0.0" );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, "" );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME, "" );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST, TR_DEFAULT_RPC_WHITELIST );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, true );
tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT, atoi( TR_DEFAULT_RPC_PORT_STR ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL, TR_DEFAULT_RPC_URL_STR );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS, true );
tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, false );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, 50 ); /* half the regular */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, 50 ); /* half the regular */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, 540 ); /* 9am */
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END, 1020 ); /* 5pm */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, TR_SCHED_ALL );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, 100 );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK, 022 );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, TR_DEFAULT_BIND_ADDRESS_IPV4 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, TR_DEFAULT_BIND_ADDRESS_IPV6 );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, false );
tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, "http://www.example.com/blocklist" );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, DEFAULT_CACHE_SIZE_MB );
tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_getDefaultDownloadDir( ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, 100 );
tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION, TR_DEFAULT_ENCRYPTION );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, 30 );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_getDefaultDownloadDir( ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, 10 );
tr_bencDictAddBool( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, true );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, atoi( TR_DEFAULT_PEER_PORT_STR ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, 49152 );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, 65535 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS, TR_DEFAULT_PEER_SOCKET_TOS_STR );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, true );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION, TR_PREALLOCATE_SPARSE );
tr_bencDictAddBool( d, TR_PREFS_KEY_PREFETCH_ENABLED, DEFAULT_PREFETCH_ENABLED );
tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, 30 );
tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, 2.0 );
tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, false );
tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, "0.0.0.0" );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, false );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, "" );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME, "" );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST, TR_DEFAULT_RPC_WHITELIST );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, true );
tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT, atoi( TR_DEFAULT_RPC_PORT_STR ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL, TR_DEFAULT_RPC_URL_STR );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS, true );
tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_SEED_QUEUE_SIZE, 10 );
tr_bencDictAddBool( d, TR_PREFS_KEY_SEED_QUEUE_ENABLED, false );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, 50 ); /* half the regular */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, 50 ); /* half the regular */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, 540 ); /* 9am */
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END, 1020 ); /* 5pm */
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, TR_SCHED_ALL );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, 100 );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, false );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK, 022 );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, TR_DEFAULT_BIND_ADDRESS_IPV4 );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, TR_DEFAULT_BIND_ADDRESS_IPV6 );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, true );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, false );
}
void
@ -368,63 +373,68 @@ tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
assert( tr_bencIsDict( d ) );
tr_bencDictReserve( d, 60 );
tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, s->isDHTEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, s->isUTPEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, s->isLPDEnabled );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, s->downloadDir );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION, s->encryptionMode );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL, tr_getMessageLevel( ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, s->peerLimit );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, s->peerLimitPerTorrent );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, s->randomPortLow );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, s->randomPortHigh );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS, format_tos(s->peerSocketTOS) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, s->peer_congestion_algorithm );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, s->isPexEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION, s->preallocationMode );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREFETCH_ENABLED, s->isPrefetchEnabled );
tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, s->desiredRatio );
tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, s->isRatioLimited );
tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, tr_sessionGetRPCBindAddress( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, tr_sessionIsRPCEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, tr_sessionGetRPCPassword( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT, tr_sessionGetRPCPort( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL, tr_sessionGetRPCUrl( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME, tr_sessionGetRPCUsername( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST, tr_sessionGetRPCWhitelist( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, tr_sessionGetRPCWhitelistEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS, s->scrapePausedTorrents );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END, tr_sessionGetAltSpeedEnd( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, tr_sessionGetAltSpeedDay( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK, s->umask );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, tr_address_to_string( &s->public_ipv4->addr ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, tr_address_to_string( &s->public_ipv6->addr ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, s->isDHTEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, s->isUTPEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, s->isLPDEnabled );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, s->downloadDir );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_DOWN ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION, s->encryptionMode );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL, tr_getMessageLevel( ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, s->peerLimit );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, s->peerLimitPerTorrent );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, s->randomPortLow );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, s->randomPortHigh );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_SOCKET_TOS, format_tos(s->peerSocketTOS) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, s->peer_congestion_algorithm );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, s->isPexEnabled );
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION, s->preallocationMode );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PREFETCH_ENABLED, s->isPrefetchEnabled );
tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, tr_sessionGetQueueStalledMinutes( s ) );
tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO, s->desiredRatio );
tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED, s->isRatioLimited );
tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS, tr_sessionGetRPCBindAddress( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED, tr_sessionIsRPCEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD, tr_sessionGetRPCPassword( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT, tr_sessionGetRPCPort( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL, tr_sessionGetRPCUrl( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME, tr_sessionGetRPCUsername( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST, tr_sessionGetRPCWhitelist( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, tr_sessionGetRPCWhitelistEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS, s->scrapePausedTorrents );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_SEED_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SEED_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END, tr_sessionGetAltSpeedEnd( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, tr_sessionGetAltSpeedDay( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK, s->umask );
tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4, tr_address_to_string( &s->public_ipv4->addr ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6, tr_address_to_string( &s->public_ipv6->addr ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
}
bool
@ -779,6 +789,18 @@ sessionSetImpl( void * vdata )
if( tr_bencDictFindBool( settings, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal) )
tr_sessionSetDeleteSource( session, boolVal );
/* torrent queues */
if( tr_bencDictFindInt( settings, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, &i ) )
tr_sessionSetQueueStalledMinutes( session, i );
if( tr_bencDictFindInt( settings, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_DOWN, i );
if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled( session, TR_DOWN, boolVal );
if( tr_bencDictFindInt( settings, TR_PREFS_KEY_SEED_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_UP, i );
if( tr_bencDictFindBool( settings, TR_PREFS_KEY_SEED_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled( session, TR_UP, boolVal );
/* files and directories */
if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PREFETCH_ENABLED, &boolVal ) )
session->isPrefetchEnabled = boolVal;
@ -2600,3 +2622,109 @@ tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilenam
session->torrentDoneScript = tr_strdup( scriptFilename );
}
}
/***
****
***/
void
tr_sessionSetQueueSize( tr_session * session, tr_direction dir, int n )
{
assert( tr_isSession( session ) );
assert( tr_isDirection( dir ) );
session->queueSize[dir] = n;
}
int
tr_sessionGetQueueSize( const tr_session * session, tr_direction dir )
{
assert( tr_isSession( session ) );
assert( tr_isDirection( dir ) );
return session->queueSize[dir];
}
void
tr_sessionSetQueueEnabled( tr_session * session, tr_direction dir, bool is_enabled )
{
assert( tr_isSession( session ) );
assert( tr_isDirection( dir ) );
assert( tr_isBool( is_enabled ) );
session->queueEnabled[dir] = is_enabled;
}
bool
tr_sessionGetQueueEnabled( const tr_session * session, tr_direction dir )
{
assert( tr_isSession( session ) );
assert( tr_isDirection( dir ) );
return session->queueEnabled[dir];
}
void
tr_sessionSetQueueStalledMinutes( tr_session * session, int minutes )
{
assert( tr_isSession( session ) );
assert( minutes > 0 );
session->queueStalledMinutes = minutes;
}
int
tr_sessionGetQueueStalledMinutes( const tr_session * session )
{
assert( tr_isSession( session ) );
return session->queueStalledMinutes;
}
tr_torrent *
tr_sessionGetNextQueuedTorrent( tr_session * session, tr_direction direction )
{
tr_torrent * tor = NULL;
tr_torrent * best_tor = NULL;
int best_position = INT_MAX;
assert( tr_isSession( session ) );
assert( tr_isDirection( direction ) );
while(( tor = tr_torrentNext( session, tor ))) {
if( !tor->isRunning && ( direction == tr_torrentGetQueueDirection( tor ) ) ) {
const int position = tr_torrentGetQueuePosition( tor );
if( ( position >= 0 ) && ( best_position > position ) ) {
best_position = position;
best_tor = tor;
}
}
}
return best_tor;
}
int
tr_sessionCountQueueFreeSlots( tr_session * session, tr_direction dir )
{
tr_torrent * tor;
int active_count;
const int max = tr_sessionGetQueueSize( session, dir );
const tr_torrent_activity activity = TR_UP ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
if( !tr_sessionGetQueueEnabled( session, dir ) )
return INT_MAX;
tor = NULL;
active_count = 0;
while(( tor = tr_torrentNext( session, tor )))
if( !tr_torrentIsStalled( tor ) )
if( tr_torrentGetActivity( tor ) == activity )
++active_count;
if( active_count >= max )
return 0;
return max - active_count;
}

View File

@ -111,6 +111,10 @@ struct tr_session
tr_benc removedTorrents;
bool queueEnabled[2];
int queueSize[2];
int queueStalledMinutes;
int umask;
int speedLimit_Bps[2];
@ -308,4 +312,10 @@ bool tr_sessionGetActiveSpeedLimit_Bps( const tr_session * session,
tr_direction dir,
int * setme );
tr_torrent * tr_sessionGetNextQueuedSeed( tr_session * session );
tr_torrent * tr_sessionGetNextQueuedTorrent( tr_session * session, tr_direction );
int tr_sessionCountQueueFreeSlots( tr_session * session, tr_direction );
#endif

View File

@ -669,7 +669,7 @@ tr_torrentInitFilePieces( tr_torrent * tor )
tr_free( firstFiles );
}
static void torrentStart( tr_torrent * tor );
static void torrentStart( tr_torrent * tor, bool bypass_queue );
/**
* Decide on a block size. Constraints:
@ -802,6 +802,7 @@ torrentInit( tr_torrent * tor, const tr_ctor * ctor )
tor->session = session;
tor->uniqueId = nextUniqueId++;
tor->magicNumber = TORRENT_MAGIC_NUMBER;
tor->queuePosition = -1;
tr_peerIdInit( tor->peer_id );
@ -905,7 +906,7 @@ torrentInit( tr_torrent * tor, const tr_ctor * ctor )
}
else if( doStart )
{
torrentStart( tor );
tr_torrentStart( tor );
}
tr_sessionUnlock( session );
@ -1089,25 +1090,61 @@ tr_torrentSetVerifyState( tr_torrent * tor, tr_verify_state state )
tor->anyDate = tr_time( );
}
tr_torrent_activity
tr_torrentGetActivity( tr_torrent * tor )
static tr_torrent_activity
torrentGetActivity( const tr_torrent * tor )
{
const bool is_seed = tr_torrentIsSeed( tor );
assert( tr_isTorrent( tor ) );
tr_torrentRecheckCompleteness( tor );
if( tor->verifyState == TR_VERIFY_NOW )
return TR_STATUS_CHECK;
if( tor->verifyState == TR_VERIFY_WAIT )
return TR_STATUS_CHECK_WAIT;
if( !tor->isRunning )
return TR_STATUS_STOPPED;
if( tor->completeness == TR_LEECH )
return TR_STATUS_DOWNLOAD;
return TR_STATUS_SEED;
if( tor->isRunning )
return is_seed ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
if( tor->queuePosition >= 0 ) {
if( is_seed && tr_sessionGetQueueEnabled( tor->session, TR_UP ) )
return TR_STATUS_SEED_WAIT;
if( !is_seed && tr_sessionGetQueueEnabled( tor->session, TR_DOWN ) )
return TR_STATUS_DOWNLOAD_WAIT;
}
return TR_STATUS_STOPPED;
}
tr_torrent_activity
tr_torrentGetActivity( tr_torrent * tor )
{
/* FIXME: is this call still needed? */
tr_torrentRecheckCompleteness( tor );
return torrentGetActivity( tor );
}
static time_t
torrentGetIdleSecs( const tr_torrent * tor )
{
int idle_secs;
const tr_torrent_activity activity = torrentGetActivity( tor );
if ((activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_SEED) && tor->startDate != 0)
idle_secs = difftime(tr_time(), MAX(tor->startDate, tor->activityDate));
else
idle_secs = -1;
return idle_secs;
}
bool
tr_torrentIsStalled( const tr_torrent * tor )
{
return torrentGetIdleSecs( tor ) > ( tr_sessionGetQueueStalledMinutes( tor->session ) * 60 );
}
static double
getVerifyProgress( const tr_torrent * tor )
{
@ -1145,6 +1182,7 @@ tr_torrentStat( tr_torrent * tor )
s->id = tor->uniqueId;
s->activity = tr_torrentGetActivity( tor );
s->error = tor->error;
s->queuePosition = tor->queuePosition;
tr_strlcpy( s->errorString, tor->errorString, sizeof( s->errorString ) );
s->manualAnnounceTime = tr_announcerNextManualAnnounce( tor );
@ -1175,11 +1213,7 @@ tr_torrentStat( tr_torrent * tor )
s->startDate = tor->startDate;
s->secondsSeeding = tor->secondsSeeding;
s->secondsDownloading = tor->secondsDownloading;
if ((s->activity == TR_STATUS_DOWNLOAD || s->activity == TR_STATUS_SEED) && s->startDate != 0)
s->idleSecs = difftime(tr_time(), MAX(s->startDate, s->activityDate));
else
s->idleSecs = -1;
s->idleSecs = torrentGetIdleSecs( tor );
s->corruptEver = tor->corruptCur + tor->corruptPrev;
s->downloadedEver = tor->downloadedCur + tor->downloadedPrev;
@ -1505,6 +1539,8 @@ freeTorrent( tr_torrent * tor )
*** Start/Stop Callback
**/
static void queueRemove( tr_torrent * tor );
static void
torrentStartImpl( void * vtor )
{
@ -1516,6 +1552,7 @@ torrentStartImpl( void * vtor )
tr_sessionLock( tor->session );
tr_torrentRecheckCompleteness( tor );
queueRemove( tor );
now = tr_time( );
tor->isRunning = true;
@ -1556,24 +1593,52 @@ tr_torrentGetCurrentSizeOnDisk( const tr_torrent * tor )
return byte_count;
}
static void
torrentStart( tr_torrent * tor )
static bool
torrentShouldQueue( const tr_torrent * tor )
{
/* already running... */
if( tor->isRunning )
return;
const tr_direction dir = tr_torrentGetQueueDirection( tor );
return tr_sessionCountQueueFreeSlots( tor->session, dir ) == 0;
}
static void queueAppend( tr_torrent * tor );
static void
torrentStart( tr_torrent * tor, bool bypass_queue )
{
switch( torrentGetActivity( tor ) )
{
case TR_STATUS_SEED:
case TR_STATUS_DOWNLOAD:
return; /* already started */
break;
case TR_STATUS_SEED_WAIT:
case TR_STATUS_DOWNLOAD_WAIT:
if( !bypass_queue )
return; /* already queued */
break;
case TR_STATUS_CHECK:
case TR_STATUS_CHECK_WAIT:
/* verifying right now... wait until that's done so
* we'll know what completeness to use/announce */
tor->startAfterVerify = true;
return;
break;
case TR_STATUS_STOPPED:
if( !bypass_queue && torrentShouldQueue( tor ) ) {
queueAppend( tor );
return;
}
break;
}
/* don't allow the torrent to be started if the files disappeared */
if( setLocalErrorIfFilesDisappeared( tor ) )
return;
/* verifying right now... wait until that's done so
* we'll know what completeness to use/announce */
if( tor->verifyState != TR_VERIFY_NONE ) {
tor->startAfterVerify = true;
return;
}
/* otherwise, start it now... */
tr_sessionLock( tor->session );
@ -1600,7 +1665,14 @@ void
tr_torrentStart( tr_torrent * tor )
{
if( tr_isTorrent( tor ) )
torrentStart( tor );
torrentStart( tor, false );
}
void
tr_torrentStartNow( tr_torrent * tor )
{
if( tr_isTorrent( tor ) )
torrentStart( tor, true );
}
static void
@ -1613,7 +1685,7 @@ torrentRecheckDoneImpl( void * vtor )
if( tor->startAfterVerify ) {
tor->startAfterVerify = false;
torrentStart( tor );
torrentStart( tor, false );
}
}
@ -1681,6 +1753,7 @@ stopTorrent( void * vtor )
tr_torrentLock( tor );
tr_verifyRemove( tor );
queueRemove( tor );
tr_peerMgrStopTorrent( tor );
tr_announcerTorrentStopped( tor );
tr_cacheFlushTorrent( tor->session->cache, tor );
@ -3085,3 +3158,211 @@ tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
{
return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
}
/***
****
***/
static int
compareTorrentByQueuePosition( const void * va, const void * vb )
{
const tr_torrent * a = * (const tr_torrent **) va;
const tr_torrent * b = * (const tr_torrent **) vb;
return a->queuePosition - b->queuePosition;
}
static bool
queueIsSequenced( tr_torrent * tor )
{
int i ;
int n ;
bool is_sequenced = true;
tr_session * session = tor->session;
tr_direction direction = tr_torrentGetQueueDirection( tor );
tr_torrent ** tmp = tr_new( tr_torrent *, session->torrentCount );
/* get all the torrents in that queue */
n = 0;
tor = NULL;
while(( tor = tr_torrentNext( session, tor )))
if( tr_torrentIsQueued( tor ) && ( direction == tr_torrentGetQueueDirection( tor ) ) )
tmp[n++] = tor;
/* sort them by position */
qsort( tmp, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
#if 0
/* print them */
fprintf( stderr, "sequence: " );
for( i=0; i<n; ++i )
fprintf( stderr, "%d ", tmp[i]->queuePosition );
fprintf( stderr, "\n" );
#endif
/* test them */
for( i=0; is_sequenced && i<n; ++i )
if( tmp[i]->queuePosition != i )
is_sequenced = false;
tr_free( tmp );
return is_sequenced;
}
int
tr_torrentGetQueuePosition( const tr_torrent * tor )
{
return tor->queuePosition;
}
void
tr_torrentSetQueuePosition( tr_torrent * tor, int pos )
{
if( tr_torrentIsQueued( tor ) )
{
int back = -1;
tr_torrent * walk;
const tr_direction direction = tr_torrentGetQueueDirection( tor );
const int old_pos = tor->queuePosition;
const time_t now = tr_time( );
if( pos < 0 )
pos = 0;
tor->queuePosition = -1;
walk = NULL;
while(( walk = tr_torrentNext( tor->session, walk )))
{
if( tr_torrentIsQueued( walk ) && ( tr_torrentGetQueueDirection( walk ) == direction ) )
{
if( old_pos < pos ) {
if( ( old_pos <= walk->queuePosition ) && ( walk->queuePosition <= pos ) ) {
walk->queuePosition--;
walk->anyDate = now;
}
}
if( old_pos > pos ) {
if( ( pos <= walk->queuePosition ) && ( walk->queuePosition < old_pos ) ) {
walk->queuePosition++;
walk->anyDate = now;
}
}
if( back < walk->queuePosition )
back = walk->queuePosition;
}
}
tor->queuePosition = MIN( pos, (back+1) );
tor->anyDate = now;
}
assert( queueIsSequenced( tor ) );
}
void
tr_torrentsQueueMoveTop( tr_torrent ** torrents_in, int n )
{
int i;
tr_torrent ** torrents = tr_memdup( torrents_in, sizeof( tr_torrent * ) * n );
qsort( torrents, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
for( i=n-1; i>=0; --i )
tr_torrentSetQueuePosition( torrents[i], 0 );
tr_free( torrents );
}
void
tr_torrentsQueueMoveUp( tr_torrent ** torrents_in, int n )
{
int i;
tr_torrent ** torrents = tr_memdup( torrents_in, sizeof( tr_torrent * ) * n );
qsort( torrents, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
for( i=0; i<n; ++i )
tr_torrentSetQueuePosition( torrents[i], torrents[i]->queuePosition - 1 );
tr_free( torrents );
}
void
tr_torrentsQueueMoveDown( tr_torrent ** torrents_in, int n )
{
int i;
tr_torrent ** torrents = tr_memdup( torrents_in, sizeof( tr_torrent * ) * n );
qsort( torrents, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
for( i=n-1; i>=0; --i )
tr_torrentSetQueuePosition( torrents[i], torrents[i]->queuePosition + 1 );
tr_free( torrents );
}
void
tr_torrentsQueueMoveBottom( tr_torrent ** torrents_in, int n )
{
int i;
tr_torrent ** torrents = tr_memdup( torrents_in, sizeof( tr_torrent * ) * n );
qsort( torrents, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
for( i=0; i<n; ++i )
tr_torrentSetQueuePosition( torrents[i], INT_MAX );
tr_free( torrents );
}
/* Ensure that the torrents queued for downloads have queuePositions contiguous from [0...n] */
static void
queueResequence( tr_session * session, tr_direction direction )
{
int i;
int n;
tr_torrent * tor = NULL;
tr_torrent ** tmp = tr_new( tr_torrent *, session->torrentCount );
const time_t now = tr_time( );
assert( tr_isSession( session ) );
assert( tr_isDirection( direction ) );
/* get all the torrents in that queue */
n = 0;
while(( tor = tr_torrentNext( session, tor ))) {
if( direction == tr_torrentGetQueueDirection( tor )) {
const int position = tr_torrentGetQueuePosition( tor );
if( position >= 0 )
tmp[n++] = tor;
}
}
/* sort them by position */
qsort( tmp, n, sizeof( tr_torrent * ), compareTorrentByQueuePosition );
/* sequence them... */
for( i=0; i<n; ++i ) {
tr_torrent * tor = tmp[i];
if( tor->queuePosition != i ) {
tor->queuePosition = i;
tor->anyDate = now;
}
}
tr_free( tmp );
}
static void
queueRemove( tr_torrent * tor )
{
if( tr_torrentIsQueued( tor ) )
{
tor->queuePosition = -1;
queueResequence( tor->session, tr_torrentGetQueueDirection( tor ) );
}
}
static void
queueAppend( tr_torrent * tor )
{
if( !tr_torrentIsQueued( tor ) )
{
/* tr_torrentSetQueuePosition() requres the torrent to be queued,
so init tor->queuePosition to the back... */
tor->queuePosition = INT_MAX;
tr_torrentSetQueuePosition( tor, INT_MAX );
}
}

View File

@ -222,6 +222,8 @@ struct tr_torrent
int secondsDownloading;
int secondsSeeding;
int queuePosition;
tr_torrent_metadata_func * metadata_func;
void * metadata_func_user_data;
@ -426,5 +428,18 @@ time_t tr_torrentGetFileMTime( const tr_torrent * tor, tr_file_index_t i );
uint64_t tr_torrentGetCurrentSizeOnDisk( const tr_torrent * tor );
bool tr_torrentIsStalled( const tr_torrent * tor );
static inline bool
tr_torrentIsQueued( const tr_torrent * tor )
{
return tor->queuePosition >= 0;
}
static inline tr_direction
tr_torrentGetQueueDirection( const tr_torrent * tor )
{
return tr_torrentIsSeed( tor ) ? TR_UP : TR_DOWN;
}
#endif

View File

@ -156,63 +156,68 @@ const char* tr_getDefaultDownloadDir( void );
#define TR_DEFAULT_PEER_LIMIT_GLOBAL_STR "240"
#define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "60"
#define TR_PREFS_KEY_ALT_SPEED_ENABLED "alt-speed-enabled"
#define TR_PREFS_KEY_ALT_SPEED_UP_KBps "alt-speed-up"
#define TR_PREFS_KEY_ALT_SPEED_DOWN_KBps "alt-speed-down"
#define TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN "alt-speed-time-begin"
#define TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED "alt-speed-time-enabled"
#define TR_PREFS_KEY_ALT_SPEED_TIME_END "alt-speed-time-end"
#define TR_PREFS_KEY_ALT_SPEED_TIME_DAY "alt-speed-time-day"
#define TR_PREFS_KEY_BIND_ADDRESS_IPV4 "bind-address-ipv4"
#define TR_PREFS_KEY_BIND_ADDRESS_IPV6 "bind-address-ipv6"
#define TR_PREFS_KEY_BLOCKLIST_ENABLED "blocklist-enabled"
#define TR_PREFS_KEY_BLOCKLIST_URL "blocklist-url"
#define TR_PREFS_KEY_MAX_CACHE_SIZE_MB "cache-size-mb"
#define TR_PREFS_KEY_DHT_ENABLED "dht-enabled"
#define TR_PREFS_KEY_UTP_ENABLED "utp-enabled"
#define TR_PREFS_KEY_LPD_ENABLED "lpd-enabled"
#define TR_PREFS_KEY_PREFETCH_ENABLED "prefetch-enabled"
#define TR_PREFS_KEY_DOWNLOAD_DIR "download-dir"
#define TR_PREFS_KEY_ENCRYPTION "encryption"
#define TR_PREFS_KEY_IDLE_LIMIT "idle-seeding-limit"
#define TR_PREFS_KEY_IDLE_LIMIT_ENABLED "idle-seeding-limit-enabled"
#define TR_PREFS_KEY_INCOMPLETE_DIR "incomplete-dir"
#define TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED "incomplete-dir-enabled"
#define TR_PREFS_KEY_MSGLEVEL "message-level"
#define TR_PREFS_KEY_PEER_LIMIT_GLOBAL "peer-limit-global"
#define TR_PREFS_KEY_PEER_LIMIT_TORRENT "peer-limit-per-torrent"
#define TR_PREFS_KEY_PEER_PORT "peer-port"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START "peer-port-random-on-start"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_LOW "peer-port-random-low"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH "peer-port-random-high"
#define TR_PREFS_KEY_PEER_SOCKET_TOS "peer-socket-tos"
#define TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM "peer-congestion-algorithm"
#define TR_PREFS_KEY_PEX_ENABLED "pex-enabled"
#define TR_PREFS_KEY_PORT_FORWARDING "port-forwarding-enabled"
#define TR_PREFS_KEY_PREALLOCATION "preallocation"
#define TR_PREFS_KEY_RATIO "ratio-limit"
#define TR_PREFS_KEY_RATIO_ENABLED "ratio-limit-enabled"
#define TR_PREFS_KEY_RENAME_PARTIAL_FILES "rename-partial-files"
#define TR_PREFS_KEY_RPC_AUTH_REQUIRED "rpc-authentication-required"
#define TR_PREFS_KEY_RPC_BIND_ADDRESS "rpc-bind-address"
#define TR_PREFS_KEY_RPC_ENABLED "rpc-enabled"
#define TR_PREFS_KEY_RPC_PASSWORD "rpc-password"
#define TR_PREFS_KEY_RPC_PORT "rpc-port"
#define TR_PREFS_KEY_RPC_USERNAME "rpc-username"
#define TR_PREFS_KEY_RPC_URL "rpc-url"
#define TR_PREFS_KEY_RPC_WHITELIST_ENABLED "rpc-whitelist-enabled"
#define TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS "scrape-paused-torrents-enabled"
#define TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME "script-torrent-done-filename"
#define TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED "script-torrent-done-enabled"
#define TR_PREFS_KEY_RPC_WHITELIST "rpc-whitelist"
#define TR_PREFS_KEY_DSPEED_KBps "speed-limit-down"
#define TR_PREFS_KEY_DSPEED_ENABLED "speed-limit-down-enabled"
#define TR_PREFS_KEY_USPEED_KBps "speed-limit-up"
#define TR_PREFS_KEY_USPEED_ENABLED "speed-limit-up-enabled"
#define TR_PREFS_KEY_UMASK "umask"
#define TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT "upload-slots-per-torrent"
#define TR_PREFS_KEY_START "start-added-torrents"
#define TR_PREFS_KEY_TRASH_ORIGINAL "trash-original-torrent-files"
#define TR_PREFS_KEY_ALT_SPEED_ENABLED "alt-speed-enabled"
#define TR_PREFS_KEY_ALT_SPEED_UP_KBps "alt-speed-up"
#define TR_PREFS_KEY_ALT_SPEED_DOWN_KBps "alt-speed-down"
#define TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN "alt-speed-time-begin"
#define TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED "alt-speed-time-enabled"
#define TR_PREFS_KEY_ALT_SPEED_TIME_END "alt-speed-time-end"
#define TR_PREFS_KEY_ALT_SPEED_TIME_DAY "alt-speed-time-day"
#define TR_PREFS_KEY_BIND_ADDRESS_IPV4 "bind-address-ipv4"
#define TR_PREFS_KEY_BIND_ADDRESS_IPV6 "bind-address-ipv6"
#define TR_PREFS_KEY_BLOCKLIST_ENABLED "blocklist-enabled"
#define TR_PREFS_KEY_BLOCKLIST_URL "blocklist-url"
#define TR_PREFS_KEY_MAX_CACHE_SIZE_MB "cache-size-mb"
#define TR_PREFS_KEY_DHT_ENABLED "dht-enabled"
#define TR_PREFS_KEY_UTP_ENABLED "utp-enabled"
#define TR_PREFS_KEY_LPD_ENABLED "lpd-enabled"
#define TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE "download-queue-size"
#define TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED "download-queue-enabled"
#define TR_PREFS_KEY_PREFETCH_ENABLED "prefetch-enabled"
#define TR_PREFS_KEY_DOWNLOAD_DIR "download-dir"
#define TR_PREFS_KEY_ENCRYPTION "encryption"
#define TR_PREFS_KEY_IDLE_LIMIT "idle-seeding-limit"
#define TR_PREFS_KEY_IDLE_LIMIT_ENABLED "idle-seeding-limit-enabled"
#define TR_PREFS_KEY_INCOMPLETE_DIR "incomplete-dir"
#define TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED "incomplete-dir-enabled"
#define TR_PREFS_KEY_MSGLEVEL "message-level"
#define TR_PREFS_KEY_PEER_LIMIT_GLOBAL "peer-limit-global"
#define TR_PREFS_KEY_PEER_LIMIT_TORRENT "peer-limit-per-torrent"
#define TR_PREFS_KEY_PEER_PORT "peer-port"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START "peer-port-random-on-start"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_LOW "peer-port-random-low"
#define TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH "peer-port-random-high"
#define TR_PREFS_KEY_PEER_SOCKET_TOS "peer-socket-tos"
#define TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM "peer-congestion-algorithm"
#define TR_PREFS_KEY_PEX_ENABLED "pex-enabled"
#define TR_PREFS_KEY_PORT_FORWARDING "port-forwarding-enabled"
#define TR_PREFS_KEY_PREALLOCATION "preallocation"
#define TR_PREFS_KEY_RATIO "ratio-limit"
#define TR_PREFS_KEY_RATIO_ENABLED "ratio-limit-enabled"
#define TR_PREFS_KEY_RENAME_PARTIAL_FILES "rename-partial-files"
#define TR_PREFS_KEY_RPC_AUTH_REQUIRED "rpc-authentication-required"
#define TR_PREFS_KEY_RPC_BIND_ADDRESS "rpc-bind-address"
#define TR_PREFS_KEY_RPC_ENABLED "rpc-enabled"
#define TR_PREFS_KEY_RPC_PASSWORD "rpc-password"
#define TR_PREFS_KEY_RPC_PORT "rpc-port"
#define TR_PREFS_KEY_RPC_USERNAME "rpc-username"
#define TR_PREFS_KEY_RPC_URL "rpc-url"
#define TR_PREFS_KEY_RPC_WHITELIST_ENABLED "rpc-whitelist-enabled"
#define TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS "scrape-paused-torrents-enabled"
#define TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME "script-torrent-done-filename"
#define TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED "script-torrent-done-enabled"
#define TR_PREFS_KEY_SEED_QUEUE_SIZE "seed-queue-size"
#define TR_PREFS_KEY_SEED_QUEUE_ENABLED "seed-queue-enabled"
#define TR_PREFS_KEY_RPC_WHITELIST "rpc-whitelist"
#define TR_PREFS_KEY_QUEUE_STALLED_MINUTES "queue-stalled-minutes"
#define TR_PREFS_KEY_DSPEED_KBps "speed-limit-down"
#define TR_PREFS_KEY_DSPEED_ENABLED "speed-limit-down-enabled"
#define TR_PREFS_KEY_USPEED_KBps "speed-limit-up"
#define TR_PREFS_KEY_USPEED_ENABLED "speed-limit-up-enabled"
#define TR_PREFS_KEY_UMASK "umask"
#define TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT "upload-slots-per-torrent"
#define TR_PREFS_KEY_START "start-added-torrents"
#define TR_PREFS_KEY_TRASH_ORIGINAL "trash-original-torrent-files"
/**
@ -736,6 +741,79 @@ bool tr_sessionGetDeleteSource ( const tr_session * );
tr_priority_t tr_torrentGetPriority( const tr_torrent * );
void tr_torrentSetPriority( tr_torrent *, tr_priority_t );
/***
****
**** Torrent Queueing
****
**** There are independent queues for seeding (TR_UP) and leeching (TR_DOWN).
****
**** If the session already has enough non-stalled seeds/leeches when
**** tr_torrentStart() is called, the torrent will be moved into the
**** appropriate queue and its state will be TR_STATUS_{DOWNLOAD,SEED}_WAIT.
****
**** To bypass the queue and unconditionally start the torrent use
**** tr_torrentStartNow().
****
**** Torrents can be moved in the queue using the simple functions
**** tr_torrentQueueMove{Top,Up,Down,Bottom}. They can be moved to
**** arbitrary points in the queue with tr_torrentSetQueuePosition().
****
***/
/** @brief Like tr_torrentStart(), but resumes right away regardless of the queues. */
void tr_torrentStartNow ( tr_torrent * );
/** @brief Return the queued torrent's position in the queue it's in. [0...n) */
int tr_torrentGetQueuePosition ( const tr_torrent * );
/** @brief Set the queued torrent's position in the queue it's in.
* Special cases: pos <= 0 moves to the front; pos >= queue length moves to the back */
void tr_torrentSetQueuePosition ( tr_torrent *, int queuePosition );
/**
**/
/** @brief Convenience function for moving a batch of torrents to the front of their queue(s) */
void tr_torrentsQueueMoveTop ( tr_torrent ** torrents, int torrentCount );
/** @brief Convenience function for moving a batch of torrents ahead one step in their queue(s) */
void tr_torrentsQueueMoveUp ( tr_torrent ** torrents, int torrentCount );
/** @brief Convenience function for moving a batch of torrents back one step in their queue(s) */
void tr_torrentsQueueMoveDown ( tr_torrent ** torrents, int torrentCount );
/** @brief Convenience function for moving a batch of torrents to the back of their queue(s) */
void tr_torrentsQueueMoveBottom ( tr_torrent ** torrents, int torrentCount );
/**
**/
/** @brief Set the number of torrents allowed to download (if direction is TR_DOWN) or seed (if direction is TR_UP) at the same time */
void tr_sessionSetQueueSize ( tr_session *, tr_direction, int max_simultaneous_seed_torrents );
/** @brief Return the number of torrents allowed to download (if direction is TR_DOWN) or seed (if direction is TR_UP) at the same time */
int tr_sessionGetQueueSize ( const tr_session *, tr_direction );
/** @brief Set whether or not to limit how many torrents can download (TR_DOWN) or seed (TR_UP) at the same time */
void tr_sessionSetQueueEnabled ( tr_session *, tr_direction, bool do_limit_simultaneous_seed_torrents );
/** @brief Return true if we're limiting how many torrents can concurrently download (TR_DOWN) or seed (TR_UP) at the same time */
bool tr_sessionGetQueueEnabled ( const tr_session *, tr_direction );
/** @brief Consider torrent as 'stalled' when it's been inactive for N minutes.
Stalled torrents are left running but are not counted by tr_sessionGetQueueSize(). */
void tr_sessionSetQueueStalledMinutes( tr_session *, int minutes );
/** @return the number of minutes a torrent can be idle before being considered as stalled */
int tr_sessionGetQueueStalledMinutes( const tr_session * );
/***
****
****
***/
/**
* Load all the torrents in tr_getTorrentDir().
* This can be used at startup to kickstart all the torrents
@ -1723,11 +1801,13 @@ static inline bool tr_torrentHasMetadata( const tr_torrent * tor )
*/
typedef enum
{
TR_STATUS_CHECK_WAIT = ( 1 << 0 ), /* Waiting in queue to check files */
TR_STATUS_CHECK = ( 1 << 1 ), /* Checking files */
TR_STATUS_DOWNLOAD = ( 1 << 2 ), /* Downloading */
TR_STATUS_SEED = ( 1 << 3 ), /* Seeding */
TR_STATUS_STOPPED = ( 1 << 4 ) /* Torrent is stopped */
TR_STATUS_STOPPED = 0, /* Torrent is stopped */
TR_STATUS_CHECK_WAIT = 1, /* Queued to check files */
TR_STATUS_CHECK = 2, /* Checking files */
TR_STATUS_DOWNLOAD_WAIT = 3, /* Queued to download */
TR_STATUS_DOWNLOAD = 4, /* Downloading */
TR_STATUS_SEED_WAIT = 5, /* Queued to seed */
TR_STATUS_SEED = 6 /* Seeding */
}
tr_torrent_activity;
@ -1916,6 +1996,10 @@ typedef struct tr_stat
/** A torrent is considered finished if it has met its seed ratio.
As a result, only paused torrents can be finished. */
bool finished;
/** The position of this torrent in the download queue.
This will be >= 0 if the torrent is queued; -1 otherwise. */
int queuePosition;
}
tr_stat;

View File

@ -247,10 +247,6 @@ FilterBar :: createActivityCombo( )
row->setData( FilterMode::SHOW_FINISHED, ActivityRole );
model->appendRow( row );
row = new QStandardItem( blankIcon, tr( "Queued" ) );
row->setData( FilterMode::SHOW_QUEUED, ActivityRole );
model->appendRow( row );
row = new QStandardItem( QIcon::fromTheme( "view-refresh", blankIcon ), tr( "Verifying" ) );
row->setData( FilterMode::SHOW_VERIFYING, ActivityRole );
model->appendRow( row );

View File

@ -20,7 +20,6 @@ const QString FilterMode::names[NUM_MODES] =
"show-seeding",
"show-paused",
"show-finished",
"show-queued",
"show-verifying",
"show-error",
};

View File

@ -26,7 +26,7 @@ class FilterMode
FilterMode( const QString& name ): myMode(modeFromName(name)) { }
static const QString names[];
enum { SHOW_ALL, SHOW_ACTIVE, SHOW_DOWNLOADING, SHOW_SEEDING, SHOW_PAUSED,
SHOW_FINISHED, SHOW_QUEUED, SHOW_VERIFYING, SHOW_ERROR, NUM_MODES };
SHOW_FINISHED, SHOW_VERIFYING, SHOW_ERROR, NUM_MODES };
static int modeFromName( const QString& name );
static const QString& nameFromMode( int mode ) { return names[mode]; }
int mode() const { return myMode; }

View File

@ -112,6 +112,7 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
ui.action_Properties->setIcon( getStockIcon( "document-properties", QStyle::SP_DesktopIcon ) );
ui.action_OpenFolder->setIcon( getStockIcon( "folder-open", QStyle::SP_DirOpenIcon ) );
ui.action_Start->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
ui.action_StartNow->setIcon( getStockIcon( "media-playback-start", QStyle::SP_MediaPlay ) );
ui.action_Announce->setIcon( getStockIcon( "network-transmit-receive" ) );
ui.action_Pause->setIcon( getStockIcon( "media-playback-pause", QStyle::SP_MediaPause ) );
ui.action_Remove->setIcon( getStockIcon( "list-remove", QStyle::SP_TrashIcon ) );
@ -124,6 +125,10 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
ui.action_Preferences->setIcon( getStockIcon( "preferences-system" ) );
ui.action_Contents->setIcon( getStockIcon( "help-contents", QStyle::SP_DialogHelpButton ) );
ui.action_About->setIcon( getStockIcon( "help-about" ) );
ui.action_QueueMoveTop->setIcon( getStockIcon( "go-top" ) );
ui.action_QueueMoveUp->setIcon( getStockIcon( "go-up", QStyle::SP_ArrowUp ) );
ui.action_QueueMoveDown->setIcon( getStockIcon( "go-down", QStyle::SP_ArrowDown ) );
ui.action_QueueMoveBottom->setIcon( getStockIcon( "go-bottom" ) );
// ui signals
connect( ui.action_Toolbar, SIGNAL(toggled(bool)), this, SLOT(setToolbarVisible(bool)));
@ -140,6 +145,11 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
connect( ui.action_SortByState, SIGNAL(toggled(bool)), this, SLOT(onSortByStateToggled(bool)));
connect( ui.action_ReverseSortOrder, SIGNAL(toggled(bool)), this, SLOT(setSortAscendingPref(bool)));
connect( ui.action_Start, SIGNAL(triggered()), this, SLOT(startSelected()));
connect( ui.action_QueueMoveTop, SIGNAL(triggered()), this, SLOT(queueMoveTop()));
connect( ui.action_QueueMoveUp, SIGNAL(triggered()), this, SLOT(queueMoveUp()));
connect( ui.action_QueueMoveDown, SIGNAL(triggered()), this, SLOT(queueMoveDown()));
connect( ui.action_QueueMoveBottom, SIGNAL(triggered()), this, SLOT(queueMoveBottom()));
connect( ui.action_StartNow, SIGNAL(triggered()), this, SLOT(startSelectedNow()));
connect( ui.action_Pause, SIGNAL(triggered()), this, SLOT(pauseSelected()));
connect( ui.action_Remove, SIGNAL(triggered()), this, SLOT(removeSelected()));
connect( ui.action_Delete, SIGNAL(triggered()), this, SLOT(deleteSelected()));
@ -162,29 +172,6 @@ TrMainWindow :: TrMainWindow( Session& session, Prefs& prefs, TorrentModel& mode
connect( ui.action_SessionDialog, SIGNAL(triggered()), mySessionDialog, SLOT(show()));
connect( ui.listView, SIGNAL(activated(const QModelIndex&)), ui.action_Properties, SLOT(trigger()));
QAction * sep2 = new QAction( this );
sep2->setSeparator( true );
QAction * sep3 = new QAction( this );
sep3->setSeparator( true );
// context menu
QList<QAction*> actions;
actions << ui.action_Properties
<< ui.action_OpenFolder
<< sep2
<< ui.action_Start
<< ui.action_Announce
<< ui.action_Pause
<< ui.action_CopyMagnetToClipboard
<< sep3
<< ui.action_Verify
<< ui.action_SetLocation
<< sep
<< ui.action_Remove
<< ui.action_Delete;
addActions( actions );
setContextMenuPolicy( Qt::ActionsContextMenu );
// signals
connect( ui.action_SelectAll, SIGNAL(triggered()), ui.listView, SLOT(selectAll()));
connect( ui.action_DeselectAll, SIGNAL(triggered()), ui.listView, SLOT(clearSelection()));
@ -725,7 +712,9 @@ TrMainWindow :: refreshActionSensitivity( )
{
int selected( 0 );
int paused( 0 );
int queued( 0 );
int selectedAndPaused( 0 );
int selectedAndQueued( 0 );
int canAnnounce( 0 );
const QAbstractItemModel * model( ui.listView->model( ) );
const QItemSelectionModel * selectionModel( ui.listView->selectionModel( ) );
@ -739,14 +728,13 @@ TrMainWindow :: refreshActionSensitivity( )
if( tor ) {
const bool isSelected( selectionModel->isSelected( modelIndex ) );
const bool isPaused( tor->isPaused( ) );
if( isSelected )
++selected;
if( isPaused )
++ paused;
if( isSelected && isPaused )
++selectedAndPaused;
if( tor->canManualAnnounce( ) )
++canAnnounce;
const bool isQueued( tor->isQueued( ) );
if( isSelected ) ++selected;
if( isQueued ) ++queued;
if( isPaused ) ++ paused;
if( isSelected && isPaused ) ++selectedAndPaused;
if( isSelected && isQueued ) ++selectedAndQueued;
if( tor->canManualAnnounce( ) ) ++canAnnounce;
}
}
@ -766,9 +754,15 @@ TrMainWindow :: refreshActionSensitivity( )
ui.action_StartAll->setEnabled( paused > 0 );
ui.action_PauseAll->setEnabled( paused < rowCount );
ui.action_Start->setEnabled( selectedAndPaused > 0 );
ui.action_StartNow->setEnabled( selectedAndPaused + selectedAndQueued > 0 );
ui.action_Pause->setEnabled( selectedAndPaused < selected );
ui.action_Announce->setEnabled( selected > 0 && ( canAnnounce == selected ) );
ui.action_QueueMoveTop->setEnabled( selectedAndQueued > 0 );
ui.action_QueueMoveUp->setEnabled( selectedAndQueued > 0 );
ui.action_QueueMoveDown->setEnabled( selectedAndQueued > 0 );
ui.action_QueueMoveBottom->setEnabled( selectedAndQueued > 0 );
if( myDetailsDialog )
myDetailsDialog->setIds( getSelectedTorrents( ) );
}
@ -803,11 +797,36 @@ TrMainWindow :: startSelected( )
mySession.startTorrents( getSelectedTorrents( ) );
}
void
TrMainWindow :: startSelectedNow( )
{
mySession.startTorrentsNow( getSelectedTorrents( ) );
}
void
TrMainWindow :: pauseSelected( )
{
mySession.pauseTorrents( getSelectedTorrents( ) );
}
void
TrMainWindow :: queueMoveTop( )
{
mySession.queueMoveTop( getSelectedTorrents( ) );
}
void
TrMainWindow :: queueMoveUp( )
{
mySession.queueMoveUp( getSelectedTorrents( ) );
}
void
TrMainWindow :: queueMoveDown( )
{
mySession.queueMoveDown( getSelectedTorrents( ) );
}
void
TrMainWindow :: queueMoveBottom( )
{
mySession.queueMoveBottom( getSelectedTorrents( ) );
}
void
TrMainWindow :: startAll( )
{
mySession.startTorrents( );
@ -1296,3 +1315,44 @@ TrMainWindow :: dropEvent( QDropEvent * event )
dynamic_cast<MyApp*>(QApplication::instance())->addTorrent( key );
}
/***
****
***/
void
TrMainWindow :: contextMenuEvent( QContextMenuEvent * event )
{
QMenu * menu = new QMenu( this );
menu->addAction( ui.action_Properties );
menu->addAction( ui.action_OpenFolder );
QAction * sep = new QAction( this );
sep->setSeparator( true );
menu->addAction( sep );
menu->addAction( ui.action_Start );
menu->addAction( ui.action_StartNow );
menu->addAction( ui.action_Announce );
QMenu * queueMenu = menu->addMenu( tr( "Queue" ) );
queueMenu->addAction( ui.action_QueueMoveTop );
queueMenu->addAction( ui.action_QueueMoveUp );
queueMenu->addAction( ui.action_QueueMoveDown );
queueMenu->addAction( ui.action_QueueMoveBottom );
menu->addAction( ui.action_Pause );
sep = new QAction( this );
sep->setSeparator( true );
menu->addAction( sep );
menu->addAction( ui.action_Verify );
menu->addAction( ui.action_SetLocation );
menu->addAction( ui.action_CopyMagnetToClipboard );
sep = new QAction( this );
sep->setSeparator( true );
menu->addAction( sep );
menu->addAction( ui.action_Remove );
menu->addAction( ui.action_Delete );
menu->popup( event->globalPos( ) );
}

View File

@ -161,11 +161,16 @@ class TrMainWindow: public QMainWindow
public slots:
void startAll( );
void startSelected( );
void startSelectedNow( );
void pauseAll( );
void pauseSelected( );
void removeSelected( );
void deleteSelected( );
void verifySelected( );
void queueMoveTop( );
void queueMoveUp( );
void queueMoveDown( );
void queueMoveBottom( );
void reannounceSelected( );
void addTorrent( const QString& filename );
void onNetworkTimer( );
@ -187,6 +192,7 @@ class TrMainWindow: public QMainWindow
virtual ~TrMainWindow( );
protected:
virtual void contextMenuEvent( QContextMenuEvent * );
virtual void dragEnterEvent( QDragEnterEvent * );
virtual void dropEvent( QDropEvent * );
};

View File

@ -51,7 +51,7 @@
<x>0</x>
<y>0</y>
<width>792</width>
<height>23</height>
<height>25</height>
</rect>
</property>
<property name="sizePolicy">
@ -64,17 +64,28 @@
<property name="title">
<string>&amp;Torrent</string>
</property>
<widget class="QMenu" name="menuQueue">
<property name="title">
<string>Queue</string>
</property>
<addaction name="action_QueueMoveTop"/>
<addaction name="action_QueueMoveUp"/>
<addaction name="action_QueueMoveDown"/>
<addaction name="action_QueueMoveBottom"/>
</widget>
<addaction name="separator"/>
<addaction name="action_Properties"/>
<addaction name="action_OpenFolder"/>
<addaction name="separator"/>
<addaction name="action_Start"/>
<addaction name="action_StartNow"/>
<addaction name="menuQueue"/>
<addaction name="action_Announce"/>
<addaction name="action_Pause"/>
<addaction name="action_CopyMagnetToClipboard"/>
<addaction name="separator"/>
<addaction name="action_SetLocation"/>
<addaction name="action_Verify"/>
<addaction name="action_CopyMagnetToClipboard"/>
<addaction name="separator"/>
<addaction name="action_Remove"/>
<addaction name="action_Delete"/>
@ -587,6 +598,40 @@
<string>&amp;Donate</string>
</property>
</action>
<action name="action_StartNow">
<property name="text">
<string>Start &amp;Now</string>
</property>
<property name="toolTip">
<string>Bypass the queue and start now</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+S</string>
</property>
<property name="priority">
<enum>QAction::LowPriority</enum>
</property>
</action>
<action name="action_QueueMoveTop">
<property name="text">
<string>Move to &amp;Top</string>
</property>
</action>
<action name="action_QueueMoveUp">
<property name="text">
<string>Move &amp;Up</string>
</property>
</action>
<action name="action_QueueMoveDown">
<property name="text">
<string>Move &amp;Down</string>
</property>
</action>
<action name="action_QueueMoveBottom">
<property name="text">
<string>Move to &amp;Bottom</string>
</property>
</action>
</widget>
<resources>
<include location="application.qrc"/>

View File

@ -532,6 +532,10 @@ PrefsDialog :: createTorrentsTab( )
HIG * hig = new HIG( this );
hig->addSectionTitle( tr( "Adding" ) );
hig->addWideControl( checkBoxNew( tr( "Show &options dialog" ), Prefs::OPTIONS_PROMPT ) );
hig->addWideControl( checkBoxNew( tr( "&Start when added" ), Prefs::START ) );
hig->addWideControl( checkBoxNew( tr( "Mo&ve .torrent file to the trash" ), Prefs::TRASH_ORIGINAL ) );
l = checkBoxNew( tr( "Automatically &add torrents from:" ), Prefs::DIR_WATCH_ENABLED );
QPushButton * b = myWatchButton = new QPushButton;
b->setIcon( folderPixmap );
@ -540,21 +544,54 @@ PrefsDialog :: createTorrentsTab( )
hig->addRow( l, b );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), b );
hig->addWideControl( checkBoxNew( tr( "Show &options dialog" ), Prefs::OPTIONS_PROMPT ) );
hig->addWideControl( checkBoxNew( tr( "&Start when added" ), Prefs::START ) );
hig->addWideControl( checkBoxNew( tr( "Mo&ve .torrent file to the trash" ), Prefs::TRASH_ORIGINAL ) );
hig->addSectionDivider( );
hig->addSectionTitle( tr( "Downloading" ) );
hig->addSectionTitle( tr( "Seeding Limits" ) );
hig->addWideControl( checkBoxNew( tr( "Append \".&part\" to incomplete files' names" ), Prefs::RENAME_PARTIAL_FILES ) );
l = checkBoxNew( tr( "Stop seeding at &ratio:" ), Prefs::RATIO_ENABLED );
r = doubleSpinBoxNew( Prefs::RATIO, 0, INT_MAX, 0.5, 2 );
hig->addRow( l, r );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
b = myDestinationButton = new QPushButton;
l = checkBoxNew( tr( "Stop seeding if idle for &N minutes:" ), Prefs::IDLE_LIMIT_ENABLED );
r = spinBoxNew( Prefs::IDLE_LIMIT, 1, INT_MAX, 5 );
hig->addRow( l, r );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
hig->finish( );
return hig;
}
QWidget *
PrefsDialog :: createDownloadTab( )
{
const int iconSize( style( )->pixelMetric( QStyle :: PM_SmallIconSize ) );
const QFileIconProvider iconProvider;
const QIcon folderIcon = iconProvider.icon( QFileIconProvider::Folder );
const QPixmap folderPixmap = folderIcon.pixmap( iconSize );
const QIcon fileIcon = iconProvider.icon( QFileIconProvider::File );
const QPixmap filePixmap = fileIcon.pixmap( iconSize );
QWidget *l;
HIG * hig = new HIG( this );
hig->addSectionTitle( tr( "Location" ) );
QPushButton * b = myDestinationButton = new QPushButton;
b->setIcon( folderPixmap );
b->setStyleSheet( "text-align: left; padding-left: 5; padding-right: 5" );
connect( b, SIGNAL(clicked(bool)), this, SLOT(onDestinationClicked(void)) );
hig->addRow( tr( "Save to &Location:" ), b );
hig->addSectionDivider( );
hig->addSectionTitle( tr( "Queue" ) );
hig->addRow( tr( "Maximum active &downloads:" ), spinBoxNew( Prefs::DOWNLOAD_QUEUE_SIZE, 1, FD_SETSIZE, 5 ) );
hig->addRow( tr( "E&xempt torrents if idle for N minutes:" ), spinBoxNew( Prefs::QUEUE_STALLED_MINUTES, 1, FD_SETSIZE, 5 ) );
hig->addSectionDivider( );
hig->addSectionTitle( tr( "Incomplete" ) );
hig->addWideControl( checkBoxNew( tr( "Append \".&part\" to incomplete files' names" ), Prefs::RENAME_PARTIAL_FILES ) );
l = myIncompleteCheckbox = checkBoxNew( tr( "Keep &incomplete files in:" ), Prefs::INCOMPLETE_DIR_ENABLED );
b = myIncompleteButton = new QPushButton;
b->setIcon( folderPixmap );
@ -571,19 +608,6 @@ PrefsDialog :: createTorrentsTab( )
hig->addRow( myTorrentDoneScriptCheckbox, b );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), b );
hig->addSectionDivider( );
hig->addSectionTitle( tr( "Seeding Limits" ) );
l = checkBoxNew( tr( "Stop seeding at &ratio:" ), Prefs::RATIO_ENABLED );
r = doubleSpinBoxNew( Prefs::RATIO, 0, INT_MAX, 0.5, 2 );
hig->addRow( l, r );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
l = checkBoxNew( tr( "Stop seeding if idle for &N minutes:" ), Prefs::IDLE_LIMIT_ENABLED );
r = spinBoxNew( Prefs::IDLE_LIMIT, 1, INT_MAX, 5 );
hig->addRow( l, r );
enableBuddyWhenChecked( qobject_cast<QCheckBox*>(l), r );
hig->finish( );
return hig;
}
@ -603,6 +627,7 @@ PrefsDialog :: PrefsDialog( Session& session, Prefs& prefs, QWidget * parent ):
QTabWidget * t = new QTabWidget( this );
t->addTab( createTorrentsTab( ), tr( "Torrents" ) );
t->addTab( createDownloadTab( ), tr( "Download" ) );
t->addTab( createSpeedTab( ), tr( "Speed" ) );
t->addTab( createPrivacyTab( ), tr( "Privacy" ) );
t->addTab( createNetworkTab( ), tr( "Network" ) );

View File

@ -78,6 +78,7 @@ class PrefsDialog: public QDialog
void setPref( int key, const QVariant& v );
bool isAllowed( int key ) const;
QWidget * createTorrentsTab( );
QWidget * createDownloadTab( );
QWidget * createSpeedTab( );
QWidget * createPrivacyTab( );
QWidget * createNetworkTab( );

View File

@ -79,6 +79,8 @@ Prefs::PrefItem Prefs::myItems[] =
{ DSPEED, TR_PREFS_KEY_DSPEED_KBps, QVariant::Int },
{ DSPEED_ENABLED, TR_PREFS_KEY_DSPEED_ENABLED, QVariant::Bool },
{ DOWNLOAD_DIR, TR_PREFS_KEY_DOWNLOAD_DIR, QVariant::String },
{ DOWNLOAD_QUEUE_ENABLED, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, QVariant::Bool },
{ DOWNLOAD_QUEUE_SIZE, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, QVariant::Int },
{ ENCRYPTION, TR_PREFS_KEY_ENCRYPTION, QVariant::Int },
{ IDLE_LIMIT, TR_PREFS_KEY_IDLE_LIMIT, QVariant::Int },
{ IDLE_LIMIT_ENABLED, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, QVariant::Bool },
@ -91,6 +93,7 @@ Prefs::PrefItem Prefs::myItems[] =
{ PEER_PORT_RANDOM_ON_START, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, QVariant::Bool },
{ PEER_PORT_RANDOM_LOW, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, QVariant::Int },
{ PEER_PORT_RANDOM_HIGH, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, QVariant::Int },
{ QUEUE_STALLED_MINUTES, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, QVariant::Int },
{ SCRIPT_TORRENT_DONE_ENABLED, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, QVariant::Bool },
{ SCRIPT_TORRENT_DONE_FILENAME, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, QVariant::String },
{ SOCKET_TOS, TR_PREFS_KEY_PEER_SOCKET_TOS, QVariant::Int },

View File

@ -84,6 +84,8 @@ class Prefs: public QObject
DSPEED,
DSPEED_ENABLED,
DOWNLOAD_DIR,
DOWNLOAD_QUEUE_ENABLED,
DOWNLOAD_QUEUE_SIZE,
ENCRYPTION,
IDLE_LIMIT,
IDLE_LIMIT_ENABLED,
@ -96,6 +98,7 @@ class Prefs: public QObject
PEER_PORT_RANDOM_ON_START,
PEER_PORT_RANDOM_LOW,
PEER_PORT_RANDOM_HIGH,
QUEUE_STALLED_MINUTES,
SCRIPT_TORRENT_DONE_ENABLED,
SCRIPT_TORRENT_DONE_FILENAME,
SOCKET_TOS,

View File

@ -151,6 +151,8 @@ Session :: updatePref( int key )
case Prefs :: BLOCKLIST_URL:
case Prefs :: DHT_ENABLED:
case Prefs :: DOWNLOAD_DIR:
case Prefs :: DOWNLOAD_QUEUE_ENABLED:
case Prefs :: DOWNLOAD_QUEUE_SIZE:
case Prefs :: DSPEED:
case Prefs :: DSPEED_ENABLED:
case Prefs :: IDLE_LIMIT:
@ -536,17 +538,13 @@ Session :: sendTorrentRequest( const char * request, const QSet<int>& ids )
refreshTorrents( ids );
}
void
Session :: pauseTorrents( const QSet<int>& ids )
{
sendTorrentRequest( "torrent-stop", ids );
}
void
Session :: startTorrents( const QSet<int>& ids )
{
sendTorrentRequest( "torrent-start", ids );
}
void Session :: pauseTorrents ( const QSet<int>& ids ) { sendTorrentRequest( "torrent-stop", ids ); }
void Session :: startTorrents ( const QSet<int>& ids ) { sendTorrentRequest( "torrent-start", ids ); }
void Session :: startTorrentsNow ( const QSet<int>& ids ) { sendTorrentRequest( "torrent-start-now", ids ); }
void Session :: queueMoveTop ( const QSet<int>& ids ) { sendTorrentRequest( "queue-move-top", ids ); }
void Session :: queueMoveUp ( const QSet<int>& ids ) { sendTorrentRequest( "queue-move-up", ids ); }
void Session :: queueMoveDown ( const QSet<int>& ids ) { sendTorrentRequest( "queue-move-down", ids ); }
void Session :: queueMoveBottom ( const QSet<int>& ids ) { sendTorrentRequest( "queue-move-bottom", ids ); }
void
Session :: refreshActiveTorrents( )

View File

@ -105,6 +105,11 @@ class Session: public QObject
public slots:
void pauseTorrents( const QSet<int>& torrentIds = QSet<int>() );
void startTorrents( const QSet<int>& torrentIds = QSet<int>() );
void startTorrentsNow( const QSet<int>& torrentIds = QSet<int>() );
void queueMoveTop( const QSet<int>& torrentIds = QSet<int>() );
void queueMoveUp( const QSet<int>& torrentIds = QSet<int>() );
void queueMoveDown( const QSet<int>& torrentIds = QSet<int>() );
void queueMoveBottom( const QSet<int>& torrentIds = QSet<int>() );
void refreshSessionInfo( );
void refreshSessionStats( );
void refreshActiveTorrents( );

View File

@ -101,11 +101,12 @@ TorrentFilter :: lessThan( const QModelIndex& left, const QModelIndex& right ) c
case SortMode :: SORT_BY_STATE:
if( !val ) val = compare( a->hasError(), b->hasError() );
if( !val ) val = compare( a->getActivity(), b->getActivity() );
if( !val ) val = a->compareQueue( *b );
// fall through
case SortMode :: SORT_BY_PROGRESS:
if( !val ) val = compare( a->percentComplete(), b->percentComplete() );
if( !val ) val = a->compareSeedRatio( *b );
// fall through
if( !val ) val = a->compareQueue( *b );
case SortMode :: SORT_BY_RATIO:
if( !val ) val = a->compareRatio( *b );
break;
@ -144,10 +145,10 @@ TorrentFilter :: activityFilterAcceptsTorrent( const Torrent * tor, const Filter
accepts = tor->peersWeAreUploadingTo( ) > 0 || tor->peersWeAreDownloadingFrom( ) > 0 || tor->isVerifying( );
break;
case FilterMode::SHOW_DOWNLOADING:
accepts = tor->isDownloading( );
accepts = tor->isDownloading( ) || tor->isWaitingToDownload( );
break;
case FilterMode::SHOW_SEEDING:
accepts = tor->isSeeding( );
accepts = tor->isSeeding( ) || tor->isWaitingToSeed( );
break;
case FilterMode::SHOW_PAUSED:
accepts = tor->isPaused( );
@ -155,9 +156,6 @@ TorrentFilter :: activityFilterAcceptsTorrent( const Torrent * tor, const Filter
case FilterMode::SHOW_FINISHED:
accepts = tor->isFinished( );
break;
case FilterMode::SHOW_QUEUED:
accepts = tor->isWaitingToVerify( );
break;
case FilterMode::SHOW_VERIFYING:
accepts = tor->isVerifying( ) || tor->isWaitingToVerify( );
break;

View File

@ -107,7 +107,8 @@ Torrent :: myProperties[] =
{ MANUAL_ANNOUNCE_TIME, "manualAnnounceTime", QVariant::DateTime, STAT_EXTRA },
{ PEERS, "peers", TrTypes::PeerList, STAT_EXTRA },
{ TORRENT_FILE, "torrentFile", QVariant::String, STAT_EXTRA },
{ BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA }
{ BANDWIDTH_PRIORITY, "bandwidthPriority", QVariant::Int, STAT_EXTRA },
{ QUEUE_POSITION, "queuePosition", QVariant::Int, STAT }
};
Torrent :: KeyList
@ -368,6 +369,18 @@ Torrent :: hasTrackerSubstring( const QString& substr ) const
return false;
}
int
Torrent :: compareQueue( const Torrent& that ) const
{
const bool a_is_queued = isQueued( );
const bool b_is_queued = that.isQueued( );
if( a_is_queued != b_is_queued )
return a_is_queued ? -1 : 1;
return that.queuePosition() - queuePosition();
}
int
Torrent :: compareSeedRatio( const Torrent& that ) const
{
@ -701,11 +714,13 @@ Torrent :: activityString( ) const
switch( getActivity( ) )
{
case TR_STATUS_CHECK_WAIT: str = tr( "Waiting to verify local data" ); break;
case TR_STATUS_CHECK: str = tr( "Verifying local data" ); break;
case TR_STATUS_DOWNLOAD: str = tr( "Downloading" ); break;
case TR_STATUS_SEED: str = tr( "Seeding" ); break;
case TR_STATUS_STOPPED: str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
case TR_STATUS_STOPPED: str = isFinished() ? tr( "Finished" ): tr( "Paused" ); break;
case TR_STATUS_CHECK_WAIT: str = tr( "Waiting to verify local data" ); break;
case TR_STATUS_CHECK: str = tr( "Verifying local data" ); break;
case TR_STATUS_DOWNLOAD_WAIT: str = tr( "Download queue #%1" ).arg( queuePosition() + 1 ); break;
case TR_STATUS_DOWNLOAD: str = tr( "Downloading" );
case TR_STATUS_SEED_WAIT: str = tr( "Seed queue #%1" ).arg( queuePosition() + 1 ); break;
case TR_STATUS_SEED: str = tr( "Seeding" ); break;
}
return str;

View File

@ -172,6 +172,7 @@ class Torrent: public QObject
PEERS,
TORRENT_FILE,
BANDWIDTH_PRIORITY,
QUEUE_POSITION,
PROPERTY_COUNT
};
@ -271,6 +272,7 @@ class Torrent: public QObject
int compareTracker( const Torrent& ) const;
int compareSeedRatio( const Torrent& ) const;
int compareRatio( const Torrent& ) const;
int compareQueue( const Torrent& ) const;
int compareETA( const Torrent& ) const;
bool hasETA( ) const { return getETA( ) >= 0; }
int getETA( ) const { return getInt( ETA ); }
@ -304,6 +306,8 @@ class Torrent: public QObject
QStringList trackers() const { return myValues[TRACKERS].value<QStringList>(); }
PeerList peers( ) const{ return myValues[PEERS].value<PeerList>(); }
const FileList& files( ) const { return myFiles; }
int queuePosition( ) const { return getInt( QUEUE_POSITION ); }
bool isQueued( ) const { return queuePosition( ) >= 0; }
public:
QString activityString( ) const;
@ -313,7 +317,9 @@ class Torrent: public QObject
bool isWaitingToVerify( ) const { return getActivity( ) == TR_STATUS_CHECK_WAIT; }
bool isVerifying( ) const { return getActivity( ) == TR_STATUS_CHECK; }
bool isDownloading( ) const { return getActivity( ) == TR_STATUS_DOWNLOAD; }
bool isWaitingToDownload( ) const { return getActivity( ) == TR_STATUS_DOWNLOAD_WAIT; }
bool isSeeding( ) const { return getActivity( ) == TR_STATUS_SEED; }
bool isWaitingToSeed( ) const { return getActivity( ) == TR_STATUS_SEED_WAIT; }
bool isReadyToTransfer( ) const { return getActivity()==TR_STATUS_DOWNLOAD || getActivity()==TR_STATUS_SEED; }
void notifyComplete( ) const;

View File

@ -526,7 +526,6 @@
<li id="sort_by_name">Name</li>
<li id="sort_by_percent_completed">Progress</li>
<li id="sort_by_ratio">Ratio</li>
<li id="sort_by_queue_order">Queue Order</li>
<li id="sort_by_state">State</li>
<li class="separator"></li>
<li id="reverse_sort_order">Reverse Sort Order</li>
@ -543,8 +542,9 @@
<div class="contextMenu" id="torrent_context_menu">
<ul>
<li id="context_pause_selected" class="disabled context_pause_selected">Pause Selected</li>
<li id="context_resume_selected" class="disabled context_resume_selected">Resume Selected</li>
<li id="context_pause_selected" class="disabled context_pause_selected">Pause</li>
<li id="context_resume_selected" class="disabled context_resume_selected">Resume</li>
<li id="context_resume_now_selected" class="disabled context_resume_selected">Resume Now</li>
<li class="separator"></li>
<li id="context_remove">Remove From List...</li>
<li id="context_removedata">Trash Data & Remove From List...</li>

View File

@ -232,7 +232,6 @@ Prefs._SortDescending = 'descending';
Prefs._SortMethod = 'sort_method';
Prefs._SortByAge = 'age';
Prefs._SortByActivity = 'activity';
Prefs._SortByQueue = 'queue_order';
Prefs._SortByName = 'name';
Prefs._SortByProgress = 'percent_completed';
Prefs._SortByRatio = 'ratio';

View File

@ -11,11 +11,21 @@ function Torrent( transferListParent, fileListParent, controller, data) {
}
// Constants
Torrent._StatusStopped = 0; /* torrent is stopped */
Torrent._StatusCheckWait = 1; /* waiting in queue to check files */
Torrent._StatusCheck = 2; /* checking files */
Torrent._StatusDownloadWait = 3; /* queued to download */
Torrent._StatusDownload = 4; /* downloading */
Torrent._StatusSeedWait = 5; /* queeud to seed */
Torrent._StatusSeed = 6; /* seeding */
/*
Torrent._StatusWaitingToCheck = 1;
Torrent._StatusChecking = 2;
Torrent._StatusDownloading = 4;
Torrent._StatusSeeding = 8;
Torrent._StatusPaused = 16;
*/
Torrent._InfiniteTimeRemaining = 215784000; // 999 Hours - may as well be infinite
Torrent._RatioUseGlobal = 0;
@ -27,10 +37,10 @@ Torrent._ErrTrackerWarning = 1;
Torrent._ErrTrackerError = 2;
Torrent._ErrLocalError = 3;
Torrent._TrackerInactive = 0;
Torrent._TrackerWaiting = 1;
Torrent._TrackerQueued = 2;
Torrent._TrackerActive = 3;
Torrent._TrackerInactive = 0;
Torrent._TrackerWaiting = 1;
Torrent._TrackerQueued = 2;
Torrent._TrackerActive = 3;
Torrent._StaticFields = [ 'hashString', 'id' ]
@ -39,10 +49,12 @@ Torrent._MetaDataFields = [ 'addedDate', 'comment', 'creator', 'dateCreated',
'isPrivate', 'name', 'totalSize', 'pieceCount', 'pieceSize' ]
Torrent._DynamicFields = [ 'downloadedEver', 'error', 'errorString', 'eta',
'haveUnchecked', 'haveValid', 'leftUntilDone', 'metadataPercentComplete', 'peers',
'peersConnected', 'peersGettingFromUs', 'peersSendingToUs', 'rateDownload', 'rateUpload',
'recheckProgress', 'sizeWhenDone', 'status', 'trackerStats', 'desiredAvailable',
'uploadedEver', 'uploadRatio', 'seedRatioLimit', 'seedRatioMode', 'downloadDir', 'isFinished' ]
'haveUnchecked', 'haveValid', 'leftUntilDone', 'metadataPercentComplete',
'peers', 'peersConnected', 'peersGettingFromUs', 'peersSendingToUs',
'queuePosition', 'rateDownload', 'rateUpload', 'recheckProgress',
'sizeWhenDone', 'status', 'trackerStats', 'desiredAvailable',
'uploadedEver', 'uploadRatio', 'seedRatioLimit', 'seedRatioMode',
'downloadDir', 'isFinished' ]
Torrent.prototype =
{
@ -233,12 +245,14 @@ Torrent.prototype =
isActiveFilter: function() { return this.peersGettingFromUs() > 0
|| this.peersSendingToUs() > 0
|| this.webseedsSendingToUs() > 0
|| this.state() == Torrent._StatusChecking; },
isActive: function() { return this.state() != Torrent._StatusPaused; },
isDownloading: function() { return this.state() == Torrent._StatusDownloading; },
|| this.state() == Torrent._StatusCheck; },
isActive: function() { return this.state() != Torrent._StatusStopped; },
isDownloading: function() { return this.state() == Torrent._StatusDownload; },
isFinished: function() { return this._isFinishedSeeding; },
isSeeding: function() { return this.state() == Torrent._StatusSeeding; },
isSeeding: function() { return this.state() == Torrent._StatusSeed; },
name: function() { return this._name; },
queuePosition: function() { return this._queue_position; },
isQueued: function() { return this.queuePosition() >= 0; },
webseedsSendingToUs: function() { return this._webseeds_sending_to_us; },
peersSendingToUs: function() { return this._peers_sending_to_us; },
peersGettingFromUs: function() { return this._peers_getting_from_us; },
@ -255,11 +269,13 @@ Torrent.prototype =
state: function() { return this._state; },
stateStr: function() {
switch( this.state() ) {
case Torrent._StatusSeeding: return 'Seeding';
case Torrent._StatusDownloading: return 'Downloading';
case Torrent._StatusPaused: return this.isFinished() ? 'Seeding complete' : 'Paused';
case Torrent._StatusChecking: return 'Verifying local data';
case Torrent._StatusWaitingToCheck: return 'Waiting to verify';
case Torrent._StatusStopped: return this.isFinished() ? 'Seeding complete' : 'Paused';
case Torrent._StatusCheckWait: return 'Waiting to verify local data';
case Torrent._StatusCheck: return 'Verifying local data';
case Torrent._StatusDownloadWait: return 'Waiting to download #' + (this.queuePosition()+1);
case Torrent._StatusDownload: return 'Downloading';
case Torrent._StatusSeedWait: return 'Waiting to seed #' + (this.queuePosition()+1);
case Torrent._StatusSeed: return 'Seeding';
default: return 'error';
}
},
@ -397,6 +413,7 @@ Torrent.prototype =
this._peers_connected = data.peersConnected;
this._peers_getting_from_us = data.peersGettingFromUs;
this._peers_sending_to_us = data.peersSendingToUs;
this._queue_position = data.queuePosition;
this._webseeds_sending_to_us = data.webseedsSendingToUs;
this._sizeWhenDone = data.sizeWhenDone;
this._recheckProgress = data.recheckProgress;
@ -455,12 +472,14 @@ Torrent.prototype =
var st = this.state( );
switch( st )
{
case Torrent._StatusPaused:
case Torrent._StatusWaitingToCheck:
case Torrent._StatusStopped:
case Torrent._StatusCheckWait:
case Torrent._StatusDownloadWait:
case Torrent._StatusSeedWait:
c = this.stateStr( );
break;
case Torrent._StatusDownloading:
case Torrent._StatusDownload:
var a = [ ];
if(!compact_mode)
a.push( 'Downloading from', this.peersSendingToUs(), 'of', this._peers_connected, 'peers', '-' );
@ -468,7 +487,7 @@ Torrent.prototype =
c = a.join(' ');
break;
case Torrent._StatusSeeding:
case Torrent._StatusSeed:
if(compact_mode){
c = this.formatUL();
} else {
@ -477,7 +496,7 @@ Torrent.prototype =
}
break;
case Torrent._StatusChecking:
case Torrent._StatusCheck:
// 'Verifying local data (40% tested)'
c = [ 'Verifying local data (', Transmission.fmt.percentString( 100.0 * this._recheckProgress ), '% tested)' ].join('');
break;
@ -647,7 +666,7 @@ Torrent.prototype =
// Update the peer details and pause/resume button
e = root._pause_resume_button_image;
if ( this.state() === Torrent._StatusPaused ) {
if ( this.state() === Torrent._StatusStopped ) {
e.alt = 'Resume';
e.className = "torrent_resume"+compact;
} else {
@ -701,6 +720,7 @@ Torrent.prototype =
test: function( filter, search )
{
var pass = false;
var s = this.state( );
switch( filter )
{
@ -708,10 +728,10 @@ Torrent.prototype =
pass = this.isActiveFilter();
break;
case Prefs._FilterSeeding:
pass = this.isSeeding();
pass = ( s == Torrent._StatusSeed ) || ( s == Torrent._StatusSeedWait );
break;
case Prefs._FilterDownloading:
pass = this.isDownloading();
pass = ( s == Torrent._StatusDownload ) || ( s == Torrent._StatusDownloadWait );
break;
case Prefs._FilterPaused:
pass = !this.isActive();
@ -739,38 +759,76 @@ Torrent.compareById = function( a, b ) {
return a.id() - b.id();
};
/** Helper function for sortTorrents(). */
Torrent.compareByAge = function( a, b ) {
return a.dateAdded() - b.dateAdded();
};
/** Helper function for sortTorrents(). */
Torrent.compareByName = function( a, b ) {
return a._name_lc.compareTo( b._name_lc );
var i = a._name_lc.compareTo( b._name_lc );
if( i )
return i;
return Torrent.compareById( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByState = function( a, b ) {
return a.state() - b.state();
Torrent.compareByQueue = function( a, b )
{
var a_is_queued = a.isQueued( );
var b_is_queued = b.isQueued( );
if( a_is_queued != b_is_queued )
return a_is_queued ? -1 : 1;
var a_pos = a.queuePosition( );
var b_pos = b.queuePosition( );
if( a_pos != b_pos )
return a_pos - b_pos;
return Torrent.compareByName( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByActivity = function( a, b ) {
return a.activity() - b.activity();
Torrent.compareByAge = function( a, b )
{
var a_age = a.dateAdded();
var b_age = b.dateAdded();
if( a_age != b_age )
return a_age - b_age;
return Torrent.compareByQueue( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByState = function( a, b )
{
var a_state = a.state( );
var b_state = b.state( );
if( a_state != b_state )
return b_state - a_state;
return Torrent.compareByQueue( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByActivity = function( a, b )
{
var a_activity = a.activity( );
var b_activity = b.activity( );
if( a_activity != b_activity )
return a_activity - b_activity;
return Torrent.compareByState( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByRatio = function( a, b ) {
var a_ratio = Math.ratio( a._upload_total, a._download_total );
var b_ratio = Math.ratio( b._upload_total, b._download_total );
return a_ratio - b_ratio;
if( a_ratio != b_ratio )
return a_ratio - b_ratio;
return Torrent.compareByState( a, b );
};
/** Helper function for sortTorrents(). */
Torrent.compareByProgress = function( a, b ) {
if( a.getPercentDone() !== b.getPercentDone() )
return a.getPercentDone() - b.getPercentDone();
return this.compareByRatio( a, b );
return Torrent.compareByRatio( a, b );
};
/**
@ -788,9 +846,6 @@ Torrent.sortTorrents = function( torrents, sortMethod, sortDirection )
case Prefs._SortByAge:
torrents.sort( this.compareByAge );
break;
case Prefs._SortByQueue:
torrents.sort( this.compareById );
break;
case Prefs._SortByProgress:
torrents.sort( this.compareByProgress );
break;

View File

@ -100,6 +100,7 @@ Transmission.prototype =
this._toolbar_remove_button = $('li#remove')[0];
this._context_pause_button = $('li#context_pause_selected')[0];
this._context_start_button = $('li#context_resume_selected')[0];
this._context_start_now_button = $('li#context_resume_now_selected')[0];
var ti = '#torrent_inspector_';
this._inspector = { };
@ -281,7 +282,10 @@ Transmission.prototype =
this.stopSelectedTorrents( );
},
contextStartSelected: function( ) {
this.startSelectedTorrents( );
this.startSelectedTorrents( false );
},
contextStartNowSelected: function( ) {
this.startSelectedTorrents( true );
},
contextRemoveSelected: function( ) {
this.removeSelectedTorrents( );
@ -311,15 +315,16 @@ Transmission.prototype =
createContextMenu: function() {
var tr = this;
var bindings = {
context_pause_selected: function(e){ tr.contextStopSelected(e); },
context_resume_selected: function(e){ tr.contextStartSelected(e); },
context_remove: function(e){ tr.contextRemoveSelected(e); },
context_removedata: function(e){ tr.contextRemoveDataSelected(e); },
context_verify: function(e){ tr.contextVerifySelected(e); },
context_reannounce: function(e){ tr.contextReannounceSelected(e); },
context_toggle_inspector: function(e){ tr.contextToggleInspector(e); },
context_select_all: function(e){ tr.contextSelectAll(e); },
context_deselect_all: function(e){ tr.contextDeselectAll(e); }
context_pause_selected: function(e){ tr.contextStopSelected(e); },
context_resume_selected: function(e){ tr.contextStartSelected(e); },
context_resume_now_selected: function(e){ tr.contextStartNowSelected(e); },
context_remove: function(e){ tr.contextRemoveSelected(e); },
context_removedata: function(e){ tr.contextRemoveDataSelected(e); },
context_verify: function(e){ tr.contextVerifySelected(e); },
context_reannounce: function(e){ tr.contextReannounceSelected(e); },
context_toggle_inspector: function(e){ tr.contextToggleInspector(e); },
context_select_all: function(e){ tr.contextSelectAll(e); },
context_deselect_all: function(e){ tr.contextDeselectAll(e); }
};
// Setup the context menu
@ -618,7 +623,7 @@ Transmission.prototype =
startSelectedClicked: function( event ) {
var tr = this;
if( tr.isButtonEnabled( event ) ) {
tr.startSelectedTorrents( );
tr.startSelectedTorrents( false );
tr.hideiPhoneAddressbar( );
}
},
@ -1994,19 +1999,19 @@ Transmission.prototype =
this.reannounceTorrents( this.getSelectedTorrents( ) );
},
startSelectedTorrents: function( ) {
this.startTorrents( this.getSelectedTorrents( ) );
startSelectedTorrents: function( force ) {
this.startTorrents( this.getSelectedTorrents( ), force );
},
startAllTorrents: function( ) {
this.startTorrents( this.getAllTorrents( ) );
this.startTorrents( this.getAllTorrents( ), false );
},
startTorrent: function( torrent ) {
this.startTorrents( [ torrent ] );
this.startTorrents( [ torrent ], false );
},
startTorrents: function( torrents ) {
startTorrents: function( torrents, force ) {
var torrent_ids = jQuery.map(torrents, function(t) { return t.id(); } );
var tr = this;
this.remote.startTorrents( torrent_ids, function(){ tr.refreshTorrents(torrent_ids) } );
this.remote.startTorrents( torrent_ids, force, function(){ tr.refreshTorrents(torrent_ids) } );
},
verifyTorrent: function( torrent ) {
this.verifyTorrents( [ torrent ] );
@ -2137,6 +2142,7 @@ Transmission.prototype =
this.setEnabled( this._context_pause_button, haveActiveSelection );
this.setEnabled( this._toolbar_start_button, havePausedSelection );
this.setEnabled( this._context_start_button, havePausedSelection );
this.setEnabled( this._context_start_now_button, havePausedSelection );
this.setEnabled( this._toolbar_remove_button, haveSelection );
this.setEnabled( this._toolbar_pause_all_button, haveActive );
this.setEnabled( this._toolbar_start_all_button, havePaused );

View File

@ -28,17 +28,17 @@ RPC._TurtleTimeEnabled = 'alt-speed-time-enabled';
RPC._TurtleTimeBegin = 'alt-speed-time-begin';
RPC._TurtleTimeEnd = 'alt-speed-time-end';
RPC._TurtleTimeDay = 'alt-speed-time-day';
RPC._PeerLimitGlobal = 'peer-limit-global';
RPC._PeerLimitPerTorrent = 'peer-limit-per-torrent';
RPC._PexEnabled = 'pex-enabled';
RPC._DhtEnabled = 'dht-enabled';
RPC._LpdEnabled = 'lpd-enabled';
RPC._BlocklistEnabled = 'blocklist-enabled';
RPC._BlocklistURL = 'blocklist-url';
RPC._BlocklistSize = 'blocklist-size';
RPC._UtpEnabled = 'utp-enabled';
RPC._PeerPortRandom = 'peer-port-random-on-start';
RPC._PortForwardingEnabled = 'port-forwarding-enabled';
RPC._PeerLimitGlobal = 'peer-limit-global';
RPC._PeerLimitPerTorrent = 'peer-limit-per-torrent';
RPC._PexEnabled = 'pex-enabled';
RPC._DhtEnabled = 'dht-enabled';
RPC._LpdEnabled = 'lpd-enabled';
RPC._BlocklistEnabled = 'blocklist-enabled';
RPC._BlocklistURL = 'blocklist-url';
RPC._BlocklistSize = 'blocklist-size';
RPC._UtpEnabled = 'utp-enabled';
RPC._PeerPortRandom = 'peer-port-random-on-start';
RPC._PortForwardingEnabled = 'port-forwarding-enabled';
RPC._StartAddedTorrent = 'start-added-torrents';
function TransmissionRemote( controller )
@ -215,8 +215,9 @@ TransmissionRemote.prototype =
this.sendTorrentSetRequests( method, torrent_ids, null, callback );
},
startTorrents: function( torrent_ids, callback ) {
this.sendTorrentActionRequests( 'torrent-start', torrent_ids, callback );
startTorrents: function( torrent_ids, noqueue, callback ) {
var name = noqueue ? 'torrent-start-now' : 'torrent-start';
this.sendTorrentActionRequests( name, torrent_ids, callback );
},
stopTorrents: function( torrent_ids, callback ) {
this.sendTorrentActionRequests( 'torrent-stop', torrent_ids, callback );