Packages
livebook
0.4.0
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/content_loader.ex
defmodule Livebook.ContentLoader do
@moduledoc false
alias Livebook.Utils.HTTP
@typedoc """
A location from where content gets loaded.
"""
@type location :: {:file, FileSystem.File.t()} | {:url, String.t()}
@doc """
Rewrite known URLs, so that they point to plain text file rather than HTML.
Currently the rewerites handle:
* GitHub files
* Gist files
"""
@spec rewrite_url(String.t()) :: String.t()
def rewrite_url(url) do
case URI.new(url) do
{:ok, uri} -> uri |> do_rewrite_url() |> URI.to_string()
{:error, _} -> url
end
end
defp do_rewrite_url(%URI{host: "github.com"} = uri) do
case String.split(uri.path, "/") do
["", owner, repo, "blob", hash | file_path] ->
path = Enum.join(["", owner, repo, hash | file_path], "/")
%{uri | path: path, host: "raw.githubusercontent.com"}
_ ->
uri
end
end
defp do_rewrite_url(%URI{host: "gist.github.com"} = uri) do
case String.split(uri.path, "/") do
["", owner, hash] ->
path = Enum.join(["", owner, hash, "raw"], "/")
%{uri | path: path, host: "gist.githubusercontent.com"}
_ ->
uri
end
end
defp do_rewrite_url(uri), do: uri
@doc """
Loads binary content from the given URl and validates if its plain text.
"""
@spec fetch_content(String.t()) :: {:ok, String.t()} | {:error, String.t()}
def fetch_content(url) do
case HTTP.request(:get, url) do
{:ok, 200, headers, body} ->
valid_content? =
case HTTP.fetch_content_type(headers) do
{:ok, content_type} ->
content_type in ["text/plain", "text/markdown", "application/octet-stream"]
:error ->
false
end
if valid_content? do
{:ok, body}
else
{:error, "invalid content type, make sure the URL points to live markdown"}
end
_ ->
{:error, "failed to download notebook from the given URL"}
end
end
@doc """
Loads a notebook content from the given location.
"""
@spec fetch_content_from_location(location()) :: {:ok, String.t()} | {:error, String.t()}
def fetch_content_from_location(location)
def fetch_content_from_location({:file, file}) do
case Livebook.FileSystem.File.read(file) do
{:ok, content} -> {:ok, content}
{:error, message} -> {:error, "failed to read #{file.path}, reason: #{message}"}
end
end
def fetch_content_from_location({:url, url}) do
url
|> rewrite_url()
|> fetch_content()
end
@doc """
Normalizes the given URL into a location.
"""
@spec url_to_location(String.t()) :: location()
def url_to_location(url)
def url_to_location("file://" <> path) do
path = Path.expand(path)
file = Livebook.FileSystem.File.local(path)
{:file, file}
end
def url_to_location(url), do: {:url, url}
@doc """
Resolves the given relative path with regard to the given location.
"""
@spec resolve_location(location(), String.t()) :: location()
def resolve_location(location, relative_path)
def resolve_location({:url, url}, relative_path) do
{:url, Livebook.Utils.expand_url(url, relative_path)}
end
def resolve_location({:file, file}, relative_path) do
{:file, Livebook.FileSystem.File.resolve(file, relative_path)}
end
end