Let torrent statechanges take mulitple things to change.
[etorrent.git] / lib / etorrent-1.0 / src / etorrent_torrent.erl
blob1f190e29ec115185a147cd65cefb65cd53d1d9f7
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, by_id/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:num_not_fetched(Id),
45 mnesia:dirty_update_counter(torrent_c_pieces, Id, Missing).
47 %%--------------------------------------------------------------------
48 %% Function: mode(Id) -> seeding | endgame | leeching
49 %% Description: Return the current mode of the torrent.
50 %%--------------------------------------------------------------------
51 mode(Id) ->
52 [#torrent { state = S}] = mnesia:dirty_read(torrent, Id),
55 %%--------------------------------------------------------------------
56 %% Function: delete(Id) -> transaction
57 %% Description: Remove the torrent identified by Id. Id is either a
58 %% an integer, or the Record itself we want to remove.
59 %%--------------------------------------------------------------------
60 delete(Id) when is_integer(Id) ->
61 mnesia:dirty_delete(torrent, Id),
62 mnesia:dirty_delete(torrent_c_pieces, Id);
63 delete(Torrent) when is_record(Torrent, torrent) ->
64 mnesia:dirty_delete(torrent_c_pieces, Torrent#torrent.id),
65 mnesia:dirty_delete_object(Torrent).
67 %%--------------------------------------------------------------------
68 %% Function: by_id(Id, Pid) -> Rows
69 %% Description: Return the torrent identified by Id
70 %%--------------------------------------------------------------------
71 by_id(Id) ->
72 mnesia:dirty_read(torrent, Id).
74 %%--------------------------------------------------------------------
75 %% Function: all() -> Rows
76 %% Description: Return all torrents, sorted by Id
77 %%--------------------------------------------------------------------
78 all() ->
79 all(#torrent.id).
81 %%--------------------------------------------------------------------
82 %% Function: all(Pos) -> Rows
83 %% Description: Return all torrents, sorted by Pos
84 %%--------------------------------------------------------------------
85 all(Pos) ->
86 mnesia:transaction(
87 fun () ->
88 Q = qlc:q([P || P <- mnesia:table(torrent)]),
89 qlc:e(qlc:keysort(Pos, Q))
90 end).
93 %%--------------------------------------------------------------------
94 %% Function: num_pieces(Id) -> integer()
95 %% Description: Return the number of pieces for torrent Id
96 %%--------------------------------------------------------------------
97 num_pieces(Id) ->
98 [R] = mnesia:dirty_read(torrent, Id),
99 R#torrent.pieces.
102 %%--------------------------------------------------------------------
103 %% Function: decrease_not_fetched(Id) -> ok | endgame
104 %% Description: track that we downloaded a piece, eventually updating
105 %% the endgame result.
106 %%--------------------------------------------------------------------
107 decrease_not_fetched(Id) ->
108 N = mnesia:dirty_update_counter(torrent_c_pieces, Id, -1),
109 case N of
110 0 ->
111 statechange(Id, endgame),
112 endgame;
113 N when is_integer(N) ->
115 end.
117 %%--------------------------------------------------------------------
118 %% Function: statechange(InfoHash, State) -> ok | not_found
119 %% Description: Set the state of an info hash.
120 %%--------------------------------------------------------------------
121 statechange(_Id, []) ->
123 statechange(Id, [What | Rest]) ->
124 F = fun() ->
125 case mnesia:read(torrent, Id, write) of
126 [T] ->
127 New = case What of
128 unknown ->
129 T#torrent{state = unknown};
130 leeching ->
131 T#torrent{state = leeching};
132 seeding ->
133 T#torrent{state = seeding};
134 endgame ->
135 T#torrent{state = endgame};
136 {add_downloaded, Amount} ->
137 T#torrent{downloaded = T#torrent.downloaded + Amount};
138 {add_upload, Amount} ->
139 T#torrent{uploaded = T#torrent.uploaded + Amount};
140 {subtract_left, Amount} ->
141 Left = T#torrent.left - Amount,
142 case Left of
143 0 ->
144 T#torrent { left = 0, state = seeding };
145 N when N =< T#torrent.total ->
146 T#torrent { left = N }
147 end;
148 {tracker_report, Seeders, Leechers} ->
149 T#torrent{seeders = Seeders, leechers = Leechers}
150 end,
151 mnesia:write(New),
152 {T#torrent.state, New#torrent.state}
154 end,
155 {atomic, Res} = mnesia:transaction(F),
156 case Res of
157 {leeching, seeding} ->
158 etorrent_event_mgr:seeding_torrent(Id),
160 _ ->
162 end,
163 statechange(Id, Rest);
164 statechange(Id, What) when is_integer(Id) ->
165 statechange(Id, [What]).
167 %%--------------------------------------------------------------------
168 %% Function: is_endgame(Id) -> bool()
169 %% Description: Returns true if the torrent is in endgame mode
170 %%--------------------------------------------------------------------
171 is_endgame(Id) ->
172 [T] = mnesia:dirty_read(torrent, Id),
173 T#torrent.state =:= endgame.
175 %%====================================================================
176 %% Internal functions
177 %%====================================================================