You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
3.9 KiB

  1. # frozen_string_literal: true
  2. class MediaAttachment < ApplicationRecord
  3. self.inheritance_column = nil
  4. enum type: [:image, :gifv, :video, :unknown]
  5. IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
  6. VIDEO_MIME_TYPES = ['video/webm', 'video/mp4'].freeze
  7. IMAGE_STYLES = { original: '1280x1280>', small: '400x400>' }.freeze
  8. VIDEO_STYLES = {
  9. small: {
  10. convert_options: {
  11. output: {
  12. vf: 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease',
  13. },
  14. },
  15. format: 'png',
  16. time: 0,
  17. },
  18. }.freeze
  19. belongs_to :account, inverse_of: :media_attachments
  20. belongs_to :status, inverse_of: :media_attachments
  21. has_attached_file :file,
  22. styles: ->(f) { file_styles f },
  23. processors: ->(f) { file_processors f },
  24. convert_options: { all: '-quality 90 -strip' }
  25. validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES
  26. validates_attachment_size :file, less_than: 8.megabytes
  27. validates :account, presence: true
  28. scope :attached, -> { where.not(status_id: nil) }
  29. scope :local, -> { where(remote_url: '') }
  30. default_scope { order('id asc') }
  31. def local?
  32. remote_url.blank?
  33. end
  34. def file_remote_url=(url)
  35. self.file = URI.parse(Addressable::URI.parse(url).normalize.to_s)
  36. end
  37. def to_param
  38. shortcode
  39. end
  40. before_create :set_shortcode
  41. before_post_process :set_type_and_extension
  42. before_save :set_meta
  43. class << self
  44. private
  45. def file_styles(f)
  46. if f.instance.file_content_type == 'image/gif'
  47. {
  48. small: IMAGE_STYLES[:small],
  49. original: {
  50. format: 'mp4',
  51. convert_options: {
  52. output: {
  53. 'movflags' => 'faststart',
  54. 'pix_fmt' => 'yuv420p',
  55. 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'',
  56. 'vsync' => 'cfr',
  57. 'b:v' => '1300K',
  58. 'maxrate' => '500K',
  59. 'crf' => 6,
  60. },
  61. },
  62. },
  63. }
  64. elsif IMAGE_MIME_TYPES.include? f.instance.file_content_type
  65. IMAGE_STYLES
  66. else
  67. VIDEO_STYLES
  68. end
  69. end
  70. def file_processors(f)
  71. if f.file_content_type == 'image/gif'
  72. [:gif_transcoder]
  73. elsif VIDEO_MIME_TYPES.include? f.file_content_type
  74. [:video_transcoder]
  75. else
  76. [:thumbnail]
  77. end
  78. end
  79. end
  80. private
  81. def set_shortcode
  82. self.type = :unknown if file.blank? && type.blank?
  83. return unless local?
  84. loop do
  85. self.shortcode = SecureRandom.urlsafe_base64(14)
  86. break if MediaAttachment.find_by(shortcode: shortcode).nil?
  87. end
  88. end
  89. def set_type_and_extension
  90. self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : :image
  91. extension = appropriate_extension
  92. basename = Paperclip::Interpolations.basename(file, :original)
  93. file.instance_write :file_name, [basename, extension].delete_if(&:empty?).join('.')
  94. end
  95. def set_meta
  96. meta = populate_meta
  97. return if meta == {}
  98. file.instance_write :meta, meta
  99. end
  100. def populate_meta
  101. meta = {}
  102. file.queued_for_write.each do |style, file|
  103. begin
  104. geo = Paperclip::Geometry.from_file file
  105. meta[style] = {
  106. width: geo.width.to_i,
  107. height: geo.height.to_i,
  108. size: "#{geo.width.to_i}x#{geo.height.to_i}",
  109. aspect: geo.width.to_f / geo.height.to_f,
  110. }
  111. rescue Paperclip::Errors::NotIdentifiedByImageMagickError
  112. meta[style] = {}
  113. end
  114. end
  115. meta
  116. end
  117. def appropriate_extension
  118. mime_type = MIME::Types[file.content_type]
  119. extensions_for_mime_type = mime_type.empty? ? [] : mime_type.first.extensions
  120. original_extension = Paperclip::Interpolations.extension(file, :original)
  121. extensions_for_mime_type.include?(original_extension) ? original_extension : extensions_for_mime_type.first
  122. end
  123. end