Current section
Files
Jump to
Current section
Files
lib/github_parse.ex
defmodule GitHubParse do
@moduledoc """
Git2PDF is a simple parser which can make github nested markdown file all in one PDF.
"""
@doc """
## Example
iex> GitHubParse.markdown_to_pdf "https://github.com/CyC2018/CS-Notes/blob/master/README.md"
url is https://github.com/CyC2018/CS-Notes/blob/master/README.md
check_url_status: url is https://xiaozhuanlan.com/CyC2018
check_url_status: url is https://github.com/CyC2018/CS-Notes/blob/master/other/LogoMakr_0zpEzN.png
check_url_status: url is https://github.com/CyC2018/CS-Notes/raw/master/other/LogoMakr_0zpEzN.png
check_url_status: url is https://cyc2018.github.io/CS-Notes
"""
def markdown_to_pdf(url) do
readme_html = url_to_html(url, false)
PdfGenerator.generate! readme_html, shell_params: ["--encoding", "utf-8"]
end
defp readme_to_pdf(url) do
readme_html = url_to_html(url, true)
PdfGenerator.generate! readme_html, shell_params: ["--encoding", "utf-8"]
end
defp url_to_html(url, is_readme, url_list \\ []) do
IO.puts("url is #{url}")
# must include else clause or it will nil
url_list = if length(url_list) == 0 do List.insert_at(url_list, 0, url) else url_list end
Process.sleep(Enum.random(2..6) * 1000)
%HTTPoison.Response{status_code: 200, body: body} = HTTPoison.get! url
readme =
if is_readme do
Enum.at(Floki.find(body, "#readme div"), 1)
else
Floki.find(body, "#readme")
end
normal_html =
if is_readme do
walk_html_node(readme)
else
walk_html(readme)
end
new_url_list = Floki.find(normal_html, "a") |> Floki.attribute("href") |> Enum.filter(fn x -> String.contains?(x, ".md") and not String.contains?(x, ".md#") end)
IO.puts("new url list is #{new_url_list}")
child_htmls = new_url_list
|> Stream.filter(fn x -> not Enum.member?(url_list, x) end)
|> Stream.map(&url_to_html(&1, false, Enum.concat(new_url_list, url_list)))
|> Enum.to_list
[Floki.raw_html(flatten_list(normal_html)) | child_htmls]
end
defp flatten_list(list) do
case list do
[[l]] -> [l]
other -> other
end
end
defp walk_html(html) do
html
|>Enum.map(&walk_html_node(&1))
|>Enum.to_list
end
defp walk_html_node(node) do
case node do
{tag, attribute_list, child_node_list} -> {tag, walk_html_attr(attribute_list), walk_html_node(child_node_list)}
list when is_list(list) -> walk_html(list)
_ -> node
end
end
defp walk_html_attr(attr_list) do
attr_list
|>Enum.map(fn(item) -> fix_url(item) end)
|>Enum.to_list
end
defp fix_url({name, value}) do
case name do
"src" -> if not String.starts_with?(value, "http") and not String.starts_with?(value, "#") do {name, check_url_status("https://github.com" <> value)} else {name, check_url_status(value)} end
"href" -> if not String.starts_with?(value, "http") and not String.starts_with?(value, "#") do {name, check_url_status("https://github.com" <> value)} else {name, check_url_status(value)} end
_ -> {name, value}
end
end
defp check_url_status(url) do
if (String.starts_with?(url, "http")) do
IO.puts("check_url_status: url is #{url}")
Process.sleep(500)
r = HTTPoison.head url
case r do
{:ok, %HTTPoison.Response{status_code: 200, body: _}} -> url
_ -> ""
end
else
""
end
end
end