Implement DirectoryEntry

This commit is contained in:
Leeingnyo 2022-05-15 14:00:18 +09:00
parent 10587f48cb
commit 55ccd928a2
2 changed files with 161 additions and 1 deletions

View File

@ -337,3 +337,145 @@ class ZippedEntry < Entry
File.exists? @zip_path
end
end
class DirectoryEntry < Entry
include YAML::Serializable
getter dir_path : String
@[YAML::Field(ignore: true)]
@sorted_files : Array(String)?
@signature : String
def initialize(@dir_path, @book)
storage = Storage.default
@encoded_path = URI.encode @dir_path
@title = File.basename @dir_path
@encoded_title = URI.encode @title
unless File.readable? @dir_path
@err_msg = "Directory #{@dir_path} is not readable."
Logger.warn "#{@err_msg} Please make sure the " \
"file permission is configured correctly."
return
end
unless DirectoryEntry.validate_directory_entry @dir_path
@err_msg = "Directory #{@dir_path} is not valid directory entry."
Logger.warn "#{@err_msg} Please make sure the " \
"directory has valid images."
return
end
size_sum = 0
sorted_files.each do |file_path|
size_sum += File.size file_path
end
@size = size_sum.humanize_bytes
@signature = Dir.directory_entry_signature @dir_path
id = storage.get_entry_id @dir_path, @signature
if id.nil?
id = random_str
storage.insert_entry_id({
path: @dir_path,
id: id,
signature: @signature,
})
end
@id = id
mtimes = sorted_files.map { |file_path| File.info(file_path).modification_time }
@mtime = mtimes.max
@pages = sorted_files.size
end
def path : String
@dir_path
end
def createtime : Time
ctime @dir_path
end
def read_page(page_num)
img = nil
begin
files = sorted_files
file_path = files[page_num - 1]
data = File.read(file_path).to_slice
if data
img = Image.new data, MIME.from_filename(file_path),
File.basename(file_path), data.size
end
rescue e
Logger.warn "Unable to read page #{page_num} of #{@dir_path}. Error: #{e}"
end
img
end
def page_dimensions
sizes = [] of Hash(String, Int32)
sorted_files.each_with_index do |path, i|
data = File.read(path).to_slice
begin
data.not_nil!
size = ImageSize.get data
sizes << {
"width" => size.width,
"height" => size.height,
}
rescue e
Logger.warn "Failed to read page #{i} of entry #{@dir_path}. #{e}"
sizes << {"width" => 1000_i32, "height" => 1000_i32}
end
end
sizes
end
def exists? : Bool
existence = File.exists? @dir_path
return false unless existence
files = DirectoryEntry.get_valid_files @dir_path
signature = Dir.directory_entry_signature @dir_path
existence = files.size > 0 && @signature == signature
@sorted_files = nil unless existence
# For more efficient,
# Fix a directory instance with new property
# and return true
existence
end
def sorted_files
cached_sorted_files = @sorted_files
return cached_sorted_files if cached_sorted_files
@sorted_files = DirectoryEntry.get_valid_files_sorted @dir_path
@sorted_files.not_nil!
end
def self.validate_directory_entry(dir_path)
files = DirectoryEntry.get_valid_files dir_path
files.size > 0
end
def self.get_valid_files(dir_path)
files = [] of String
Dir.entries(dir_path).each do |fn|
next if fn.starts_with? "."
path = File.join dir_path, fn
next unless is_supported_image_file path
next if File.directory? path
next unless File.readable? path
files << path
end
files
end
def self.get_valid_files_sorted(dir_path)
files = DirectoryEntry.get_valid_files dir_path
files.sort! { |a, b| compare_numerically a, b }
end
end

View File

@ -19,7 +19,7 @@ class File
# information as long as the above changes do not happen together with
# a file/folder rename, with no library scan in between.
def self.signature(filename) : UInt64
if is_supported_file filename
if is_supported_file(filename) || is_supported_image_file(filename)
File.info(filename).inode
else
0u64
@ -64,6 +64,9 @@ class Dir
path = File.join dirname, fn
if File.directory? path
signatures << Dir.contents_signature path, cache
if DirectoryEntry.validate_directory_entry path
signatures << Dir.directory_entry_signature path, cache
end
else
# Only add its signature value to `signatures` when it is a
# supported file
@ -76,4 +79,19 @@ class Dir
cache[dirname] = hash
hash
end
def self.directory_entry_signature(dirname, cache = {} of String => String)
return cache[dirname + "?entry"] if cache[dirname + "?entry"]?
Fiber.yield
signatures = [] of String
image_files = DirectoryEntry.get_valid_files_sorted dirname
if image_files.size > 0
image_files.each do |path|
signatures << File.signature(path).to_s
end
end
hash = Digest::SHA1.hexdigest(signatures.join)
cache[dirname + "?entry"] = hash
hash
end
end