kelredd-pruview 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kelredd-pruview might be problematic. Click here for more details.
- data/README.rdoc +62 -0
- data/Rakefile +41 -0
- data/features/document.feature +21 -0
- data/features/step_definitions/common_steps.rb +32 -0
- data/features/step_definitions/document_steps.rb +16 -0
- data/features/step_definitions/support/env.rb +20 -0
- data/features/step_definitions/support/helpers.rb +5 -0
- data/features/step_definitions/video_image_steps.rb +16 -0
- data/features/step_definitions/video_steps.rb +16 -0
- data/features/video.feature +26 -0
- data/features/video_image.feature +20 -0
- data/lib/pruview.rb +8 -0
- data/lib/pruview/USWebCoatedSWOP.icc +0 -0
- data/lib/pruview/document.rb +138 -0
- data/lib/pruview/exceptions.rb +8 -0
- data/lib/pruview/sRGB.icm +0 -0
- data/lib/pruview/version.rb +13 -0
- data/lib/pruview/video.rb +130 -0
- data/lib/pruview/video_image.rb +40 -0
- data/test/files/basic.jpg +0 -0
- data/test/files/basic.mpg +0 -0
- data/test/files/error.jpg +0 -0
- data/test/files/error.mov +0 -0
- data/test/files/invalid_format.poop +0 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/pruview_test.rb +13 -0
- metadata +103 -0
data/README.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= Pruview
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
A gem to ease generating image previews (thumbnails) of various files
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
# install image magick with whatever build dependencies you need for image handling, etc...)
|
10
|
+
# if you want to preview movie files
|
11
|
+
# => install ffmpeg with whatever build dependencies you need for video/audio handling, etc...)
|
12
|
+
# if you want to preview postscript files (PDFs, Adobe Illustrator, etc...)
|
13
|
+
# => install any postscript handling libs or dependencies for image magick
|
14
|
+
|
15
|
+
sudo gem install kelredd-pruview --source http://gems.github.com
|
16
|
+
|
17
|
+
== Dependencies
|
18
|
+
|
19
|
+
* image magick + any special file type handling dependencies
|
20
|
+
* ffmpeg + h264 encoder + any special file type handling dependencies (for video previewing)
|
21
|
+
# something like 'sudo port install ffmpeg +gpl +lame +x264 +xvid' on Mac OSX Leopard
|
22
|
+
* gawk
|
23
|
+
* Rubygems: mini_magick
|
24
|
+
|
25
|
+
== Usage
|
26
|
+
|
27
|
+
require 'pruview'
|
28
|
+
|
29
|
+
# Preview a document (using image magick)
|
30
|
+
document = Pruview::Document.new(<document_file_path>, <pruview_output_path>)
|
31
|
+
doc_image_thumb_path = document.to_jpg(<thumb_file_name>, <thumb_pixel_width>, <thumb_pixel_height)
|
32
|
+
|
33
|
+
# Preview a video (ffmpeg to get an image from movie, then image magick to resize that image)
|
34
|
+
video_image_path = Pruview::VideoImage.to_jpg(<video_file_path>, <pruview_output_path>, <video_image_file_name>)
|
35
|
+
document = Pruview::Document.new(video_image_path, <pruview_output_path>)
|
36
|
+
video_image_thumb_path = document.to_jpg(<thumb_file_name>, <thumb_pixel_width>, <thumb_pixel_height)
|
37
|
+
|
38
|
+
|
39
|
+
== License
|
40
|
+
|
41
|
+
Copyright (c) 2007-2009 Kelly Redding
|
42
|
+
|
43
|
+
Permission is hereby granted, free of charge, to any person
|
44
|
+
obtaining a copy of this software and associated documentation
|
45
|
+
files (the "Software"), to deal in the Software without
|
46
|
+
restriction, including without limitation the rights to use,
|
47
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
48
|
+
copies of the Software, and to permit persons to whom the
|
49
|
+
Software is furnished to do so, subject to the following
|
50
|
+
conditions:
|
51
|
+
|
52
|
+
The above copyright notice and this permission notice shall be
|
53
|
+
included in all copies or substantial portions of the Software.
|
54
|
+
|
55
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
56
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
57
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
58
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
59
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
60
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
61
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
62
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
require 'lib/pruview/version'
|
6
|
+
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'pruview'
|
11
|
+
s.version = Pruview::Version.to_s
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = %w(README.rdoc)
|
14
|
+
s.rdoc_options = %w(--main README.rdoc)
|
15
|
+
s.summary = "A gem to ease generating image previews (thumbnails) of various files"
|
16
|
+
s.author = 'Kelly Redding'
|
17
|
+
s.email = 'kelly@kelredd.com'
|
18
|
+
s.homepage = ''
|
19
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{features,lib,test}/**/*")
|
20
|
+
# s.executables = ['pruview']
|
21
|
+
|
22
|
+
s.add_dependency('mini_magick')
|
23
|
+
s.add_dependency('flvtool2')
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
27
|
+
pkg.gem_spec = spec
|
28
|
+
end
|
29
|
+
|
30
|
+
Rake::TestTask.new do |t|
|
31
|
+
t.libs << 'test'
|
32
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Generate the gemspec to serve this Gem from Github'
|
37
|
+
task :github do
|
38
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
39
|
+
File.open(file, 'w') {|f| f << spec.to_ruby }
|
40
|
+
puts "Created gemspec: #{file}"
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Feature: Document
|
2
|
+
In order have file thumbnails programmatically
|
3
|
+
As a user
|
4
|
+
I want create document file thumbnails
|
5
|
+
|
6
|
+
Scenario: Basic Image thumbnail
|
7
|
+
Given I have a basic image document
|
8
|
+
When I create a jpg version
|
9
|
+
Then I should have a jpg file
|
10
|
+
|
11
|
+
Scenario: Invalid source image
|
12
|
+
Given I have an invalid image document
|
13
|
+
Then Pruview should complain about an invalid source file
|
14
|
+
|
15
|
+
Scenario: Invalid output path
|
16
|
+
Given I have an invalid output path for documents
|
17
|
+
Then Pruview should complain about an invalid output path
|
18
|
+
|
19
|
+
Scenario: Invalid source format
|
20
|
+
Given I have an invalid format document
|
21
|
+
Then Pruview should complain about an invalid source file format
|
@@ -0,0 +1,32 @@
|
|
1
|
+
When /^I create a[n]* (.+) version$/ do |format|
|
2
|
+
@output = @file.send("to_#{format}", 'file', 50, 50)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^I should have a[n]* (.+) file$/ do |format|
|
6
|
+
assert File.exists?(@output)
|
7
|
+
assert File.extname(@output), format
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^Pruview should complain about an invalid source file$/ do
|
11
|
+
assert @complaint
|
12
|
+
assert_kind_of Pruview::Exceptions::InvalidError, @complaint
|
13
|
+
assert_match /^Invalid source file/, @complaint.message
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^Pruview should complain about an invalid output path$/ do
|
17
|
+
assert @complaint
|
18
|
+
assert_kind_of Pruview::Exceptions::InvalidError, @complaint
|
19
|
+
assert_match /^Invalid target directory/, @complaint.message
|
20
|
+
end
|
21
|
+
|
22
|
+
Then /^Pruview should complain about an invalid source file format$/ do
|
23
|
+
assert @complaint
|
24
|
+
assert_kind_of Pruview::Exceptions::InvalidError, @complaint
|
25
|
+
assert_match /not supported - file extension: .poop/, @complaint.message
|
26
|
+
end
|
27
|
+
|
28
|
+
Then /^Pruview should complain about an error$/ do
|
29
|
+
assert @complaint
|
30
|
+
assert_kind_of Pruview::Exceptions::InvalidError, @complaint
|
31
|
+
assert_match /not supported - file extension: .poop/, @complaint.message
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Given /^I have a[n]* (.+) document$/ do |file_type|
|
2
|
+
begin
|
3
|
+
@file = Pruview::Document.new(FILES[file_type], OUTPUT_PATH)
|
4
|
+
rescue Exception => err
|
5
|
+
@complaint = err
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^I have an invalid output path for documents$/ do
|
10
|
+
begin
|
11
|
+
@file = Pruview::Document.new(FILES['basic image'], INVALID_OUTPUT_PATH)
|
12
|
+
rescue Exception => err
|
13
|
+
@complaint = err
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test/unit/assertions'
|
2
|
+
World(Test::Unit::Assertions)
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/../../../lib/pruview'
|
5
|
+
|
6
|
+
FILES_PATH = "./test/files"
|
7
|
+
OUTPUT_PATH = "./test_output"
|
8
|
+
INVALID_OUTPUT_PATH = "./test_output/invalid"
|
9
|
+
FILES = {
|
10
|
+
'basic image' => "./test/files/basic.jpg",
|
11
|
+
'invalid image' => "./test/files/invalid.jpg",
|
12
|
+
'error image' => "./test/files/error.jpg",
|
13
|
+
'basic video' => "./test/files/basic.mpg",
|
14
|
+
'invalid video' => "./test/files/invalid.mov",
|
15
|
+
'error video' => "./test/files/error.mov",
|
16
|
+
'invalid format' => "./test/files/invalid_format.poop"
|
17
|
+
}
|
18
|
+
|
19
|
+
FileUtils.mkdir_p File.expand_path(FILES_PATH) unless File.exists? File.expand_path(FILES_PATH)
|
20
|
+
FileUtils.mkdir_p File.expand_path(OUTPUT_PATH) unless File.exists? File.expand_path(OUTPUT_PATH)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Given /^I have a[n]* (.+) video image (.+)$/ do |file_type, format|
|
2
|
+
begin
|
3
|
+
@output = Pruview::VideoImage.send("to_#{format}", FILES[file_type], OUTPUT_PATH, 'file')
|
4
|
+
rescue Exception => err
|
5
|
+
@complaint = err
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^I have an invalid output path for video images$/ do
|
10
|
+
begin
|
11
|
+
@output = Pruview::VideoImage.to_jpg(FILES['basic video'], INVALID_OUTPUT_PATH, 'file')
|
12
|
+
rescue Exception => err
|
13
|
+
@complaint = err
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Given /^I have a[n]* (.+) file$/ do |file_type|
|
2
|
+
begin
|
3
|
+
@file = Pruview::Video.new(FILES[file_type], OUTPUT_PATH)
|
4
|
+
rescue Exception => err
|
5
|
+
@complaint = err
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^I have an invalid output path for videos$/ do
|
10
|
+
begin
|
11
|
+
@file = Pruview::Video.new(FILES['basic video'], INVALID_OUTPUT_PATH)
|
12
|
+
rescue Exception => err
|
13
|
+
@complaint = err
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Video
|
2
|
+
In order to use video in more universal ways
|
3
|
+
As a user
|
4
|
+
I want convert video to other formats for viewing
|
5
|
+
|
6
|
+
Scenario: Basic video to FLV
|
7
|
+
Given I have a basic video file
|
8
|
+
When I create an flv version
|
9
|
+
Then I should have an flv file
|
10
|
+
|
11
|
+
Scenario: Basic video to MOV
|
12
|
+
Given I have a basic video file
|
13
|
+
When I create an mov version
|
14
|
+
Then I should have an mov file
|
15
|
+
|
16
|
+
Scenario: Invalid source video
|
17
|
+
Given I have an invalid video file
|
18
|
+
Then Pruview should complain about an invalid source file
|
19
|
+
|
20
|
+
Scenario: Invalid output path
|
21
|
+
Given I have an invalid output path for videos
|
22
|
+
Then Pruview should complain about an invalid output path
|
23
|
+
|
24
|
+
Scenario: Invalid source format
|
25
|
+
Given I have an invalid format file
|
26
|
+
Then Pruview should complain about an invalid source file format
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: Video Image
|
2
|
+
In order have video image thumbnails programmatically
|
3
|
+
As a user
|
4
|
+
I want create video image file thumbnails
|
5
|
+
|
6
|
+
Scenario: Basic Image thumbnail
|
7
|
+
Given I have a basic video video image jpg
|
8
|
+
Then I should have a jpg file
|
9
|
+
|
10
|
+
Scenario: Invalid source image
|
11
|
+
Given I have an invalid video video image jpg
|
12
|
+
Then Pruview should complain about an invalid source file
|
13
|
+
|
14
|
+
Scenario: Invalid output path
|
15
|
+
Given I have an invalid output path for video images
|
16
|
+
Then Pruview should complain about an invalid output path
|
17
|
+
|
18
|
+
Scenario: Invalid source format
|
19
|
+
Given I have an invalid format video image jpg
|
20
|
+
Then Pruview should complain about an invalid source file format
|
data/lib/pruview.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mini_magick'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), 'pruview', 'exceptions.rb')
|
6
|
+
require File.join(File.dirname(__FILE__), 'pruview', 'document.rb')
|
7
|
+
require File.join(File.dirname(__FILE__), 'pruview', 'video.rb')
|
8
|
+
require File.join(File.dirname(__FILE__), 'pruview', 'video_image.rb')
|
Binary file
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Pruview
|
2
|
+
|
3
|
+
class Document
|
4
|
+
|
5
|
+
def initialize(source, target_dir)
|
6
|
+
raise Pruview::Exceptions::InvalidError, "Invalid source file: #{source.to_s}" if !File.file?(source)
|
7
|
+
raise Pruview::Exceptions::InvalidError, "Invalid target directory: #{target_dir.to_s}" if !File.directory?(target_dir)
|
8
|
+
raise Pruview::Exceptions::InvalidError, "Document not supported - file extension: " + file_extension(source) if !format_supported?(source)
|
9
|
+
@source = source
|
10
|
+
@target_dir = target_dir
|
11
|
+
@image = process_image(get_image(source))
|
12
|
+
@tempfile = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_jpg(name, width, height, crop = false)
|
16
|
+
scale_img = scale_image(width, height, crop)
|
17
|
+
scale_img.combine_options do |img|
|
18
|
+
img.format 'jpg'
|
19
|
+
img.quality '90'
|
20
|
+
img.interlace 'plane'
|
21
|
+
end
|
22
|
+
target = File.join(@target_dir, name.to_s + '.jpg')
|
23
|
+
scale_img.write(target)
|
24
|
+
return target
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def format_supported?(source)
|
30
|
+
file_ext = file_extension(source)
|
31
|
+
#return true if file_ext == PSD_EXT # don't support photoshop for now
|
32
|
+
POSTSCRIPT_EXT.each { |extension| return true if file_ext == extension }
|
33
|
+
IMAGE_EXT.each { |extension| return true if file_ext == extension }
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_postscript?(source)
|
38
|
+
file_ext = file_extension(source)
|
39
|
+
POSTSCRIPT_EXT.each { |extension| return true if file_ext == extension }
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_extension(source_file)
|
44
|
+
File.extname(source_file).downcase.chomp
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_image(source)
|
48
|
+
source = get_postscript_source(source) if format_postscript?(source)
|
49
|
+
begin
|
50
|
+
return MiniMagick::Image.from_file(source)
|
51
|
+
rescue Exception => err
|
52
|
+
raise "Error reading source image: #{err.message}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_postscript_source(source)
|
57
|
+
begin
|
58
|
+
@tempfile = MiniMagick::ImageTempFile.new("pruview.jpg")
|
59
|
+
@tempfile.binmode
|
60
|
+
ensure
|
61
|
+
@tempfile.close
|
62
|
+
end
|
63
|
+
run_system_command("convert -format jpg \"#{source}[0]\" \"#{@tempfile.path}\"", "Error processing postscript document")
|
64
|
+
return @tempfile.path
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_image(image)
|
68
|
+
image.format PROCESS_FORMAT
|
69
|
+
set_RGB_colorspace(image)
|
70
|
+
image.strip
|
71
|
+
return image
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_RGB_colorspace(image)
|
75
|
+
colorspace = run_system_command("identify -format \"%r\" #{image.path}", "Error reading document colorspace")
|
76
|
+
puts "Colorspace: #{colorspace}"
|
77
|
+
if colorspace =~ /CMYK/
|
78
|
+
image.combine_options do |img|
|
79
|
+
img.profile File.join(File.dirname(__FILE__), 'USWebCoatedSWOP.icc')
|
80
|
+
img.profile File.join(File.dirname(__FILE__), 'sRGB.icm')
|
81
|
+
img.colorspace 'sRGB'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def scale_image(width, height, crop = false)
|
87
|
+
begin
|
88
|
+
image = MiniMagick::Image.from_file(@image.path)
|
89
|
+
crop_image(image, crop)
|
90
|
+
image.resize "#{width}x#{height}" if crop || @image[:width].to_i > width || @image[:height] > height
|
91
|
+
return image
|
92
|
+
rescue Exception => err
|
93
|
+
raise "Error scaling image: #{err.message}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def crop_image(image, ratio)
|
98
|
+
if ratio.kind_of?(Array) && ratio.length == 2
|
99
|
+
ratio_width, ratio_height = ratio
|
100
|
+
puts "image orig size: #{image[:width].to_i}x#{image[:height].to_i}"
|
101
|
+
puts "ratio_width: #{ratio_width}, ratio_height: #{ratio_height}"
|
102
|
+
if ratio_width > ratio_height || (ratio_width == ratio_height && image[:height].to_i > image[:width].to_i)
|
103
|
+
# calc ratio height from width
|
104
|
+
rheight = (image[:width].to_i*(ratio_height.to_f / ratio_width.to_f)).round
|
105
|
+
puts "rheight: #{rheight}"
|
106
|
+
# shave off height
|
107
|
+
shave_off = ((image[:height].to_i - rheight)/2).round
|
108
|
+
puts "shave off height: #{image[:height].to_i - rheight}"
|
109
|
+
image.shave("0x#{shave_off}")
|
110
|
+
puts "image crop size: #{image[:width].to_i}x#{image[:height].to_i}"
|
111
|
+
elsif ratio_height > ratio_width || (ratio_width == ratio_height && image[:width].to_i > image[:height].to_i)
|
112
|
+
# calc ratio width from height
|
113
|
+
rwidth = (image[:height].to_i*(ratio_width.to_f / ratio_height.to_f)).round
|
114
|
+
# shave off width
|
115
|
+
shave_off = ((image[:width].to_i - rwidth).to_f / 2.to_f).round
|
116
|
+
image.shave("#{shave_off}x0")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
return image
|
120
|
+
end
|
121
|
+
|
122
|
+
def run_system_command(command, error_message)
|
123
|
+
output = `#{command}`
|
124
|
+
raise "#{error_message}: error given #{$?}\n#{output}" if $? != 0
|
125
|
+
return output
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
# Configurations
|
131
|
+
Document::PROCESS_FORMAT = 'jpg'
|
132
|
+
|
133
|
+
Document::PSD_EXT = '.psd'
|
134
|
+
Document::POSTSCRIPT_EXT = ['.pdf', '.eps', '.ai']
|
135
|
+
Document::IMAGE_EXT = ['.bmp', '.gif', '.jpg', '.jpeg', '.png', '.tga']
|
136
|
+
|
137
|
+
end
|
138
|
+
|
Binary file
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Pruview
|
2
|
+
|
3
|
+
class Video
|
4
|
+
|
5
|
+
# this class assumes you have 'ffmpeg' and 'flvtool2' installed and in your path
|
6
|
+
|
7
|
+
def initialize(source, target_dir, bitrate_mult = 1)
|
8
|
+
raise Pruview::Exceptions::InvalidError, "Invalid source file:: #{source.to_s}" if !File.file?(source)
|
9
|
+
raise Pruview::Exceptions::InvalidError, "Invalid target directory: #{target_dir.to_s}" if !File.directory?(target_dir)
|
10
|
+
raise Pruview::Exceptions::InvalidError, "Video not supported - file extension: " + file_extension(source) if !format_supported?(source)
|
11
|
+
@source = source
|
12
|
+
@target_dir = target_dir
|
13
|
+
@bitrate_multiplier = bitrate_mult
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_flv(name, width, height, scale_static = false)
|
17
|
+
target = to_base(name, width, height, '.flv', scale_static)
|
18
|
+
run("#{FLVTOOL} -U #{target}", "Unable to add meta-data for #{target}.")
|
19
|
+
return target
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_mov(name, width, height, scale_static = false)
|
23
|
+
target = to_base(name, width, height, '.mov', scale_static)
|
24
|
+
# TODO: run qt-faststart
|
25
|
+
return target
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def to_base(name, width, height, extension, scale_static)
|
31
|
+
target = File.join(@target_dir, name.to_s + extension)
|
32
|
+
info_yml = File.join(@target_dir, name.to_s + '_info.yml')
|
33
|
+
run(build_command(@source, target, width, height, get_info(info_yml), scale_static), "Unable to convert #{@source} to #{target}.")
|
34
|
+
return target
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_supported?(source)
|
38
|
+
file_ext = file_extension(source)
|
39
|
+
EXT.each { |extension| return true if file_ext == extension }
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_extension(source_file)
|
44
|
+
File.extname(source_file).downcase.chomp
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_info(yml)
|
48
|
+
run("#{FFYML} #{@source} #{yml}", "Unable to get video info")
|
49
|
+
YAML.load_file(yml)
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_command(source, target, width, height, info, scale_static)
|
53
|
+
command = "#{FFMPEG} -i #{source}"
|
54
|
+
command += get_scale_command(info['width'], info['height'], width, height, scale_static)
|
55
|
+
scale_factor = get_scale_factor(info['width'], info['height'], width, height)
|
56
|
+
if file_extension(target) != '.flv' # use h264 codec with lower bitrate scaling factor
|
57
|
+
command += " -vcodec libx264"
|
58
|
+
scale_factor /= 2.0
|
59
|
+
end
|
60
|
+
puts "scale factor: #{scale_factor.to_s}"
|
61
|
+
if !info['bitrate'].zero?
|
62
|
+
command += " -b #{(info['bitrate']*@bitrate_multiplier*scale_factor).to_s}"
|
63
|
+
else
|
64
|
+
command += " -sameq"
|
65
|
+
end
|
66
|
+
command += " -ab #{AUDIO_BITRATE}"
|
67
|
+
command += " -ar #{AUDIO_SAMPLING}"
|
68
|
+
command += " -y #{target}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_scale_factor(source_width, source_height, scale_width, scale_height)
|
72
|
+
if source_width > source_height
|
73
|
+
return (scale_width.to_f / source_width.to_f)
|
74
|
+
else
|
75
|
+
return (scale_height.to_f / source_height.to_f)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_scale_command(source_width, source_height, scale_width, scale_height, static)
|
80
|
+
# this type of scaling assumes a static overall resolution with black padding added appropriately
|
81
|
+
# to keep the meaningful video area at the source aspect ratio
|
82
|
+
scale_params = get_scaling_params(source_width, source_height, scale_width, scale_height)
|
83
|
+
scale_command = " -s #{scale_params[:width].to_s}x#{scale_params[:height].to_s}"
|
84
|
+
if static == true
|
85
|
+
scale_command += " -padcolor #{PAD_COLOR}"
|
86
|
+
scale_command += " -padleft #{scale_params[:left]}" if scale_params[:left] > 0
|
87
|
+
scale_command += " -padtop #{scale_params[:top]}" if scale_params[:top] > 0
|
88
|
+
scale_command += " -padright #{scale_params[:right]}" if scale_params[:right] > 0
|
89
|
+
scale_command += " -padbottom #{scale_params[:bottom]}" if scale_params[:bottom] > 0
|
90
|
+
end
|
91
|
+
scale_command
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_scaling_params(source_width, source_height, scale_width, scale_height)
|
95
|
+
params = {}
|
96
|
+
params[:left],params[:top],params[:right],params[:bottom] = 0,0,0,0
|
97
|
+
params[:width],params[:height] = scale_width, scale_height
|
98
|
+
scale_aspect = scale_width.to_f / scale_height.to_f
|
99
|
+
source_aspect = source_width.to_f / source_height.to_f
|
100
|
+
if(source_aspect > scale_aspect)
|
101
|
+
params[:height] = (scale_width.to_f / source_aspect).to_i
|
102
|
+
params[:height] += 1 if params[:height] % 2 != 0
|
103
|
+
params[:top] = params[:bottom] = ((scale_height - params[:height]).to_f / 2).to_i
|
104
|
+
params[:top] = params[:bottom] += 1 if params[:top] % 2 != 0
|
105
|
+
elsif(source_aspect < scale_aspect)
|
106
|
+
params[:width] = (scale_height.to_f * source_aspect).to_i
|
107
|
+
params[:width] += 1 if params[:width] % 2 != 0
|
108
|
+
params[:left] = params[:right] = ((scale_width - params[:width]).to_f / 2).to_i
|
109
|
+
params[:left] = params[:right] += 1 if params[:left] % 2 != 0
|
110
|
+
end
|
111
|
+
return params
|
112
|
+
end
|
113
|
+
|
114
|
+
def run(command, error_message = "Unknown error.")
|
115
|
+
raise "Ffmpeg error: " + error_message + " - command: '#{command}'" if !system(command)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Configurations
|
119
|
+
Video::FFYML = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'bin', 'ffyml'))
|
120
|
+
Video::FFMPEG = 'ffmpeg'
|
121
|
+
Video::FLVTOOL = 'flvtool2'
|
122
|
+
Video::PAD_COLOR = "000000"
|
123
|
+
Video::AUDIO_BITRATE = '128' # kbps
|
124
|
+
Video::AUDIO_SAMPLING = '44100'
|
125
|
+
|
126
|
+
Video::EXT = ['.avi', '.flv', '.mov', '.mpg', '.mp4']
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Pruview
|
2
|
+
class VideoImage
|
3
|
+
|
4
|
+
# this class assumes you have 'ffmpeg' installed and in your path
|
5
|
+
|
6
|
+
def self.to_jpg(source, target_dir, name)
|
7
|
+
raise Pruview::Exceptions::InvalidError, "Invalid source file:: #{source.to_s}" if !File.file?(source)
|
8
|
+
raise Pruview::Exceptions::InvalidError, "Invalid target directory: #{target_dir.to_s}" if !File.directory?(target_dir)
|
9
|
+
raise Pruview::Exceptions::InvalidError, "Video not supported - file extension: " + file_extension(source) if !format_supported?(source)
|
10
|
+
target = File.join(target_dir, name.to_s + '.jpg')
|
11
|
+
run(build_command(source, '-ss 00:00:00.2', 'mjpeg', target), "Unable to get preview image for #{target}")
|
12
|
+
# TODO: analyze image - create better
|
13
|
+
return target
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def self.format_supported?(source)
|
19
|
+
file_ext = file_extension(source)
|
20
|
+
Video::EXT.each { |extension| return true if file_ext == extension }
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.file_extension(source_file)
|
25
|
+
File.extname(source_file).downcase.chomp
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.build_command(source, time_str, format, target)
|
29
|
+
command = "#{Video::FFMPEG} -i #{source}"
|
30
|
+
command += " #{time_str}"
|
31
|
+
command += " -f #{format}" if !format.empty?
|
32
|
+
command += " -an -y #{target}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.run(command, error_message = "Unknown error.")
|
36
|
+
raise "Ffmpeg error: " + error_message + " - command: '#{command}'" if !system(command)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
Binary file
|
Binary file
|
File without changes
|
File without changes
|
File without changes
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kelredd-pruview
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kelly Redding
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-22 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mini_magick
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: flvtool2
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: kelly@kelredd.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- README.rdoc
|
45
|
+
- Rakefile
|
46
|
+
- features/document.feature
|
47
|
+
- features/step_definitions
|
48
|
+
- features/step_definitions/common_steps.rb
|
49
|
+
- features/step_definitions/document_steps.rb
|
50
|
+
- features/step_definitions/support
|
51
|
+
- features/step_definitions/support/env.rb
|
52
|
+
- features/step_definitions/support/helpers.rb
|
53
|
+
- features/step_definitions/video_image_steps.rb
|
54
|
+
- features/step_definitions/video_steps.rb
|
55
|
+
- features/video.feature
|
56
|
+
- features/video_image.feature
|
57
|
+
- lib/pruview
|
58
|
+
- lib/pruview/document.rb
|
59
|
+
- lib/pruview/exceptions.rb
|
60
|
+
- lib/pruview/sRGB.icm
|
61
|
+
- lib/pruview/USWebCoatedSWOP.icc
|
62
|
+
- lib/pruview/version.rb
|
63
|
+
- lib/pruview/video.rb
|
64
|
+
- lib/pruview/video_image.rb
|
65
|
+
- lib/pruview.rb
|
66
|
+
- test/files
|
67
|
+
- test/files/basic.jpg
|
68
|
+
- test/files/basic.mpg
|
69
|
+
- test/files/error.jpg
|
70
|
+
- test/files/error.mov
|
71
|
+
- test/files/invalid_format.poop
|
72
|
+
- test/test_helper.rb
|
73
|
+
- test/unit
|
74
|
+
- test/unit/pruview_test.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: ""
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --main
|
80
|
+
- README.rdoc
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.2.0
|
99
|
+
signing_key:
|
100
|
+
specification_version: 2
|
101
|
+
summary: A gem to ease generating image previews (thumbnails) of various files
|
102
|
+
test_files: []
|
103
|
+
|