vcs_ruby 0.8.4 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/vcs.rb +17 -17
- data/lib/capturer.rb +2 -2
- data/lib/command.rb +2 -2
- data/lib/contact_sheet.rb +36 -23
- data/lib/ffmpeg.rb +3 -2
- data/lib/font.rb +52 -8
- data/lib/libav.rb +3 -2
- data/lib/mplayer.rb +5 -0
- data/lib/thumbnail.rb +4 -2
- data/lib/time_index.rb +1 -1
- data/lib/tools.rb +1 -1
- data/lib/vcs.rb +2 -2
- data/lib/version.info +1 -1
- metadata +34 -2
data/bin/vcs.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
|
3
|
-
# Video Contact Sheet Ruby:
|
3
|
+
# Video Contact Sheet Ruby:
|
4
4
|
# ----------------------
|
5
5
|
#
|
6
6
|
# Generates contact sheets of videos
|
@@ -19,10 +19,10 @@ include VCSRuby
|
|
19
19
|
|
20
20
|
|
21
21
|
# Configuration can Override options
|
22
|
-
options =
|
23
|
-
{
|
24
|
-
quiet: false,
|
25
|
-
verbose: false,
|
22
|
+
options =
|
23
|
+
{
|
24
|
+
quiet: false,
|
25
|
+
verbose: false,
|
26
26
|
capturer: :any,
|
27
27
|
format: nil,
|
28
28
|
output: []
|
@@ -35,10 +35,10 @@ arguments =
|
|
35
35
|
'--capturer' => [:ffmpeg, :libav, :mplayer, :any],
|
36
36
|
'--format' => [:png, :jpg, :jpeg, :tiff],
|
37
37
|
'--funky' => [:polaroid, :photos, :overlap, :rotate, :photoframe, :polaroidframe, :film, :random]
|
38
|
-
}
|
38
|
+
}
|
39
39
|
|
40
40
|
# Command Line Parameters
|
41
|
-
optparse = OptionParser.new do|opts|
|
41
|
+
optparse = OptionParser.new do|opts|
|
42
42
|
opts.separator $vcs_ruby_name + ' ' + $vcs_ruby_version.to_s
|
43
43
|
opts.separator ''
|
44
44
|
opts.on( '-i [INTERVAL]', '--interval [INTERVAL]', 'Set the interval [INTERVAL]') do |interval|
|
@@ -83,7 +83,7 @@ optparse = OptionParser.new do|opts|
|
|
83
83
|
opts.on( '-s [SIGNATURE]', '--signature [SIGNATURE]', 'Change the image signature to your preference.') do |signature|
|
84
84
|
options[:signature] = signature
|
85
85
|
end
|
86
|
-
opts.on( '--no-signature', 'Remove footer with signature') do
|
86
|
+
opts.on( '--no-signature', 'Remove footer with signature') do
|
87
87
|
options[:no_signature] = true
|
88
88
|
end
|
89
89
|
opts.on( '-l [HIGHLIGHT]', '--highlight [HIGHLIGHT]' 'Add the frame found at timestamp [HIGHLIGHT] as a highlight.') do |highlight|
|
@@ -95,14 +95,14 @@ optparse = OptionParser.new do|opts|
|
|
95
95
|
opts.on("-V", "--verbose", "More verbose Output.") do
|
96
96
|
options[:verbose] = true
|
97
97
|
end
|
98
|
-
opts.on( '-v', '--version', 'Version' ) do
|
98
|
+
opts.on( '-v', '--version', 'Version' ) do
|
99
99
|
puts $vcs_ruby_name + ' ' + $vcs_ruby_version.to_s
|
100
|
-
exit 0
|
101
|
-
end
|
102
|
-
|
103
|
-
opts.on( '-h', '--help', 'Prints help' ) do
|
100
|
+
exit 0
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on( '-h', '--help', 'Prints help' ) do
|
104
104
|
options[:help] = true
|
105
|
-
end
|
105
|
+
end
|
106
106
|
|
107
107
|
opts.separator ''
|
108
108
|
opts.separator 'Examples:'
|
@@ -121,7 +121,7 @@ optparse = OptionParser.new do|opts|
|
|
121
121
|
end
|
122
122
|
|
123
123
|
Tools::print_help optparse if ARGV.empty?
|
124
|
-
|
124
|
+
|
125
125
|
optparse.parse!
|
126
126
|
|
127
127
|
Tools::print_help optparse if options[:help] || ARGV.empty?
|
@@ -137,7 +137,7 @@ begin
|
|
137
137
|
sheet.initialize_filename(options[:output][index]) if options[:output][index]
|
138
138
|
sheet.build
|
139
139
|
end
|
140
|
-
|
141
|
-
|
140
|
+
rescue Exception => e
|
141
|
+
STDERR.puts "ERROR: #{e.message}"
|
142
142
|
end
|
143
143
|
|
data/lib/capturer.rb
CHANGED
data/lib/command.rb
CHANGED
@@ -20,11 +20,11 @@ module VCSRuby
|
|
20
20
|
result = nil
|
21
21
|
if Tools::windows?
|
22
22
|
streams = '2> nul' if streams === 0
|
23
|
-
|
23
|
+
|
24
24
|
result = `cmd /C #{@command} #{parameter} #{streams}`
|
25
25
|
else
|
26
26
|
streams = "2> /dev/null" if streams === 0
|
27
|
-
|
27
|
+
|
28
28
|
result =`#{@command} #{parameter} #{streams}`
|
29
29
|
end
|
30
30
|
|
data/lib/contact_sheet.rb
CHANGED
@@ -9,11 +9,11 @@ require 'yaml'
|
|
9
9
|
require 'vcs'
|
10
10
|
|
11
11
|
module VCSRuby
|
12
|
-
class ContactSheet
|
12
|
+
class ContactSheet
|
13
13
|
attr_accessor :capturer, :format, :signature, :title, :highlight
|
14
14
|
attr_reader :thumbnail_width, :thumbnail_height
|
15
15
|
attr_reader :length, :from, :to
|
16
|
-
|
16
|
+
|
17
17
|
def initialize video, capturer = :any
|
18
18
|
@capturer = capturer
|
19
19
|
@configuration = Configuration.new
|
@@ -22,7 +22,7 @@ module VCSRuby
|
|
22
22
|
initialize_filename(File.basename(@video, '.*'))
|
23
23
|
puts "Processing #{File.basename(video)}..." unless Tools.quiet?
|
24
24
|
detect_video_properties
|
25
|
-
|
25
|
+
|
26
26
|
@thumbnails = []
|
27
27
|
|
28
28
|
@tempdir = Dir.mktmpdir
|
@@ -56,7 +56,7 @@ module VCSRuby
|
|
56
56
|
@interval = interval
|
57
57
|
end
|
58
58
|
|
59
|
-
def rows
|
59
|
+
def rows
|
60
60
|
@rows
|
61
61
|
end
|
62
62
|
|
@@ -105,7 +105,7 @@ module VCSRuby
|
|
105
105
|
raise "Invalid To Time"
|
106
106
|
end
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
|
110
110
|
def self.finalize(tempdir)
|
111
111
|
proc do
|
@@ -117,7 +117,7 @@ module VCSRuby
|
|
117
117
|
def build
|
118
118
|
initialize_thumbnails
|
119
119
|
capture_thumbnails
|
120
|
-
|
120
|
+
|
121
121
|
puts "Composing standard contact sheet..." unless Tools.quiet?
|
122
122
|
s = splice_montage(montage_thumbs)
|
123
123
|
|
@@ -135,24 +135,27 @@ private
|
|
135
135
|
def selected_capturer
|
136
136
|
result = nil
|
137
137
|
if @capturer == nil || @capturer == :any
|
138
|
-
result =
|
138
|
+
result = available_capturers.first
|
139
139
|
else
|
140
|
-
result =
|
140
|
+
result = available_capturers.select{ |c| c.name == @capturer }.first
|
141
141
|
end
|
142
|
-
raise "Selected Capturer (#{@capturer.to_s}) not available" unless result
|
142
|
+
raise "Selected Capturer (#{@capturer.to_s}) not available. Install one of these: #{@capturers.map{ |c| c.name }.join(', ')}" unless result
|
143
143
|
return result
|
144
144
|
end
|
145
145
|
|
146
146
|
def initialize_capturers video
|
147
|
-
capturers = []
|
148
|
-
capturers << LibAV.new(video)
|
149
|
-
capturers << MPlayer.new(video)
|
150
|
-
capturers << FFmpeg.new(video)
|
147
|
+
@capturers = []
|
148
|
+
@capturers << LibAV.new(video)
|
149
|
+
@capturers << MPlayer.new(video)
|
150
|
+
@capturers << FFmpeg.new(video)
|
151
151
|
|
152
152
|
@video = video
|
153
|
-
@capturers = capturers.select{ |c| c.available? }
|
154
153
|
|
155
|
-
puts "Available capturers: #{
|
154
|
+
puts "Available capturers: #{available_capturers.map{ |c| c.to_s }.join(', ')}" if Tools.verbose?
|
155
|
+
end
|
156
|
+
|
157
|
+
def available_capturers
|
158
|
+
@capturers.select{ |c| c.available? }
|
156
159
|
end
|
157
160
|
|
158
161
|
def initialize_thumbnails
|
@@ -209,7 +212,7 @@ private
|
|
209
212
|
end
|
210
213
|
montage.geometry "+#{@configuration.padding}+#{@configuration.padding}"
|
211
214
|
# rows or columns can be nil (auto fit)
|
212
|
-
montage.tile "#{@columns}x#{@rows}"
|
215
|
+
montage.tile "#{@columns}x#{@rows}"
|
213
216
|
montage << file_path
|
214
217
|
end
|
215
218
|
return file_path
|
@@ -220,9 +223,9 @@ private
|
|
220
223
|
MiniMagick::Tool::Convert.new do |convert|
|
221
224
|
convert << montage_path
|
222
225
|
convert.background @configuration.contact_background
|
223
|
-
convert.splice '5x10'
|
226
|
+
convert.splice '5x10'
|
224
227
|
convert << file_path
|
225
|
-
end
|
228
|
+
end
|
226
229
|
file_path
|
227
230
|
end
|
228
231
|
|
@@ -232,7 +235,9 @@ private
|
|
232
235
|
convert.stack do |ul|
|
233
236
|
ul.size "#{montage.width}x#{@configuration.title_font.line_height}"
|
234
237
|
ul.xc @configuration.title_background
|
235
|
-
|
238
|
+
if @configuration.title_font.exists?
|
239
|
+
ul.font @configuration.title_font.path
|
240
|
+
end
|
236
241
|
ul.pointsize @configuration.title_font.size
|
237
242
|
ul.background @configuration.title_background
|
238
243
|
ul.fill @configuration.title_color
|
@@ -285,7 +290,9 @@ private
|
|
285
290
|
a.size "#{montage.width - 18}x1"
|
286
291
|
a.xc @configuration.header_background
|
287
292
|
a.size.+
|
288
|
-
|
293
|
+
if @configuration.header_font.exists?
|
294
|
+
a.font @configuration.header_font.path
|
295
|
+
end
|
289
296
|
a.pointsize @configuration.header_font.size
|
290
297
|
a.background @configuration.header_background
|
291
298
|
a.fill 'Black'
|
@@ -293,11 +300,15 @@ private
|
|
293
300
|
b.gravity 'West'
|
294
301
|
b.stack do |c|
|
295
302
|
c.label 'Filename: '
|
296
|
-
|
303
|
+
if @configuration.header_font.exists?
|
304
|
+
c.font @configuration.header_font.path
|
305
|
+
end
|
297
306
|
c.label File.basename(@video)
|
298
307
|
c.append.+
|
299
308
|
end
|
300
|
-
|
309
|
+
if @configuration.header_font.exists?
|
310
|
+
b.font @configuration.header_font.path
|
311
|
+
end
|
301
312
|
b.label "File size: #{Tools.to_human_size(File.size(@video))}"
|
302
313
|
b.label "Length: #{@length.to_timestamp}"
|
303
314
|
b.append
|
@@ -323,7 +334,9 @@ private
|
|
323
334
|
a.size "#{montage.width}x#{signature_height}"
|
324
335
|
a.gravity 'Center'
|
325
336
|
a.xc @configuration.signature_background
|
326
|
-
|
337
|
+
if @configuration.signature_font.exists?
|
338
|
+
a.font @configuration.signature_font.path
|
339
|
+
end
|
327
340
|
a.pointsize @configuration.signature_font.size
|
328
341
|
a.fill @configuration.signature_color
|
329
342
|
a.annotate(0, @signature)
|
data/lib/ffmpeg.rb
CHANGED
@@ -17,7 +17,8 @@ module VCSRuby
|
|
17
17
|
@video = video
|
18
18
|
@ffmpeg = Command.new :ffmpeg, 'ffmpeg'
|
19
19
|
@ffprobe = Command.new :ffmpeg, 'ffprobe'
|
20
|
-
|
20
|
+
|
21
|
+
detect_version if available?
|
21
22
|
end
|
22
23
|
|
23
24
|
def name
|
@@ -108,7 +109,7 @@ private
|
|
108
109
|
video_stream = split_stream_line(is_stream?(@cache, /Video/).first)
|
109
110
|
|
110
111
|
dimensions = /(\d*)x(\d*) \[PAR (\d*:\d*) DAR (\d*:\d*)\]/.match(video_stream[DIMENSION])
|
111
|
-
|
112
|
+
|
112
113
|
if dimensions
|
113
114
|
@par = dimensions[3]
|
114
115
|
@dar = dimensions[4]
|
data/lib/font.rb
CHANGED
@@ -3,30 +3,74 @@
|
|
3
3
|
#
|
4
4
|
|
5
5
|
require 'mini_magick'
|
6
|
+
require 'pp'
|
6
7
|
|
7
8
|
module VCSRuby
|
9
|
+
IMFont = Struct.new(:name, :family, :style, :stretch, :weight, :glyphs)
|
10
|
+
|
8
11
|
class Font
|
9
12
|
attr_reader :name, :path, :size
|
10
13
|
|
14
|
+
@@fonts = {}
|
15
|
+
|
11
16
|
def initialize name, size
|
12
17
|
@name = name
|
13
18
|
@path = find_path
|
14
19
|
@size = size
|
15
20
|
end
|
16
|
-
|
21
|
+
|
22
|
+
def exists?
|
23
|
+
load_font_cache if @@fonts.length == 0
|
24
|
+
|
25
|
+
!!font_by_name(@name)
|
26
|
+
end
|
27
|
+
|
17
28
|
def find_path
|
18
|
-
if
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
load_font_cache if @@fonts.length == 0
|
30
|
+
|
31
|
+
if exists?
|
32
|
+
font_by_name(@name).glyphs
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def font_by_name name
|
39
|
+
if name =~ /\./
|
40
|
+
key, font = @@fonts.select{ |key, f| f.glyphs =~ /#{name}\z/ }.first
|
41
|
+
return font
|
42
|
+
else
|
43
|
+
@@fonts[name]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_font_cache
|
48
|
+
|
49
|
+
fonts = MiniMagick::Tool::Identify.new(whiny: false) do |identify|
|
50
|
+
identify.list 'font'
|
51
|
+
end
|
52
|
+
|
53
|
+
parse_fonts(fonts)
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_fonts(fonts)
|
57
|
+
font = nil
|
58
|
+
fonts.lines.each do |line|
|
59
|
+
key, value = line.strip.split(':', 2).map(&:strip)
|
60
|
+
|
61
|
+
next if ['Path'].include? key
|
62
|
+
|
63
|
+
if key == 'Font'
|
64
|
+
@@fonts[value] = font = IMFont.new(value)
|
65
|
+
else
|
66
|
+
font.send(key + '=', value)
|
67
|
+
end
|
24
68
|
end
|
25
69
|
end
|
26
70
|
|
27
71
|
def line_height
|
28
72
|
MiniMagick::Tool::Convert.new do |convert|
|
29
|
-
convert.font path
|
73
|
+
convert.font path if exists?
|
30
74
|
convert.pointsize size
|
31
75
|
convert << 'label:F'
|
32
76
|
convert.format '%h'
|
data/lib/libav.rb
CHANGED
@@ -17,7 +17,8 @@ module VCSRuby
|
|
17
17
|
@video = video
|
18
18
|
@avconv = Command.new :libav, 'avconv'
|
19
19
|
@avprobe = Command.new :libav, 'avprobe'
|
20
|
-
|
20
|
+
|
21
|
+
detect_version if available?
|
21
22
|
end
|
22
23
|
|
23
24
|
def name
|
@@ -100,7 +101,7 @@ private
|
|
100
101
|
video_stream = split_stream_line(is_stream?(@cache, /Video/).first)
|
101
102
|
|
102
103
|
dimensions = /(\d*)x(\d*) \[PAR (\d*:\d*) DAR (\d*:\d*)\]/.match(video_stream[DIMENSION])
|
103
|
-
|
104
|
+
|
104
105
|
if dimensions
|
105
106
|
@par = dimensions[3]
|
106
107
|
@dar = dimensions[4]
|
data/lib/mplayer.rb
CHANGED
data/lib/thumbnail.rb
CHANGED
@@ -17,7 +17,7 @@ module VCSRuby
|
|
17
17
|
@filters = [ method(:resize_filter), method(:timestamp_filter), method(:softshadow_filter) ]
|
18
18
|
end
|
19
19
|
|
20
|
-
def capture
|
20
|
+
def capture
|
21
21
|
@capper.grab @time, @image_path
|
22
22
|
end
|
23
23
|
|
@@ -65,7 +65,9 @@ private
|
|
65
65
|
box.fill @configuration.timestamp_color
|
66
66
|
box.pointsize @configuration.timestamp_font.size
|
67
67
|
box.gravity 'SouthEast'
|
68
|
-
|
68
|
+
if @configuration.timestamp_font.exists?
|
69
|
+
box.font @configuration.timestamp_font.path
|
70
|
+
end
|
69
71
|
box.annotate('+10+10', " #{@time.to_timestamp} ")
|
70
72
|
end
|
71
73
|
convert.flatten
|
data/lib/time_index.rb
CHANGED
data/lib/tools.rb
CHANGED
@@ -61,7 +61,7 @@ module VCSRuby
|
|
61
61
|
|
62
62
|
def self.to_human_size size
|
63
63
|
powers = { 'B' => 1 << 10, 'KiB' => 1 << 20, 'MiB' => 1 << 30, 'GiB' => 1 << 40, 'TiB' => 1 << 50 }
|
64
|
-
powers.each_pair do |prefix, power|
|
64
|
+
powers.each_pair do |prefix, power|
|
65
65
|
if size < power
|
66
66
|
return format('%.2f',size.to_f / (power >> 10)) + ' ' + prefix
|
67
67
|
end
|
data/lib/vcs.rb
CHANGED
data/lib/version.info
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.6
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vcs_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.5
|
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: 2016-
|
12
|
+
date: 2016-05-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mini_magick
|
@@ -27,6 +27,38 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 4.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.5.0
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.5.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 10.0.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 10.0.0
|
30
62
|
description: Creates a contact sheet, a preview, of a video, usable as library or
|
31
63
|
as a script. Based on VCS *NIX. Creating Thumbnails with libav, ffmpeg or mplayer
|
32
64
|
and compose it with ImageMagick into nice looking sheets.
|