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, by_id
/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: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 %%--------------------------------------------------------------------
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 %%--------------------------------------------------------------------
72 mnesia:dirty_read(torrent
, Id
).
74 %%--------------------------------------------------------------------
75 %% Function: all() -> Rows
76 %% Description: Return all torrents, sorted by Id
77 %%--------------------------------------------------------------------
81 %%--------------------------------------------------------------------
82 %% Function: all(Pos) -> Rows
83 %% Description: Return all torrents, sorted by Pos
84 %%--------------------------------------------------------------------
88 Q
= qlc:q([P
|| P
<- mnesia:table(torrent
)]),
89 qlc:e(qlc:keysort(Pos
, Q
))
93 %%--------------------------------------------------------------------
94 %% Function: num_pieces(Id) -> integer()
95 %% Description: Return the number of pieces for torrent Id
96 %%--------------------------------------------------------------------
98 [R
] = mnesia:dirty_read(torrent
, Id
),
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),
111 statechange(Id
, endgame
),
113 N
when is_integer(N
) ->
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
]) ->
125 case mnesia:read(torrent
, Id
, write
) of
129 T#torrent
{state
= unknown
};
131 T#torrent
{state
= leeching
};
133 T#torrent
{state
= seeding
};
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
,
144 T#torrent
{ left
= 0, state
= seeding
};
145 N
when N
=< T#torrent
.total
->
146 T#torrent
{ left
= N
}
148 {tracker_report
, Seeders
, Leechers
} ->
149 T#torrent
{seeders
= Seeders
, leechers
= Leechers
}
152 {T#torrent
.state
, New#torrent
.state
}
155 {atomic
, Res
} = mnesia:transaction(F
),
157 {leeching
, seeding
} ->
158 etorrent_event_mgr:seeding_torrent(Id
),
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 %%--------------------------------------------------------------------
172 [T
] = mnesia:dirty_read(torrent
, Id
),
173 T#torrent
.state
=:= endgame
.
175 %%====================================================================
176 %% Internal functions
177 %%====================================================================