diff options
Diffstat (limited to 'app/models/media_attachment.rb')
-rw-r--r-- | app/models/media_attachment.rb | 160 |
1 files changed, 98 insertions, 62 deletions
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 70a671b4a..e05879188 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -24,16 +24,18 @@ class MediaAttachment < ApplicationRecord self.inheritance_column = nil - enum type: [:image, :gifv, :video, :audio, :unknown] + enum type: [:image, :gifv, :video, :unknown, :audio] - IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].freeze - VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v', '.mov'].freeze - AUDIO_FILE_EXTENSIONS = ['.mp3', '.m4a', '.wav', '.ogg'].freeze + MAX_DESCRIPTION_LENGTH = 1_500 - IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze - VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime'].freeze - VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm', 'video/quicktime'].freeze - AUDIO_MIME_TYPES = ['audio/mpeg', 'audio/mp4', 'audio/vnd.wav', 'audio/wav', 'audio/x-wav', 'audio/x-wave', 'audio/ogg',].freeze + IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif).freeze + VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze + AUDIO_FILE_EXTENSIONS = %w(.ogg .oga .mp3 .wav .flac .opus .aac .m4a .3gp .wma).freeze + + IMAGE_MIME_TYPES = %w(image/jpeg image/png image/gif).freeze + VIDEO_MIME_TYPES = %w(video/webm video/mp4 video/quicktime video/ogg).freeze + VIDEO_CONVERTIBLE_MIME_TYPES = %w(video/webm video/quicktime).freeze + AUDIO_MIME_TYPES = %w(audio/wave audio/wav audio/x-wav audio/x-pn-wave audio/ogg audio/mpeg audio/mp3 audio/webm audio/flac audio/aac audio/m4a audio/x-m4a audio/mp4 audio/3gpp video/x-ms-asf).freeze BLURHASH_OPTIONS = { x_comp: 4, @@ -53,55 +55,73 @@ class MediaAttachment < ApplicationRecord }, }.freeze - AUDIO_STYLES = { + VIDEO_STYLES = { + small: { + convert_options: { + output: { + 'loglevel' => 'fatal', + vf: 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease', + }, + }, + format: 'png', + time: 0, + file_geometry_parser: FastGeometryParser, + blurhash: BLURHASH_OPTIONS, + }, + original: { - format: 'mp4', + keep_same_format: true, convert_options: { output: { - filter_complex: '"[0:a]compand,showwaves=s=640x360:mode=line,format=yuv420p[v]"', - map: '"[v]" -map 0:a', - threads: 2, - vcodec: 'libx264', - acodec: 'aac', - movflags: '+faststart', + 'loglevel' => 'fatal', + 'map_metadata' => '-1', + 'c:v' => 'copy', + 'c:a' => 'copy', }, }, }, }.freeze - VIDEO_STYLES = { - small: { + AUDIO_STYLES = { + original: { + format: 'mp3', + content_type: 'audio/mpeg', convert_options: { output: { - vf: 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease', + 'loglevel' => 'fatal', + 'map_metadata' => '-1', + 'q:a' => 2, }, }, - format: 'png', - time: 0, - file_geometry_parser: FastGeometryParser, - blurhash: BLURHASH_OPTIONS, }, }.freeze VIDEO_FORMAT = { format: 'mp4', + content_type: 'video/mp4', convert_options: { output: { 'loglevel' => 'fatal', 'movflags' => 'faststart', - 'pix_fmt' => 'yuv420p', - 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', - 'vsync' => 'cfr', - 'c:v' => 'h264', - 'b:v' => '500K', - 'maxrate' => '1300K', - 'bufsize' => '1300K', - 'crf' => 18, + 'pix_fmt' => 'yuv420p', + 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', + 'vsync' => 'cfr', + 'c:v' => 'h264', + 'maxrate' => '1300K', + 'bufsize' => '1300K', + 'frames:v' => 60 * 60 * 3, + 'crf' => 18, + 'map_metadata' => '-1', }, }, }.freeze - IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 8.megabytes).to_i + VIDEO_CONVERTED_STYLES = { + small: VIDEO_STYLES[:small], + original: VIDEO_FORMAT, + }.freeze + + IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i belongs_to :account, inverse_of: :media_attachments, optional: true @@ -111,22 +131,23 @@ class MediaAttachment < ApplicationRecord has_attached_file :file, styles: ->(f) { file_styles f }, processors: ->(f) { file_processors f }, - convert_options: { all: '-quality 90 -strip' } + convert_options: { all: '-quality 90 -strip +set modify-date +set create-date' } validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES - validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :video_or_gifv? - validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :video_or_gifv? - remotable_attachment :file, VIDEO_LIMIT + validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :larger_media_format? + validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :larger_media_format? + remotable_attachment :file, VIDEO_LIMIT, suppress_errors: false include Attachmentable validates :account, presence: true - validates :description, length: { maximum: 420 }, if: :local? + validates :description, length: { maximum: MAX_DESCRIPTION_LENGTH }, if: :local? scope :attached, -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) } scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) } scope :local, -> { where(remote_url: '') } scope :remote, -> { where.not(remote_url: '') } + scope :cached, -> { remote.where.not(file_file_name: nil) } default_scope { order(id: :asc) } @@ -138,8 +159,12 @@ class MediaAttachment < ApplicationRecord file.blank? && remote_url.present? end - def video_or_gifv? - video? || gifv? + def larger_media_format? + video? || gifv? || audio? + end + + def audio_or_video? + audio? || video? end def to_param @@ -171,37 +196,37 @@ class MediaAttachment < ApplicationRecord before_save :set_meta class << self + def supported_mime_types + IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES + end + + def supported_file_extensions + IMAGE_FILE_EXTENSIONS + VIDEO_FILE_EXTENSIONS + AUDIO_FILE_EXTENSIONS + end + private def file_styles(f) - if f.instance.file_content_type == 'image/gif' - { - small: IMAGE_STYLES[:small], - original: VIDEO_FORMAT, - } - elsif IMAGE_MIME_TYPES.include? f.instance.file_content_type + if f.instance.file_content_type == 'image/gif' || VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type) + VIDEO_CONVERTED_STYLES + elsif IMAGE_MIME_TYPES.include?(f.instance.file_content_type) IMAGE_STYLES - elsif AUDIO_MIME_TYPES.include? f.instance.file_content_type - AUDIO_STYLES - elsif VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type) - { - small: VIDEO_STYLES[:small], - original: VIDEO_FORMAT, - } - else + elsif VIDEO_MIME_TYPES.include?(f.instance.file_content_type) VIDEO_STYLES + else + AUDIO_STYLES end end def file_processors(f) if f.file_content_type == 'image/gif' [:gif_transcoder, :blurhash_transcoder] - elsif VIDEO_MIME_TYPES.include? f.file_content_type - [:video_transcoder, :blurhash_transcoder] - elsif AUDIO_MIME_TYPES.include? f.file_content_type - [:audio_transcoder] + elsif VIDEO_MIME_TYPES.include?(f.file_content_type) + [:video_transcoder, :blurhash_transcoder, :type_corrector] + elsif AUDIO_MIME_TYPES.include?(f.file_content_type) + [:transcoder, :type_corrector] else - [:lazy_thumbnail, :blurhash_transcoder] + [:lazy_thumbnail, :blurhash_transcoder, :type_corrector] end end end @@ -220,16 +245,26 @@ class MediaAttachment < ApplicationRecord end def prepare_description - self.description = description.strip[0...420] unless description.nil? + self.description = description.strip[0...MAX_DESCRIPTION_LENGTH] unless description.nil? end def set_type_and_extension - self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : AUDIO_MIME_TYPES.include?(file_content_type) ? :audio : :image + self.type = begin + if VIDEO_MIME_TYPES.include?(file_content_type) + :video + elsif AUDIO_MIME_TYPES.include?(file_content_type) + :audio + else + :image + end + end end def set_meta meta = populate_meta + return if meta == {} + file.instance_write :meta, meta end @@ -252,7 +287,7 @@ class MediaAttachment < ApplicationRecord width: width, height: height, size: "#{width}x#{height}", - aspect: width.to_f / height.to_f, + aspect: width.to_f / height, } end @@ -267,11 +302,12 @@ class MediaAttachment < ApplicationRecord frame_rate: movie.frame_rate, duration: movie.duration, bitrate: movie.bitrate, - } + }.compact end def reset_parent_cache return if status_id.nil? + Rails.cache.delete("statuses/#{status_id}") end end |