Current section
Files
Jump to
Current section
Files
lib/magnet.ex
defmodule Magnet do
defstruct(
name: nil,
length: nil,
info_hash: [],
fallback: nil,
source: [],
keywords: [],
manifest: nil,
announce: [],
experimental: %{}
)
defdelegate decode(data), to: Magnet.Decoder
defdelegate encode(data), to: Magnet.Encoder
end
defimpl Collectable, for: Magnet do
def into(original) do
{original, fn
# ignore entries with empty values
acc, {:cont, {_, ""}} ->
acc
# as (Acceptable Source) - Web link to the file online
acc, {:cont, {"as", value}} ->
uri = URI.decode value
%Magnet{acc|fallback: uri}
# dn (Display Name) - Suggested filename
acc, {:cont, {"dn", value}} ->
%Magnet{acc|name: value}
# kt (Keyword Topic) - Key words for the given torrent
acc, {:cont, {<<"kt", priority::binary>>, value}} ->
entry = parse_suffix_number(priority, String.split(value, "+"))
%Magnet{acc|keywords: [entry|acc.keywords]}
# mt (Manifest Topic) - link to a file that contains a list of magneto (MAGMA - MAGnet MAnifest)
acc, {:cont, {"mt", value}} ->
%Magnet{acc|manifest: value}
# tr (address TRacker) - Tracker/Announce URLs for BitTorrent downloads
acc, {:cont, {<<"tr", priority::binary>>, value}} ->
announce = URI.decode(value)
entry = parse_suffix_number(priority, announce)
%Magnet{acc|announce: [entry|acc.announce]}
# xl (eXact Length) - File size in bytes
acc, {:cont, {"xl", value}} ->
length = String.to_integer(value)
%Magnet{acc|length: length}
# xs (eXact Source) - peer-to-peer links
acc, {:cont, {<<"xs", priority::binary>>, value}} ->
uri = URI.decode(value)
entry = parse_suffix_number(priority, uri)
%Magnet{acc|source: [entry|acc.source]}
# xt (eXact Topic) - URN containing file hash
acc, {:cont, {<<"xt", priority::binary>>, value}} ->
entry = parse_suffix_number(priority, value)
%Magnet{acc|info_hash: [entry|acc.info_hash]}
acc, {:cont, {<<"x.", key::binary>>, value}} ->
%Magnet{acc|experimental: Map.put(acc.experimental, key, value)}
acc, :done ->
keywords =
acc.keywords
|> sort_by_priority
|> List.flatten
|> Enum.dedup
%Magnet{acc|info_hash: prepare_list(acc.info_hash),
announce: prepare_list(acc.announce),
source: prepare_list(acc.source),
keywords: keywords}
_, :halt ->
:ok
end}
end
defp prepare_list(list) do
list
|> sort_by_priority
|> Enum.dedup
end
defp sort_by_priority(priority_list) do
priority_list
|> Enum.sort_by(&(elem(&1, 0)))
|> Enum.map(&(elem(&1, 1)))
end
defp parse_suffix_number("", value),
do: {0, value}
defp parse_suffix_number(<<".", number::binary>>, value) do
{num, _} = Integer.parse(number)
{num, value}
end
end