Handle HAVE_ALL and HAVE_NONE. Cleanup the BITFIELD message.
[etorrent.git] / lib / etorrent-1.0 / src / etorrent_torrent.erl
blobb91feee624d6bfbf2fd9685ca025a0e7ed52a237
1 %%%-------------------------------------------------------------------
2 %%% File : etorrent_torrent.erl
3 %%% Author : Jesper Louis Andersen <>
4 %%% Description : Library for manipulating the torrent table.
5 %%%
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").
13 %% API
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 %%====================================================================
19 %% API
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) ->
28 State = case L of
29 0 ->
30 etorrent_event_mgr:seeding_torrent(Id),
31 seeding;
32 _ -> leeching
33 end,
34 F = fun() ->
35 mnesia:write(#torrent { id = Id,
36 left = L,
37 total = T,
38 uploaded = U,
39 downloaded = D,
40 pieces = NPieces,
41 state = State })
42 end,
43 {atomic, _} = mnesia:transaction(F),
44 Missing = etorrent_piece_mgr:num_not_fetched(Id),
45 mnesia:dirty_update_counter(torrent_c_pieces, Id, Missing),
46 ok.
48 %%--------------------------------------------------------------------
49 %% Function: mode(Id) -> seeding | endgame | leeching
50 %% Description: Return the current mode of the torrent.
51 %%--------------------------------------------------------------------
52 mode(Id) ->
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 %%--------------------------------------------------------------------
69 select(Id) ->
70 mnesia:dirty_read(torrent, Id).
72 %%--------------------------------------------------------------------
73 %% Function: all() -> Rows
74 %% Description: Return all torrents, sorted by Id
75 %%--------------------------------------------------------------------
76 all() ->
77 all(#torrent.id).
79 %%--------------------------------------------------------------------
80 %% Function: all(Pos) -> Rows
81 %% Description: Return all torrents, sorted by Pos
82 %%--------------------------------------------------------------------
83 all(Pos) ->
84 mnesia:transaction(
85 fun () ->
86 Q = qlc:q([P || P <- mnesia:table(torrent)]),
87 qlc:e(qlc:keysort(Pos, Q))
88 end).
91 %%--------------------------------------------------------------------
92 %% Function: num_pieces(Id) -> integer()
93 %% Description: Return the number of pieces for torrent Id
94 %%--------------------------------------------------------------------
95 num_pieces(Id) ->
96 [R] = mnesia:dirty_read(torrent, Id),
97 R#torrent.pieces.
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),
107 case N of
108 0 ->
109 statechange(Id, endgame),
110 endgame;
111 N when is_integer(N) ->
113 end.
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]) ->
122 F = fun() ->
123 case mnesia:read(torrent, Id, write) of
124 [T] ->
125 New = case What of
126 unknown ->
127 T#torrent{state = unknown};
128 leeching ->
129 T#torrent{state = leeching};
130 seeding ->
131 T#torrent{state = seeding};
132 endgame ->
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,
140 case Left of
141 0 ->
142 T#torrent { left = 0, state = seeding };
143 N when N =< T#torrent.total ->
144 T#torrent { left = N }
145 end;
146 {tracker_report, Seeders, Leechers} ->
147 T#torrent{seeders = Seeders, leechers = Leechers}
148 end,
149 mnesia:write(New),
150 {T#torrent.state, New#torrent.state}
152 end,
153 {atomic, Res} = mnesia:transaction(F),
154 case Res of
155 {leeching, seeding} ->
156 etorrent_event_mgr:seeding_torrent(Id),
158 _ ->
160 end,
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 %%--------------------------------------------------------------------
169 is_endgame(Id) ->
170 case mnesia:dirty_read(torrent, Id) of
171 [T] -> T#torrent.state =:= endgame;
172 [] -> false % The torrent isn't there anymore.
173 end.
175 %%====================================================================
176 %% Internal functions
177 %%====================================================================