From a84850c094bafbeb46ce4c073ef5f4d1613385f0 Mon Sep 17 00:00:00 2001 From: Toks Date: Mon, 30 Jun 2014 13:21:07 -0400 Subject: [PATCH] WebM support --- Gemfile | 1 + app/models/amazon_backup.rb | 2 +- app/models/post.rb | 18 ++++- app/models/upload.rb | 75 ++++++++++++++++--- app/presenters/post_presenter.rb | 2 + app/views/moderator/post/queues/show.html.erb | 2 +- .../posts/partials/show/_information.html.erb | 2 +- app/views/posts/partials/show/_video.html.erb | 3 + 8 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 app/views/posts/partials/show/_video.html.erb diff --git a/Gemfile b/Gemfile index 5fadd1c2b..84ce70366 100644 --- a/Gemfile +++ b/Gemfile @@ -43,6 +43,7 @@ gem 'statistics2' gem 'capistrano' gem 'capistrano-ext' gem 'radix62', '~> 1.0.1' +gem 'streamio-ffmpeg' # needed for looser jpeg header compat gem 'ruby-imagespec', :require => "image_spec", :git => "https://github.com/r888888888/ruby-imagespec.git", :branch => "exif-fixes" diff --git a/app/models/amazon_backup.rb b/app/models/amazon_backup.rb index c7d3eeb3c..8f4c20ed2 100644 --- a/app/models/amazon_backup.rb +++ b/app/models/amazon_backup.rb @@ -27,7 +27,7 @@ class AmazonBackup < ActiveRecord::Base AWS::S3::S3Object.store(File.basename(post.file_path), open(post.file_path, "rb"), Danbooru.config.amazon_s3_bucket_name, "Content-MD5" => base64_md5) end - if post.is_image? && File.exists?(post.preview_file_path) + if post.has_preview? && File.exists?(post.preview_file_path) AWS::S3::S3Object.store("preview/#{post.md5}.jpg", open(post.preview_file_path, "rb"), Danbooru.config.amazon_s3_bucket_name) end diff --git a/app/models/post.rb b/app/models/post.rb index 873d7f8fc..8ce7c6b43 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -44,13 +44,13 @@ class Post < ActiveRecord::Base module FileMethods def distribute_files RemoteFileManager.new(file_path).distribute - RemoteFileManager.new(preview_file_path).distribute if is_image? + RemoteFileManager.new(preview_file_path).distribute if has_preview? RemoteFileManager.new(large_file_path).distribute if has_large? end def delete_remote_files RemoteFileManager.new(file_path).delete - RemoteFileManager.new(preview_file_path).delete if is_image? + RemoteFileManager.new(preview_file_path).delete if has_preview? RemoteFileManager.new(large_file_path).delete if has_large? end @@ -93,7 +93,7 @@ class Post < ActiveRecord::Base end def preview_file_url - if !is_image? + if !has_preview? return "/images/download-preview.png" end @@ -127,6 +127,18 @@ class Post < ActiveRecord::Base def is_flash? file_ext =~ /swf/i end + + def is_video? + file_ext =~ /webm/i + end + + def has_preview? + is_image? || is_video? + end + + def has_dimensions? + is_image? || is_flash? || is_video? + end end module ImageMethods diff --git a/app/models/upload.rb b/app/models/upload.rb index 9316ea142..a28bbf5a3 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -51,7 +51,7 @@ class Upload < ActiveRecord::Base def validate_file_content_type unless is_valid_content_type? - raise "invalid content type (only JPEG, PNG, GIF, and SWF files are allowed)" + raise "invalid content type (only JPEG, PNG, GIF, SWF, and WebM files are allowed)" end end @@ -72,6 +72,18 @@ class Upload < ActiveRecord::Base return false end end + + def validate_no_audio + if is_video? && video.audio_channels.present? + raise "video must not have audio channels" + end + end + + def validate_video_duration + if is_video? && video.duration > 120 + raise "video must not be longer than 2 minutes" + end + end end module ConversionMethods @@ -90,6 +102,8 @@ class Upload < ActiveRecord::Base calculate_hash(file_path) validate_md5_uniqueness validate_md5_confirmation + validate_no_audio + validate_video_duration calculate_file_size(file_path) if has_dimensions? calculate_dimensions(file_path) @@ -151,15 +165,23 @@ class Upload < ActiveRecord::Base end def is_image? - ["jpg", "gif", "png"].include?(file_ext) + %w(jpg gif png).include?(file_ext) + end + + def is_flash? + %w(swf).include?(file_ext) + end + + def is_video? + %w(webm).include?(file_ext) end end module ResizerMethods def generate_resizes(source_path) - if is_image? - generate_resize_for(Danbooru.config.small_image_width, Danbooru.config.small_image_width, source_path, 85) - generate_resize_for(Danbooru.config.large_image_width, nil, source_path) if image_width > Danbooru.config.large_image_width + generate_resize_for(Danbooru.config.small_image_width, Danbooru.config.small_image_width, source_path, 85) + if is_image? && image_width > Danbooru.config.large_image_width + generate_resize_for(Danbooru.config.large_image_width, nil, source_path) end end @@ -168,29 +190,45 @@ class Upload < ActiveRecord::Base raise Error.new("file not found") end - Danbooru.resize(source_path, resized_file_path_for(width), width, height, quality) + if is_image? + Danbooru.resize(source_path, resized_file_path_for(width), width, height, quality) + elsif is_video? + dimension_ratio = image_width.to_f / image_height + if dimension_ratio > 1 + height = (width / dimension_ratio).to_i + else + width = (height * dimension_ratio).to_i + end + video.screenshot(resized_file_path_for(width), {:seek_time => 0, :resolution => "#{width}x#{height}"}) + FileUtils.chmod(0664, resized_file_path_for(width)) + end end end module DimensionMethods # Figures out the dimensions of the image. def calculate_dimensions(file_path) - File.open(file_path, "rb") do |file| - image_size = ImageSpec.new(file) - self.image_width = image_size.width - self.image_height = image_size.height + if is_video? + self.image_width = video.width + self.image_height = video.height + else + File.open(file_path, "rb") do |file| + image_size = ImageSpec.new(file) + self.image_width = image_size.width + self.image_height = image_size.height + end end end # Does this file have image dimensions? def has_dimensions? - %w(jpg gif png swf).include?(file_ext) + %w(jpg gif png swf webm).include?(file_ext) end end module ContentTypeMethods def is_valid_content_type? - file_ext =~ /jpg|gif|png|swf/ + file_ext =~ /jpg|gif|png|swf|webm/ end def content_type_to_file_ext(content_type) @@ -207,6 +245,9 @@ class Upload < ActiveRecord::Base when "application/x-shockwave-flash" "swf" + when "video/webm" + "webm" + else "bin" end @@ -226,6 +267,9 @@ class Upload < ActiveRecord::Base when /^CWS/, /^FWS/, /^ZWS/ "application/x-shockwave-flash" + when /^\x1a\x45\xdf\xa3/ + "video/webm" + else "application/octet-stream" end @@ -320,6 +364,12 @@ class Upload < ActiveRecord::Base end end + module VideoMethods + def video + @video ||= FFMPEG::Movie.new(file_path) + end + end + module SearchMethods def uploaded_by(user_id) where("uploader_id = ?", user_id) @@ -381,6 +431,7 @@ class Upload < ActiveRecord::Base include CgiFileMethods include StatusMethods include UploaderMethods + include VideoMethods extend SearchMethods include ApiMethods diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb index 2dc528e25..ffa8fed46 100644 --- a/app/presenters/post_presenter.rb +++ b/app/presenters/post_presenter.rb @@ -155,6 +155,8 @@ class PostPresenter < Presenter if @post.is_flash? template.render("posts/partials/show/flash", :post => @post) + elsif @post.is_video? + template.render("posts/partials/show/video", :post => @post) elsif !@post.is_image? template.render("posts/partials/show/download", :post => @post) elsif @post.is_image? diff --git a/app/views/moderator/post/queues/show.html.erb b/app/views/moderator/post/queues/show.html.erb index e20bfcf34..5abf215ac 100644 --- a/app/views/moderator/post/queues/show.html.erb +++ b/app/views/moderator/post/queues/show.html.erb @@ -38,7 +38,7 @@
  • Size: <%= number_to_human_size(post.file_size) %> - <% if post.is_image? %> + <% if post.has_dimensions? %> (<%= post.image_width %>x<%= post.image_height %>) <% end %>
  • diff --git a/app/views/posts/partials/show/_information.html.erb b/app/views/posts/partials/show/_information.html.erb index b42a5c943..fbd51e4f7 100644 --- a/app/views/posts/partials/show/_information.html.erb +++ b/app/views/posts/partials/show/_information.html.erb @@ -7,7 +7,7 @@ <% end %>
  • Size: <%= link_to_if post.visible?, number_to_human_size(post.file_size), post.file_url %> - <% if post.is_image? %> + <% if post.has_dimensions? %> (<%= post.image_width %>x<%= post.image_height %>) <% end %>
  • diff --git a/app/views/posts/partials/show/_video.html.erb b/app/views/posts/partials/show/_video.html.erb new file mode 100644 index 000000000..aa0090c38 --- /dev/null +++ b/app/views/posts/partials/show/_video.html.erb @@ -0,0 +1,3 @@ +<%= content_tag(:video, nil, :width => post.image_width, :height => post.image_height, :autoplay => true, :loop => true, :src => post.file_url) %> + +

    <%= link_to "Save this video (right click and save)", post.file_url %>