{
our
$MAJOR
= 0.074;
our
$MINOR
= 0;
our
$DEV
= 1;
our
$VERSION
=
sprintf
(
'%1.3f%03d'
. (
$DEV
? ((
$DEV
< 0 ?
''
:
'_'
) .
'%03d'
) : (
''
)),
$MAJOR
,
$MINOR
,
abs
$DEV
);
has
'torrent'
=> (
isa
=>
'Net::BitTorrent::Torrent'
,
is
=>
'ro'
,
weak_ref
=> 1,
writer
=>
'_torrent'
,
predicate
=>
'has_torrent'
,
handles
=> {
client
=>
'client'
}
);
has
'tiers'
=> (
traits
=> [
'Array'
],
isa
=>
'NBTypes::Tracker::Tier'
,
is
=>
'rw'
,
coerce
=> 1,
default
=>
sub
{ [] },
handles
=> {
_push_tier
=>
'push'
,
_shuffle_tiers
=>
'shuffle'
}
);
my
$tier_constraint
;
sub
add_tier {
my
(
$self
,
$urls
) =
@_
;
$tier_constraint
//=
Moose::Util::TypeConstraints::find_type_constraint(
'NBTypes::Tracker::Tier'
);
$self
->_push_tier(
$tier_constraint
->coerce(
$urls
));
$self
->_shuffle_tiers;
}
for
my
$type
(
qw[announce scrape]
) {
has
"_${type}_quests"
=> (
isa
=>
'ArrayRef[Ref]'
,
is
=>
'ro'
,
init_arg
=>
undef
,
traits
=> [
'Array'
],
handles
=> {
"add_${type}_quest"
=>
'push'
,
"${type}_quests"
=>
'elements'
,
"get_${type}_quest"
=>
'get'
,
"grep_${type}_quests"
=>
'grep'
,
"map_${type}_quests"
=>
'map'
},
default
=>
sub
{ [] }
);
after
"add_${type}_quest"
=>
sub
{
Scalar::Util::weaken
$_
[0]->{
"_${type}_quests"
}->[-1];
};
}
sub
announce {
my
(
$self
,
$event
,
$code
) =
@_
;
Scalar::Util::weaken
$self
;
my
%args
= (
info_hash
=>
$self
->torrent->info_hash->to_Hex,
peer_id
=>
$self
->client->peer_id,
port
=>
$self
->client->port,
uploaded
=>
$self
->torrent->uploaded,
downloaded
=>
$self
->torrent->downloaded,
left
=>
$self
->torrent->left
);
$args
{
'info_hash'
} =~ s|(..)|\%$1|g;
my
$quest
;
$quest
= [
$event
,
$code
,
[],
AE::timer(
0,
15 * 60,
sub
{
return
if
!
$self
;
for
my
$tier
(@{
$self
->tiers}) {
$tier
->[0]->announce(
$event
,
\
%args
,
sub
{
my
(
$announce
) =
@_
;
{
my
%seen
= ();
@{
$quest
->[2]}
=
grep
{ !
$seen
{
$_
->[0]}{
$_
->[1]}++ }
@{
$quest
->[2]},
@{
$announce
->{
'peers'
}};
}
}
);
}
$event
=
undef
if
$event
;
}
)
];
$self
->add_announce_quest(
$quest
);
return
$quest
;
}
sub
scrape {
my
(
$self
,
$code
) =
@_
;
Scalar::Util::weaken
$self
;
my
%args
= (
info_hash
=>
$self
->torrent->info_hash->to_Hex);
$args
{
'info_hash'
} =~ s|(..)|\%$1|g;
my
$quest
= [
0,
$code
,
[],
AE::timer(
0,
15 * 60,
sub
{
return
if
!
$self
;
for
my
$tier
(@{
$self
->tiers}) {
$tier
->[0]->scrape(\
%args
,
$code
);
}
}
)
];
$self
->add_scrape_quest(
$quest
);
return
$quest
;
}
}
1;