From 45064853de51ddaa32124844267ef18dc2b1dbef Mon Sep 17 00:00:00 2001 From: evazion Date: Mon, 18 May 2020 00:31:12 -0500 Subject: [PATCH] uploads: move thumbnail generation code to MediaFile. * Move image thumbnail generation code to MediaFile::Image. * Move video thumbnail generation code to MediaFile::Video. * Move ugoira->webm conversion code to MediaFile::Ugoira. This separates thumbnail generation from the upload process so that it's possible to generate thumbnails outside of uploads. --- app/logical/danbooru_image_resizer.rb | 36 ---- app/logical/media_file.rb | 38 +++- app/logical/media_file/flash.rb | 2 +- app/logical/media_file/image.rb | 43 ++++- app/logical/media_file/ugoira.rb | 98 +++++++++- app/logical/media_file/video.rb | 24 ++- app/logical/pixiv_ugoira_converter.rb | 90 --------- app/logical/upload_service/utils.rb | 52 ++--- app/models/upload.rb | 4 + test/factories/upload.rb | 2 +- test/files/ugoira.json | 7 + test/{fixtures => files}/ugoira.zip | Bin test/unit/media_file_test.rb | 43 +++++ test/unit/pixiv_ugoira_converter_test.rb | 24 --- test/unit/upload_service_test.rb | 229 ++--------------------- 15 files changed, 269 insertions(+), 423 deletions(-) delete mode 100644 app/logical/danbooru_image_resizer.rb delete mode 100644 app/logical/pixiv_ugoira_converter.rb create mode 100644 test/files/ugoira.json rename test/{fixtures => files}/ugoira.zip (100%) delete mode 100644 test/unit/pixiv_ugoira_converter_test.rb diff --git a/app/logical/danbooru_image_resizer.rb b/app/logical/danbooru_image_resizer.rb deleted file mode 100644 index 08e17ac2b..000000000 --- a/app/logical/danbooru_image_resizer.rb +++ /dev/null @@ -1,36 +0,0 @@ -module DanbooruImageResizer - module_function - - # Taken from ArgyllCMS 2.0.0 (see also: https://ninedegreesbelow.com/photography/srgb-profile-comparison.html) - SRGB_PROFILE = "#{Rails.root}/config/sRGB.icm" - - # http://jcupitt.github.io/libvips/API/current/libvips-resample.html#vips-thumbnail - if Vips.at_least_libvips?(8, 8) - THUMBNAIL_OPTIONS = { size: :down, linear: false, no_rotate: true, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } - CROP_OPTIONS = { linear: false, no_rotate: true, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE, crop: :attention } - else - THUMBNAIL_OPTIONS = { size: :down, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } - CROP_OPTIONS = { linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE, crop: :attention } - end - - # http://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave - JPEG_OPTIONS = { background: 255, strip: true, interlace: true, optimize_coding: true } - - # https://github.com/jcupitt/libvips/wiki/HOWTO----Image-shrinking - # http://jcupitt.github.io/libvips/API/current/Using-vipsthumbnail.md.html - def resize(file, width, height, resize_quality = 90) - output_file = Tempfile.new - resized_image = Vips::Image.thumbnail(file.path, width, height: height, **THUMBNAIL_OPTIONS) - resized_image.jpegsave(output_file.path, Q: resize_quality, **JPEG_OPTIONS) - - output_file - end - - def crop(file, width, height, resize_quality = 90) - output_file = Tempfile.new - resized_image = Vips::Image.thumbnail(file.path, width, height: height, **CROP_OPTIONS) - resized_image.jpegsave(output_file.path, Q: resize_quality, **JPEG_OPTIONS) - - output_file - end -end diff --git a/app/logical/media_file.rb b/app/logical/media_file.rb index 91ca4a95f..709b8743d 100644 --- a/app/logical/media_file.rb +++ b/app/logical/media_file.rb @@ -5,20 +5,20 @@ class MediaFile # delegate all File methods to `file`. delegate *(File.instance_methods - MediaFile.instance_methods), to: :file - def self.open(file) + def self.open(file, **options) file = Kernel.open(file, "r", binmode: true) unless file.respond_to?(:read) case file_ext(file) when :jpg, :gif, :png - MediaFile::Image.new(file) + MediaFile::Image.new(file, **options) when :swf - MediaFile::Flash.new(file) + MediaFile::Flash.new(file, **options) when :webm, :mp4 - MediaFile::Video.new(file) + MediaFile::Video.new(file, **options) when :zip - MediaFile::Ugoira.new(file) + MediaFile::Ugoira.new(file, **options) else - MediaFile.new(file) + MediaFile.new(file, **options) end end @@ -45,7 +45,7 @@ class MediaFile end end - def initialize(file) + def initialize(file, **options) @file = file end @@ -73,5 +73,29 @@ class MediaFile file.size end + def is_image? + file_ext.in?([:jpg, :png, :gif]) + end + + def is_video? + file_ext.in?([:webm, :mp4]) + end + + def is_ugoira? + file_ext == :zip + end + + def is_flash? + file_ext == :swf + end + + def preview(width, height, **options) + nil + end + + def crop(width, height, **options) + nil + end + memoize :dimensions, :file_ext, :file_size, :md5 end diff --git a/app/logical/media_file/flash.rb b/app/logical/media_file/flash.rb index 696d68dbe..be0f8c829 100644 --- a/app/logical/media_file/flash.rb +++ b/app/logical/media_file/flash.rb @@ -1,7 +1,7 @@ # Adapted from https://github.com/dim/ruby-imagespec/blob/f2f3ce8bb5b1b411f8658e66a891a095261d94c0/lib/image_spec/parser/swf.rb # License: https://github.com/dim/ruby-imagespec/blob/master/LICENSE -class MediaFile::Flash < MediaFile::Image +class MediaFile::Flash < MediaFile def dimensions # Read the entire stream into memory because the # dimensions aren't stored in a standard location diff --git a/app/logical/media_file/image.rb b/app/logical/media_file/image.rb index b18608d91..d26ad93f5 100644 --- a/app/logical/media_file/image.rb +++ b/app/logical/media_file/image.rb @@ -1,9 +1,46 @@ class MediaFile::Image < MediaFile - def dimensions - image.size + # Taken from ArgyllCMS 2.0.0 (see also: https://ninedegreesbelow.com/photography/srgb-profile-comparison.html) + SRGB_PROFILE = "#{Rails.root}/config/sRGB.icm" + + # http://jcupitt.github.io/libvips/API/current/VipsForeignSave.html#vips-jpegsave + JPEG_OPTIONS = { Q: 90, background: 255, strip: true, interlace: true, optimize_coding: true } + + # http://jcupitt.github.io/libvips/API/current/libvips-resample.html#vips-thumbnail + if Vips.at_least_libvips?(8, 8) + THUMBNAIL_OPTIONS = { size: :down, linear: false, no_rotate: true, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } + CROP_OPTIONS = { crop: :attention, linear: false, no_rotate: true, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } + else + THUMBNAIL_OPTIONS = { size: :down, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } + CROP_OPTIONS = { crop: :attention, linear: false, auto_rotate: false, export_profile: SRGB_PROFILE, import_profile: SRGB_PROFILE } end + def dimensions + image.size + rescue Vips::Error + [0, 0] + end + + # https://github.com/jcupitt/libvips/wiki/HOWTO----Image-shrinking + # http://jcupitt.github.io/libvips/API/current/Using-vipsthumbnail.md.html + def preview(width, height) + output_file = Tempfile.new(["image-preview", ".jpg"]) + resized_image = image.thumbnail_image(width, height: height, **THUMBNAIL_OPTIONS) + resized_image.jpegsave(output_file.path, **JPEG_OPTIONS) + + MediaFile::Image.new(output_file) + end + + def crop(width, height) + output_file = Tempfile.new(["image-crop", ".jpg"]) + resized_image = image.thumbnail_image(width, height: height, **CROP_OPTIONS) + resized_image.jpegsave(output_file.path, **JPEG_OPTIONS) + + MediaFile::Image.new(output_file) + end + + private + def image - @image ||= Vips::Image.new_from_file(file.path) + @image ||= Vips::Image.new_from_file(file.path, fail: true) end end diff --git a/app/logical/media_file/ugoira.rb b/app/logical/media_file/ugoira.rb index 7c9d30817..1b3791e92 100644 --- a/app/logical/media_file/ugoira.rb +++ b/app/logical/media_file/ugoira.rb @@ -1,13 +1,93 @@ class MediaFile::Ugoira < MediaFile - def dimensions - tempfile = Tempfile.new - folder = Zip::File.new(file.path) - folder.first.extract(tempfile.path) { true } + extend Memoist + class Error < StandardError; end + attr_reader :frame_data - image_file = MediaFile.open(tempfile) - image_file.dimensions - ensure - image_file.close - tempfile.close! + def self.conversion_enabled? + system("ffmpeg -version > /dev/null") && system("mkvmerge --version > /dev/null") end + + def initialize(file, frame_data: {}, **options) + super(file, **options) + @frame_data = frame_data + end + + def close + file.close + zipfile.close + preview_frame.close + end + + def dimensions + preview_frame.dimensions + end + + def preview(width, height) + preview_frame.preview(width, height) + end + + def crop(width, height) + preview_frame.crop(width, height) + end + + # XXX should take width and height and resize image + def convert + raise NotImplementedError, "can't convert ugoira to webm: ffmpeg or mkvmerge not installed" unless self.class.conversion_enabled? + + Dir.mktmpdir("ugoira-#{md5}") do |tmpdir| + output_file = Tempfile.new(["ugoira-conversion", ".webm"], binmode: true) + + FileUtils.mkdir_p("#{tmpdir}/images") + + zipfile.each do |entry| + path = File.join(tmpdir, "images", entry.name) + entry.extract(path) + end + + # Duplicate last frame to avoid it being displayed only for a very short amount of time. + last_file_name = zipfile.entries.last.name + last_file_name =~ /\A(\d{6})(\.\w{,4})\Z/ + new_last_index = $1.to_i + 1 + file_ext = $2 + new_last_filename = ("%06d" % new_last_index) + file_ext + path_from = File.join(tmpdir, "images", last_file_name) + path_to = File.join(tmpdir, "images", new_last_filename) + FileUtils.cp(path_from, path_to) + + delay_sum = 0 + timecodes_path = File.join(tmpdir, "timecodes.tc") + File.open(timecodes_path, "w+") do |f| + f.write("# timecode format v2\n") + frame_data.each do |img| + f.write("#{delay_sum}\n") + delay_sum += (img["delay"] || img["delay_msec"]) + end + f.write("#{delay_sum}\n") + f.write("#{delay_sum}\n") + end + + ext = zipfile.first.name.match(/\.(\w{,4})$/)[1] + ffmpeg_out, status = Open3.capture2e("ffmpeg -i #{tmpdir}/images/%06d.#{ext} -codec:v libvpx -crf 4 -b:v 5000k -an #{tmpdir}/tmp.webm") + raise Error, "ffmpeg failed: #{ffmpeg_out}" unless status.success? + + mkvmerge_out, status = Open3.capture2e("mkvmerge -o #{output_file.path} --webm --timecodes 0:#{tmpdir}/timecodes.tc #{tmpdir}/tmp.webm") + raise Error, "mkvmerge failed: #{mkvmerge_out}" unless status.success? + + MediaFile.open(output_file) + end + end + + private + + def zipfile + Zip::File.new(file.path) + end + + def preview_frame + tempfile = Tempfile.new("ugoira-preview", binmode: true) + zipfile.entries.first.extract(tempfile.path) { true } # 'true' means overwrite the existing tempfile. + MediaFile.open(tempfile) + end + + memoize :zipfile, :preview_frame end diff --git a/app/logical/media_file/video.rb b/app/logical/media_file/video.rb index d11b08ad7..396a2e19e 100644 --- a/app/logical/media_file/video.rb +++ b/app/logical/media_file/video.rb @@ -1,9 +1,29 @@ class MediaFile::Video < MediaFile + extend Memoist + def dimensions [video.width, video.height] end - def video - @video ||= FFMPEG::Movie.new(file.path) + def preview(max_width, max_height) + preview_frame.preview(max_width, max_height) end + + def crop(max_width, max_height) + preview_frame.crop(max_width, max_height) + end + + private + + def video + FFMPEG::Movie.new(file.path) + end + + def preview_frame + vp = Tempfile.new(["video-preview", ".jpg"], binmode: true) + video.screenshot(vp.path, seek_time: 0) + MediaFile.open(vp.path) + end + + memoize :video, :preview_frame end diff --git a/app/logical/pixiv_ugoira_converter.rb b/app/logical/pixiv_ugoira_converter.rb deleted file mode 100644 index ed3fac9d7..000000000 --- a/app/logical/pixiv_ugoira_converter.rb +++ /dev/null @@ -1,90 +0,0 @@ -class PixivUgoiraConverter - def self.enabled? - system("ffmpeg -version > /dev/null") && system("mkvmerge --version > /dev/null") - end - - def self.generate_webm(ugoira_file, frame_data) - raise NotImplementedError, "can't convert ugoira to webm: ffmpeg or mkvmerge not installed" unless enabled? - - folder = Zip::File.new(ugoira_file.path) - output_file = Tempfile.new(binmode: true) - write_path = output_file.path - - Dir.mktmpdir do |tmpdir| - FileUtils.mkdir_p("#{tmpdir}/images") - folder.each_with_index do |file, i| - path = File.join(tmpdir, "images", file.name) - file.extract(path) - end - - # Duplicate last frame to avoid it being displayed only for a very short amount of time. - last_file_name = folder.to_a.last.name - last_file_name =~ /\A(\d{6})(\.\w{,4})\Z/ - new_last_index = $1.to_i + 1 - file_ext = $2 - new_last_filename = ("%06d" % new_last_index) + file_ext - path_from = File.join(tmpdir, "images", last_file_name) - path_to = File.join(tmpdir, "images", new_last_filename) - FileUtils.cp(path_from, path_to) - - delay_sum = 0 - timecodes_path = File.join(tmpdir, "timecodes.tc") - File.open(timecodes_path, "w+") do |f| - f.write("# timecode format v2\n") - frame_data.each do |img| - f.write("#{delay_sum}\n") - delay_sum += (img["delay"] || img["delay_msec"]) - end - f.write("#{delay_sum}\n") - f.write("#{delay_sum}\n") - end - - ext = folder.first.name.match(/\.(\w{,4})$/)[1] - ffmpeg_out, status = Open3.capture2e("ffmpeg -i #{tmpdir}/images/%06d.#{ext} -codec:v libvpx -crf 4 -b:v 5000k -an #{tmpdir}/tmp.webm") - - if !status.success? - Rails.logger.error "[write_webm] ******************************" - Rails.logger.error "[write_webm] failed write_path=#{write_path}" - Rails.logger.error "[write_webm] ffmepg output:" - ffmpeg_out.split(/\n/).each do |line| - Rails.logger.error "[write_webm][ffmpeg] #{line}" - end - Rails.logger.error "[write_webm] ******************************" - end - - mkvmerge_out, status = Open3.capture2e("mkvmerge -o #{write_path} --webm --timecodes 0:#{tmpdir}/timecodes.tc #{tmpdir}/tmp.webm") - - if !status.success? - Rails.logger.error "[write_webm] ******************************" - Rails.logger.error "[write_webm] failed write_path=#{write_path}" - Rails.logger.error "[write_webm] mkvmerge output:" - mkvmerge_out.split(/\n/).each do |line| - Rails.logger.error "[write_webm][mkvmerge] #{line}" - end - Rails.logger.error "[write_webm] ******************************" - end - end - - 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, Danbooru.config.small_image_width, 85) - ensure - file&.close! - end - - def self.generate_preview(ugoira_file) - 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. - - DanbooruImageResizer.resize(file, Danbooru.config.small_image_width, Danbooru.config.small_image_width, 85) - ensure - file.close! - end -end diff --git a/app/logical/upload_service/utils.rb b/app/logical/upload_service/utils.rb index bc7780c5a..49d11cc08 100644 --- a/app/logical/upload_service/utils.rb +++ b/app/logical/upload_service/utils.rb @@ -25,53 +25,25 @@ class UploadService source =~ /^https?:\/\// end - def generate_resizes(file, upload) - if upload.is_video? - video = FFMPEG::Movie.new(file.path) - crop_file = generate_video_crop_for(video, Danbooru.config.small_image_width) - preview_file = generate_video_preview_for(video, Danbooru.config.small_image_width, Danbooru.config.small_image_width) + def generate_resizes(media_file) + preview_file = media_file.preview(Danbooru.config.small_image_width, Danbooru.config.small_image_width) + crop_file = media_file.crop(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, 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 + if media_file.is_ugoira? + sample_file = media_file.convert + elsif media_file.is_image? && media_file.width > Danbooru.config.large_image_width + sample_file = media_file.preview(Danbooru.config.large_image_width, nil) + else + sample_file = nil end [preview_file, crop_file, sample_file] end - def generate_video_crop_for(video, width) - vp = Tempfile.new(["video-preview", ".jpg"], binmode: true) - video.screenshot(vp.path, :seek_time => 0, :resolution => "#{video.width}x#{video.height}") - crop = DanbooruImageResizer.crop(vp, width, width, 85) - vp.close - return crop - end - - def generate_video_preview_for(video, width, height) - dimension_ratio = video.width.to_f / video.height - if dimension_ratio > 1 - height = (width / dimension_ratio).to_i - else - width = (height * dimension_ratio).to_i - end - - output_file = Tempfile.new(["video-preview", ".jpg"], binmode: true) - video.screenshot(output_file.path, :seek_time => 0, :resolution => "#{width}x#{height}") - output_file - end - def process_file(upload, file, original_post_id: nil) - media_file = MediaFile.open(file) - upload.file = file + media_file = upload.media_file + upload.file_ext = media_file.file_ext.to_s upload.file_size = media_file.file_size upload.md5 = media_file.md5 @@ -81,7 +53,7 @@ class UploadService upload.validate!(:file) upload.tag_string = "#{upload.tag_string} #{Utils.automatic_tags(upload, file)}" - preview_file, crop_file, sample_file = Utils.generate_resizes(file, upload) + preview_file, crop_file, sample_file = Utils.generate_resizes(media_file) begin Utils.distribute_files(file, upload, :original, original_post_id: original_post_id) diff --git a/app/models/upload.rb b/app/models/upload.rb index 036bbdb95..e96ab255b 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -100,6 +100,10 @@ class Upload < ApplicationRecord end module FileMethods + def media_file + @media_file ||= MediaFile.open(file, frame_data: context.to_h.dig("ugoira", "frame_data")) + end + def is_image? %w(jpg gif png).include?(file_ext) end diff --git a/test/factories/upload.rb b/test/factories/upload.rb index 1468c17b6..c96a12754 100644 --- a/test/factories/upload.rb +++ b/test/factories/upload.rb @@ -17,7 +17,7 @@ FactoryBot.define do factory(:ugoira_upload) do file do f = Tempfile.new - IO.copy_stream("#{Rails.root}/test/fixtures/ugoira.zip", f.path) + IO.copy_stream("#{Rails.root}/test/files/ugoira.zip", f.path) ActionDispatch::Http::UploadedFile.new(tempfile: f, filename: "ugoira.zip") end end diff --git a/test/files/ugoira.json b/test/files/ugoira.json new file mode 100644 index 000000000..d10aaee9d --- /dev/null +++ b/test/files/ugoira.json @@ -0,0 +1,7 @@ +[ + {"file": "000000.jpg", "delay": 200}, + {"file": "000001.jpg", "delay": 200}, + {"file": "000002.jpg", "delay": 200}, + {"file": "000003.jpg", "delay": 200}, + {"file": "000004.jpg", "delay": 250} +] diff --git a/test/fixtures/ugoira.zip b/test/files/ugoira.zip similarity index 100% rename from test/fixtures/ugoira.zip rename to test/files/ugoira.zip diff --git a/test/unit/media_file_test.rb b/test/unit/media_file_test.rb index 2277b56e2..e026f82dc 100644 --- a/test/unit/media_file_test.rb +++ b/test/unit/media_file_test.rb @@ -82,4 +82,47 @@ class MediaFileTest < ActiveSupport::TestCase should "determine the correct filesize for a jpeg file" do assert_equal(28086, MediaFile.open("test/files/test.jpg").file_size) end + + context "#preview" do + should "generate a preview image" do + assert_equal([150, 101], MediaFile.open("test/files/test.jpg").preview(150, 150).dimensions) + assert_equal([113, 150], MediaFile.open("test/files/test.png").preview(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test.gif").preview(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test-512x512.webm").preview(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test-300x300.mp4").preview(150, 150).dimensions) + end + + should "be able to fit to width only" do + assert_equal([400, 268], MediaFile.open("test/files/test.jpg").preview(400, nil).dimensions) + end + end + + context "#crop" do + should "generate a cropped preview image" do + assert_equal([150, 150], MediaFile.open("test/files/test.jpg").crop(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test.png").crop(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test.gif").crop(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test-512x512.webm").crop(150, 150).dimensions) + assert_equal([150, 150], MediaFile.open("test/files/test-300x300.mp4").crop(150, 150).dimensions) + end + end + + context "for a ugoira" do + setup do + skip unless MediaFile::Ugoira.conversion_enabled? + frame_data = JSON.parse(File.read("test/files/ugoira.json")) + @ugoira = MediaFile.open("test/files/ugoira.zip", frame_data: frame_data) + end + + should "generate a preview" do + assert_equal([60, 60], @ugoira.preview(150, 150).dimensions) + assert_equal([150, 150], @ugoira.crop(150, 150).dimensions) + end + + should "convert to a webm" do + webm = @ugoira.convert + assert_equal(:webm, webm.file_ext) + assert_equal([60, 60], webm.dimensions) + end + end end diff --git a/test/unit/pixiv_ugoira_converter_test.rb b/test/unit/pixiv_ugoira_converter_test.rb deleted file mode 100644 index 201cf0112..000000000 --- a/test/unit/pixiv_ugoira_converter_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require "test_helper" - -class PixivUgoiraConverterTest < ActiveSupport::TestCase - context "An ugoira converter" do - setup do - @zipfile = upload_file("test/fixtures/ugoira.zip").tempfile - @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" => 250} - ] - end - - should "output to webm" do - skip "ffmpeg is not installed" unless PixivUgoiraConverter.enabled? - sample_file = PixivUgoiraConverter.generate_webm(@zipfile, @frame_data) - preview_file = PixivUgoiraConverter.generate_preview(@zipfile) - assert_operator(sample_file.size, :>, 1_000) - assert_operator(preview_file.size, :>, 0) - end - end -end diff --git a/test/unit/upload_service_test.rb b/test/unit/upload_service_test.rb index 7761bfe9a..7716c873f 100644 --- a/test/unit/upload_service_test.rb +++ b/test/unit/upload_service_test.rb @@ -15,8 +15,6 @@ class UploadServiceTest < ActiveSupport::TestCase } context "::Utils" do - subject { UploadService::Utils } - context "#get_file_for_upload" do context "for a non-source site" do setup do @@ -26,10 +24,8 @@ class UploadServiceTest < ActiveSupport::TestCase end should "work on a jpeg" do - file = subject.get_file_for_upload(@upload) - + file = UploadService::Utils.get_file_for_upload(@upload) assert_operator(File.size(file.path), :>, 0) - file.close end end @@ -43,13 +39,9 @@ class UploadServiceTest < ActiveSupport::TestCase should "work on an ugoira url" do begin - file = subject.get_file_for_upload(@upload) - + file = UploadService::Utils.get_file_for_upload(@upload) assert_operator(File.size(file.path), :>, 0) - file.close - rescue Net::OpenTimeout - skip "network problems" end end end @@ -64,15 +56,12 @@ class UploadServiceTest < ActiveSupport::TestCase end should "work on an ugoira url" do - skip unless PixivUgoiraConverter.enabled? - file = subject.get_file_for_upload(@upload) + file = UploadService::Utils.get_file_for_upload(@upload) assert_not_nil(@upload.context["ugoira"]) assert_operator(File.size(file.path), :>, 0) file.close - rescue Net::OpenTimeout - skip "network failure" end end end @@ -84,14 +73,14 @@ class UploadServiceTest < ActiveSupport::TestCase context "with an original_post_id" do should "run" do - subject.expects(:distribute_files).times(3) - subject.process_file(@upload, @upload.file.tempfile, original_post_id: 12345) + UploadService::Utils.expects(:distribute_files).times(3) + UploadService::Utils.process_file(@upload, @upload.file.tempfile, original_post_id: 12345) end end should "run" do - subject.expects(:distribute_files).times(3) - subject.process_file(@upload, @upload.file.tempfile) + UploadService::Utils.expects(:distribute_files).times(3) + UploadService::Utils.process_file(@upload, @upload.file.tempfile) assert_equal("jpg", @upload.file_ext) assert_equal(28086, @upload.file_size) assert_equal("ecef68c44edb8a0d6a3070b5f8e8ee76", @upload.md5) @@ -99,184 +88,6 @@ class UploadServiceTest < ActiveSupport::TestCase assert_equal(500, @upload.image_width) end end - - context ".generate_resizes" do - context "for an ugoira" do - setup do - context = UGOIRA_CONTEXT - @file = upload_file("test/fixtures/ugoira.zip") - @upload = mock - @upload.stubs(:is_video?).returns(false) - @upload.stubs(:is_ugoira?).returns(true) - @upload.stubs(:context).returns(context) - end - - should "generate a preview and a video" do - skip unless PixivUgoiraConverter.enabled? - - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_operator(File.size(sample.path), :>, 0) - assert_equal(60, MediaFile.open(preview).width) - assert_equal(60, MediaFile.open(preview).height) - assert_equal(150, MediaFile.open(crop).width) - assert_equal(150, MediaFile.open(crop).height) - preview.close - preview.unlink - sample.close - sample.unlink - end - end - - context "for a video" do - teardown do - @file.close - end - - context "for an mp4" do - setup do - @file = upload_file("test/files/test-300x300.mp4") - @upload = mock - @upload.stubs(:is_video?).returns(true) - @upload.stubs(:is_ugoira?).returns(false) - end - - should "generate a video" do - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_equal(150, MediaFile.open(preview).width) - assert_equal(150, MediaFile.open(preview).height) - assert_equal(150, MediaFile.open(crop).width) - assert_equal(150, MediaFile.open(crop).height) - preview.close - preview.unlink - crop.close - crop.unlink - end - end - - context "for a webm" do - setup do - @file = upload_file("test/files/test-512x512.webm") - @upload = mock - @upload.stubs(:is_video?).returns(true) - @upload.stubs(:is_ugoira?).returns(false) - end - - should "generate a video" do - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_equal(150, MediaFile.open(preview).width) - assert_equal(150, MediaFile.open(preview).height) - assert_equal(150, MediaFile.open(crop).width) - assert_equal(150, MediaFile.open(crop).height) - preview.close - preview.unlink - crop.close - crop.unlink - end - end - end - - context "for an image" do - teardown do - @file.close - end - - setup do - @upload = mock - @upload.stubs(:is_video?).returns(false) - @upload.stubs(:is_ugoira?).returns(false) - @upload.stubs(:is_image?).returns(true) - @upload.stubs(:image_width).returns(1200) - @upload.stubs(:image_height).returns(200) - end - - context "for a jpeg" do - setup do - @file = upload_file("test/files/test.jpg") - end - - should "generate a preview" do - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_operator(File.size(sample.path), :>, 0) - preview.close - preview.unlink - sample.close - sample.unlink - end - end - - context "for a png" do - setup do - @file = upload_file("test/files/test.png") - end - - should "generate a preview" do - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_operator(File.size(sample.path), :>, 0) - preview.close - preview.unlink - sample.close - sample.unlink - end - end - - context "for a gif" do - setup do - @file = upload_file("test/files/test.png") - end - - should "generate a preview" do - preview, crop, sample = subject.generate_resizes(@file, @upload) - assert_operator(File.size(preview.path), :>, 0) - assert_operator(File.size(crop.path), :>, 0) - assert_operator(File.size(sample.path), :>, 0) - preview.close - preview.unlink - sample.close - sample.unlink - end - end - end - end - - context ".generate_video_preview_for" do - context "for an mp4" do - setup do - @path = "test/files/test-300x300.mp4" - @video = FFMPEG::Movie.new(@path) - end - - should "generate a video" do - sample = subject.generate_video_preview_for(@video, 100, 100) - assert_operator(File.size(sample.path), :>, 0) - sample.close - sample.unlink - end - end - - context "for a webm" do - setup do - @path = "test/files/test-512x512.webm" - @video = FFMPEG::Movie.new(@path) - end - - should "generate a video" do - sample = subject.generate_video_preview_for(@video, 100, 100) - assert_operator(File.size(sample.path), :>, 0) - sample.close - sample.unlink - end - end - end end context "::Preprocessor" do @@ -302,7 +113,7 @@ class UploadServiceTest < ActiveSupport::TestCase end should "download the file" do - @service = subject.new(source: @source, referer_url: @ref) + @service = UploadService::Preprocessor.new(source: @source, referer_url: @ref) @upload = @service.start! assert_equal("preprocessed", @upload.status) assert_equal(9800, @upload.file_size) @@ -321,7 +132,7 @@ class UploadServiceTest < ActiveSupport::TestCase should "download the file" do begin - @service = subject.new(source: @source, referer_url: @ref) + @service = UploadService::Preprocessor.new(source: @source, referer_url: @ref) @upload = @service.start! rescue Net::OpenTimeout skip "network failure" @@ -341,9 +152,9 @@ class UploadServiceTest < ActiveSupport::TestCase end should "download the file" do - skip unless PixivUgoiraConverter.enabled? + skip unless MediaFile::Ugoira.conversion_enabled? - @service = subject.new(source: @source) + @service = UploadService::Preprocessor.new(source: @source) begin @upload = @service.start! rescue Net::OpenTimeout @@ -364,7 +175,7 @@ class UploadServiceTest < ActiveSupport::TestCase end should "download the file" do - @service = subject.new(source: @source) + @service = UploadService::Preprocessor.new(source: @source) begin @upload = @service.start! rescue Net::OpenTimeout @@ -386,7 +197,7 @@ class UploadServiceTest < ActiveSupport::TestCase end should "work for a video" do - @service = subject.new(source: @source) + @service = UploadService::Preprocessor.new(source: @source) @upload = @service.start! assert_equal("preprocessed", @upload.status) assert_not_nil(@upload.md5) @@ -405,7 +216,7 @@ class UploadServiceTest < ActiveSupport::TestCase end should "leave the upload in an error state" do - @service = subject.new(source: @source) + @service = UploadService::Preprocessor.new(source: @source) @upload = @service.start! assert_match(/error:/, @upload.status) end @@ -413,7 +224,7 @@ class UploadServiceTest < ActiveSupport::TestCase context "for an invalid content type" do should "fail" do - upload = subject.new(source: "http://www.example.com").start! + upload = UploadService::Preprocessor.new(source: "http://www.example.com").start! assert_match(/\Aerror:.*File ext is invalid/, upload.status) end end @@ -429,7 +240,7 @@ class UploadServiceTest < ActiveSupport::TestCase end should "overwrite the attributes" do - @service = subject.new(source: @source, rating: 'e') + @service = UploadService::Preprocessor.new(source: @source, rating: 'e') @upload = @service.start! @service.finish! @upload.reload @@ -737,7 +548,7 @@ class UploadServiceTest < ActiveSupport::TestCase context "a post that is replaced by a ugoira" do should "save the frame data" do - skip "ffmpeg not installed" unless PixivUgoiraConverter.enabled? + skip unless MediaFile::Ugoira.conversion_enabled? begin as_user { @post.replace!(replacement_url: "http://www.pixiv.net/member_illust.php?mode=medium&illust_id=62247364") } @post.reload @@ -751,8 +562,6 @@ class UploadServiceTest < ActiveSupport::TestCase 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 @@ -760,7 +569,7 @@ class UploadServiceTest < ActiveSupport::TestCase context "a post that is replaced to another file then replaced back to the original file" do should "not delete the original files" do begin - skip unless PixivUgoiraConverter.enabled? + skip unless MediaFile::Ugoira.conversion_enabled? @post.unstub(:queue_delete_files) # this is called thrice to delete the file for 62247364 @@ -802,7 +611,7 @@ class UploadServiceTest < ActiveSupport::TestCase # swap the images between @post1 and @post2. begin as_user do - skip unless PixivUgoiraConverter.enabled? + skip unless MediaFile::Ugoira.conversion_enabled? @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")