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.
@@ -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] ||= File.extname(params[:input]) == '.m3u8' ? 'hls' : 'video'
21
- params[:type] == 'hls' ? VideoScreenshoter::Hls.new(params) : VideoScreenshoter::Video.new(params)
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
- self.times = params[:times].to_a.map do |time|
16
- if time.is_a?(String) && matches = time.match(/(.*)%$/)
17
- time = matches[1].to_f / 100 * duration
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
- time = duration + time if time < 0
20
- time = time.to_i
21
- time
22
- end.uniq
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
- "#{ffmpeg} -i #{input} -acodec -an -ss #{time} #{size} -f image2 -vframes 1 -y #{output} 2>/dev/null 1>&2"
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} #{File.join(File.dirname(input), File.basename(input, File.extname(input)) + '_' + preset_name.to_s + File.extname(input))}"
80
+ "cp #{input} #{output_with_preset(input, preset_name)}"
46
81
  else
47
- "#{imagemagick} #{input} #{preset} #{File.join(File.dirname(input), File.basename(input, File.extname(input)) + '_' + preset_name.to_s + File.extname(input))}"
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
- `#{cmd}`
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
@@ -1,3 +1,3 @@
1
1
  module VideoScreenshoter
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -8,12 +8,7 @@ module VideoScreenshoter
8
8
  end
9
9
 
10
10
  def run
11
- times.each do |time|
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.1
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: 2013-07-10 00:00:00.000000000 Z
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: 1646458223958900487
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: 1646458223958900487
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