Elixir 从 shoutcast 获取元数据
Elixir fetching metadata from shoutcast
我想制作一个程序来显示当前正在播放的来自互联网广播流 (SomaFM) 的歌曲。我在 Elixir 中使用 HTTPoison 库。但我没有得到回应。它只是挂起。
我正在使用以下代码:
HTTPoison.start
url = "http://ice1.somafm.com/lush-128-mp3"
headers = [{"Icy-Metadata", "1"}]
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(url, headers) do
body |> Poison.decode! |> IO.inspect
else
{:error, %HTTPoison.Error{reason: reason}} ->
IO.inspect reason
end
end
其实我对 elixir 很陌生,所以如果有人能帮助我,我将不胜感激。
当您使用 get
请求时,您正在请求音频文件。如果它在流式传输,那么我想它永远不会停止 "downloading"。您将需要以不同的方式进行操作。
我实际上编写了一个快速示例库。您可以复制此模块代码,因为您已经拥有 HTTPoison
,您应该已经拥有 hackney
作为依赖项。
示例模块:https://github.com/ryanwinchester/shoutcast_ex/blob/master/lib/shoutcast.ex
defmodule Shoutcast do
defmodule Meta do
defstruct [:offset, :length, :data, :raw, :string]
@type t :: %__MODULE__{
data: map,
offset: integer,
length: integer,
raw: binary,
string: String.t
}
end
def read_meta(url) do
{:ok, _status, headers, ref} = :hackney.get(url, [{'Icy-Metadata', '1'}], "", [])
offset = get_offset(headers)
{:ok, data} = read_body(offset + 4081, ref, <<>>)
{meta_length, meta} = extract_meta(data, offset)
{:ok,
%Meta{
data: process_meta(meta),
offset: offset,
length: meta_length,
raw: meta,
string: String.trim(meta, <<0>>)
}
}
end
# Stream the body until we get what we want.
defp read_body(max_length, ref, acc) when max_length > byte_size(acc) do
case :hackney.stream_body(ref) do
{:ok, data} -> read_body(max_length, ref, <<acc::binary, data::binary>>)
:done -> {:ok, acc}
{:error, reason} -> {:error, reason}
end
end
defp read_body(_, _, acc), do: {:ok, acc}
# Get the byte offset from the `icy-metaint` header.
defp get_offset(headers) do
headers
|> Enum.into(%{})
|> Map.get("icy-metaint")
|> String.to_integer()
end
# Extract the meta data from the binary file stream.
defp extract_meta(data, offset) do
<< _::binary-size(offset), length::binary-size(1), chunk::binary >> = data
# The `length` byte will equal the metadata length/16.
# Multiply by 16 to get the actual metadata length.
<<l>> = length
meta_length = l * 16
<< meta::binary-size(meta_length), _::binary >> = chunk
{meta_length, meta}
end
# Process the binary meta data into a map.
defp process_meta(meta) do
meta
|> String.trim_trailing(<<0>>)
|> String.split(";")
|> Enum.map(&String.split(&1, "="))
|> Enum.reject(&(&1 == [""]))
|> Enum.map(fn [k, v] -> {k, String.trim(v, "'")} end)
|> Enum.into(%{})
end
end
# Get meta from stream
{:ok, meta} = Shoutcast.read_meta("http://ice1.somafm.com/lush-128-mp3")
# Get title
meta.data["StreamTitle"]
我添加了 Meta
结构来保存一些我觉得有趣的数据,如果您只对那。
我想制作一个程序来显示当前正在播放的来自互联网广播流 (SomaFM) 的歌曲。我在 Elixir 中使用 HTTPoison 库。但我没有得到回应。它只是挂起。
我正在使用以下代码:
HTTPoison.start
url = "http://ice1.somafm.com/lush-128-mp3"
headers = [{"Icy-Metadata", "1"}]
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(url, headers) do
body |> Poison.decode! |> IO.inspect
else
{:error, %HTTPoison.Error{reason: reason}} ->
IO.inspect reason
end
end
其实我对 elixir 很陌生,所以如果有人能帮助我,我将不胜感激。
当您使用 get
请求时,您正在请求音频文件。如果它在流式传输,那么我想它永远不会停止 "downloading"。您将需要以不同的方式进行操作。
我实际上编写了一个快速示例库。您可以复制此模块代码,因为您已经拥有 HTTPoison
,您应该已经拥有 hackney
作为依赖项。
示例模块:https://github.com/ryanwinchester/shoutcast_ex/blob/master/lib/shoutcast.ex
defmodule Shoutcast do
defmodule Meta do
defstruct [:offset, :length, :data, :raw, :string]
@type t :: %__MODULE__{
data: map,
offset: integer,
length: integer,
raw: binary,
string: String.t
}
end
def read_meta(url) do
{:ok, _status, headers, ref} = :hackney.get(url, [{'Icy-Metadata', '1'}], "", [])
offset = get_offset(headers)
{:ok, data} = read_body(offset + 4081, ref, <<>>)
{meta_length, meta} = extract_meta(data, offset)
{:ok,
%Meta{
data: process_meta(meta),
offset: offset,
length: meta_length,
raw: meta,
string: String.trim(meta, <<0>>)
}
}
end
# Stream the body until we get what we want.
defp read_body(max_length, ref, acc) when max_length > byte_size(acc) do
case :hackney.stream_body(ref) do
{:ok, data} -> read_body(max_length, ref, <<acc::binary, data::binary>>)
:done -> {:ok, acc}
{:error, reason} -> {:error, reason}
end
end
defp read_body(_, _, acc), do: {:ok, acc}
# Get the byte offset from the `icy-metaint` header.
defp get_offset(headers) do
headers
|> Enum.into(%{})
|> Map.get("icy-metaint")
|> String.to_integer()
end
# Extract the meta data from the binary file stream.
defp extract_meta(data, offset) do
<< _::binary-size(offset), length::binary-size(1), chunk::binary >> = data
# The `length` byte will equal the metadata length/16.
# Multiply by 16 to get the actual metadata length.
<<l>> = length
meta_length = l * 16
<< meta::binary-size(meta_length), _::binary >> = chunk
{meta_length, meta}
end
# Process the binary meta data into a map.
defp process_meta(meta) do
meta
|> String.trim_trailing(<<0>>)
|> String.split(";")
|> Enum.map(&String.split(&1, "="))
|> Enum.reject(&(&1 == [""]))
|> Enum.map(fn [k, v] -> {k, String.trim(v, "'")} end)
|> Enum.into(%{})
end
end
# Get meta from stream
{:ok, meta} = Shoutcast.read_meta("http://ice1.somafm.com/lush-128-mp3")
# Get title
meta.data["StreamTitle"]
我添加了 Meta
结构来保存一些我觉得有趣的数据,如果您只对那。