uploads: merge tags when a duplicate is uploaded (fix #3130).
Automatically merge tags when uploading a duplicate. There are two cases: * You try to upload an image, but it's already on Danbooru. In this case you'll be immediately redirected to the original post, before you can start tagging the upload. * You're uploading an image, it wasn't a dupe when you first opened the upload page, but you got sniped while tagging it. In this case your tags will be merged with the original post, and you will be redirected to the original post. There are a few corner cases: * If you don't have permission to edit the original post, for example because it's banned or has a censored tag, then your tags won't be merged and will be silently ignored. * Only the tags, rating, and parent ID will be merged. The source and artist commentary won't be merged. This is so that if an artist uploads the exact same file to multiple sites, the new source won't override the original source. * Some tags might be contradictory. For example, the new post might be tagged translation_request, but the original post might already be translated. It's up to the user to fix these things afterwards.
This commit is contained in:
@@ -67,12 +67,17 @@ class PostsController < ApplicationController
|
||||
@post = authorize Post.new_from_upload(permitted_attributes(Post))
|
||||
@post.save
|
||||
|
||||
if @post.errors.any?
|
||||
if @post.errors.none?
|
||||
respond_with(@post)
|
||||
elsif @post.errors.of_kind?(:md5, :taken)
|
||||
@original_post = Post.find_by!(md5: @post.md5)
|
||||
@original_post.update(rating: @post.rating, parent_id: @post.parent_id, tag_string: "#{@original_post.tag_string} #{@post.tag_string}")
|
||||
flash[:notice] = "Duplicate of post ##{@original_post.id}; merging tags"
|
||||
redirect_to @original_post
|
||||
else
|
||||
@upload = UploadMediaAsset.find(params[:post][:upload_media_asset_id]).upload
|
||||
flash[:notice] = @post.errors.full_messages.join("; ")
|
||||
respond_with(@post, render: { template: "uploads/show" })
|
||||
else
|
||||
respond_with(@post)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ class UploadsController < ApplicationController
|
||||
def show
|
||||
@upload = authorize Upload.find(params[:id])
|
||||
@post = Post.new(uploader: @upload.uploader, uploader_ip_addr: @upload.uploader_ip_addr, source: @upload.source, rating: nil, **permitted_attributes(Post))
|
||||
respond_with(@upload)
|
||||
|
||||
if request.format.html? && @upload.is_completed? && @upload.media_assets.first&.post.present?
|
||||
flash[:notice] = "Duplicate of post ##{@upload.media_assets.first.post.id}"
|
||||
redirect_to @upload.media_assets.first.post
|
||||
else
|
||||
respond_with(@upload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@ class MediaAsset < ApplicationRecord
|
||||
LARGE_IMAGE_WIDTH = Danbooru.config.large_image_width
|
||||
STORAGE_SERVICE = Danbooru.config.storage_manager
|
||||
|
||||
has_one :post, foreign_key: :md5, primary_key: :md5
|
||||
has_one :media_metadata, dependent: :destroy
|
||||
has_one :pixiv_ugoira_frame_data, class_name: "PixivUgoiraFrameData", foreign_key: :md5, primary_key: :md5
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal(expected, response.parsed_body.css("link[rel=canonical]").attribute("href").value)
|
||||
end
|
||||
|
||||
def create_post!(user: create(:user), rating: "q", tag_string: "tagme", **params)
|
||||
def create_post!(user: create(:user), media_asset: build(:media_asset), rating: "q", tag_string: "tagme", **params)
|
||||
upload = build(:upload, uploader: user)
|
||||
asset = create(:upload_media_asset, upload: upload)
|
||||
asset = create(:upload_media_asset, upload: upload, media_asset: media_asset)
|
||||
post_auth posts_path, user, params: { post: { upload_media_asset_id: asset.id, rating: rating, tag_string: tag_string, **params }}
|
||||
|
||||
Post.last
|
||||
@@ -655,6 +655,21 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal("test", @post.tag_string)
|
||||
end
|
||||
|
||||
should "re-render the upload page if the upload fails" do
|
||||
@post = create_post!(rating: "z", tag_string: "tagme")
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "merge the tags and redirect to the original post if the upload is a duplicate of an existing post" do
|
||||
media_asset = create(:media_asset)
|
||||
post1 = create_post!(rating: "s", tag_string: "post1", media_asset: media_asset)
|
||||
post2 = create_post!(rating: "e", tag_string: "post2", media_asset: media_asset)
|
||||
|
||||
assert_redirected_to post1
|
||||
assert_equal("post1 post2", post1.reload.tag_string)
|
||||
assert_equal("e", post1.rating)
|
||||
end
|
||||
|
||||
should "apply the rating from the tags" do
|
||||
@post = create_post!(rating: nil, tag_string: "rating:s")
|
||||
|
||||
|
||||
@@ -130,6 +130,14 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
should "redirect a completed upload to the original post if it's a duplicate of an existing post" do
|
||||
@upload = create(:completed_file_upload, uploader: @user)
|
||||
@post = create(:post, md5: @upload.media_assets.first.md5, media_asset: @upload.media_assets.first)
|
||||
get_auth upload_path(@upload), @user
|
||||
|
||||
assert_redirected_to @post
|
||||
end
|
||||
end
|
||||
|
||||
context "create action" do
|
||||
|
||||
@@ -311,27 +311,6 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
CurrentUser.ip_addr = nil
|
||||
end
|
||||
|
||||
context "with a preprocessed predecessor" do
|
||||
setup do
|
||||
@predecessor = FactoryBot.create(:source_upload, status: "preprocessed", source: @source, image_height: 0, image_width: 0, file_size: 1, md5: 'd34e4cf0a437a5d65f8e82b7bcd02606', file_ext: "jpg")
|
||||
@tags = 'hello world'
|
||||
end
|
||||
|
||||
context "when the file has already been uploaded" do
|
||||
setup do
|
||||
@asset = MediaAsset.find_by_md5("d34e4cf0a437a5d65f8e82b7bcd02606")
|
||||
@post = create(:post, md5: "d34e4cf0a437a5d65f8e82b7bcd02606", media_asset: @asset)
|
||||
@service = subject.new(source: @source)
|
||||
end
|
||||
|
||||
should "point to the dup post in the upload" do
|
||||
@upload = subject.new(source: @source, tag_string: @tags).start!
|
||||
@predecessor.reload
|
||||
assert_equal("error: ActiveRecord::RecordInvalid - Validation failed: Md5 duplicate: #{@post.id}", @predecessor.status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with a source containing unicode characters" do
|
||||
should "normalize unicode characters in the source field" do
|
||||
source1 = "poke\u0301mon" # pokémon (nfd form)
|
||||
|
||||
Reference in New Issue
Block a user