forked from e621ng/e621ng
add image cropping support
This commit is contained in:
parent
df73c682db
commit
64446d49e1
2
Procfile
2
Procfile
@ -1,2 +1,2 @@
|
||||
unicorn: bundle exec rails server
|
||||
jobs: bundle exec script/delayed_job run
|
||||
jobs: bundle exec rake jobs:work
|
||||
|
@ -1,6 +1,7 @@
|
||||
$(function() {
|
||||
$("#maintoggle").click(function() {
|
||||
$('#nav').toggle();
|
||||
$('#maintoggle').toggleClass('toggler-active');
|
||||
$('#maintoggle-on').toggle();
|
||||
$('#maintoggle-off').toggle();
|
||||
});
|
||||
});
|
||||
|
@ -21,6 +21,16 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
div#page aside#sidebar {
|
||||
input#tags {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
width: 20%;
|
||||
}
|
||||
}
|
||||
|
||||
div#page {
|
||||
padding: 0 0.25vw;
|
||||
> div /* div#c-$controller */ {
|
||||
@ -40,12 +50,12 @@
|
||||
}
|
||||
|
||||
#maintoggle {
|
||||
display: inline;
|
||||
background-color: lighten($link_color, 13%);
|
||||
padding: 0.8em 1.2em 0.8em 1.2em;
|
||||
border-radius: 20% 20% 0 0;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 3vw;
|
||||
right: 4vw;
|
||||
font-size: 2em;
|
||||
|
||||
&.toggler-active {
|
||||
background-color: lighten($link_color, 25%);
|
||||
@ -128,8 +138,8 @@
|
||||
|
||||
article.post-preview {
|
||||
margin: 0.5vw;
|
||||
width: 48.5vw;
|
||||
height: 48.5vw;
|
||||
width: 32vw;
|
||||
height: 32vw;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
@ -139,10 +149,10 @@
|
||||
//display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
img {
|
||||
img.cropped {
|
||||
//object-fit: contain;
|
||||
//width: 48.5vw;
|
||||
//height: 48.5vw;
|
||||
width: 32vw;
|
||||
height: 32vw;
|
||||
margin: 0 auto;
|
||||
border: none !important;
|
||||
}
|
||||
@ -190,6 +200,8 @@
|
||||
}
|
||||
|
||||
#nav {
|
||||
font-size: 2em;
|
||||
line-height: 2em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -210,7 +222,6 @@
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: 2px solid lighten($link_color, 13%);
|
||||
text-align: center;
|
||||
line-height: 2em;
|
||||
h1 {
|
||||
|
@ -5,6 +5,7 @@ module DanbooruImageResizer
|
||||
THUMBNAIL_OPTIONS = { size: :down, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE }
|
||||
# http://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave
|
||||
JPEG_OPTIONS = { background: 255, strip: true, interlace: true, optimize_coding: true }
|
||||
CROP_OPTIONS = { linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, crop: :attention }
|
||||
|
||||
# XXX libvips-8.4 on Debian doesn't support the `Vips::Image.thumbnail` method.
|
||||
# On 8.4 we have to shell out to vipsthumbnail instead. Remove when Debian supports 8.5.
|
||||
@ -16,6 +17,14 @@ module DanbooruImageResizer
|
||||
end
|
||||
end
|
||||
|
||||
def self.crop(file, length, quality = 90)
|
||||
if Vips.at_least_libvips?(8, 5)
|
||||
crop_ruby(file, length, quality)
|
||||
else
|
||||
crop_shell(file, length, quality)
|
||||
end
|
||||
end
|
||||
|
||||
# https://github.com/jcupitt/libvips/wiki/HOWTO----Image-shrinking
|
||||
# http://jcupitt.github.io/libvips/API/current/Using-vipsthumbnail.md.html
|
||||
def self.resize_ruby(file, width, height, resize_quality)
|
||||
@ -26,6 +35,14 @@ module DanbooruImageResizer
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.crop_ruby(file, length, resize_quality)
|
||||
output_file = Tempfile.new
|
||||
resized_image = Vips::Image.thumbnail(file.path, length, height: length, **CROP_OPTIONS)
|
||||
resized_image.jpegsave(output_file.path, Q: resize_quality, **JPEG_OPTIONS)
|
||||
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.resize_shell(file, width, height, quality)
|
||||
output_file = Tempfile.new(["resize", ".jpg"])
|
||||
|
||||
@ -47,4 +64,25 @@ module DanbooruImageResizer
|
||||
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.crop_shell(file, length, quality)
|
||||
output_file = Tempfile.new(["crop", ".jpg"])
|
||||
|
||||
# --size=WxH will upscale if the image is smaller than the target size.
|
||||
# Fix the target size so that it's not bigger than the image.
|
||||
image = Vips::Image.new_from_file(file.path)
|
||||
|
||||
arguments = [
|
||||
file.path,
|
||||
"--eprofile=#{SRGB_PROFILE}",
|
||||
"--crop=none",
|
||||
"--size=#{length}",
|
||||
"--format=#{output_file.path}[Q=#{quality},background=255,strip,interlace,optimize_coding]"
|
||||
]
|
||||
|
||||
success = system("vipsthumbnail", *arguments)
|
||||
raise RuntimeError, "vipsthumbnail failed (exit status: #{$?.exitstatus})" if !success
|
||||
|
||||
output_file
|
||||
end
|
||||
end
|
||||
|
@ -1,12 +0,0 @@
|
||||
class ImageCropper
|
||||
def self.enabled?
|
||||
Danbooru.config.aws_sqs_cropper_url.present?
|
||||
end
|
||||
|
||||
def self.notify(post)
|
||||
if post.is_image?
|
||||
sqs = SqsService.new(Danbooru.config.aws_sqs_cropper_url)
|
||||
sqs.send_message("#{post.id},https://#{Danbooru.config.hostname}/data/#{post.md5}.#{post.file_ext}")
|
||||
end
|
||||
end
|
||||
end
|
@ -64,8 +64,18 @@ class PixivUgoiraConverter
|
||||
output_file
|
||||
end
|
||||
|
||||
def self.generate_crop(ugoira_file)
|
||||
file = Tempfile.new(["ugoira-crop", ".zip"], binmode: true)
|
||||
zipfile = Zip::File.new(ugoira_file.path)
|
||||
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.
|
||||
|
||||
DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
|
||||
ensure
|
||||
file.close!
|
||||
end
|
||||
|
||||
def self.generate_preview(ugoira_file)
|
||||
file = Tempfile.new(binmode: true)
|
||||
file = Tempfile.new(["ugoira-preview", ".zip"], binmode: true)
|
||||
zipfile = Zip::File.new(ugoira_file.path)
|
||||
zipfile.entries.first.extract(file.path) { true } # 'true' means overwrite the existing tempfile.
|
||||
|
||||
|
@ -57,6 +57,8 @@ class StorageManager
|
||||
"#{root_url}/images/download-preview.png"
|
||||
elsif type == :preview
|
||||
"#{base_url}/preview/#{subdir}#{file}"
|
||||
elsif type == :crop
|
||||
"#{base_url}/crop/#{subdir}#{file}"
|
||||
elsif type == :large && post.has_large?
|
||||
"#{base_url}/sample/#{subdir}#{seo_tags}#{file}"
|
||||
else
|
||||
@ -77,6 +79,8 @@ class StorageManager
|
||||
case type
|
||||
when :preview
|
||||
"#{base_dir}/preview/#{subdir}#{file}"
|
||||
when :crop
|
||||
"#{base_dir}/crop/#{subdir}#{file}"
|
||||
when :large
|
||||
"#{base_dir}/sample/#{subdir}#{file}"
|
||||
when :original
|
||||
@ -90,6 +94,8 @@ class StorageManager
|
||||
case type
|
||||
when :preview
|
||||
"#{md5}.jpg"
|
||||
when :crop
|
||||
"#{md5}.jpg"
|
||||
when :large
|
||||
"#{large_image_prefix}#{md5}.#{large_file_ext}"
|
||||
when :original
|
||||
|
@ -5,6 +5,7 @@ class StorageManager::Local < StorageManager
|
||||
temp_path = dest_path + "-" + SecureRandom.uuid + ".tmp"
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(temp_path))
|
||||
io.rewind
|
||||
bytes_copied = IO.copy_stream(io, temp_path)
|
||||
raise Error, "store failed: #{bytes_copied}/#{io.size} bytes copied" if bytes_copied != io.size
|
||||
|
||||
|
@ -130,21 +130,23 @@ class UploadService
|
||||
def self.generate_resizes(file, upload)
|
||||
if upload.is_video?
|
||||
video = FFMPEG::Movie.new(file.path)
|
||||
crop_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
|
||||
preview_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width)
|
||||
|
||||
elsif upload.is_ugoira?
|
||||
preview_file = PixivUgoiraConverter.generate_preview(file)
|
||||
crop_file = PixivUgoiraConverter.generate_crop(file)
|
||||
sample_file = PixivUgoiraConverter.generate_webm(file, upload.context["ugoira"]["frame_data"])
|
||||
|
||||
elsif upload.is_image?
|
||||
preview_file = DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 85)
|
||||
|
||||
crop_file = DanbooruImageResizer.crop(file, Danbooru.config.small_image_width, 85)
|
||||
if upload.image_width > Danbooru.config.large_image_width
|
||||
sample_file = DanbooruImageResizer.resize(file, Danbooru.config.large_image_width, upload.image_height, 90)
|
||||
end
|
||||
end
|
||||
|
||||
[preview_file, sample_file]
|
||||
[preview_file, crop_file, sample_file]
|
||||
end
|
||||
|
||||
def self.generate_video_preview_for(video, width, height)
|
||||
@ -155,7 +157,7 @@ class UploadService
|
||||
width = (height * dimension_ratio).to_i
|
||||
end
|
||||
|
||||
output_file = Tempfile.new(binmode: true)
|
||||
output_file = Tempfile.new(["video-preview", ".jpg"], binmode: true)
|
||||
video.screenshot(output_file.path, {:seek_time => 0, :resolution => "#{width}x#{height}"})
|
||||
output_file
|
||||
end
|
||||
@ -178,14 +180,16 @@ class UploadService
|
||||
|
||||
upload.tag_string = "#{upload.tag_string} #{Utils.automatic_tags(upload, file)}"
|
||||
|
||||
preview_file, sample_file = Utils.generate_resizes(file, upload)
|
||||
preview_file, crop_file, sample_file = Utils.generate_resizes(file, upload)
|
||||
|
||||
begin
|
||||
Utils.distribute_files(file, upload, :original)
|
||||
Utils.distribute_files(sample_file, upload, :large) if sample_file.present?
|
||||
Utils.distribute_files(preview_file, upload, :preview) if preview_file.present?
|
||||
Utils.distribute_files(crop_file, upload, :crop) if crop_file.present?
|
||||
ensure
|
||||
preview_file.try(:close!)
|
||||
crop_file.try(:close!)
|
||||
sample_file.try(:close!)
|
||||
end
|
||||
|
||||
@ -583,13 +587,13 @@ class UploadService
|
||||
)
|
||||
end
|
||||
|
||||
notify_cropper(@post) if ImageCropper.enabled?
|
||||
upload.update(status: "completed", post_id: @post.id)
|
||||
@post
|
||||
end
|
||||
|
||||
def convert_to_post(upload)
|
||||
Post.new.tap do |p|
|
||||
p.has_cropped = true
|
||||
p.tag_string = upload.tag_string
|
||||
p.md5 = upload.md5
|
||||
p.file_ext = upload.file_ext
|
||||
@ -607,8 +611,4 @@ class UploadService
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def notify_cropper(post)
|
||||
# ImageCropper.notify(post)
|
||||
end
|
||||
end
|
||||
|
@ -173,6 +173,10 @@ class Post < ApplicationRecord
|
||||
storage_manager.file_path(md5, file_ext, :preview)
|
||||
end
|
||||
|
||||
def crop_file_url
|
||||
storage_manager.file_url(self, :crop)
|
||||
end
|
||||
|
||||
def open_graph_image_url
|
||||
if is_image?
|
||||
if has_large?
|
||||
|
@ -92,6 +92,8 @@ class User < ApplicationRecord
|
||||
has_many :post_approvals, :dependent => :destroy
|
||||
has_many :post_disapprovals, :dependent => :destroy
|
||||
has_many :post_votes
|
||||
has_many :post_archives
|
||||
has_many :note_versions
|
||||
has_many :bans, -> {order("bans.id desc")}
|
||||
has_one :recent_ban, -> {order("bans.id desc")}, :class_name => "Ban"
|
||||
|
||||
|
@ -34,14 +34,16 @@ class PostPresenter < Presenter
|
||||
end
|
||||
html << %{<a href="#{path}/#{post.id}#{tag_param}">}
|
||||
|
||||
if options[:show_cropped] && post.has_cropped?
|
||||
src = post.cropped_file_url
|
||||
if CurrentUser.id == 1 && options[:show_cropped] && post.has_cropped? && !CurrentUser.user.disable_cropped_thumbnails?
|
||||
src = post.crop_file_url
|
||||
imgClass = "cropped"
|
||||
else
|
||||
src = post.preview_file_url
|
||||
imgClass = nil
|
||||
end
|
||||
|
||||
tooltip = "#{post.tag_string} rating:#{post.rating} score:#{post.score}"
|
||||
html << %{<img itemprop="thumbnailUrl" src="#{src}" title="#{h(tooltip)}" alt="#{h(post.tag_string)}">}
|
||||
html << %{<img class="#{imgClass}" itemprop="thumbnailUrl" src="#{src}" title="#{h(tooltip)}" alt="#{h(post.tag_string)}">}
|
||||
html << %{</a>}
|
||||
|
||||
if options[:pool]
|
||||
@ -71,7 +73,7 @@ class PostPresenter < Presenter
|
||||
|
||||
def self.preview_class(post, description = nil, options = {})
|
||||
klass = "post-preview"
|
||||
klass << " large-cropped" if post.has_cropped? && options[:show_cropped]
|
||||
# klass << " large-cropped" if post.has_cropped? && options[:show_cropped]
|
||||
klass << " pooled" if description
|
||||
klass << " post-status-pending" if post.is_pending?
|
||||
klass << " post-status-flagged" if post.is_flagged?
|
||||
|
@ -12,7 +12,7 @@ module PostSetPresenters
|
||||
end
|
||||
|
||||
posts.each do |post|
|
||||
html << PostPresenter.preview(post, options.merge(:tags => @post_set.tag_string, :raw => @post_set.raw))
|
||||
html << PostPresenter.preview(post, options.merge(:show_cropped => true, :tags => @post_set.tag_string, :raw => @post_set.raw))
|
||||
html << "\n"
|
||||
end
|
||||
|
||||
|
@ -88,7 +88,12 @@
|
||||
<%= render "news_updates/listing" %>
|
||||
|
||||
<h1><%= link_to Danbooru.config.app_name, "/" %></h1>
|
||||
<span id="maintoggle"> ☰ </span> <!-- Maybe icon would be better, I'm not sure -->
|
||||
|
||||
<div id="maintoggle">
|
||||
<a href="#"><i id="maintoggle-on" class="fas fa-bars"></i></a>
|
||||
<a href="#"><i id="maintoggle-off" class="fas fa-times" style="display: none;"></i></a>
|
||||
</div>
|
||||
|
||||
<nav id="nav">
|
||||
<%= render "layouts/main_links" %>
|
||||
<%= yield :secondary_links %>
|
||||
|
@ -33,7 +33,7 @@
|
||||
<li><%= link_to "Wiki", new_wiki_page_path(wiki_page: { title: @post_set.tag_string }), id: "show-excerpt-link" %></li>
|
||||
<% end %>
|
||||
|
||||
<li id="searchbox-redirect-link"><a href="#search-box">Search»</a></li>
|
||||
<li id="searchbox-redirect-link"><a href="#search-box">Search »</a></li>
|
||||
</menu>
|
||||
|
||||
<%= render "posts/partials/index/edit" %>
|
||||
|
@ -13,8 +13,8 @@
|
||||
<% if params[:random] %>
|
||||
<%= hidden_field_tag :random, params[:random] %>
|
||||
<% end %>
|
||||
<%= hidden_field_tag "ms", "1" %>
|
||||
<%= submit_tag "Go", :name => nil, :class => "ui-button ui-widget ui-corner-all tiny gradient" %>
|
||||
<%= hidden_field_tag "ms", "1" %>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
|
@ -80,7 +80,7 @@
|
||||
|
||||
<%= f.input :disable_post_tooltips, :as => :select, :hint => "Disable advanced tooltips when hovering over thumbnails", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
|
||||
|
||||
<%#= f.input :disable_cropped_thumbnails, :as => :select, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
|
||||
<%= f.input :disable_cropped_thumbnails, :as => :select, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
|
||||
|
||||
<div class="input text optional field_with_hint">
|
||||
<label class="text optional" for="user_dmail_filter_attributes_words">Dmail filter</label>
|
||||
|
@ -93,19 +93,20 @@ else
|
||||
user = User.find_by_name("albert")
|
||||
end
|
||||
|
||||
CurrentUser.user = User.admins.first
|
||||
CurrentUser.as_admin
|
||||
|
||||
if Upload.count == 0
|
||||
puts "Creating uploads"
|
||||
1.upto(100) do |i|
|
||||
1.upto(50) do |i|
|
||||
color1 = rand(4096).to_s(16)
|
||||
color2 = rand(4096).to_s(16)
|
||||
width = rand(2000) + 100
|
||||
height = rand(2000) + 100
|
||||
url = "http://ipsumimage.appspot.com/#{width}x#{height}"
|
||||
tags = rand_sentence(12).scan(/[a-z]+/).join(" ")
|
||||
url = "http://ipsumimage.appspot.com/#{width}x#{height},#{color1}"
|
||||
tags = rand(1_000_000_000).to_s.scan(/../).join(" ")
|
||||
|
||||
Upload.create!(:source => url, :content_type => "image/gif", :rating => "q", :tag_string => tags, :server => Socket.gethostname)
|
||||
service = UploadService.new(source: url, tag_string: tags, rating: "s")
|
||||
service.start!
|
||||
end
|
||||
else
|
||||
puts "Skipping uploads"
|
||||
|
@ -4,11 +4,11 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
UGOIRA_CONTEXT = {
|
||||
"ugoira" => {
|
||||
"frame_data" => [
|
||||
{"file" => "000000.jpg", "delay" => 200},
|
||||
{"file" => "000001.jpg", "delay" => 200},
|
||||
{"file" => "000002.jpg", "delay" => 200},
|
||||
{"file" => "000003.jpg", "delay" => 200},
|
||||
{"file" => "000004.jpg", "delay" => 200},
|
||||
{"file" => "000005.jpg", "delay" => 250}
|
||||
{"file" => "000004.jpg", "delay" => 250}
|
||||
],
|
||||
"content_type" => "image/jpeg"
|
||||
}
|
||||
@ -17,6 +17,34 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
context "::Utils" do
|
||||
subject { UploadService::Utils }
|
||||
|
||||
context "#download_from_source" do
|
||||
setup do
|
||||
@jpeg = "https://upload.wikimedia.org/wikipedia/commons/c/c5/Moraine_Lake_17092005.jpg"
|
||||
@ugoira = "https://i.pximg.net/img-zip-ugoira/img/2017/04/04/08/57/38/62247364_ugoira1920x1080.zip"
|
||||
end
|
||||
|
||||
should "work on a jpeg" do
|
||||
file = subject.download_from_source(@jpeg) do |context|
|
||||
assert_not_nil(context[:downloaded_source])
|
||||
assert_not_nil(context[:source])
|
||||
end
|
||||
|
||||
assert_operator(File.size(file.path), :>, 0)
|
||||
file.close
|
||||
end
|
||||
|
||||
should "work on an ugoira url" do
|
||||
file = subject.download_from_source(@ugoira, referer_url: "https://www.pixiv.net") do |context|
|
||||
assert_not_nil(context[:downloaded_source])
|
||||
assert_not_nil(context[:source])
|
||||
assert_not_nil(context[:ugoira])
|
||||
end
|
||||
|
||||
assert_operator(File.size(file.path), :>, 0)
|
||||
file.close
|
||||
end
|
||||
end
|
||||
|
||||
context ".calculate_ugoira_dimensions" do
|
||||
context "for a valid ugoira file" do
|
||||
setup do
|
||||
@ -112,7 +140,7 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
should "run" do
|
||||
subject.expects(:distribute_files).twice
|
||||
subject.expects(:distribute_files).times(3)
|
||||
subject.process_file(@upload, @file)
|
||||
assert_equal("jpg", @upload.file_ext)
|
||||
assert_equal(28086, @upload.file_size)
|
||||
@ -275,41 +303,6 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context ".download_from_source" do
|
||||
setup do
|
||||
@ugoira_source = "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364"
|
||||
@jpeg_source = "https://upload.wikimedia.org/wikipedia/commons/c/c5/Moraine_Lake_17092005.jpg"
|
||||
@upload = Upload.new
|
||||
end
|
||||
|
||||
should "work on a jpeg" do
|
||||
file = subject.download_from_source(@jpeg_source) do |context|
|
||||
assert_not_nil(context[:downloaded_source])
|
||||
assert_not_nil(context[:source])
|
||||
end
|
||||
|
||||
assert_operator(File.size(file.path), :>, 0)
|
||||
file.close
|
||||
end
|
||||
|
||||
should "work on an ugoira url" do
|
||||
file = subject.download_from_source(@ugoira_source, referer_url: "https://www.pixiv.net") do |context|
|
||||
assert_not_nil(context[:downloaded_source])
|
||||
assert_not_nil(context[:source])
|
||||
assert_not_nil(context[:ugoira])
|
||||
end
|
||||
|
||||
assert_operator(File.size(file.path), :>, 0)
|
||||
file.close
|
||||
end
|
||||
|
||||
should "initialize fields on the upload" do
|
||||
subject.download_for_upload(@ugoira_source, @upload)
|
||||
assert_not_nil(@upload.source)
|
||||
assert_not_nil(@upload.context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "::Preprocessor" do
|
||||
@ -383,388 +376,6 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
context "::Replacer" do
|
||||
context "for a file replacement" do
|
||||
setup do
|
||||
@new_file = upload_file("test/files/test.jpg")
|
||||
@old_file = upload_file("test/files/test.png")
|
||||
travel_to(1.month.ago) do
|
||||
@user = FactoryBot.create(:user)
|
||||
end
|
||||
as_user do
|
||||
@post = FactoryBot.create(:post, md5: Digest::MD5.hexdigest(@old_file.read))
|
||||
@old_md5 = @post.md5
|
||||
@post.stubs(:queue_delete_files)
|
||||
@replacement = FactoryBot.create(:post_replacement, post: @post, replacement_url: "", replacement_file: @new_file)
|
||||
end
|
||||
end
|
||||
|
||||
subject { UploadService::Replacer.new(post: @post, replacement: @replacement) }
|
||||
|
||||
context "#process!" do
|
||||
should "create a new upload" do
|
||||
assert_difference(-> { Upload.count }) do
|
||||
as_user { subject.process! }
|
||||
end
|
||||
end
|
||||
|
||||
should "create a comment" do
|
||||
assert_difference(-> { @post.comments.count }) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "not create a new post" do
|
||||
assert_difference(-> { Post.count }, 0) do
|
||||
as_user { subject.process! }
|
||||
end
|
||||
end
|
||||
|
||||
should "update the post's MD5" do
|
||||
assert_changes(-> { @post.md5 }) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "preserve the old values" do
|
||||
as_user { subject.process! }
|
||||
assert_equal(1500, @replacement.image_width_was)
|
||||
assert_equal(1000, @replacement.image_height_was)
|
||||
assert_equal(2000, @replacement.file_size_was)
|
||||
assert_equal("jpg", @replacement.file_ext_was)
|
||||
assert_equal(@old_md5, @replacement.md5_was)
|
||||
end
|
||||
|
||||
should "record the new values" do
|
||||
as_user { subject.process! }
|
||||
assert_equal(500, @replacement.image_width)
|
||||
assert_equal(335, @replacement.image_height)
|
||||
assert_equal(28086, @replacement.file_size)
|
||||
assert_equal("jpg", @replacement.file_ext)
|
||||
assert_equal("ecef68c44edb8a0d6a3070b5f8e8ee76", @replacement.md5)
|
||||
end
|
||||
|
||||
should "correctly update the attributes" do
|
||||
as_user { subject.process! }
|
||||
assert_equal(500, @post.image_width)
|
||||
assert_equal(335, @post.image_height)
|
||||
assert_equal(28086, @post.file_size)
|
||||
assert_equal("jpg", @post.file_ext)
|
||||
assert_equal("ecef68c44edb8a0d6a3070b5f8e8ee76", @post.md5)
|
||||
assert(File.exists?(@post.file.path))
|
||||
end
|
||||
end
|
||||
|
||||
context "a post with the same file" do
|
||||
should "not raise a duplicate error" do
|
||||
upload_file("test/files/test.png") do |file|
|
||||
assert_nothing_raised do
|
||||
as_user { @post.replace!(replacement_file: file, replacement_url: "") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "not queue a deletion or log a comment" do
|
||||
upload_file("test/files/test.png") do |file|
|
||||
assert_no_difference(-> { @post.comments.count }) do
|
||||
as_user { @post.replace!(replacement_file: file, replacement_url: "") }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "for a source replacement" do
|
||||
setup do
|
||||
@new_url = "https://upload.wikimedia.org/wikipedia/commons/c/c5/Moraine_Lake_17092005.jpg"
|
||||
travel_to(1.month.ago) do
|
||||
@user = FactoryBot.create(:user)
|
||||
end
|
||||
as_user do
|
||||
@post = FactoryBot.create(:post, uploader_ip_addr: "127.0.0.2")
|
||||
@post.stubs(:queue_delete_files)
|
||||
@replacement = FactoryBot.create(:post_replacement, post: @post, replacement_url: @new_url)
|
||||
end
|
||||
end
|
||||
|
||||
subject { UploadService::Replacer.new(post: @post, replacement: @replacement) }
|
||||
|
||||
context "a post when given a final_source" do
|
||||
should "change the source to the final_source" do
|
||||
replacement_url = "http://data.tumblr.com/afed9f5b3c33c39dc8c967e262955de2/tumblr_orwwptNBCE1wsfqepo1_raw.png"
|
||||
final_source = "https://noizave.tumblr.com/post/162094447052"
|
||||
|
||||
as_user { @post.replace!(replacement_url: replacement_url, final_source: final_source) }
|
||||
|
||||
assert_equal(final_source, @post.source)
|
||||
end
|
||||
end
|
||||
|
||||
context "a post when replaced with a HTML source" do
|
||||
should "record the image URL as the replacement URL, not the HTML source" do
|
||||
skip "Twitter key not set" unless Danbooru.config.twitter_api_key
|
||||
replacement_url = "https://twitter.com/nounproject/status/540944400767922176"
|
||||
image_url = "https://pbs.twimg.com/media/B4HSEP5CUAA4xyu.png:orig"
|
||||
as_user { @post.replace!(replacement_url: replacement_url) }
|
||||
|
||||
assert_equal(image_url, @post.replacements.last.replacement_url)
|
||||
end
|
||||
end
|
||||
|
||||
context "#undo!" do
|
||||
setup do
|
||||
@user = travel_to(1.month.ago) { FactoryBot.create(:user) }
|
||||
as_user do
|
||||
@post = FactoryBot.create(:post, source: "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")
|
||||
@post.stubs(:queue_delete_files)
|
||||
@post.replace!(replacement_url: "https://danbooru.donmai.us/data/preview/download.png", tags: "-tag1 tag2")
|
||||
end
|
||||
|
||||
@replacement = @post.replacements.last
|
||||
end
|
||||
|
||||
should "update the attributes" do
|
||||
as_user do
|
||||
subject.undo!
|
||||
end
|
||||
|
||||
assert_equal("lowres tag2", @post.tag_string)
|
||||
assert_equal(272, @post.image_width)
|
||||
assert_equal(92, @post.image_height)
|
||||
assert_equal(5969, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", @post.md5)
|
||||
assert_equal("8f9327db2597fa57d2f42b4a6c5a9855", Digest::MD5.file(@post.file).hexdigest)
|
||||
assert_equal("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", @post.source)
|
||||
end
|
||||
end
|
||||
|
||||
context "#process!" do
|
||||
should "create a new upload" do
|
||||
assert_difference(-> { Upload.count }) do
|
||||
as_user { subject.process! }
|
||||
end
|
||||
end
|
||||
|
||||
should "create a comment" do
|
||||
assert_difference(-> { @post.comments.count }) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "not create a new post" do
|
||||
assert_difference(-> { Post.count }, 0) do
|
||||
as_user { subject.process! }
|
||||
end
|
||||
end
|
||||
|
||||
should "update the post's MD5" do
|
||||
assert_changes(-> { @post.md5 }) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "update the post's source" do
|
||||
assert_changes(-> { @post.source }, nil, from: @post.source, to: @new_url) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "not change the post status or uploader" do
|
||||
assert_no_changes(-> { {ip_addr: @post.uploader_ip_addr.to_s, uploader: @post.uploader_id, pending: @post.is_pending?} }) do
|
||||
as_user { subject.process! }
|
||||
@post.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "leave a system comment" do
|
||||
as_user { subject.process! }
|
||||
comment = @post.comments.last
|
||||
assert_not_nil(comment)
|
||||
assert_equal(User.system.id, comment.creator_id)
|
||||
assert_match(/replaced this post/, comment.body)
|
||||
end
|
||||
end
|
||||
|
||||
context "a post with a pixiv html source" do
|
||||
setup do
|
||||
Delayed::Worker.delay_jobs = true
|
||||
end
|
||||
|
||||
teardown do
|
||||
Delayed::Worker.delay_jobs = false
|
||||
end
|
||||
|
||||
should "replace with the full size image" do
|
||||
begin
|
||||
as_user do
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
end
|
||||
|
||||
assert_equal(80, @post.image_width)
|
||||
assert_equal(82, @post.image_height)
|
||||
assert_equal(16275, @post.file_size)
|
||||
assert_equal("png", @post.file_ext)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", @post.md5)
|
||||
assert_equal("4ceadc314938bc27f3574053a3e1459a", Digest::MD5.file(@post.file).hexdigest)
|
||||
assert_equal("https://i.pximg.net/img-original/img/2017/04/04/08/54/15/62247350_p0.png", @post.replacements.last.replacement_url)
|
||||
assert_equal("https://i.pximg.net/img-original/img/2017/04/04/08/54/15/62247350_p0.png", @post.source)
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
|
||||
should "delete the old files after thirty days" do
|
||||
begin
|
||||
@post.unstub(:queue_delete_files)
|
||||
FileUtils.expects(:rm_f).times(3)
|
||||
|
||||
as_user { @post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350") }
|
||||
|
||||
travel_to((PostReplacement::DELETION_GRACE_PERIOD + 1).days.from_now) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "a post that is replaced by a ugoira" do
|
||||
should "save the frame data" do
|
||||
skip "ffmpeg not installed" unless check_ffmpeg
|
||||
begin
|
||||
as_user { @post.replace!(replacement_url: "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364") }
|
||||
@post.reload
|
||||
|
||||
assert_equal(80, @post.image_width)
|
||||
assert_equal(82, @post.image_height)
|
||||
assert_equal(2804, @post.file_size)
|
||||
assert_equal("zip", @post.file_ext)
|
||||
assert_equal("cad1da177ef309bf40a117c17b8eecf5", @post.md5)
|
||||
assert_equal("cad1da177ef309bf40a117c17b8eecf5", Digest::MD5.file(@post.file).hexdigest)
|
||||
|
||||
assert_equal("https://i.pximg.net/img-zip-ugoira/img/2017/04/04/08/57/38/62247364_ugoira1920x1080.zip", @post.source)
|
||||
assert_equal([{"delay"=>125, "file"=>"000000.jpg"}, {"delay"=>125,"file"=>"000001.jpg"}], @post.pixiv_ugoira_frame_data.data)
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "a post that is replaced to another file then replaced back to the original file" do
|
||||
setup do
|
||||
Delayed::Worker.delay_jobs = true
|
||||
end
|
||||
|
||||
teardown do
|
||||
Delayed::Worker.delay_jobs = false
|
||||
end
|
||||
|
||||
should "not delete the original files" do
|
||||
begin
|
||||
FileUtils.expects(:rm_f).never
|
||||
|
||||
as_user do
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
@post.reload
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364")
|
||||
@post.reload
|
||||
Upload.destroy_all
|
||||
@post.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
end
|
||||
|
||||
assert_nothing_raised { @post.file(:original) }
|
||||
assert_nothing_raised { @post.file(:preview) }
|
||||
|
||||
travel_to((PostReplacement::DELETION_GRACE_PERIOD + 1).days.from_now) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
assert_nothing_raised { @post.file(:original) }
|
||||
assert_nothing_raised { @post.file(:preview) }
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "two posts that have had their files swapped" do
|
||||
setup do
|
||||
Delayed::Worker.delay_jobs = true
|
||||
|
||||
as_user do
|
||||
@post1 = FactoryBot.create(:post)
|
||||
@post2 = FactoryBot.create(:post)
|
||||
end
|
||||
end
|
||||
|
||||
teardown do
|
||||
Delayed::Worker.delay_jobs = false
|
||||
end
|
||||
|
||||
should "not delete the still active files" do
|
||||
# swap the images between @post1 and @post2.
|
||||
begin
|
||||
as_user do
|
||||
@post1.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
@post2.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364")
|
||||
@post2.replace!(replacement_url: "https://www.google.com/intl/en_ALL/images/logo.gif")
|
||||
Upload.destroy_all
|
||||
@post1.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364")
|
||||
@post2.replace!(replacement_url: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247350")
|
||||
end
|
||||
|
||||
Timecop.travel(Time.now + PostReplacement::DELETION_GRACE_PERIOD + 1.day) do
|
||||
Delayed::Worker.new.work_off
|
||||
end
|
||||
|
||||
assert_nothing_raised { @post1.file(:original) }
|
||||
assert_nothing_raised { @post2.file(:original) }
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "a post with notes" do
|
||||
setup do
|
||||
Note.any_instance.stubs(:merge_version?).returns(false)
|
||||
|
||||
as_user do
|
||||
@post.update(image_width: 160, image_height: 164)
|
||||
@note = @post.notes.create(x: 80, y: 82, width: 80, height: 82, body: "test")
|
||||
@note.reload
|
||||
end
|
||||
end
|
||||
|
||||
should "rescale the notes" do
|
||||
assert_equal([80, 82, 80, 82], [@note.x, @note.y, @note.width, @note.height])
|
||||
|
||||
begin
|
||||
assert_difference(-> { @note.versions.count }) do
|
||||
# replacement image is 80x82, so we're downscaling by 50% (160x164 -> 80x82).
|
||||
as_user do
|
||||
@post.replace!(replacement_url: "https://upload.wikimedia.org/wikipedia/commons/c/c5/Moraine_Lake_17092005.jpg")
|
||||
end
|
||||
@note.reload
|
||||
end
|
||||
|
||||
assert_equal([1024, 768, 1024, 768], [@note.x, @note.y, @note.width, @note.height])
|
||||
rescue Net::OpenTimeout
|
||||
skip "Remote connection to Pixiv failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#start!" do
|
||||
subject { UploadService }
|
||||
|
||||
@ -839,7 +450,7 @@ class UploadServiceTest < ActiveSupport::TestCase
|
||||
|
||||
context "with a preprocessed predecessor" do
|
||||
setup do
|
||||
@predecessor = FactoryBot.create(:source_upload, status: "preprocessed", source: @source, file_size: 0, md5: "something", image_height: 0, image_width: 0, file_ext: "jpg")
|
||||
@predecessor = FactoryBot.create(:source_upload, status: "preprocessed", source: @source, image_height: 0, image_width: 0, file_size: 1, md5: 'blank', file_ext: "jpg")
|
||||
@tags = 'hello world'
|
||||
end
|
||||
|
||||
|
@ -15,7 +15,6 @@ class PostTest < ActiveSupport::TestCase
|
||||
CurrentUser.ip_addr = "127.0.0.1"
|
||||
mock_saved_search_service!
|
||||
mock_pool_archive_service!
|
||||
ImageCropper.stubs(:enabled?).returns(false)
|
||||
end
|
||||
|
||||
def teardown
|
||||
|
Loading…
Reference in New Issue
Block a user