video_screenshoter 0.1.1 → 0.2.1

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.
@@ -6,14 +6,16 @@ require "video_screenshoter/hls"
6
6
 
7
7
  module VideoScreenshoter
8
8
  class << self
9
- attr_accessor :ffmpeg, :output_dir, :output_file, :verbose
9
+ attr_accessor :ffmpeg, :imagemagick, :output_dir, :output_file, :verbose
10
10
  end
11
11
  self.ffmpeg = '/usr/local/bin/ffmpeg'
12
12
  self.output_dir = '/tmp/screenshots'
13
13
  self.output_file = 'scr%03d.jpg'
14
14
  self.verbose = false
15
+ self.imagemagick = '/usr/bin/convert'
15
16
 
16
17
  def self.new params
18
+ raise ArgumentError.new('Input is needed') unless params[:input]
17
19
  raise ArgumentError.new('Incorrect type param') unless [nil, 'hls', 'video'].include? params[:type]
18
20
  params[:type] ||= File.extname(params[:input]) == '.m3u8' ? 'hls' : 'video'
19
21
  params[:type] == 'hls' ? VideoScreenshoter::Hls.new(params) : VideoScreenshoter::Video.new(params)
@@ -2,14 +2,14 @@
2
2
 
3
3
  module VideoScreenshoter
4
4
  class Abstract
5
- attr_accessor :ffmpeg, :output_dir, :output_file, :input, :times, :duration, :verbose, :size
5
+ attr_accessor :ffmpeg, :imagemagick, :output_dir, :output_file, :input, :times, :duration, :verbose, :size, :presets
6
6
 
7
7
  def initialize params
8
- [:ffmpeg, :output_dir, :output_file, :verbose].each do |attr|
8
+ [:ffmpeg, :imagemagick, :output_dir, :output_file, :verbose].each do |attr|
9
9
  self.send("#{attr}=".to_sym, params[attr].nil? ? VideoScreenshoter.send(attr) : params[attr])
10
10
  end
11
11
  FileUtils.mkdir_p self.output_dir
12
- self.input = params[:input] or raise ArgumentError.new('Input is needed')
12
+ self.input = params[:input]
13
13
  self.duration = input_duration
14
14
  raise ArgumentError.new('Incorrect or empty m3u8 playlist') if duration.to_i == 0
15
15
  self.times = params[:times].to_a.map do |time|
@@ -21,16 +21,43 @@ module VideoScreenshoter
21
21
  time
22
22
  end.uniq
23
23
  self.size = params[:size] ? "-s #{params[:size]}" : ''
24
+ if params[:presets] && params[:presets].is_a?(Hash)
25
+ self.presets = {}
26
+ params[:presets].each do |name, preset|
27
+ self.presets[name.to_sym] = !preset || preset.empty? || preset.index('-') == 0 ? preset : "-resize #{preset}"
28
+ end
29
+ end
24
30
  end
25
31
 
26
- def output_fullpath time
27
- sprintf(File.join(output_dir, output_file), time)
32
+ def output_fullpath time, preset = nil
33
+ res = sprintf(File.join(output_dir, output_file), time)
34
+ res.sub!(File.extname(res), "_#{preset}#{File.extname(res)}") if preset
35
+ res
28
36
  end
29
37
 
30
- def command input, output, time
38
+ def ffmpeg_command input, output, time
31
39
  "#{ffmpeg} -i #{input} -acodec -an -ss #{time} #{size} -f image2 -vframes 1 -y #{output} 2>/dev/null 1>&2"
32
40
  end
33
41
 
42
+ def imagemagick_command input, preset_name
43
+ preset = presets[preset_name.to_sym]
44
+ 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))}"
46
+ else
47
+ "#{imagemagick} #{input} #{preset} #{File.join(File.dirname(input), File.basename(input, File.extname(input)) + '_' + preset_name.to_s + File.extname(input))}"
48
+ end
49
+ end
50
+
51
+ def imagemagick_run scr
52
+ if presets
53
+ presets.each do |preset_name, preset|
54
+ cmd = imagemagick_command(scr, preset_name)
55
+ puts cmd if verbose
56
+ `#{cmd}`
57
+ end
58
+ end
59
+ end
60
+
34
61
  def run
35
62
  raise NotImplementedError
36
63
  end
@@ -13,17 +13,22 @@ module VideoScreenshoter
13
13
  chunk_limits.push({:start => time, :end => time + chunk.first, :path => chunk.last})
14
14
  time + chunk.first
15
15
  end
16
+ threads = []
16
17
  times.each do |time|
17
- if chunk_limit = chunk_limits.select { |c| time >= c[:start] && time <= c[:end] }.first
18
- path = File.join(File.dirname(input), chunk_limit[:path])
19
- rel_time = time - chunk_limit[:start]
20
- cmd = command(path, output_fullpath(time), rel_time)
21
- puts cmd if verbose
22
- `#{cmd}`
23
- else
24
- puts "Time #{time} is incorrect" if verbose
18
+ threads << Thread.new do
19
+ if chunk_limit = chunk_limits.select { |c| time >= c[:start] && time <= c[:end] }.first
20
+ path = File.join(File.dirname(input), chunk_limit[:path])
21
+ rel_time = time - chunk_limit[:start]
22
+ cmd = ffmpeg_command(path, output_fullpath(time), rel_time)
23
+ puts cmd if verbose
24
+ `#{cmd}`
25
+ imagemagick_run output_fullpath(time)
26
+ else
27
+ puts "Time #{time} is incorrect" if verbose
28
+ end
25
29
  end
