Skip to content

Instantly share code, notes, and snippets.

@iomonad
Created August 14, 2018 07:04
Show Gist options
  • Save iomonad/289521a705ede167488594d4079e9b5b to your computer and use it in GitHub Desktop.
Save iomonad/289521a705ede167488594d4079e9b5b to your computer and use it in GitHub Desktop.
Binge - curses client for transmission-daemon
Binge
=====
Another curses client for transmission-daemon. Not fully featured,
but sufficient for my daily torrents. Minimal and experimental.
Screenshot: http://i.imgur.com/2X82oyS.png
Dependencies
============
apt-get install libcurses-perl libwww-perl libcpanel-json-xs-perl \
libscalar-list-utils-perl libmath-round-perl libjson-maybexs-perl
Key Bindings
============
u/d change up/download speed*
l change upload ratio*
R delete and remove*
r remove*
p start/stop*
J/K move up/down torrent queue*
Shft-PgUp/PgDwn move to top/bottom*
s sort torrent queue
ctrl-c quit
* the currently focused torrent
#!/usr/bin/perl
## Another minimal curses client for transmission-daemon.
## Not fully featured, but sufficient for my daily torrents.
## 6 May, 2017
use v5.24 ;
use strictures ;
use warnings ;
use feature qw(signatures) ;
no warnings qw(experimental::signatures) ;
use Curses ;
use LWP::UserAgent ;
use JSON::MaybeXS ;
use List::Util qw( none all any ) ;
use Math::Round qw( round ) ;
## Access tr-daemon via rpc
my $ua = LWP::UserAgent->new( agent => 'Transmission-Client' ) ;
my $url = 'http://localhost:9091/transmission/rpc' ;
my $trd_timeout = 10 ;
## Torrent data found via the rpc interface.
my $torrents = {} ;
my $trackerinfo = {} ;
my @queued ;
my %selected = ( id => -1, qpos => -1 ) ;
my $hilight = 0 ;
my $update_cnt = 0 ;
## Initial curses setup.
initscr();
noecho() ; ## Don't echo() while we do getch
cbreak() ; ## line buffering disabled
keypad(1) ; ## we get f1, f2 etc...
timeout(900) ; ## getch exits after .9 sec
curs_set(0) ; ## hide cursor
start_color() ;
## 0 black, 1 red, 2 green, 3 yellow, 4 blue, 5 magenta, 6 cyan, 7 white
init_pair 1, COLOR_RED, COLOR_BLACK ;
init_pair 2, COLOR_GREEN, COLOR_BLACK ;
init_pair 4, COLOR_BLUE, COLOR_BLACK ;
init_pair 5, COLOR_MAGENTA, COLOR_BLACK ;
init_pair 7, COLOR_BLACK, COLOR_WHITE ;
my $CP_DIVIDER = COLOR_PAIR(1);
my $CP_DIM1 = COLOR_PAIR(2);
my $CP_STD = COLOR_PAIR(4);
my $CP_BRIGHT = COLOR_PAIR(5);
my $CP_HILIGHT = COLOR_PAIR(7);
## Display initial torrent data
get_torrent_data() ;
display() ;
## MAIN LOOP
while ( 1 ) {
## Get latest torrent data.
for (my $cnt = 0 ; $cnt <= $trd_timeout ; $cnt++) {
last if get_torrent_data() ;
sleep .2 ;
die "Connection not found" if $cnt == $trd_timeout ;
}
my $ch = getchar() ;
if (defined $ch) {
timeout(900) ;
$hilight = 1 ;
my @updownkeys = qw( u d U D ) ;
my @movekeys = ( KEY_SHOME, KEY_SEND, "J", "K" ) ;
my @navkeys = ( KEY_DOWN, KEY_UP, KEY_HOME, KEY_END, KEY_PPAGE, KEY_NPAGE, "j", "k" ) ;
## If Esc, then no torrent is focused.
if ($ch eq "\e" or not scalar @queued) { %selected = ( id => -1, qpos => -1) ;
## Modify the upload ratio of the focused torrent
} elsif ($ch eq "L" or $ch eq "l") { change_ratio() ;
## Modify the up/download rate of the focused torrent
} elsif ( any { $_ eq $ch } @updownkeys ) { change_rate($ch) ;
## Navigate torrent queue with keys.
} elsif ( any { $_ eq $ch } @navkeys ) { keyselect_torrent($ch) ;
## Move the focused torrent up/down the torrent queue.
} elsif ( any { $_ eq $ch } @movekeys ) { move_torrent($ch) ;
## Remove the focused torrent
} elsif ($ch eq "r" or $ch eq "R") { remove_torrent($ch) ; next ;
## Start/stop the focused torrent.
} elsif ($ch eq "p" ) { toggle_pause($ch) ; next ;
## Sort the torrent queue.
} elsif ($ch eq "s" ) { queuesort() ; next ;
## if key char $ch not recognized, then...
} else {
timeout(2500) ;
$hilight = 0 ;
}
## if key char $ch undefined, then...
} elsif ($hilight) {
timeout(900) ;
$hilight-- ;
} else {
timeout(2500) ;
}
display() ;
}
say STDERR "Exiting..." ;
endwin() ;
#### SUBROUTINES
## Change upload/download rate for the focused torrent.
sub change_rate($ch) {
my @updownkeys = qw( u d U D ) ;
return 0 if $selected{id} == -1 or none { $_ eq $ch } @updownkeys ;
my ($key1, $key2, $msg) ;
($key1, $key2, $msg) = ( "uploadLimit", "uploadLimited", "Upload") if $ch =~ /u/i ;
($key1, $key2, $msg) = ("downloadLimit", "downloadLimited", "Download") if $ch =~ /d/i ;
my %args = ( ids => [ $selected{id} ], fields => [ $key1, $key2 ] ) ;
my $data = rpc('torrent-get', %args) ;
return 0 if not $data ;
for ($key1, $key2) { return 0 unless defined $data->{torrents}->[0]->{$_} } ;
my $foo = $data->{torrents}->[0]->{$key1} ;
my $bar = $data->{torrents}->[0]->{$key2} ;
my $rate = $bar ? $foo : -1 ;
while ( 1 ) {
$rate = -1 if $rate < 0 ;
my $string = sprintf("%s %s%5d", $msg, "speed:", $rate) ;
clrtoeol($LINES - 1, 0) ;
addstring($LINES - 1, 2, "$string") ;
my $ch = getchar() ;
next unless defined $ch ;
if ($ch eq KEY_UP) { $rate = $rate >= 0 ? $rate + 100 : 100 ;
} elsif ($ch eq KEY_DOWN) { $rate = $rate - 100 > 0 ? $rate - 100 : -1 ;
} elsif ($ch eq KEY_RIGHT) { $rate = $rate >= 0 ? $rate + 10 : 10 ;
} elsif ($ch eq KEY_LEFT) { $rate = $rate - 10 > 0 ? $rate - 10 : -1 ;
} elsif ($ch eq "\e" or $ch eq "q") { return 0
} elsif ($ch eq "\n") { last
}
}
%args = ( ids => [ $selected{id} ], $key2 => 0 ) if $rate == -1 ;
%args = ( ids => [ $selected{id} ], $key1 => $rate, $key2 => 1 ) if $rate != -1 ;
rpc('torrent-set', %args) ;
return 1 ;
}
## Asks the user a yes/no question.
sub confirm($msg) {
clrtoeol($LINES - 1, 0) ;
addstring($LINES - 1, 2, $msg) ;
while (1) {
my $ch = getchar() ;
next unless defined $ch ;
if ($ch eq "y") {
clrtoeol($LINES - 1, 0) ;
refresh() ;
return 1 ;
} elsif ($ch eq "n" or $ch eq "\e" or $ch eq "q") {
clrtoeol($LINES - 1, 0) ;
refresh() ;
return 0 ;
}
}
}
## Shows message on bottom line for 1 second.
sub shortmessage($msg) {
my $row = $LINES - 1 ;
clrtoeol($row, 0) ;
addstring($row, 2, $msg) ;
refresh() ;
sleep 1 ;
clrtoeol($row, 0) ;
refresh() ;
return 1 ;
}
## Remove (and sometimes delete) the focused torrent.
sub remove_torrent($ch) {
timeout(0) ;
my ($id, $qpos) = @selected{qw( id qpos )} ;
if ($ch eq "r" and confirm("Remove torrent? y/n")) {
my %args = (ids => [ $id ], ) ;
rpc('torrent-remove', %args ) ;
shortmessage("Removed.") ;
return 1 ;
} elsif ($ch eq "R" and confirm("Remove and delete torrent? y/n")) {
my %args = (ids => [ $id ], "delete-local-data" => 1) ;
rpc('torrent-remove', %args ) ;
shortmessage("Removed.") ;
return 1 ;
} else {
return 0 ;
}
}
## Move the focused torrent up/down the torrent queue.
sub move_torrent($ch) {
my $method = 0 ;
$method = "queue-move-up" if $ch eq "K" ;
$method = "queue-move-down" if $ch eq "J" ;
$method = "queue-move-top" if $ch eq KEY_SHOME ;
$method = "queue-move-bottom" if $ch eq KEY_SEND ;
return 0 unless $method ;
$hilight = 2 ;
timeout(0) ;
return rpc("$method", ( ids => [ $selected{id} ] ) ) ;
}
## Pause/unpause the focused torrent.
sub toggle_pause($ch) {
$hilight = 2 ;
timeout(0) ;
my ($id, $qpos) = @selected{qw( id qpos )} ;
my $status = $queued[$qpos]->{status} ;
if ($queued[$qpos]->{id} == $id) {
rpc('torrent-start', ids => $id) if $status == 0 and $ch eq "p" ;
rpc('torrent-stop', ids => $id) if $status != 0 and $ch eq "p" ;
}
}
## Change seeding ratio of the focused torrent
sub change_ratio {
my $qpos = $selected{qpos} ;
my $ratio = $queued[$qpos]->{seedRatioLimit} ;
while ( 1 ) {
$ratio = -1 if $ratio < 0 ;
my $foo = $ratio <= 0 ? sprintf("%4d", $ratio) : sprintf("%4.1f", $ratio) ;
clrtoeol($LINES - 1, 0) ;
addstring($LINES - 1, 2, "Upload ratio: $foo") ;
my $ch = getchar() ;
if (defined $ch ) {
if ($ch eq KEY_UP) { $ratio++
} elsif ($ch eq KEY_DOWN) { $ratio--
} elsif ($ch eq KEY_RIGHT) { $ratio += .1
} elsif ($ch eq KEY_LEFT) { $ratio -= .1
} elsif ($ch eq "\e" or $ch eq "q") { return 0
} elsif ($ch eq "\n") { last
}
}
}
my %args = (
ids => [ $selected{id} ],
seedRatioLimit => $ratio,
seedRatioMode => 1,
) ;
rpc('torrent-set', %args) ;
return 1 ;
}
## Use the nav keys to focus a torrent.
sub keyselect_torrent($ch) {
$selected{qpos}++ if ($ch eq KEY_DOWN or $ch eq "j") and $selected{qpos} < $#queued ;
$selected{qpos}-- if ($ch eq KEY_UP or $ch eq "k") and $selected{qpos} >= 0 ;
$selected{qpos} = 0 if $ch eq KEY_HOME ;
$selected{qpos} = $#queued if $ch eq KEY_END ;
if ($ch eq KEY_PPAGE) {
my $qpos = $selected{qpos} - $LINES + 2 ;
$selected{qpos} = $qpos >= 0 ? $qpos : 0 ;
}
if ($ch eq KEY_NPAGE) {
my $qpos = $selected{qpos} + $LINES - 2 ;
$selected{qpos} = $qpos > $#queued ? $#queued : $qpos ;
}
my $qpos = $selected{qpos} ;
$selected{id} = $qpos >= 0 ? $queued[$qpos]->{id} : -1 ;
}
sub queuesort {
sub by_active_seeds {
my ($aa, $bb) ;
my $astatus = $a->{status} ; my $bstatus = $b->{status} ;
if ($astatus == 6 and $bstatus == 6) {
$aa = $a->{rateUpload} ;
$bb = $b->{rateUpload} ;
} else {
$aa = 0 ; $bb = 0 ;
}
$bb <=> $aa ;
}
sub by_status {
my %tr = ( 4 => 6, 3 => 5, 6 => 4, 5 => 3, 0 => 2, 1 => 1, 2 => 0 ) ;
my $aa = $a->{status} ; my $bb = $b->{status} ;
my $astatus = $tr{$aa} ; my $bstatus = $tr{$bb} ;
$bstatus <=> $astatus ;
}
sub by_date {
my $aa = defined($a->{addedDate}) ? $a->{addedDate} : 0 ;
my $bb = defined($b->{addedDate}) ? $b->{addedDate} : 0 ;
$bb <=> $aa ;
#$aa <=> $bb ;
}
my @sorted = sort by_active_seeds
sort by_status
sort by_date @queued ;
for (my $num = 0 ; $num <= $#sorted ; $num++ ) {
my %args = ( ids => [ $sorted[$num]->{id} ], queuePosition => $num ) ;
rpc('torrent-set', %args) ;
}
timeout(0) ;
shortmessage("Sorting...") ;
%selected = ( id => -1, qpos => -1) ;
return 1 ;
}
#### DISPLAY SUBROUTINES
## Update/refresh all displayed torrents.
sub display() {
## Compute range of torrents to display.
my ($first, $last) ;
my $total = $LINES - 2 ;
if ( $#queued < $total ) {
$first = 0 ;
$last = $#queued ;
} elsif ( $selected{qpos} <= int($total/2) ) {
$first = 0 ; ## first is an index into @queued
$last = $total - 1 ; ## last is an index in @queued
} elsif ( ($#queued - $selected{qpos}) < int($total/2) ) {
$first = $#queued - $total + 1 ;
$last = $#queued ;
} else {
my $half = int( $total/2 ) ;
$first = $selected{qpos} - $half ;
$last = $selected{qpos} - $half + $total -1 ;
}
## The top/bottom lines are blanked.
clrtoeol(0, 0) ;
clrtoeol($LINES - 1, 0) ;
my $row = 1 ;
for (my $qpos = $first ; $qpos <= $last ; $qpos++, $row++ ) {
my $col = 0 ;
my $name_width = $COLS - 64 ;
addstring($row, $col, " ") ; $col += 2 ;
$col += print_name($row, $col, $qpos, $name_width) ;
$col += print_div($row, $col, $qpos) ;
$col += print_sizeWhenDone($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_percentDone($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_ratio($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_peers($row, $col, $qpos,"seederCount") ;
$col += print_div($row, $col, $qpos) ;
$col += print_peers($row, $col, $qpos,"leecherCount") ;
$col += print_div($row, $col, $qpos) ;
$col += print_connected($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_eta($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_rateDownload($row, $col, $qpos) ;
$col += print_div($row, $col, $qpos) ;
$col += print_rateUpload($row, $col, $qpos) ;
addstring($row, $col, " ") ;
}
for ( ; $row < $total + 1 ; $row++ ) { clrtoeol($row, 0) } ;
}
sub print_name($row,$col,$qpos,$width,) {
my $status = $queued[$qpos]->{status} ;
my $name = sprintf( "%-${width}s", substr($queued[$qpos]->{name}, 0, $width) ) ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($status == 4) { attron($CP_BRIGHT) ;
} elsif ($status == 6) { attron($CP_STD) ;
} else { attron($CP_DIM1) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, $name) ;
standend() ;
return $width ;
} ;
sub print_sizeWhenDone($row,$col,$qpos) {
my ($num, $unit) = format_number($queued[$qpos]->{sizeWhenDone}) ;
my $output = sprintf "%3s%s", $num, $unit ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($unit eq "G") { attron($CP_BRIGHT) ;
} else { attron($CP_STD) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$output") ;
standend() ;
return 4 ;
}
sub print_ratio($row,$col,$qpos) {
my $ratio = $queued[$qpos]->{uploadRatio} ;
my $status = $queued[$qpos]->{status} ;
my $fmt1 = "%4.2f" ; my $fmt2 = "%4.1f" ; my $fmt3 = "%4.0f" ;
my $output ;
if ($ratio < 0) { $output = " " ;
} elsif ($ratio < 10) { $output = sprintf($fmt1, $ratio) ;
} elsif ($ratio < 100) { $output = sprintf($fmt2, $ratio) ;
} else { $output = sprintf($fmt3, $ratio) ;
} ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($ratio < 0) { attron($CP_DIM1) ;
} elsif ($ratio < 1) { attron($CP_BRIGHT) ;
} elsif ($ratio > 1 and $status != 0) { attron($CP_STD) ;
} else { attron($CP_DIM1) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$output") ;
standend() ;
return 4 ;
}
sub print_peers($row,$col,$qpos,$key) {
my $peers = $queued[$qpos]->{$key} ;
my $output ;
if ($peers == -1) { $output = " " ;
} elsif ($peers < 1000) { $output = sprintf "%3d", $peers ;
} else { $output = sprintf "%2dK", round($peers/1000) ;
} ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($peers < 1000) { attron($CP_STD) ;
} else { attron($CP_BRIGHT) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$output") ;
standend() ;
return 3 ;
}
sub print_connected($row,$col,$qpos) {
my $peers = $queued[$qpos]->{peersConnected} ;
my $output = $peers ? sprintf("%2d", $peers) : " " ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($peers >= 50) { attron($CP_BRIGHT) ;
} else { attron($CP_STD) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$output") ;
standend() ;
return 2 ;
}
sub print_percentDone($row,$col,$qpos) {
my $status = $queued[$qpos]->{status} ;
my $perc = $queued[$qpos]->{percentDone} * 100 ;
my $elvis = sprintf "%3d%%", $perc ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($perc == 100) { attron($CP_DIM1) ;
} elsif ($status == 4) { attron($CP_BRIGHT) ;
} else { attron($CP_STD) ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$elvis") ;
standend() ;
return 4 ;
}
sub print_eta($row,$col,$qpos) {
my $eta = $queued[$qpos]->{eta} ;
my $status = $queued[$qpos]->{status} ;
my $output ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($status == 4) {
if ($eta > 604800) { $output = sprintf "%2d%s", round($eta/604800), "w" ;
} elsif ($eta > 86400) { $output = sprintf "%2d%s", round($eta/86400), "d" ;
} elsif ($eta > 3600) { $output = sprintf "%2d%s", round($eta/3600), "h" ;
} elsif ($eta > 600) { $output = sprintf "%2d%s", round($eta/60), "m" ;
} elsif ($eta > 60) { $output = sprintf "%2d%s", round($eta/60), "m" ;
} elsif ($eta > 0) { $output = sprintf "%2d%s", $eta, "s" ;
} else { $output = " " ;
}
if ($hilight and $qpos == $selected{qpos}) { attron($CP_HILIGHT) ;
} elsif ($eta > 3600) { attron($CP_DIM1) ;
} elsif ($eta > 600) { attron($CP_STD) ;
} elsif ($eta > 0) { attron($CP_BRIGHT) ;
}
} else { $output = " " ;
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, "$output") ;
standend() ;
return 3 ;
}
sub print_rateDownload($row,$col,$qpos) {
my $output ;
my $status = $queued[$qpos]->{status} ;
my $rate = $queued[$qpos]->{rateDownload} ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($status != 4 or $rate <= 0) {
$output = " " ;
} else {
my ($num, $unit) = format_number($rate) ;
$output = sprintf "%5s", "+$num$unit" ;
if ($rate > 512000) { attron($CP_BRIGHT) ;
} else { attron($CP_STD) ;
}
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, $output) ;
standend() ;
return 5 ;
}
sub print_rateUpload($row,$col,$qpos) {
my $output ;
my $status = $queued[$qpos]->{status} ;
my $rate = $queued[$qpos]->{rateUpload} ;
attrset(A_BOLD) if $qpos == $selected{qpos} ;
if ($status !~ /4|6/ or $rate <= 0) {
$output = " }" ;
} else {
my ($num, $unit) = format_number($rate) ;
$output = sprintf "%5s", "-$num$unit" ;
if ($rate > 512000) { attron($CP_BRIGHT) ;
} else { attron($CP_STD) ;
}
}
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addstring($row, $col, $output) ;
standend() ;
return 5 ;
}
sub print_div($row,$col,$qpos) {
attrset(A_BOLD) if $qpos == $selected{qpos} ;
attron($CP_DIVIDER) ;
attrset($CP_HILIGHT) if $qpos == $selected{qpos} and $hilight ;
addch($row,$col," ") ;
addch(ACS_VLINE) ;
addch(" ") ;
standend() ;
return 3 ;
}
sub format_number {
my $num = shift ;
$num = 0 if $num <= 0 ;
if ($num < 1000) { return $num , "B" ;
} elsif ($num < 1022976) { return int($num/1024) , "K" ;
} elsif ($num < 10276044) { return sprintf("%.1f", $num/1048576) , "M" ;
} elsif ($num < 1047527424) { return int($num/1048576) , "M" ;
} elsif ($num < 10522669875) { return sprintf("%.1f", $num/1073741824) , "G" ;
} else { return int($num/1073741824) , "G" ;
}
}
#### DATA/RPC SUBROUTINES
sub get_torrent_data {
## Interrogate tr-daemon for various data fields.
my $data ;
my @fields = qw(
eta id name sizeWhenDone seedRatioLimit queuePosition status
uploadRatio rateUpload rateDownload percentDone peersConnected
) ;
my %args = ( fields => \@fields ) ;
if ($update_cnt++%6 == 0) {
push @fields, "trackerStats" ;
%args = ( fields => \@fields ) ;
$data = rpc('torrent-get', %args) ;
} else {
$data = rpc('torrent-get', %args) ;
}
## Sort torrents by queuePosition.
@queued = () ;
return 0 if not $data ;
@queued = @{ $data->{torrents} } ;
@queued = sort { $a->{queuePosition} <=> $b->{queuePosition} } @queued ;
## Iterate over trackers to find seeds/peers,
foreach my $t ( @queued ) {
my $id = $t->{id} ;
if (defined $t->{trackerStats}->[0]) {
my ($seeds, $leeches) = (-1) x 2 ;
foreach my $stats (@{ $t->{trackerStats} }) {
$seeds = $stats->{seederCount} if $stats->{seederCount} > $seeds ;
$leeches = $stats->{leecherCount} if $stats->{leecherCount} > $leeches ;
}
## Assign to the highest values found.
$trackerinfo->{$id}->{seederCount} = $seeds ;
$trackerinfo->{$id}->{leecherCount} = $leeches ;
} ;
## Default seederCount and leecherCount to -1.
$trackerinfo->{$id}->{seederCount} = -1 unless defined $trackerinfo->{$id}->{seederCount} ;
$trackerinfo->{$id}->{leecherCount} = -1 unless defined $trackerinfo->{$id}->{leecherCount} ;
## Write %trackerinfo into @queued torrents.
$t->{seederCount} = $trackerinfo->{$id}->{seederCount} ;
$t->{leecherCount} = $trackerinfo->{$id}->{leecherCount} ;
}
## Store torrents by "id" instead of "queuePosition".
$torrents = {} ;
foreach my $t (@queued) { my $id = $t->{id} ; $torrents->{$id} = $t ; } ;
## Store current focused torrent in %selected.
my $id = $selected{id} ;
my $qpos = $selected{qpos} ;
if ($id != -1 and defined $torrents->{$id}) {
$selected{qpos} = $torrents->{$id}->{queuePosition} ;
} elsif ($qpos != -1) {
$qpos = $#queued if $qpos > $#queued ;
$selected{id} = $queued[$qpos]->{id} ;
$selected{qpos} = $qpos ;
} else {
%selected = ( id => -1, qpos => -1 ) ;
}
return 1 ;
}
## Partially cribbed from Transmission::Curses.
## https://trac.transmissionbt.com/browser/trunk/extras/rpc-spec.txt
## https://trac.transmissionbt.com/browser/trunk/libtransmission/transmission.h
sub rpc {
my $method = shift or return ;
my %args = @_ ;
my $nested = delete $args{_nested}; # internal flag
my($tag, $res, $post);
if (ref $args{ids} eq 'ARRAY') {
for my $id (@{ $args{ids} }) { $id += 0 if $id =~ /^\d+$/ ; }
}
$tag = int rand 2*16 - 1;
$post = JSON::MaybeXS->new->encode({
method => $method, tag => $tag, arguments => \%args,
}) ;
$res = $ua->post( $url, Content => $post ) ;
unless ( $res->is_success ) {
if ($res->code == 409 and ! $nested) {
my $sid = $res->header('X-Transmission-Session-Id') ;
$ua->default_header('X-Transmission-Session-Id' => $sid) ;
return rpc($method => %args, _nested => 1) ;
} else {
return 0 ;
} ;
}
$res = JSON::MaybeXS->new->decode( $res->content ) ;
return 0 unless $res->{tag} = $tag ;
return 0 if $res->{result} ne 'success' ;
return $res->{'arguments'} ;
}
! the colors im using with binge.
*color0: #171717
*color1: #333333
*color2: #595959
*color4: #8C8C8C
*color5: #CCCCCC
*color7: #8C8C8C
*color8: #171717
*color9: #333333
*color10: #595959
*color12: #8C8C8C
*color13: #CCCCCC
*color15: #8C8C8C
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment