diff --git a/doc/rtorrent_fast_resume.pl b/doc/rtorrent_fast_resume.pl new file mode 100644 index 00000000..e01849f5 --- /dev/null +++ b/doc/rtorrent_fast_resume.pl @@ -0,0 +1,128 @@ +#!/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 = $t->{info}{files}[$f]{length}; + 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 +$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; diff --git a/doc/torrent_data b/doc/torrent_data new file mode 100644 index 00000000..9207ae87 --- /dev/null +++ b/doc/torrent_data @@ -0,0 +1,23 @@ +#!/usr/bin/perl + +# Perl script to read torrent data + +use strict; +use warnings; +use Convert::Bencode_XS qw(bdecode); +use Data::Dumper; + +$/ = undef; +$| = 1; + +my $in; +my $in_file = $ARGV[0]; +if ($in_file) { open($in, '<', $in_file) || die "Cannot open $in_file for input!\n"; } +else { $in = *STDIN; } + +print "Total input torrent size: ".sprintf('%.2f', (-s $in_file) / 1024)." KB\n" if $in_file; +print "Decoding ".($in_file || 'torrent from standard input')."..."; +my $t = bdecode(scalar <$in>); +print "done\n\n"; + +print Data::Dumper->new([$t], ['*Torrent'])->Indent(1)->Useqq(1)->Quotekeys(0)->Sortkeys(1)->Dump;