#!/usr/bin/perl
# autonuts v2.1 - automation of gnut using expect.pm
# by steven fountain (cider@compulsion.org)
# web: http://enraptured.compulsion.org/autonuts
# web: http://enraptured.compulsion.org/code/
#
# autonuts is designed to pillage and data-mine gnutella using the
# command line gnutella client "gnut", using perl and Expect.pm
# in a very hacked fashion.
#
# this tool succeeds at identifying unique file names
# when given enough responses to chew through.
#
# in order to have it not grab files you already have, you must
# change the $archive variable to point to where your mp3's are,
# ie, /mp3
# then you must move the contents of a retrieval for "the fish"
# into /mp3/the_fish
# or "fish" as
# /mp3/fish
#
# after identifying available unique songs by a band that you
# dont already have, you can then queue them all..
# take the path of no return and jump into interactive mode
# to watch the files download.
#
# usage: autonuts your simple text bandname here
# ie: autonuts sneakerpimps &
# autonuts the cure &
# autonuts fugazi &
# ;)
#
# requirements:
# expect perl module w/stty/pty dependencies (deb users: libexpect-
+perl)
# gnut (deb users: gnut)
# some bandwidth
# some harddrive space
#
# where your files are mostly stored.. for re-running
$archive = "/ok/muse"; # change to /mp3 or something if i had drives?
use Term::ANSIColor; # allways like to have this on my plate...
use Expect; # gnut's kinda like ftp. *wink*
use Text::Soundex; # for assistance in finding unique filenames
$|++; # i like autoflushing too much.
$*{STDERR} = \*{STDOUT}; # wouldnt want to miss anything now
$SIG{INT} = sub { $nut -> hard_close(); die "exiting.\n"; }; # hand
+le ctl-C
$Expect::Debug=0;
$Expect::Exp_Internals=0;
$Expect::Log_Stdout=0; # turn this on for annoying debugging...
$Expect::Multiline_Matching = 0;
$Expect::Exp_Max_Accum = 0;
$Expect::Manual_Stty = 1;
# take a band name from command line.
$band = join ' ', @ARGV;
$band = lc($band);
if ($band =~ /^\w/) { print "Good bandname found from commandline: $ba
+nd\n"; }
else { undef $band; }
# coloring debugging output routines
sub red { print color 'red'; }
sub pred { $_ = shift; print color 'red'; print "$_"; print color 'res
+et'; print "\n"; }
sub pgreen { $_ = shift; print color 'green'; print "$_"; print color
+'reset'; print "\n"; }
sub pbgreen { $_ = shift; print color 'bold green'; print "$_"; print
+color 'reset'; print "\n"; }
sub white { print color 'bold white'; }
sub creset { print color 'reset'; }
sub pret { print "\n"; }
sub wait_for_prompt {
$quiet = @_;
print "waiting for prompt.. ";
unless($nut -> expect(15, "autonuts>")) {
print "NOT ok.\n";
return 0;
}
print "ok.\n";
return 1;
}
sub waitfor {
$waitfor = shift;
$howlong = shift || 60;
print "expecting \"$waitfor\" for ${howlong}s.. ";
unless($nut -> expect($howlong, '-re', "$waitfor")) {
print "NOT found.\n";
return 0;
}
print "ok.\n";
return 1;
}
sub start {
print "spawning gnut process.. ";
($nut = Expect->spawn("gnut 2>&1")) || die "Couldn't spawn gnut: $
+!";
print "ok.\n";
print "waiting for gnut to start.. ";
unless($nut -> expect(15, "at your service")) {
die "gnut didn't start successfully.\n"; }
print "ok.\n";
}
@settings = (
"set prompt autonuts>\\n", # without this, wait_for_prompt would b
+reak
"set response_format sz:{S} :: sp:{s} :: {f#} :: {N} ::", # needed
+ for "resp"
"set paginate 0", # gets rid of requests to more every 25 lines
"set stats_format 1", # one line serverstats instead of two, i thi
+nk
"set max_downloads 100",
"set max_incoming 100",
"set sort_order p", # sort by speed, show the fastest ones at bott
+om
);
sub settings {
foreach $setting (@settings) {
print "$setting.. ";
$nut->send_slow(0, "$setting\r\n");
print "ok. ";
wait_for_prompt();
}
}
sub sane {
print "waiting for network to become ready..\n";
until($network_is_ready) {
print "gathering... ";
$nut->clear_accum();
$nut->send_slow(0, "info\r\n");
unless($nut -> expect(2, '-re', "HOST STATS:") ) {
print "blah.\n";
}
$host_stats = $nut->exp_after();
@host_stats = ();
@host_stats = split /\n/, $host_stats;
foreach (@host_stats) {
if (/Unique GUIDs in memory: (\d+)/) {
$users = $1;
print "unique users: $users (not >500)";
$network_is_ready++ if ($users > 500);
}
}
print " resting 20s" unless $network_is_ready;
sleep 20 unless $network_is_ready;
print ";\n";
}
wait_for_prompt(quiet);
$nut->send_slow(0, "info\r\n");
wait_for_prompt(quiet);
pgreen "sane connectivity scenario reached.";
}
#until($cmd eq "stop"||$cmd eq "quit"||(!$nut)) {
# print "autonuts: ";
# chomp($cmd = <STDIN>) unless $auto;
# $auto=0 if $auto; # disable if previously auto...
sub info {
$nut->clear_accum();
$nut->send_slow(0, "info\r\n");
waitfor "HOST STATS:";
pgreen "playing back...";
$info = $nut->exp_after();
@info = ();
@info = split /\n/, $info;
foreach (@info) {
chomp;
$l++;
pgreen "$l: $_";
}
undef $l;
wait_for_prompt();
}
sub band {
print "band presently set to: $band\n";
}
sub bandset {
$band = shift;
$band = lc($band);
band();
}
sub search {
pred("searching for stuff by ${band}... 75 second headstart...
+ ");
$nut->clear_accum();
$nut->send_slow(0, "search $band\r");
sleep 75;
# the funny thing is that after 60s this picks up ~365, 495, e
+tc.
waitfor("responses received.", 120) ? $results=0 : $results=1
+;
$nut->send_slow(0, "\r");
search() unless $results;
resp() if $results;
}
sub resp {
pred("showing responses...");
$nut->clear_accum();
$nut->send_slow(0, "resp\r");
$results = wait_for_prompt();
if($results) {
$responses = $nut->exp_before();
@responses = ();
@responses = split /\n/, $responses;
$fmp3=0;
%leech = ();
%titles = ();
%sounds = ();
foreach (@responses) {
chomp;
$l++;
pgreen "$l: $_";
if (/mp3/i) {
$fmp3++;
/sz:(.*?)\s+::\ssp:\s{0,}(\d+)\s::\s(\d+)\s::\s(.*?)\s
+::/;
$song = $4; # and print "song: $song\n";
$number = $3; # and print "number: $number\n";
$speed = $2; # and print "speed: $speed\n";
$size = $1; # and print "size: $size\n";
$isize = int($size);
next unless ($size =~ /m$/i); # should be in the meg b
+allpark
next unless ($isize > 2); # should be bigger then two
+megs
next unless ($isize < 10); # should be less than ten m
+egs
next unless ($speed > 200); # should get it faster tha
+n isdn
$song = lc($song); # change the song to lowercase
$song =~ s/_/ /g; # change underscores to spaces
$song =~ s/\.mp3//gi; # remove the file extension
$song =~ s/\s{0,}[\(\[].*?[\[\(].*?[\]\)].*?[\]\)]\s{0
+,}//g; # remove double variant comments eg: (remix(its phat))
$song =~ s/\s{0,}[\(\[].*?[\]\)]\s{0,}//g; # remove va
+riant comments eg: (remix)
$song =~ s/${band} - .* - (.*?)$/${band} - $1/g; # gre
+edy attempt at removing album names
next unless ($song =~ /$band - .*?/i); # should resemb
+le band with a title
$title = $1 if ($song =~ /${band} - (.*?)$/);
$code = soundex $title;
$already_have = 0;
foreach (@already_have) {
$ihave = $_;
$ihavecode = $1 if ($ihave =~ /^(.*?) /);
$uhave = "$code $title";
# if it sounds like something we already have,
# we probably dont want it.
if ($uhave =~ /^$ihavecode /) {
$already_have = 1;
print "passing by: $code $title\n";
}
}
if ($already_have eq 0) {
print "marking: $code $title\n";
$sounds{$code}{title} = $title;
$sounds{$code}{number} = $number;
}
};
}
unless ($l > 20 && $number / 2 < $l) {
print "not happy with these results, trying again.\n";
$auto=1;
$resp_sucks++;
if ($resp_sucks > 5) {
undef $resp_sucks;
print "hrm. you might want to try typing search again.
+\n";
print "searching again...\n";
search(); # ha figured it out heh
}
search();
} else {
print "i'm happy with the results. you should now type lis
+t\n";
}
undef $l;
}
$auto=1 unless $fmp3;
}
sub list {
foreach $codes (sort keys %sounds) {
$uniq++;
print "$codes [$sounds{$codes}{number}]\t$sounds{$codes}{t
+itle}\n";
}
print "$uniq uniq.\n";
search() unless ($uniq =~ /^\d+/);
undef $uniq;
}
sub getuniq {
print "i'll take";
foreach $codes (sort keys %sounds) {
$num = $sounds{$codes}{number};
print " ${num},";
$nut->send_slow(0, "g $num\r\n");
}
print " and that aughta do it.\n";
print "requested all unique files.\n";
}
sub log {
if ($log) { pred "turning log off."; $log=0; $Expect::Log_Stdo
+ut=0; }
unless ($log) { pred "turning log on."; $log=1; $Expect::Log_S
+tdout=1; }
}
sub before {
print $nut->exp_before();
}
sub after {
print $nut->exp_after();
}
sub interact {
print "please do not return to autonuts, i'm changing your pro
+mpt...\n";
print "setting prompt.. ";
$nut->send_slow(0, "set prompt gnut+autonutted>\r\n");
print "ok.\n";
$nut->interact();
$cmd = "stop"; # eh who knows might work...
}
sub help {
print "commands..\nband info search resp list getuniq log befo
+re after interact\nif you run them in no specific order your mileage
+will vary\n";
}
sub check_existing_songs {
$group = shift;
$group =~ s/ /_/g;
print "opening existing $group...\n";
opendir OFF, "$archive/$group" or return "no archive directory fou
+nd.\n";
while($song = readdir OFF) {
next if ($song =~ /^\.|\.\.$/);
next unless ($song =~ /\.mp3$/i);
$song = lc($song); # change the song to lowercase
$song =~ s/_/ /g; # change underscores to spaces
$song =~ s/\.mp3//gi; # remove the file extension
$song =~ s/\s{0,}[\(\[].*?[\[\(].*?[\]\)].*?[\]\)]\s{0,}//g; #
+ remove double variant comments eg: (remix(its phat))
$song =~ s/\s{0,}[\(\[].*?[\]\)]\s{0,}//g; # remove variant co
+mments eg: (remix)
$song =~ s/${band} - .* - (.*?)$/${band} - $1/g; # greedy atte
+mpt at removing album names
next unless ($song =~ /$band - .*?/i); # should resemble band
+with a title
$title = $1 if ($song =~ /${band} - (.*?)$/);
$code = soundex $title;
print "already have: $code $title\n";
push @already_have, "$code $title";
}
closedir OFF;
}
# roadmap..
print "band: " unless(defined($band));
chomp($band = <STDIN>) unless(defined($band));
start();
settings();
check_existing_songs($band);
sane();
bandset($band) if(defined($band));
search();
list();
getuniq();
print "abandoning you now to the gnut program, press enter once inside
+.\n";
interact(); # lost into gnut...
# shouldnt get here...
print "aah, they got me.\n";
print "i'm dying.\n";
$nut->hard_close();
die "dead.\n";
In reply to autonuts
by cider
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
|
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.