Skip to content

Instantly share code, notes, and snippets.

@jonatanklosko
Last active July 5, 2023 20:45
Show Gist options
  • Save jonatanklosko/20e28aa772a888a25a829337a4b805e1 to your computer and use it in GitHub Desktop.
Save jonatanklosko/20e28aa772a888a25a829337a4b805e1 to your computer and use it in GitHub Desktop.
# This script automatically converts notebooks using the legacy images/
# directory to use notebook files introduced in Livebook v0.11.0.
#
# Usage: elixir convert_notebook_images.exs <notebooks-dir>
Mix.install([
{:jason, "~> 1.4.0"}
])
dir =
case System.argv() do
[path] ->
Path.expand(path)
_ ->
IO.puts("Usage: elixir convert_notebook_images.exs <notebooks-dir>")
System.halt(1)
end
case System.shell("git status --porcelain", cd: dir, stderr_to_stdout: true) do
{"", 0} ->
:ok
{_, 0} ->
IO.puts("Please commit all the changes before running this script")
System.halt(1)
{_, _} ->
IO.puts(
"Please run this script only in git repositories. You can initialize a fresh" <>
" git repo and then remove .git directory if the conversion is successful"
)
System.halt(1)
end
# Copy all images to the files directory
File.cp_r!(Path.join(dir, "images"), Path.join(dir, "files"))
for notebook_path <- Path.wildcard(Path.join(dir, "*.livemd")) do
source = File.read!(notebook_path)
image_names =
Regex.scan(~r{!\[.*?\]\(images/(.*?)\)}, source, capture: :all_but_first) |> List.flatten()
if image_names != [] do
# Update Markdown images to point to the files directory
source =
Regex.replace(~r{!\[(.*?)\]\(images/(.*?)\)}, source, fn _, alt, name ->
"![#{alt}](files/#{name})"
end)
# Update metadata to include the used files
file_entries =
for name <- Enum.sort(image_names) do
Jason.OrderedObject.new(name: name, type: :attachment)
end
source =
source
|> String.split("\n")
|> Enum.split_while(fn line ->
not String.starts_with?(line, "# ") and not String.starts_with?(line, "<!-- livebook:")
end)
|> case do
{leading, ["# " <> _ | _] = rest} ->
json = Jason.encode!(%{file_entries: file_entries})
leading ++ ["", "<!-- livebook:#{json} -->", ""] ++ rest
{leading, ["<!-- livebook:" <> meta_rest | rest]} ->
meta = meta_rest |> String.replace_trailing(" -->", "") |> Jason.decode!()
json =
meta
|> Map.put(:file_entries, file_entries)
|> Enum.sort()
|> Jason.OrderedObject.new()
|> Jason.encode!()
leading ++ ["", "<!-- livebook:#{json} -->", ""] ++ rest
end
|> Enum.join("\n")
|> String.trim_leading("\n")
File.write!(notebook_path, source)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment