103 lines
2.9 KiB
Ruby
103 lines
2.9 KiB
Ruby
# typed: strict
|
|
class Domain::PostFileThumbnail < ReduxApplicationRecord
|
|
include AttrJsonRecordAliases
|
|
self.table_name = "domain_post_file_thumbnails"
|
|
|
|
belongs_to :post_file,
|
|
foreign_key: :post_file_id,
|
|
class_name: "::Domain::PostFile",
|
|
inverse_of: :thumbnails
|
|
|
|
has_many :perceptual_hashes,
|
|
class_name: "::Domain::PerceptualHash",
|
|
foreign_key: :thumbnail_id,
|
|
dependent: :destroy
|
|
|
|
validates :thumbnail_type,
|
|
presence: true,
|
|
uniqueness: {
|
|
scope: :post_file_id,
|
|
},
|
|
inclusion: {
|
|
in: Domain::ThumbnailType.values.map(&:name),
|
|
}
|
|
|
|
TMP_DIR = T.let(File.join(BlobFile::ROOT_DIR, "tmp-files"), String)
|
|
|
|
THUMBNAIL_ROOT_DIR =
|
|
T.let(File.join(BlobFile::ROOT_DIR, "post_file_thumbnails"), String)
|
|
|
|
THUMBNAIL_CONTENT_TYPES =
|
|
T.let(
|
|
[
|
|
%r{image/jpeg},
|
|
%r{image/jpg},
|
|
%r{image/png},
|
|
%r{image/gif},
|
|
%r{image/webp},
|
|
],
|
|
T::Array[Regexp],
|
|
)
|
|
|
|
sig { returns(T.nilable(String)) }
|
|
def absolute_file_path
|
|
return nil unless thumbnail_type = self.thumbnail_type
|
|
return nil unless post_file_id = self.post_file&.id
|
|
return nil unless sha256 = self.post_file&.blob_sha256
|
|
sha256_hex = HexUtil.bin2hex(sha256)
|
|
path_segments = [
|
|
THUMBNAIL_ROOT_DIR,
|
|
thumbnail_type,
|
|
*BlobFile.path_segments([2, 2, 1], sha256_hex),
|
|
]
|
|
path_segments[-1] = "#{path_segments[-1]}.jpeg"
|
|
path_segments.join("/")
|
|
end
|
|
|
|
sig do
|
|
params(
|
|
post_file: Domain::PostFile,
|
|
thumbnail_type: Domain::ThumbnailType,
|
|
).returns(T.nilable(Domain::PostFileThumbnail))
|
|
end
|
|
def self.find_or_create_from_post_file(post_file, thumbnail_type)
|
|
if t = find_by(post_file: post_file, thumbnail_type: thumbnail_type.name)
|
|
return t
|
|
end
|
|
return nil unless post_file.state_ok?
|
|
return nil unless log_entry = post_file.log_entry
|
|
unless THUMBNAIL_CONTENT_TYPES.any? { |regex|
|
|
regex.match?(log_entry.content_type)
|
|
}
|
|
return nil
|
|
end
|
|
|
|
file_path = post_file.blob&.absolute_file_path
|
|
return nil unless file_path
|
|
|
|
thumbnail =
|
|
Domain::PostFileThumbnail.new(
|
|
post_file: post_file,
|
|
thumbnail_type: thumbnail_type.name,
|
|
)
|
|
|
|
thumbnail_path = thumbnail.absolute_file_path.to_s
|
|
unless File.exist?(thumbnail_path)
|
|
FileUtils.mkdir_p(File.dirname(thumbnail_path))
|
|
tmp_file_path = File.join(TMP_DIR, "thumbnail-#{SecureRandom.uuid}.jpeg")
|
|
image_data =
|
|
Vips::Image.thumbnail(
|
|
file_path,
|
|
thumbnail_type.width,
|
|
height: thumbnail_type.height,
|
|
size: :force,
|
|
)
|
|
image_data.jpegsave(tmp_file_path, Q: thumbnail_type.quality, strip: true)
|
|
FileUtils.mv(tmp_file_path, thumbnail_path)
|
|
end
|
|
|
|
thumbnail.save!
|
|
thumbnail
|
|
end
|
|
end
|