video_screenshoter 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/video_screenshoter.rb +10 -3
- data/lib/video_screenshoter/abstract.rb +47 -12
- data/lib/video_screenshoter/image.rb +32 -0
- data/lib/video_screenshoter/version.rb +1 -1
- data/lib/video_screenshoter/video.rb +1 -6
- data/test/fixtures/test.jpg +0 -0
- data/test/video_screenshoter_test.rb +18 -0
- metadata +7 -4
data/lib/video_screenshoter.rb
CHANGED
@@ -3,6 +3,8 @@ require "fileutils"
|
|
3
3
|
require "video_screenshoter/abstract"
|
4
4
|
require "video_screenshoter/video"
|
5
5
|
require "video_screenshoter/hls"
|
6
|
+
require "video_screenshoter/image"
|
7
|
+
require "shellwords"
|
6
8
|
|
7
9
|
module VideoScreenshoter
|
8
10
|
class << self
|
@@ -16,8 +18,13 @@ module VideoScreenshoter
|
|
16
18
|
|
17
19
|
def self.new params
|
18
20
|
raise ArgumentError.new('Input is needed') unless params[:input]
|
19
|
-
raise ArgumentError.new('Incorrect type param') unless [nil, 'hls', 'video'].include? params[:type]
|
20
|
-
params[:type]
|
21
|
-
|
21
|
+
raise ArgumentError.new('Incorrect type param') unless [nil, 'hls', 'video', 'image'].include? params[:type]
|
22
|
+
if params[:type] == 'hls' || File.extname(params[:input]).downcase == '.m3u8'
|
23
|
+
VideoScreenshoter::Hls.new(params)
|
24
|
+
elsif params[:type] == 'image' || ['.gif','.png','.jpg','.jpeg', '.png'].include?(File.extname(params[:input]).downcase)
|
25
|
+
VideoScreenshoter::Image.new(params)
|
26
|
+
else
|
27
|
+
VideoScreenshoter::Video.new(params)
|
28
|
+
end
|
22
29
|
end
|
23
30
|
end
|
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
module VideoScreenshoter
|
4
4
|
class Abstract
|
5
|
-
attr_accessor :ffmpeg, :imagemagick, :output_dir, :output_file, :input, :times, :duration, :verbose, :size, :presets
|
5
|
+
attr_accessor :ffmpeg, :imagemagick, :output_dir, :output_file, :input, :exact, :times, :offset_start, :offset_end, :duration, :verbose, :size, :presets
|
6
6
|
|
7
7
|
def initialize params
|
8
|
+
params.each_with_index do |param, index|
|
9
|
+
params[index] = Shellwords.escape(param) if param.is_a?(String)
|
10
|
+
end
|
8
11
|
[:ffmpeg, :imagemagick, :output_dir, :output_file, :verbose].each do |attr|
|
9
12
|
self.send("#{attr}=".to_sym, params[attr].nil? ? VideoScreenshoter.send(attr) : params[attr])
|
10
13
|
end
|
@@ -12,15 +15,36 @@ module VideoScreenshoter
|
|
12
15
|
self.input = params[:input]
|
13
16
|
self.duration = input_duration
|
14
17
|
raise ArgumentError.new('Incorrect or empty m3u8 playlist') if duration.to_i == 0
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
|
19
|
+
# if false ffmpeg uses fast seek by keyframes like: ffmpeg -ss ... -i
|
20
|
+
self.exact = params[:exact]
|
21
|
+
|
22
|
+
if params[:times]
|
23
|
+
self.exact = true if exact.nil?
|
24
|
+
self.times = params[:times].to_a.map do |time|
|
25
|
+
if time.is_a?(String) && matches = time.match(/(.*)%$/)
|
26
|
+
time = matches[1].to_f / 100 * duration
|
27
|
+
end
|
28
|
+
time = duration + time if time < 0
|
29
|
+
time = time.to_i
|
30
|
+
time
|
31
|
+
end.uniq
|
32
|
+
elsif (number = params[:number].to_i) > 0
|
33
|
+
[:offset_start, :offset_end].each do |attr|
|
34
|
+
if percent = params[attr].to_s.match(/^(\d+)%$/).to_a[1]
|
35
|
+
self.send("#{attr}=", duration * percent.to_i / 100.0)
|
36
|
+
else
|
37
|
+
self.send("#{attr}=", params[attr].to_f)
|
38
|
+
end
|
18
39
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
40
|
+
self.times = number.times.to_a.map { |time| ((offset_start + (duration - offset_start - offset_end) / number * time) * 100).round / 100.0 }.uniq
|
41
|
+
else
|
42
|
+
raise ArgumentError.new('times or number required') if times.empty?
|
43
|
+
end
|
44
|
+
|
23
45
|
self.size = params[:size] ? "-s #{params[:size]}" : ''
|
46
|
+
|
47
|
+
# TODO possibility to replace original image by presetted image
|
24
48
|
if params[:presets] && params[:presets].is_a?(Hash)
|
25
49
|
self.presets = {}
|
26
50
|
params[:presets].each do |name, preset|
|
@@ -36,15 +60,26 @@ module VideoScreenshoter
|
|
36
60
|
end
|
37
61
|
|
38
62
|
def ffmpeg_command input, output, time
|
39
|
-
"
|
63
|
+
is = exact ? "-i #{input} -ss #{time}" : "-ss #{time} -i #{input}"
|
64
|
+
"#{ffmpeg} #{is} -acodec -an #{size} -f image2 -vframes 1 -y #{output} 1>/dev/null 2>&1"
|
65
|
+
end
|
66
|
+
|
67
|
+
def ffmpeg_run time = nil
|
68
|
+
cmd = ffmpeg_command(input, output_fullpath(time), time)
|
69
|
+
puts cmd if verbose
|
70
|
+
system cmd
|
71
|
+
end
|
72
|
+
|
73
|
+
def output_with_preset input, preset_name
|
74
|
+
File.join(output_dir, File.basename(input, File.extname(input)) + '_' + preset_name.to_s + File.extname(input))
|
40
75
|
end
|
41
76
|
|
42
77
|
def imagemagick_command input, preset_name
|
43
78
|
preset = presets[preset_name.to_sym]
|
44
79
|
if !preset || preset.empty?
|
45
|
-
"cp #{input} #{
|
80
|
+
"cp #{input} #{output_with_preset(input, preset_name)}"
|
46
81
|
else
|
47
|
-
"#{imagemagick} #{input} #{preset} #{
|
82
|
+
"#{imagemagick} #{input} #{preset} #{output_with_preset(input, preset_name)}"
|
48
83
|
end
|
49
84
|
end
|
50
85
|
|
@@ -53,7 +88,7 @@ module VideoScreenshoter
|
|
53
88
|
presets.each do |preset_name, preset|
|
54
89
|
cmd = imagemagick_command(scr, preset_name)
|
55
90
|
puts cmd if verbose
|
56
|
-
|
91
|
+
system cmd
|
57
92
|
end
|
58
93
|
end
|
59
94
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module VideoScreenshoter
|
4
|
+
class Image < Abstract
|
5
|
+
|
6
|
+
def initialize params
|
7
|
+
params.each_with_index do |param, index|
|
8
|
+
params[index] = Shellwords.escape(param) if param.is_a?(String)
|
9
|
+
end
|
10
|
+
[:ffmpeg, :imagemagick, :output_dir, :output_file, :verbose].each do |attr|
|
11
|
+
self.send("#{attr}=".to_sym, params[attr].nil? ? VideoScreenshoter.send(attr) : params[attr])
|
12
|
+
end
|
13
|
+
FileUtils.mkdir_p self.output_dir
|
14
|
+
self.input = params[:input]
|
15
|
+
if params[:presets] && params[:presets].is_a?(Hash)
|
16
|
+
self.presets = {}
|
17
|
+
params[:presets].each do |name, preset|
|
18
|
+
self.presets[name.to_sym] = !preset || preset.empty? || preset.index('-') == 0 ? preset : "-resize #{preset}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
raise ArgumentError.new('Presets are needed') if presets.nil? || presets.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
imagemagick_run input
|
26
|
+
Hash[*presets.keys.map { |p| [p, output_with_preset(input, p)] }.flatten]
|
27
|
+
end
|
28
|
+
|
29
|
+
alias :make_screenshots :run
|
30
|
+
alias :make_thumbnails :run
|
31
|
+
end
|
32
|
+
end
|
@@ -8,12 +8,7 @@ module VideoScreenshoter
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def run
|
11
|
-
times.each
|
12
|
-
cmd = ffmpeg_command(input, output_fullpath(time), time)
|
13
|
-
puts cmd if verbose
|
14
|
-
`#{cmd}`
|
15
|
-
imagemagick_run output_fullpath(time)
|
16
|
-
end
|
11
|
+
times.each { |time| ffmpeg_run(time) and imagemagick_run(output_fullpath(time)) }
|
17
12
|
end
|
18
13
|
|
19
14
|
alias :make_screenshots :run
|
Binary file
|
@@ -81,4 +81,22 @@ class VideoScreenshoterTest < Test::Unit::TestCase
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
84
|
+
|
85
|
+
context 'image' do
|
86
|
+
setup do
|
87
|
+
@input = 'test/fixtures/test.jpg'
|
88
|
+
@output_dir = '/tmp/image_screenshots_test'
|
89
|
+
@presets = {:big=>"1280x720", :small=>"144x81", :big43=>"-resize 1280x960^ -gravity center -crop 1280x960+0+0", :small43=>"-resize 144x108^ -gravity center -crop 144x108+0+0"}
|
90
|
+
`rm -r #{@output_dir}` if File.exists? @output_dir
|
91
|
+
@res = VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :presets => @presets, :type => 'image').run
|
92
|
+
end
|
93
|
+
should 'create screenshots' do
|
94
|
+
assert_equal @presets.count, @res.count
|
95
|
+
@res.values.each do |res|
|
96
|
+
assert File.exists?(res)
|
97
|
+
assert File.size(res) > 0
|
98
|
+
end
|
99
|
+
assert File.size(@res.values.first) != File.size(@res.values.last)
|
100
|
+
end
|
101
|
+
end
|
84
102
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: video_screenshoter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -90,8 +90,10 @@ files:
|
|
90
90
|
- lib/video_screenshoter.rb
|
91
91
|
- lib/video_screenshoter/abstract.rb
|
92
92
|
- lib/video_screenshoter/hls.rb
|
93
|
+
- lib/video_screenshoter/image.rb
|
93
94
|
- lib/video_screenshoter/version.rb
|
94
95
|
- lib/video_screenshoter/video.rb
|
96
|
+
- test/fixtures/test.jpg
|
95
97
|
- test/test_helper.rb
|
96
98
|
- test/video_screenshoter_test.rb
|
97
99
|
- video_screenshoter.gemspec
|
@@ -110,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
112
|
version: '0'
|
111
113
|
segments:
|
112
114
|
- 0
|
113
|
-
hash:
|
115
|
+
hash: -2212613341637156165
|
114
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
117
|
none: false
|
116
118
|
requirements:
|
@@ -119,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
121
|
version: '0'
|
120
122
|
segments:
|
121
123
|
- 0
|
122
|
-
hash:
|
124
|
+
hash: -2212613341637156165
|
123
125
|
requirements:
|
124
126
|
- ffmpeg
|
125
127
|
- imagemagick
|
@@ -129,5 +131,6 @@ signing_key:
|
|
129
131
|
specification_version: 3
|
130
132
|
summary: Make screenshots from video or m3u8 files using ffmpeg
|
131
133
|
test_files:
|
134
|
+
- test/fixtures/test.jpg
|
132
135
|
- test/test_helper.rb
|
133
136
|
- test/video_screenshoter_test.rb
|