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.
This commit is contained in:
evazion
2022-02-10 14:03:13 -06:00
parent 5b2fcaaafc
commit 02edb52569
5 changed files with 45 additions and 8 deletions

View File

@@ -96,12 +96,12 @@ export default class FileUploadComponent {
this.$component.find("progress").removeClass("hidden");
this.$component.find("input").attr("disabled", "disabled");
while (upload.status === "pending" || upload.status === "processing") {
while (upload.media_asset_count <= 1 && upload.status !== "completed" && upload.status !== "error") {
await Utility.delay(500);
upload = await $.get(`/uploads/${upload.id}.json`);
}
if (upload.status === "completed") {
if (upload.media_asset_count > 0) {
let params = new URLSearchParams(window.location.search);
let isBookmarklet = params.has("url");
params.delete("url");

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
class ProcessUploadMediaAssetJob < ApplicationJob
queue_with_priority -1
def perform(upload_media_asset)
upload_media_asset.process_upload!
end
end

View File

@@ -205,10 +205,11 @@ class MediaAsset < ApplicationRecord
#
# This can't be called inside a transaction because the transaction will
# fail if there's a RecordNotUnique error when the asset already exists.
def upload!(media_file)
def upload!(media_file, &block)
raise Error, "File is corrupt" if media_file.is_corrupt?
media_asset = create!(file: media_file, status: :processing)
yield media_asset if block_given?
media_asset.distribute_files!(media_file)
media_asset.update!(status: :active)
media_asset
@@ -218,6 +219,7 @@ class MediaAsset < ApplicationRecord
raise if e.is_a?(ActiveRecord::RecordInvalid) && !e.record.errors.of_kind?(:md5, :taken)
media_asset = find_by!(md5: media_file.md5, status: [:processing, :active])
yield media_asset if block_given?
# XXX If the asset is still being processed by another thread, wait up
# to 30 seconds for it to finish.

View File

@@ -95,14 +95,13 @@ class Upload < ApplicationRecord
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, status: "active")
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_asset], status: "completed", media_asset_count: 1)
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

View File

@@ -6,6 +6,9 @@ class UploadMediaAsset < ApplicationRecord
belongs_to :upload
belongs_to :media_asset, optional: true
after_create :async_process_upload!
after_save :update_upload_status, if: :saved_change_to_status?
enum status: {
pending: 0,
processing: 100,
@@ -35,5 +38,29 @@ class UploadMediaAsset < ApplicationRecord
Sources::Strategies.find(source_url, page_url)
end
def async_process_upload!
ProcessUploadMediaAssetJob.perform_later(self)
end
def process_upload!
update!(status: :processing)
strategy = Sources::Strategies.find(source_url)
media_file = strategy.download_file!(source_url)
MediaAsset.upload!(media_file) do |media_asset|
update!(media_asset: media_asset)
end
update!(status: :active)
rescue Exception => e
update!(status: :failed, error: e.message)
end
def update_upload_status
upload.with_lock do
upload.update!(status: "completed") if upload.upload_media_assets.all?(&:finished?)
end
end
memoize :source_strategy
end