Files
redux-scraper/app/models/domain/post_file_thumbnail.rb
2025-03-03 23:28:05 +00:00

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