mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-05 17:52:29 +00:00
132 lines
3.9 KiB
Perl
Executable File
132 lines
3.9 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
# Perl script to add rTorrent fast resume data to torrent files.
|
|
#
|
|
# Usage:
|
|
# rtorrent_fast_resume.pl [base-directory] < plain.torrent > with_fast_resume.torrent
|
|
# -OR-
|
|
# rtorrent_fast_resume.pl [base-directory] plain.torrent [with_fast_resume.torrent]
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Convert::Bencode_XS qw(bencode bdecode);
|
|
use File::Spec; # core module
|
|
use POSIX; # core module
|
|
|
|
$/ = undef;
|
|
$| = 1;
|
|
|
|
# Process ARGV
|
|
my $d = $ARGV[0];
|
|
if ($d and not -d $d) {
|
|
if (-f $d and -s $d and not $ARGV[2]) { # missing directory, but has file
|
|
$ARGV[2] = $ARGV[1];
|
|
$ARGV[1] = $ARGV[0];
|
|
$d = '';
|
|
}
|
|
else { die "$d is not a directory\n"; }
|
|
}
|
|
$d ||= ".";
|
|
$d .= "/" unless $d =~ m#/$#;
|
|
|
|
my ($in, $out, $msg);
|
|
my ($in_file, $out_file) = ('', '');
|
|
if ($ARGV[1]) {
|
|
$in_file = $ARGV[1];
|
|
open($in, ($ARGV[2] ? '<' : '+<'), $in_file) || die "Cannot open $in_file for input!\n";
|
|
unless ($ARGV[2]) {
|
|
$out = $in;
|
|
$out_file = $in_file;
|
|
}
|
|
}
|
|
else { $in = *STDIN; }
|
|
if ($ARGV[2]) {
|
|
$out_file = $ARGV[2];
|
|
open($out, '>', $out_file) || die "Cannot open $out_file for output!\n";
|
|
$msg = *STDOUT;
|
|
}
|
|
elsif (!$ARGV[1]) { $out = *STDOUT; }
|
|
$msg //= *STDERR;
|
|
|
|
print {$msg} "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file;
|
|
print {$msg} "Decoding ".($in_file || 'torrent from standard input')."...";
|
|
my $t = bdecode(scalar <$in>);
|
|
|
|
die "No info key.\n" unless ref $t eq "HASH" and exists $t->{info};
|
|
my $psize = $t->{info}{"piece length"} or die "No piece length key.\n";
|
|
print {$msg} "done\n\n";
|
|
|
|
my @files;
|
|
my $tsize = 0;
|
|
if (exists $t->{info}{files}) {
|
|
print {$msg} "Multi file torrent: $t->{info}{name}\n";
|
|
for (@{$t->{info}{files}}) {
|
|
push @files, join "/", $t->{info}{name},@{$_->{path}};
|
|
$tsize += $_->{length};
|
|
}
|
|
}
|
|
else {
|
|
print {$msg} "Single file torrent: $t->{info}{name}\n";
|
|
@files = ($t->{info}{name});
|
|
$tsize = $t->{info}{length};
|
|
}
|
|
my $chunks = int(($tsize + $psize - 1) / $psize);
|
|
print {$msg} "Total size: ".sprintf('%.2f', $tsize / 1024**2)." MB; $chunks chunks; ", scalar @files, " files.\n\n";
|
|
|
|
die "Inconsistent piece information!\n" if $chunks*20 != length $t->{info}{pieces};
|
|
|
|
print {$msg} "Adding fast resume information...";
|
|
# flags => 1+16+
|
|
|
|
my $pmod = 0;
|
|
$t->{libtorrent_resume}{bitfield} = $chunks;
|
|
foreach my $f (0..$#files) {
|
|
die "$d$files[$f] not found.\n" unless -e "$d$files[$f]";
|
|
my $mtime = (stat "$d$files[$f]")[9];
|
|
|
|
# Compute number of chunks per file
|
|
my $fsize = (exists $t->{info}{files}) ? $t->{info}{files}[$f]{length} : 1;
|
|
my $fchunks = ($pmod ? 1 : 0);
|
|
if ($pmod >= $fsize) { ($fsize, $pmod ) = (0, $pmod-$fsize); }
|
|
else { ($pmod, $fsize) = (0, $fsize-$pmod); }
|
|
$fchunks += ceil($fsize / $psize);
|
|
$pmod ||= $psize - ($fsize % $psize);
|
|
|
|
$t->{libtorrent_resume}{files}[$f] = {
|
|
priority => 0, # Don't download; we already have the file, so don't clobber it!
|
|
mtime => $mtime,
|
|
completed => $fchunks,
|
|
};
|
|
};
|
|
$t->{libtorrent_resume}{'uncertain_pieces.timestamp'} = time;
|
|
|
|
# Some extra information to re-enforce the fact that this is a finished torrent
|
|
if (exists $t->{info}{files}) {
|
|
$d .= $t->{info}{name};
|
|
}
|
|
|
|
$t->{rtorrent} = {
|
|
state => 1, # started
|
|
state_changed => time,
|
|
state_counter => 1,
|
|
chunks_wanted => 0,
|
|
chunks_done => $chunks,
|
|
complete => 1,
|
|
hashing => 0, # Not hashing
|
|
directory => File::Spec->file_name_is_absolute($d) ? $d : File::Spec->rel2abs($d),
|
|
((tied_to_file => File::Spec->file_name_is_absolute($out_file) ? $out_file : File::Spec->rel2abs($out_file)) x!! $out_file),
|
|
'timestamp.finished' => 0,
|
|
'timestamp.started' => time,
|
|
};
|
|
|
|
print {$msg} "done\n";
|
|
|
|
print {$msg} "Encoding ".($out_file || 'torrent from standard output')."...";
|
|
seek($out, 0, 0); # just in case files are the same
|
|
print {$out} bencode($t);
|
|
|
|
close($in);
|
|
close($out);
|
|
print {$msg} "done\n";
|
|
exit;
|