Files
manyfold/app/uploaders/application_uploader.rb

149 lines
4.1 KiB
Ruby

require "shrine/storage/file_system"
require "shrine/storage/s3"
require "shrine/storage/tus"
require "image_processing/mini_magick"
class ApplicationUploader < Shrine
plugin :activerecord
plugin :add_metadata
plugin :refresh_metadata
plugin :metadata_attributes, size: "size"
plugin :restore_cached_data
plugin :keep_files
plugin :determine_mime_type, analyzer: ->(io, analyzers) do
(
Mime::Type.lookup_by_extension(File.extname(io.try(:metadata)&.fetch("filename", "") || "").tr(".", "")&.downcase) ||
analyzers[:marcel].call(io, filename_fallback: true)
).to_s
end
plugin :rack_response
plugin :dynamic_storage
plugin :tus
plugin :remote_url, max_size: SiteSettings.max_file_upload_size
plugin :infer_extension
plugin :derivatives, create_on_promote: true
plugin :remove_attachment
self.storages = {
cache: Shrine::Storage::FileSystem.new("tmp/shrine"),
downloads: Shrine::Storage::FileSystem.new("tmp/downloads")
}
F3D_OPTS = {
"ambient-occlusion" => "1",
"anti-aliasing" => "true",
"axis" => "0",
"background-color" => "0,0,0",
"filename" => "0",
"grid" => "1",
"grid-color" => "0,255,255",
"grid-subdivisions" => 0,
"grid-unit" => "10",
"no-config" => "1",
"output" => "-",
"resolution" => "512,512",
"tone-mapping" => "1",
"translucency-support" => "1"
}.freeze
CAMERA_OPTS = {
"+z" => "-1,1,-0.5",
"+y" => "-1,-0.5,-1"
}
storage(/library_(\d+)/) do |m|
Library.find(m[1]).storage # rubocop:disable Pundit/UsePolicyScope
rescue ActiveRecord::RecordNotFound
nil
end
class Attacher
def store_key
Library.default.storage_key
end
end
def generate_location(io, record: nil, derivative: nil, metadata: {}, **)
".manyfold/#{super}"
end
add_metadata :ctime do |io|
Shrine.with_file(io) { |it| [it.mtime, it.ctime].compact.min }
rescue NoMethodError
end
add_metadata :mtime do |io|
Shrine.with_file(io) { |it| it.mtime }
rescue NoMethodError
end
add_metadata :remote_etag do |io|
io.meta["etag"]
rescue NoMethodError
end
add_metadata :remote_last_modified do |io|
io.meta["last-modified"]
rescue NoMethodError
end
add_metadata :object do |io|
Shrine.with_file(io) do |it|
scene = Assimp.import_file(it.path)
scene.apply_post_processing(
0x80000000 # GenBoundingBox step, currently missing from assimp-ffi
)
bboxes = scene.meshes.map(&:aabb)
{
"bounding_box" => {
"minimum" => {
"x" => bboxes.map { |it| it.min.x }.min,
"y" => bboxes.map { |it| it.min.y }.min,
"z" => bboxes.map { |it| it.min.z }.min
},
"maximum" => {
"x" => bboxes.map { |it| it.max.x }.max,
"y" => bboxes.map { |it| it.max.y }.max,
"z" => bboxes.map { |it| it.max.z }.max
}
}
}
rescue => ex
# Assimp doesn't raise a specific error for failed load,
# just throws a string, so we have to catch all and absorb
Rails.logger.debug { "Load error: '#{ex.message}'" }
nil
end
end
Attacher.derivatives do |original|
if SiteSettings.generate_image_derivatives && context[:record]&.is_image?
Shrine.with_file(original) do |it|
magick = ImageProcessing::MiniMagick.source(it)
{
preview: magick.resize_to_limit!(320, 320),
carousel: magick.resize_to_limit!(1024, 768)
}
end
elsif SiteSettings.generate_model_renders && SupportedMimeTypes.can_render?(context[:record].mime_type)
Shrine.with_file(original) do |it|
up = context[:record]&.up_direction
options = F3D_OPTS.merge(
"up" => up,
"camera-direction" => CAMERA_OPTS[up]
).map { |k, v| "--#{k}=#{v}" }
output, _err = Open3.capture3("f3d", it.path, *options)
{
render: (output.length > 0) ? StringIO.new(output) : nil
}.compact
end
else
{}
end
rescue => ex
Rails.logger.warn "Error in derivative generation for #{context[:record].to_param}: #{ex.message}"
{}
end
end