26
30
  end
31
+ threads.each { |t| t.join }
27
32
  end
28
33
 
29
34
  alias :make_screenshots :run
@@ -1,3 +1,3 @@
1
1
  module VideoScreenshoter
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -9,9 +9,10 @@ module VideoScreenshoter
9
9
 
10
10
  def run
11
11
  times.each do |time|
12
- cmd = command(input, output_fullpath(time), time)
12
+ cmd = ffmpeg_command(input, output_fullpath(time), time)
13
13
  puts cmd if verbose
14
14
  `#{cmd}`
15
+ imagemagick_run output_fullpath(time)
15
16
  end
16
17
  end
17
18
 
@@ -5,12 +5,32 @@ class VideoScreenshoterTest < Test::Unit::TestCase
5
5
  setup do
6
6
  @input = 'http://techslides.com/demos/sample-videos/small.mp4'
7
7
  @output_dir = '/tmp/video_screenshots_test'
8
- VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => [1, -2, '50%', '-10%']).make_screenshots
8
+ `rm -r #{@output_dir}` if File.exists?(@output_dir)
9
9
  end
10
- should 'create screenshots' do
11
- [1, 2, 3, 5].each do |sec|
12
- assert File.exists? File.join(@output_dir, "scr00#{sec}.jpg")
13
- assert File.size(File.join(@output_dir, "scr00#{sec}.jpg")) > 0
10
+
11
+ context '' do
12
+ setup do
13
+ VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => [1, -2, '50%', '-10%']).make_screenshots
14
+ end
15
+
16
+ should 'create screenshots' do
17
+ [1, 2, 3, 5].each do |sec|
18
+ assert File.exists? File.join(@output_dir, "scr00#{sec}.jpg")
19
+ assert File.size(File.join(@output_dir, "scr00#{sec}.jpg")) > 0
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'with presets' do
25
+ setup do
26
+ VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => [1, -1], :presets => {:big => '1024x768', :small => '-resize 400x100^ -gravity center -crop 400x100+0+0'}, :imagemagick => '/usr/bin/convert').make_screenshots
27
+ end
28
+ should 'create screenshots' do
29
+ [1,4].each do |sec|
30
+ assert File.exists? File.join(@output_dir, "scr00#{sec}.jpg")
31
+ assert File.exists? File.join(@output_dir, "scr00#{sec}_big.jpg")
32
+ assert File.exists? File.join(@output_dir, "scr00#{sec}_small.jpg")
33
+ end
14
34
  end
15
35
  end
16
36
  end
@@ -30,12 +50,33 @@ class VideoScreenshoterTest < Test::Unit::TestCase
30
50
  setup do
31
51
  @input = 'http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear4/prog_index.m3u8'
32
52
  @output_dir = '/tmp/hls_screenshots_test'
33
- VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => ['10%', '50%', '-10%']).make_thumbnails
53
+ `rm -r #{@output_dir}` if File.exists? @output_dir
34
54
  end
35
- should 'create screenshots' do
36
- ["scr1620.jpg", "scr180.jpg", "scr900.jpg"].each do |scr|
37
- assert File.exists?(File.join(@output_dir, scr))
38
- assert File.size(File.join(@output_dir, scr)) > 0
55
+
56
+ context '' do
57
+ setup do
58
+ VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => ['10%', '50%', '-10%']).make_thumbnails
59
+ end
60
+ should 'create screenshots' do
61
+ ["scr1620.jpg", "scr180.jpg", "scr900.jpg"].each do |scr|
62
+ assert File.exists?(File.join(@output_dir, scr))
63
+ assert File.size(File.join(@output_dir, scr)) > 0
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'with presets' do
69
+ setup do
70
+ VideoScreenshoter.imagemagick = '/usr/bin/convert'
71
+ VideoScreenshoter.new(:input => @input, :output_dir => @output_dir, :times => ['10%', '50%', '-10%'], :presets => {:big => '1024x768', :small => '140x100!'}).make_thumbnails
72
+ end
73
+ should 'create screenshots' do
74
+ ["scr1620.jpg", "scr180.jpg", "scr900.jpg"].each do |scr|
75
+ assert File.exists?(File.join(@output_dir, scr))
76
+ assert File.exists?(File.join(@output_dir, scr.sub('.jpg', '_big.jpg')))
77
+ assert File.exists?(File.join(@output_dir, scr.sub('.jpg', '_small.jpg')))
78
+ end
79
+ assert `/usr/local/bin/ffmpeg -i #{File.join(@output_dir, "scr900_small.jpg")} 2>&1`.include?('140x100')
39
80
  end
40
81
  end
41
82
  end
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "shoulda-context"
25
25
 
26
26
  spec.requirements << 'ffmpeg'
27
+ spec.requirements << 'imagemagick'
27
28
  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.1.1
4
+ version: 0.2.1
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-05-17 00:00:00.000000000 Z
12
+ date: 2013-07-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -110,7 +110,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
110
  version: '0'
111
111
  segments:
112
112
  - 0
113
- hash: -2399110922537615442
113
+ hash: 1646458223958900487
114
114
  required_rubygems_version: !ruby/object:Gem::Requirement
115
115
  none: false
116
116
  requirements:
@@ -119,9 +119,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  version: '0'
120
120
  segments:
121
121
  - 0
122
- hash: -2399110922537615442
122
+ hash: 1646458223958900487
123
123
  requirements:
124
124
  - ffmpeg
125
+ - imagemagick
125
126
  rubyforge_project:
126
127
  rubygems_version: 1.8.25
127
128
  signing_key: