lib/memex.ex

defmodule Memex do
  @moduledoc """
  Expand your memory with the magic of Elixir.

  The concept behind this project is basically throwing any resource against it
  and `Memex` should be able to figure out how to handle and store this stuff in
  a meaningful and queryable way.
  """
  alias Memex.Repo

  @doc """
  Slurp stuff.

  ## Examples

      iex> Memex.slurp("https://soundcloud.com/podcast")
      %Podcast{
        __meta__: #Ecto.Schema.Metadata<:loaded, "podcasts">,
        ...
      }

  """
  def slurp("https://soundcloud.com/" <> username) do
    feed = Soundcloud.Feed.fetch(username: username) |> Feedraptor.parse()

    podcast =
      Repo.insert!(%Podcast{
        title: feed.title,
        summary: feed.itunes_summary,
        description: feed.description,
        subtitle: feed.itunes_subtitle
      })
      |> Repo.preload([:categories, :episodes])

    podcast_changeset = Ecto.Changeset.change(podcast)

    for category_name <- feed.itunes_categories do
      category = get_or_create(Category, name: category_name)

      podcast_category_changeset =
        podcast_changeset |> Ecto.Changeset.put_assoc(:categories, [category])

      Repo.update!(podcast_category_changeset)
    end

    Enum.map(feed.entries, fn e ->
      episode =
        Ecto.build_assoc(podcast, :episodes, %Episode{
          title: e.title,
          enclosure_url: e.enclosure_url,
          subtitle: e.itunes_subtitle,
          summary: e.summary,
          url: e.url
        })

      Repo.insert!(episode)
    end)

    Repo.preload(Repo.get(Podcast, podcast.id), [:categories, :episodes])
  end

  defp get_or_create(queryable, clauses) do
    result = Repo.get_by(queryable, clauses)

    if result do
      result
    else
      Map.merge(queryable.__struct__, Map.new(clauses)) |> Repo.insert!()
    end
  end
end