|
| 1 | +require "yaml" |
| 2 | + |
| 3 | +require "./entry" |
| 4 | + |
| 5 | +class DirEntry < Entry |
| 6 | + include YAML::Serializable |
| 7 | + |
| 8 | + getter dir_path : String |
| 9 | + |
| 10 | + @[YAML::Field(ignore: true)] |
| 11 | + @sorted_files : Array(String)? |
| 12 | + |
| 13 | + @signature : String |
| 14 | + |
| 15 | + def initialize(@dir_path, @book) |
| 16 | + storage = Storage.default |
| 17 | + @path = @dir_path |
| 18 | + @encoded_path = URI.encode @dir_path |
| 19 | + @title = File.basename @dir_path |
| 20 | + @encoded_title = URI.encode @title |
| 21 | + |
| 22 | + unless File.readable? @dir_path |
| 23 | + @err_msg = "Directory #{@dir_path} is not readable." |
| 24 | + Logger.warn "#{@err_msg} Please make sure the " \ |
| 25 | + "file permission is configured correctly." |
| 26 | + return |
| 27 | + end |
| 28 | + |
| 29 | + unless DirEntry.is_valid? @dir_path |
| 30 | + @err_msg = "Directory #{@dir_path} is not valid directory entry." |
| 31 | + Logger.warn "#{@err_msg} Please make sure the " \ |
| 32 | + "directory has valid images." |
| 33 | + return |
| 34 | + end |
| 35 | + |
| 36 | + size_sum = 0 |
| 37 | + sorted_files.each do |file_path| |
| 38 | + size_sum += File.size file_path |
| 39 | + end |
| 40 | + @size = size_sum.humanize_bytes |
| 41 | + |
| 42 | + @signature = Dir.directory_entry_signature @dir_path |
| 43 | + id = storage.get_entry_id @dir_path, @signature |
| 44 | + if id.nil? |
| 45 | + id = random_str |
| 46 | + storage.insert_entry_id({ |
| 47 | + path: @dir_path, |
| 48 | + id: id, |
| 49 | + signature: @signature, |
| 50 | + }) |
| 51 | + end |
| 52 | + @id = id |
| 53 | + |
| 54 | + @mtime = sorted_files.map do |file_path| |
| 55 | + File.info(file_path).modification_time |
| 56 | + end.max |
| 57 | + @pages = sorted_files.size |
| 58 | + end |
| 59 | + |
| 60 | + def read_page(page_num) |
| 61 | + img = nil |
| 62 | + begin |
| 63 | + files = sorted_files |
| 64 | + file_path = files[page_num - 1] |
| 65 | + data = File.read(file_path).to_slice |
| 66 | + if data |
| 67 | + img = Image.new data, MIME.from_filename(file_path), |
| 68 | + File.basename(file_path), data.size |
| 69 | + end |
| 70 | + rescue e |
| 71 | + Logger.warn "Unable to read page #{page_num} of #{@dir_path}. Error: #{e}" |
| 72 | + end |
| 73 | + img |
| 74 | + end |
| 75 | + |
| 76 | + def page_dimensions |
| 77 | + sizes = [] of Hash(String, Int32) |
| 78 | + sorted_files.each_with_index do |path, i| |
| 79 | + data = File.read(path).to_slice |
| 80 | + begin |
| 81 | + data.not_nil! |
| 82 | + size = ImageSize.get data |
| 83 | + sizes << { |
| 84 | + "width" => size.width, |
| 85 | + "height" => size.height, |
| 86 | + } |
| 87 | + rescue e |
| 88 | + Logger.warn "Failed to read page #{i} of entry #{@dir_path}. #{e}" |
| 89 | + sizes << {"width" => 1000_i32, "height" => 1000_i32} |
| 90 | + end |
| 91 | + end |
| 92 | + sizes |
| 93 | + end |
| 94 | + |
| 95 | + def examine : Bool |
| 96 | + existence = File.exists? @dir_path |
| 97 | + return false unless existence |
| 98 | + files = DirEntry.image_files @dir_path |
| 99 | + signature = Dir.directory_entry_signature @dir_path |
| 100 | + existence = files.size > 0 && @signature == signature |
| 101 | + @sorted_files = nil unless existence |
| 102 | + |
| 103 | + # For more efficient, update a directory entry with new property |
| 104 | + # and return true like Title.examine |
| 105 | + existence |
| 106 | + end |
| 107 | + |
| 108 | + def sorted_files |
| 109 | + cached_sorted_files = @sorted_files |
| 110 | + return cached_sorted_files if cached_sorted_files |
| 111 | + @sorted_files = DirEntry.sorted_image_files @dir_path |
| 112 | + @sorted_files.not_nil! |
| 113 | + end |
| 114 | + |
| 115 | + def self.image_files(dir_path) |
| 116 | + Dir.entries(dir_path) |
| 117 | + .reject(&.starts_with? ".") |
| 118 | + .map { |fn| File.join dir_path, fn } |
| 119 | + .select { |fn| is_supported_image_file fn } |
| 120 | + .reject { |fn| File.directory? fn } |
| 121 | + .select { |fn| File.readable? fn } |
| 122 | + end |
| 123 | + |
| 124 | + def self.sorted_image_files(dir_path) |
| 125 | + self.image_files(dir_path) |
| 126 | + .sort { |a, b| compare_numerically a, b } |
| 127 | + end |
| 128 | + |
| 129 | + def self.is_valid?(path : String) : Bool |
| 130 | + image_files(path).size > 0 |
| 131 | + end |
| 132 | +end |
0 commit comments