Files
danbooru/app/models/upload.rb
evazion 04d242c60c uploads: save filename, image URL, page URL for uploads.
* Save the filename for files uploaded from disk. This could be used in
  the future to extract source data if the filename is from a known site.

* Save both the image URL and the page URL for files uploaded from
  source. This is needed for multi-file uploads. The image URL is the
  URL of the file actually downloaded from the source. This can be
  different from the URL given by the user, if the user tried to upload
  a sample URL and we automatically changed it to the original URL. The
  page URL is the URL of the page containing the image. We don't always
  know this, for example if someone uploads a Twitter image without the
  bookmarklet, then we can't find the page URL.

* Add a fix script to backfill URLs for existing uploads. For file
  uploads, the filename will be set to "unknown.jpg". For source
  uploads, we fetch the source data again to get the image and page
  URLs. This may fail for uploads that have been deleted from the
  source since uploading.
2022-02-12 15:22:41 -06:00

122 lines
3.4 KiB
Ruby

# frozen_string_literal: true
class Upload < ApplicationRecord
extend Memoist
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
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
def source_strategy
return nil if source.blank?
Sources::Strategies.find(source, referer_url)
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}")
update!(upload_media_assets: [upload_media_asset], status: "completed", media_asset_count: 1)
elsif source.present?
strategy = Sources::Strategies.find(source, referer_url)
image_url = strategy.image_url
page_url = strategy.page_url
media_file = strategy.download_file!(strategy.image_url)
media_asset = MediaAsset.upload!(media_file)
upload_media_asset = UploadMediaAsset.new(media_asset: media_asset, source_url: image_url, page_url: page_url)
update!(upload_media_assets: [upload_media_asset], status: "completed", media_asset_count: 1)
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
memoize :source_strategy
end