Files
danbooru/app/models/upload.rb
evazion 02edb52569 uploads: enable multi-file uploads when uploading from source.
Make the upload page automatically detect when a source URL has multiple images
and let the user choose which images to post.

For example, when uploading a Twitter or Pixiv post with more than one image, we
direct the user to a page showing a thumbnail for each image and letting
them choose which ones to post.

This is similar to the batch upload page, except we actually download each image
in the background, instead of just hotlinking or proxying the thumbnails through
our servers. This avoids various problems with proxying and makes new features
possible, like showing which images in the batch have already been posted.
2022-02-14 16:13:55 -06:00

116 lines
3.2 KiB
Ruby

# frozen_string_literal: true
class Upload < ApplicationRecord
attr_accessor :file
belongs_to :uploader, class_name: "User"
has_many :upload_media_assets, dependent: :destroy
has_many :media_assets, through: :upload_media_assets
normalize :source, :normalize_source
validates :source, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { source.present? }
validates :referer_url, format: { with: %r{\Ahttps?://}i, message: "is not a valid URL" }, if: -> { referer_url.present? }
validate :validate_file_and_source, on: :create
after_create :async_process_upload!
scope :pending, -> { where(status: "pending") }
scope :completed, -> { where(status: "completed") }
scope :failed, -> { where(status: "error") }
def self.visible(user)
if user.is_admin?
all
else
where(uploader: user)
end
end
concerning :StatusMethods do
def is_pending?
status == "pending"
end
def is_processing?
status == "processing"
end
def is_completed?
status == "completed"
end
def is_errored?
status == "error"
end
def is_finished?
is_completed? || is_errored?
end
end
concerning :ValidationMethods do
def validate_file_and_source
if file.present? && source.present?
errors.add(:base, "Can't give both a file and a source")
elsif file.blank? && source.blank?
errors.add(:base, "No file or source given")
end
end
end
concerning :SourceMethods do
class_methods do
# percent-encode unicode characters in the URL
def normalize_source(url)
return nil if url.nil?
Addressable::URI.normalized_encode(url)
end
end
end
def self.search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :source, :referer_url, :status, :media_asset_count, :uploader, :upload_media_assets, :media_assets)
q.apply_default_order(params)
end
def async_process_upload!
if file.present?
ProcessUploadJob.perform_now(self)
elsif source.present?
ProcessUploadJob.perform_later(self)
else
raise "No file or source given" # Should never happen
end
end
def process_upload!
update!(status: "processing")
if file.present?
media_file = MediaFile.open(file.tempfile)
media_asset = MediaAsset.upload!(media_file)
upload_media_asset = UploadMediaAsset.new(media_asset: media_asset, source_url: "file://#{file.original_filename}", status: "active")
update!(upload_media_assets: [upload_media_asset], status: "completed", media_asset_count: 1)
elsif source.present?
strategy = Sources::Strategies.find(source, referer_url)
page_url = strategy.page_url
upload_media_assets = strategy.image_urls.map do |image_url|
UploadMediaAsset.new(source_url: image_url, page_url: page_url, media_asset: nil)
end
update!(upload_media_assets: upload_media_assets, media_asset_count: upload_media_assets.size)
else
raise "No file or source given" # Should never happen
end
rescue Exception => e
update!(status: "error", error: e.message)
end
def self.available_includes
[:uploader, :upload_media_assets, :media_assets]
end
end