• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

camatcode / basenji / 86eb394777b2a14e273500308061729e50e910e2

16 Jul 2025 11:43PM UTC coverage: 55.458%. First build
86eb394777b2a14e273500308061729e50e910e2

Pull #49

github

camatcode
perf: clean up
Pull Request #49: perf: performance improvements

18 of 46 new or added lines in 10 files covered. (39.13%)

884 of 1594 relevant lines covered (55.46%)

314.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.35
/lib/basenji/reader/process/comic_optimizer.ex
1
defmodule Basenji.Reader.Process.ComicOptimizer do
2
  @moduledoc false
3

4
  alias Basenji.FilenameSanitizer
5
  alias Basenji.Reader
6

7
  require Logger
8

9
  def optimize(comic_file_path, tmp_dir, result_directory) do
10
    if String.ends_with?(comic_file_path, "optimized.cbz") || basenji_comment?(comic_file_path) do
1✔
11
      {:ok, comic_file_path}
12
    else
13
      comic_name =
1✔
14
        Path.basename(comic_file_path)
15
        |> Path.rootname()
16
        |> FilenameSanitizer.sanitize()
17

18
      with {:ok, %{page_count: page_count}} <- Reader.info(comic_file_path),
1✔
19
           {:ok, %{entries: entries}} <- Reader.read(comic_file_path, optimize: true) do
1✔
20
        :ok = File.mkdir_p!(tmp_dir)
1✔
21
        :ok = File.mkdir_p!(result_directory)
1✔
22

23
        image_dir_name = "0_images#{System.monotonic_time()}"
1✔
24
        images_dir = Path.join(tmp_dir, image_dir_name)
1✔
25
        :ok = File.mkdir_p!(images_dir)
1✔
26

27
        padding = String.length("#{page_count}")
1✔
28

29
        1..page_count
30
        |> Task.async_stream(
31
          &extract_page(&1, entries, padding, images_dir),
4✔
32
          max_concurrency: 8,
33
          timeout: max(300_000, page_count * 2_000)
34
        )
35
        |> Stream.run()
1✔
36

37
        optimized_name = "#{comic_name}_optimized.cbz"
×
38

39
        zip(tmp_dir, image_dir_name, optimized_name, result_directory)
×
40
      end
41
    end
42
  end
43

44
  defp extract_page(page_idx, entries, padding, images_dir) do
45
    %{file_name: file_name, stream_fun: stream_func} = Enum.at(entries, page_idx - 1)
4✔
46
    ext = Path.extname(file_name)
4✔
47
    clean_name = "#{String.pad_leading("#{page_idx + 1}", padding, "0")}#{ext}"
4✔
48
    file_path = Path.join(images_dir, clean_name)
4✔
49
    data = stream_func.() |> Enum.to_list() |> :binary.list_to_bin()
4✔
NEW
50
    File.write!(file_path, data)
×
51
  end
52

53
  def basenji_comment?(comic_file_path) do
54
    {:ok, %{format: format}} = Reader.info(comic_file_path)
1✔
55

56
    if format == :cbz do
1✔
57
      Reader.exec("zipinfo", ["-z", comic_file_path])
58
      |> case do
×
59
        {:ok, output} ->
60
          String.contains?(output, "Optimized by Basenji")
×
61

62
        _ ->
×
63
          false
64
      end
65
    else
66
      false
67
    end
68
  end
69

70
  defp zip(parent_dir, images_dir_name, zip_file_name, result_directory) do
71
    flags = [
×
72
      # Recursive
73
      "-r",
74
      # Maximum compression level
75
      "-9",
76
      # Quiet
77
      "-q"
78
    ]
79

80
    response =
×
81
      System.cmd("zip", flags ++ [zip_file_name, images_dir_name], cd: parent_dir)
82
      |> case do
×
83
        {_, 0} ->
84
          comment = "Optimized by Basenji v#{Application.spec(:basenji, :vsn)} on #{DateTime.utc_now()}"
×
85
          System.cmd("sh", ["-c", "echo '#{comment}' | zip -z #{zip_file_name}"], cd: parent_dir)
×
86
          zip_file = Path.join(parent_dir, zip_file_name)
×
87
          final_place = Path.join(result_directory, zip_file_name)
×
88
          :ok = File.cp!(zip_file, final_place)
×
89
          :ok = File.rm!(zip_file)
×
90
          {:ok, final_place}
91

92
        {error_output, exit_code} ->
×
93
          {:error, {exit_code, error_output}}
94
      end
95

96
    File.rm_rf!(Path.join(parent_dir, images_dir_name))
×
97
    response
×
98
  end
99
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc