summaryrefslogblamecommitdiff
path: root/lib/lsg/icecast.ex
blob: 5a531926b572482e7f225747419130d86656ee1e (plain) (tree)
1
2
3
                         

                



























                                                                 

                                                                       









                                                                
                                                                         
                                      







































                                                                                  
                                                                                                






























                                                                                                                      
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
    case :ok do #:fuse.ask(@fuse, :sync) 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