1 %%%-------------------------------------------------------------------
2 %%% File : etorrent_torrent.erl
3 %%% Author : Jesper Louis Andersen <>
4 %%% Description : Library for manipulating the torrent table.
6 %%% Created : 15 Jun 2008 by Jesper Louis Andersen <>
7 %%%-------------------------------------------------------------------
8 -module(etorrent_torrent
).
10 -include_lib("stdlib/include/qlc.hrl").
11 -include("etorrent_mnesia_table.hrl").
14 -export([new
/3, delete
/1, select
/1, all
/0, statechange
/2,
15 num_pieces
/1, decrease_not_fetched
/1,
16 is_endgame
/1, mode
/1]).
18 %%====================================================================
20 %%====================================================================
21 %%--------------------------------------------------------------------
22 %% Function: new(Id, LoadData, NPieces) -> NPieces
23 %% Description: Initialize a torrent entry for Id with the tracker
24 %% state as given. Pieces is the number of pieces for this torrent.
25 %% Precondition: The #piece table has been filled with the torrents pieces.
26 %%--------------------------------------------------------------------
27 new(Id
, {{uploaded
, U
}, {downloaded
, D
}, {left
, L
}, {total
, T
}}, NPieces
) ->
30 etorrent_event_mgr:seeding_torrent(Id
),
35 mnesia:write(#torrent
{ id
= Id
,
43 {atomic
, _
} = mnesia:transaction(F
),
44 Missing
= etorrent_piece_mgr:num_not_fetched(Id
),
45 mnesia:dirty_update_counter(torrent_c_pieces
, Id
, Missing
),
48 %%--------------------------------------------------------------------
49 %% Function: mode(Id) -> seeding | endgame | leeching
50 %% Description: Return the current mode of the torrent.
51 %%--------------------------------------------------------------------
53 [#torrent
{ state
= S
}] = mnesia:dirty_read(torrent
, Id
),
56 %%--------------------------------------------------------------------
57 %% Function: delete(Id) -> transaction
58 %% Description: Remove the torrent identified by Id. Id is either a
59 %% an integer, or the Record itself we want to remove.
60 %%--------------------------------------------------------------------
61 delete(Id
) when is_integer(Id
) ->
62 mnesia:dirty_delete(torrent
, Id
),
63 mnesia:dirty_delete(torrent_c_pieces
, Id
).
65 %%--------------------------------------------------------------------
66 %% Function: select(Id, Pid) -> Rows
67 %% Description: Return the torrent identified by Id
68 %%--------------------------------------------------------------------
70 mnesia:dirty_read(torrent
, Id
).
72 %%--------------------------------------------------------------------
73 %% Function: all() -> Rows
74 %% Description: Return all torrents, sorted by Id
75 %%--------------------------------------------------------------------
79 %%--------------------------------------------------------------------
80 %% Function: all(Pos) -> Rows
81 %% Description: Return all torrents, sorted by Pos
82 %%--------------------------------------------------------------------
86 Q
= qlc:q([P
|| P
<- mnesia:table(torrent
)]),
87 qlc:e(qlc:keysort(Pos
, Q
))
91 %%--------------------------------------------------------------------
92 %% Function: num_pieces(Id) -> integer()
93 %% Description: Return the number of pieces for torrent Id
94 %%--------------------------------------------------------------------
96 [R
] = mnesia:dirty_read(torrent
, Id
),
100 %%--------------------------------------------------------------------
101 %% Function: decrease_not_fetched(Id) -> ok | endgame
102 %% Description: track that we downloaded a piece, eventually updating
103 %% the endgame result.
104 %%--------------------------------------------------------------------
105 decrease_not_fetched(Id
) ->
106 N
= mnesia:dirty_update_counter(torrent_c_pieces
, Id
, -1),
109 statechange(Id
, endgame
),
111 N
when is_integer(N
) ->
115 %%--------------------------------------------------------------------
116 %% Function: statechange(InfoHash, State) -> ok | not_found
117 %% Description: Set the state of an info hash.
118 %%--------------------------------------------------------------------
119 statechange(_Id
, []) ->
121 statechange(Id
, [What
| Rest
]) ->
123 case mnesia:read(torrent
, Id
, write
) of
127 T#torrent
{state
= unknown
};
129 T#torrent
{state
= leeching
};
131 T#torrent
{state
= seeding
};
133 T#torrent
{state
= endgame
};
134 {add_downloaded
, Amount
} ->
135 T#torrent
{downloaded
= T#torrent
.downloaded
+ Amount
};
136 {add_upload
, Amount
} ->
137 T#torrent
{uploaded
= T#torrent
.uploaded
+ Amount
};
138 {subtract_left
, Amount
} ->
139 Left
= T#torrent
.left
- Amount
,
142 T#torrent
{ left
= 0, state
= seeding
};
143 N
when N
=< T#torrent
.total
->
144 T#torrent
{ left
= N
}
146 {tracker_report
, Seeders
, Leechers
} ->
147 T#torrent
{seeders
= Seeders
, leechers
= Leechers
}
150 {T#torrent
.state
, New#torrent
.state
}
153 {atomic
, Res
} = mnesia:transaction(F
),
155 {leeching
, seeding
} ->
156 etorrent_event_mgr:seeding_torrent(Id
),
161 statechange(Id
, Rest
);
162 statechange(Id
, What
) when is_integer(Id
) ->
163 statechange(Id
, [What
]).
165 %%--------------------------------------------------------------------
166 %% Function: is_endgame(Id) -> bool()
167 %% Description: Returns true if the torrent is in endgame mode
168 %%--------------------------------------------------------------------
170 case mnesia:dirty_read(torrent
, Id
) of
171 [T
] -> T#torrent
.state
=:= endgame
;
172 [] -> false
% The torrent isn't there anymore.
175 %%====================================================================
176 %% Internal functions
177 %%====================================================================