summaryrefslogtreecommitdiff
path: root/lib/nola/icecast.ex
blob: 021af0b593609a9b131407d58e1397f5ce2e1cd4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
defmodule Nola.Icecast do
  use GenServer
  require Logger
  @hackney_pool :default
  @httpoison_opts [hackney: [pool: @hackney_pool]]
  @fuse __MODULE__

  def start_link, do: GenServer.start_link(__MODULE__, [], [])

  def init(_) do
    GenServer.cast(self(), :poll)
    {:ok, nil}
  end

  def handle_cast(:poll, state) do
    state = poll(state)
    {:noreply, state}
  end

  def handle_info(:poll, state) do
    state = poll(state)
    {:noreply, state}
  end

  defp poll(state) do
    state =
      case request(base_url(), :get) do
        {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
          # update_json_stats(Jason.decode(body))
          stats = update_stats(body)

          if state != stats do
            Logger.info("Icecast Update: " <> inspect(stats))
            Nola.IcecastAgent.update(stats)

            Registry.dispatch(Nola.BroadcastRegistry, "icecast", fn ws ->
              for {pid, _} <- ws, do: send(pid, {:icecast, stats})
            end)

            stats
          else
            state
          end

        error ->
          Logger.error("Icecast HTTP Error: #{inspect(error)}")
          state
      end

    interval = Application.get_env(:nola, :icecast_poll_interval, 60_000)
    :timer.send_after(interval, :poll)
    state
  end

  defp update_stats(html) do
    raw =
      Floki.find(html, "div.roundbox")
      |> Enum.map(fn html ->
        html = Floki.raw_html(html)
        [{"h3", _, ["Mount Point /" <> mount]}] = Floki.find(html, "h3.mount")

        stats =
          Floki.find(html, "tr")
          |> Enum.map(fn {"tr", _, tds} ->
            [{"td", _, keys}, {"td", _, values}] = tds
            key = List.first(keys)
            value = List.first(values)
            {key, value}
          end)
          |> Enum.into(Map.new())

        {mount, stats}
      end)
      |> Enum.into(Map.new())

    live? = if Map.get(raw["live"], "Content Type:", false), do: true, else: false

    np =
      if live? do
        raw["live"]["Currently playing:"]
      else
        raw["autodj"]["Currently playing:"]
      end

    genre = raw["live"]["Genre:"] || nil
    %{np: np || "", live: live? || false, genre: genre}
  end

  defp update_json_stats({:ok, body}) do
    Logger.debug("JSON STATS: #{inspect(body)}")
  end

  defp update_json_stats(error) do
    Logger.error("Failed to decode JSON Stats: #{inspect(error)}")
  end

  defp request(uri, method, body \\ [], headers \\ []) do
    headers = [{"user-agent", "Nola-API[115ans.net, sys.115ans.net] href@random.sh"}] ++ headers
    options = @httpoison_opts
    # :fuse.ask(@fuse, :sync) do
    case :ok do
      :ok -> run_request(method, uri, body, headers, options)
      :blown -> :blown
    end
  end

  # This is to work around hackney's behaviour of returning `{:error, :closed}` when a pool connection has been closed
  # (keep-alive expired). We just retry the request immediatly up to five times.
  defp run_request(method, uri, body, headers, options),
    do: run_request(method, uri, body, headers, options, 0)

  defp run_request(method, uri, body, headers, options, retries) when retries < 4 do
    case HTTPoison.request(method, uri, body, headers, options) do
      {:error, :closed} -> run_request(method, uri, body, headers, options, retries + 1)
      other -> other
    end
  end

  defp run_request(method, uri, body, headers, options, _exceeded_retries),
    do: {:error, :unavailable}

  #
  # -- URIs
  #

  defp stats_json_url do
    base_url() <> "/status-json.xsl"
  end

  defp base_url do
    "http://91.121.59.45:8089"
  end
end