uploads: refactor media asset validation logic.
Refactor the upload validation logic to not depend on the current user. Fixes several broken upload tests.
This commit is contained in:
@@ -105,6 +105,11 @@ class MediaFile
|
|||||||
dimensions.second
|
dimensions.second
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Integer] the resolution of the file
|
||||||
|
def resolution
|
||||||
|
width * height
|
||||||
|
end
|
||||||
|
|
||||||
# @return [String] the MD5 hash of the file, as a hex string.
|
# @return [String] the MD5 hash of the file, as a hex string.
|
||||||
def md5
|
def md5
|
||||||
Digest::MD5.file(file.path).hexdigest
|
Digest::MD5.file(file.path).hexdigest
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class PostReplacementProcessor
|
|||||||
if media_file.md5 == post.md5
|
if media_file.md5 == post.md5
|
||||||
media_asset = post.media_asset
|
media_asset = post.media_asset
|
||||||
else
|
else
|
||||||
|
MediaAsset.validate_media_file!(media_file, replacement.creator)
|
||||||
media_asset = MediaAsset.upload!(media_file)
|
media_asset = MediaAsset.upload!(media_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+19
-26
@@ -6,6 +6,7 @@ class MediaAsset < ApplicationRecord
|
|||||||
FILE_TYPES = %w[jpg png gif mp4 webm swf zip]
|
FILE_TYPES = %w[jpg png gif mp4 webm swf zip]
|
||||||
FILE_KEY_LENGTH = 9
|
FILE_KEY_LENGTH = 9
|
||||||
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
|
VARIANTS = %i[preview 180x180 360x360 720x720 sample original]
|
||||||
|
MAX_FILE_SIZE = Danbooru.config.max_file_size.to_i
|
||||||
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
|
MAX_VIDEO_DURATION = Danbooru.config.max_video_duration.to_i
|
||||||
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
|
MAX_IMAGE_RESOLUTION = Danbooru.config.max_image_resolution
|
||||||
MAX_IMAGE_WIDTH = Danbooru.config.max_image_width
|
MAX_IMAGE_WIDTH = Danbooru.config.max_image_width
|
||||||
@@ -45,10 +46,7 @@ class MediaAsset < ApplicationRecord
|
|||||||
|
|
||||||
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }, if: :md5_changed?
|
validates :md5, uniqueness: { conditions: -> { where(status: [:processing, :active]) } }, if: :md5_changed?
|
||||||
validates :file_ext, inclusion: { in: FILE_TYPES, message: "File is not an image or video" }
|
validates :file_ext, inclusion: { in: FILE_TYPES, message: "File is not an image or video" }
|
||||||
validates :file_size, numericality: { less_than_or_equal_to: Danbooru.config.max_file_size, message: ->(asset, _) { "too large (size: #{asset.file_size.to_formatted_s(:human_size)}; max size: #{Danbooru.config.max_file_size.to_formatted_s(:human_size)})" } }
|
|
||||||
validates :file_key, length: { is: FILE_KEY_LENGTH }, uniqueness: true, if: :file_key_changed?
|
validates :file_key, length: { is: FILE_KEY_LENGTH }, uniqueness: true, if: :file_key_changed?
|
||||||
validate :validate_duration, on: :create
|
|
||||||
validate :validate_resolution, on: :create
|
|
||||||
|
|
||||||
before_create :initialize_file_key
|
before_create :initialize_file_key
|
||||||
|
|
||||||
@@ -248,8 +246,6 @@ class MediaAsset < ApplicationRecord
|
|||||||
def upload!(media_file, &block)
|
def upload!(media_file, &block)
|
||||||
media_file = MediaFile.open(media_file) unless media_file.is_a?(MediaFile)
|
media_file = MediaFile.open(media_file) unless media_file.is_a?(MediaFile)
|
||||||
|
|
||||||
raise Error, "File is corrupt" if media_file.is_corrupt?
|
|
||||||
|
|
||||||
media_asset = create!(file: media_file, status: :processing)
|
media_asset = create!(file: media_file, status: :processing)
|
||||||
yield media_asset if block_given?
|
yield media_asset if block_given?
|
||||||
|
|
||||||
@@ -294,6 +290,24 @@ class MediaAsset < ApplicationRecord
|
|||||||
media_asset&.update!(status: :failed)
|
media_asset&.update!(status: :failed)
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_media_file!(media_file, uploader)
|
||||||
|
if !media_file.file_ext.to_s.in?(FILE_TYPES)
|
||||||
|
raise Error, "File is not an image or video"
|
||||||
|
elsif media_file.is_corrupt?
|
||||||
|
raise Error, "File is corrupt"
|
||||||
|
elsif media_file.file_size > MAX_FILE_SIZE
|
||||||
|
raise Error, "File size too large (size: #{media_file.file_size.to_formatted_s(:human_size)}; max size: #{MAX_FILE_SIZE.to_formatted_s(:human_size)})"
|
||||||
|
elsif media_file.resolution > MAX_IMAGE_RESOLUTION
|
||||||
|
raise Error, "Image resolution is too large (resolution: #{(media_file.resolution / 1_000_000.0).round(1)} megapixels (#{media_file.width}x#{media_file.height}); max: #{MAX_IMAGE_RESOLUTION / 1_000_000} megapixels)"
|
||||||
|
elsif media_file.width > MAX_IMAGE_WIDTH
|
||||||
|
raise Error, "Image width is too large (width: #{media_file.width}; max width: #{MAX_IMAGE_WIDTH})"
|
||||||
|
elsif media_file.height > MAX_IMAGE_HEIGHT
|
||||||
|
raise Error, "Image height is too large (height: #{media_file.height}; max height: #{MAX_IMAGE_HEIGHT})"
|
||||||
|
elsif media_file.duration.to_i > MAX_VIDEO_DURATION && !uploader.is_admin?
|
||||||
|
raise Error, "Duration must be less than #{MAX_VIDEO_DURATION} seconds"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def file=(file_or_path)
|
def file=(file_or_path)
|
||||||
@@ -397,27 +411,6 @@ class MediaAsset < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
concerning :ValidationMethods do
|
|
||||||
def validate_resolution
|
|
||||||
resolution = image_width * image_height
|
|
||||||
|
|
||||||
if resolution > MAX_IMAGE_RESOLUTION
|
|
||||||
errors.add(:base, "Image resolution is too large (resolution: #{(resolution / 1_000_000.0).round(1)} megapixels (#{image_width}x#{image_height}); max: #{MAX_IMAGE_RESOLUTION / 1_000_000} megapixels)")
|
|
||||||
elsif image_width > MAX_IMAGE_WIDTH
|
|
||||||
errors.add(:image_width, "is too large (width: #{image_width}; max width: #{MAX_IMAGE_WIDTH})")
|
|
||||||
elsif image_height > MAX_IMAGE_HEIGHT
|
|
||||||
errors.add(:image_height, "is too large (height: #{image_height}; max height: #{MAX_IMAGE_HEIGHT})")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_duration
|
|
||||||
return if CurrentUser.user.is_admin?
|
|
||||||
if duration.to_i > MAX_VIDEO_DURATION
|
|
||||||
errors.add(:base, "duration must be less than #{MAX_VIDEO_DURATION} seconds")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.generate_file_key
|
def self.generate_file_key
|
||||||
loop do
|
loop do
|
||||||
key = SecureRandom.send(:choose, [*"0".."9", *"A".."Z", *"a".."z"], FILE_KEY_LENGTH) # base62
|
key = SecureRandom.send(:choose, [*"0".."9", *"A".."Z", *"a".."z"], FILE_KEY_LENGTH) # base62
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ class UploadMediaAsset < ApplicationRecord
|
|||||||
media_file = source_extractor.download_file!(source_url)
|
media_file = source_extractor.download_file!(source_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
MediaAsset.validate_media_file!(media_file, upload.uploader)
|
||||||
MediaAsset.upload!(media_file) do |media_asset|
|
MediaAsset.upload!(media_file) do |media_asset|
|
||||||
update!(media_asset: media_asset)
|
update!(media_asset: media_asset)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user