media assets: fix dimensions of flash files.
Use ExifTool to get the dimensions of Flash files instead of calculating it ourselves. Avoids copying third-party code. Fixes a bug where Flash files with fractional dimensions (e.g. 607.6 x 756.6) had their dimensions rounded down instead of rounded up. Fixes another bug where Flash files could return negative dimensions. This happened for two files: * https://danbooru.donmai.us/media_assets/228662 (-179.2 x -339.2) * https://danbooru.donmai.us/media_assets/228664 (-179.2 x -339.2) Now we round these up to 1x1. This is still wrong, but it's less wrong than before.
This commit is contained in:
@@ -1,91 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This file contains code derived from
|
||||
# https://github.com/dim/ruby-imagespec/blob/f2f3ce8bb5b1b411f8658e66a891a095261d94c0/lib/image_spec/parser/swf.rb
|
||||
#
|
||||
# Copyright (c) 2020, Danbooru Project contributors
|
||||
# Copyright (c) 2008, Brandon Anderson (anderson.brandon@gmail.com)
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# Neither the name of the original author nor the names of contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
class MediaFile::Flash < MediaFile
|
||||
# XXX Some Flash files have fractional dimensions; round up to nearest integer.
|
||||
# XXX Some Flash files have negative dimensions; clamp to positive numbers.
|
||||
def dimensions
|
||||
# Read the entire stream into memory because the
|
||||
# dimensions aren't stored in a standard location
|
||||
contents = File.read(file.path, binmode: true).force_encoding("ASCII-8BIT")
|
||||
|
||||
# Our 'signature' is the first 3 bytes
|
||||
# Either FWS or CWS. CWS indicates compression
|
||||
signature = contents[0..2]
|
||||
|
||||
# SWF version
|
||||
_version = contents[3].unpack('C').join.to_i
|
||||
|
||||
# Determine the length of the uncompressed stream
|
||||
length = contents[4..7].unpack('V').join.to_i
|
||||
|
||||
# If we do, in fact, have compression
|
||||
if signature == 'CWS'
|
||||
# Decompress the body of the SWF
|
||||
body = Zlib::Inflate.inflate(contents[8..length])
|
||||
|
||||
# And reconstruct the stream contents to the first 8 bytes (header)
|
||||
# Plus our decompressed body
|
||||
contents = contents[0..7] + body
|
||||
end
|
||||
|
||||
# Determine the nbits of our dimensions rectangle
|
||||
nbits = contents.unpack('C' * contents.length)[8] >> 3
|
||||
|
||||
# Determine how many bits long this entire RECT structure is
|
||||
rectbits = 5 + nbits * 4 # 5 bits for nbits, as well as nbits * number of fields (4)
|
||||
|
||||
# Determine how many bytes rectbits composes (ceil(rectbits/8))
|
||||
rectbytes = (rectbits.to_f / 8).ceil
|
||||
|
||||
# Unpack the RECT structure from the stream in little-endian bit order, then join it into a string
|
||||
rect = contents[8..(8 + rectbytes)].unpack("#{'B8' * rectbytes}").join
|
||||
|
||||
# Read in nbits incremenets starting from 5
|
||||
dimensions = []
|
||||
4.times do |n|
|
||||
s = 5 + (n * nbits) # Calculate our start index
|
||||
e = s + (nbits - 1) # Calculate our end index
|
||||
dimensions[n] = rect[s..e].to_i(2) # Read that range (binary) and convert it to an integer
|
||||
end
|
||||
|
||||
# The values we have here are in "twips"
|
||||
# 20 twips to a pixel (that's why SWFs are fuzzy sometimes!)
|
||||
width = (dimensions[1] - dimensions[0]) / 20
|
||||
height = (dimensions[3] - dimensions[2]) / 20
|
||||
|
||||
[width, height]
|
||||
[metadata.width.ceil.clamp(1..), metadata.height.ceil.clamp(1..)]
|
||||
end
|
||||
|
||||
memoize :dimensions
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
require_relative "base"
|
||||
|
||||
CurrentUser.user = User.system
|
||||
condition = ENV.fetch("COND", "TRUE")
|
||||
fix = ENV.fetch("FIX", "false").truthy?
|
||||
|
||||
@@ -17,6 +18,7 @@ MediaAsset.active.where(condition).find_each do |asset|
|
||||
# Setting `file` updates the metadata if it's different.
|
||||
asset.file = media_file
|
||||
asset.media_metadata.file = media_file
|
||||
asset.post.assign_attributes(image_width: asset.image_width, image_height: asset.image_height, file_ext: asset.file_ext, file_size: asset.file_size) if asset.post.present?
|
||||
|
||||
old = asset.media_metadata.metadata_was.to_h
|
||||
new = asset.media_metadata.metadata.to_h
|
||||
@@ -24,6 +26,7 @@ MediaAsset.active.where(condition).find_each do |asset|
|
||||
puts ({ id: asset.id, **asset.changes, **metadata_changes }).to_json
|
||||
|
||||
if fix
|
||||
asset.post.save! if asset.post&.changed?
|
||||
asset.save! if asset.changed?
|
||||
asset.media_metadata.save! if asset.media_metadata.changed?
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ class MediaFileTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
should "determine the correct dimensions for a flash file" do
|
||||
assert_equal([607, 756], MediaFile.open("test/files/compressed.swf").dimensions)
|
||||
assert_equal([608, 757], MediaFile.open("test/files/compressed.swf").dimensions)
|
||||
end
|
||||
|
||||
should "work if called twice" do
|
||||
@@ -55,8 +55,8 @@ class MediaFileTest < ActiveSupport::TestCase
|
||||
assert_equal([500, 335], mf.dimensions)
|
||||
|
||||
mf = MediaFile.open("test/files/compressed.swf")
|
||||
assert_equal([607, 756], mf.dimensions)
|
||||
assert_equal([607, 756], mf.dimensions)
|
||||
assert_equal([608, 757], mf.dimensions)
|
||||
assert_equal([608, 757], mf.dimensions)
|
||||
end
|
||||
|
||||
should "work for a video if called twice" do
|
||||
|
||||
Reference in New Issue
Block a user