vcs_ruby 1.1.8 → 1.1.9
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.
- checksums.yaml +7 -0
- data/bin/vcs.rb +171 -169
- data/lib/FFmpeg/ffmpeg.rb +121 -121
- data/lib/FFmpeg/ffmpeg_audio_stream.rb +37 -37
- data/lib/FFmpeg/ffmpeg_meta_info.rb +36 -36
- data/lib/FFmpeg/ffmpeg_video_stream.rb +62 -56
- data/lib/MPlayer/mplayer.rb +101 -101
- data/lib/MPlayer/mplayer_audio_stream.rb +33 -33
- data/lib/MPlayer/mplayer_meta_info.rb +39 -39
- data/lib/MPlayer/mplayer_video_stream.rb +44 -44
- data/lib/black.yml +12 -12
- data/lib/capturer.rb +69 -69
- data/lib/command.rb +51 -51
- data/lib/configuration.rb +150 -150
- data/lib/contact_sheet.rb +380 -366
- data/lib/defaults.yml +39 -39
- data/lib/font.rb +81 -81
- data/lib/frame.rb +161 -161
- data/lib/libAV/libav.rb +151 -151
- data/lib/libAV/libav_audio_stream.rb +37 -37
- data/lib/libAV/libav_meta_info.rb +36 -36
- data/lib/libAV/libav_video_stream.rb +62 -56
- data/lib/oldstyle.yml +22 -22
- data/lib/stream.rb +24 -24
- data/lib/time_index.rb +121 -121
- data/lib/tools.rb +81 -70
- data/lib/vcs.rb +26 -26
- data/lib/version.info +1 -1
- data/lib/version.rb +25 -25
- data/lib/video.rb +92 -88
- data/lib/white.yml +12 -12
- metadata +39 -47
data/lib/defaults.yml
CHANGED
@@ -1,39 +1,39 @@
|
|
1
|
-
main:
|
2
|
-
rows: 4
|
3
|
-
columns: 4
|
4
|
-
interval: ~
|
5
|
-
padding: 2
|
6
|
-
quality: 95
|
7
|
-
filter:
|
8
|
-
timestamp: true
|
9
|
-
polaroid: false
|
10
|
-
softshadow: true
|
11
|
-
style:
|
12
|
-
header:
|
13
|
-
font: DejaVuSans.ttf
|
14
|
-
size: 14
|
15
|
-
color: Black
|
16
|
-
background: "#afcd7a"
|
17
|
-
title:
|
18
|
-
font: DejaVuSans.ttf
|
19
|
-
size: 33
|
20
|
-
color: Black
|
21
|
-
background: White
|
22
|
-
highlight:
|
23
|
-
background: LightGoldenRod
|
24
|
-
contact:
|
25
|
-
background: White
|
26
|
-
timestamp:
|
27
|
-
font: DejaVuSans.ttf
|
28
|
-
size: 14
|
29
|
-
color: White
|
30
|
-
background: "#000000aa"
|
31
|
-
signature:
|
32
|
-
font: DejaVuSans.ttf
|
33
|
-
size: 10
|
34
|
-
color: Black
|
35
|
-
background: SlateGray
|
36
|
-
lowlevel:
|
37
|
-
blank_evasion: true
|
38
|
-
blank_threshold: 0.08
|
39
|
-
blank_alternatives: [ -2, 2, -5, 5, -10, 10, -15, 15, -30, 30, 0]
|
1
|
+
main:
|
2
|
+
rows: 4
|
3
|
+
columns: 4
|
4
|
+
interval: ~
|
5
|
+
padding: 2
|
6
|
+
quality: 95
|
7
|
+
filter:
|
8
|
+
timestamp: true
|
9
|
+
polaroid: false
|
10
|
+
softshadow: true
|
11
|
+
style:
|
12
|
+
header:
|
13
|
+
font: DejaVuSans.ttf
|
14
|
+
size: 14
|
15
|
+
color: Black
|
16
|
+
background: "#afcd7a"
|
17
|
+
title:
|
18
|
+
font: DejaVuSans.ttf
|
19
|
+
size: 33
|
20
|
+
color: Black
|
21
|
+
background: White
|
22
|
+
highlight:
|
23
|
+
background: LightGoldenRod
|
24
|
+
contact:
|
25
|
+
background: White
|
26
|
+
timestamp:
|
27
|
+
font: DejaVuSans.ttf
|
28
|
+
size: 14
|
29
|
+
color: White
|
30
|
+
background: "#000000aa"
|
31
|
+
signature:
|
32
|
+
font: DejaVuSans.ttf
|
33
|
+
size: 10
|
34
|
+
color: Black
|
35
|
+
background: SlateGray
|
36
|
+
lowlevel:
|
37
|
+
blank_evasion: true
|
38
|
+
blank_threshold: 0.08
|
39
|
+
blank_alternatives: [ -2, 2, -5, 5, -10, 10, -15, 15, -30, 30, 0]
|
data/lib/font.rb
CHANGED
@@ -1,81 +1,81 @@
|
|
1
|
-
#
|
2
|
-
# Font helper
|
3
|
-
#
|
4
|
-
|
5
|
-
require 'mini_magick'
|
6
|
-
require 'pp'
|
7
|
-
|
8
|
-
module VCSRuby
|
9
|
-
IMFont = Struct.new(:name, :family, :style, :stretch, :weight, :glyphs)
|
10
|
-
|
11
|
-
class Font
|
12
|
-
attr_reader :name, :path, :size
|
13
|
-
|
14
|
-
@@fonts = {}
|
15
|
-
|
16
|
-
def initialize name, size
|
17
|
-
@name = name
|
18
|
-
@path = find_path
|
19
|
-
@size = size
|
20
|
-
end
|
21
|
-
|
22
|
-
def exists?
|
23
|
-
load_font_cache if @@fonts.length == 0
|
24
|
-
|
25
|
-
!!font_by_name(@name)
|
26
|
-
end
|
27
|
-
|
28
|
-
def find_path
|
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 [nil, 'Path'].include? key
|
62
|
-
|
63
|
-
if key == 'Font'
|
64
|
-
@@fonts[value] = font = IMFont.new(value)
|
65
|
-
else
|
66
|
-
font.send("#{key}=", value)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def line_height
|
72
|
-
MiniMagick::Tool::Convert.new do |convert|
|
73
|
-
convert.font path if exists?
|
74
|
-
convert.pointsize size
|
75
|
-
convert << 'label:F'
|
76
|
-
convert.format '%h'
|
77
|
-
convert << 'info:'
|
78
|
-
end.to_i
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
1
|
+
#
|
2
|
+
# Font helper
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'mini_magick'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
module VCSRuby
|
9
|
+
IMFont = Struct.new(:name, :family, :style, :stretch, :weight, :glyphs)
|
10
|
+
|
11
|
+
class Font
|
12
|
+
attr_reader :name, :path, :size
|
13
|
+
|
14
|
+
@@fonts = {}
|
15
|
+
|
16
|
+
def initialize name, size
|
17
|
+
@name = name
|
18
|
+
@path = find_path
|
19
|
+
@size = size
|
20
|
+
end
|
21
|
+
|
22
|
+
def exists?
|
23
|
+
load_font_cache if @@fonts.length == 0
|
24
|
+
|
25
|
+
!!font_by_name(@name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_path
|
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 [nil, 'Path'].include? key
|
62
|
+
|
63
|
+
if key == 'Font'
|
64
|
+
@@fonts[value] = font = IMFont.new(value)
|
65
|
+
else
|
66
|
+
font.send("#{key}=", value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def line_height
|
72
|
+
MiniMagick::Tool::Convert.new do |convert|
|
73
|
+
convert.font path if exists?
|
74
|
+
convert.pointsize size
|
75
|
+
convert << 'label:F'
|
76
|
+
convert.format '%h'
|
77
|
+
convert << 'info:'
|
78
|
+
end.to_i
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/frame.rb
CHANGED
@@ -1,161 +1,161 @@
|
|
1
|
-
#
|
2
|
-
# Frame from video
|
3
|
-
#
|
4
|
-
|
5
|
-
require 'mini_magick'
|
6
|
-
|
7
|
-
module VCSRuby
|
8
|
-
class Frame
|
9
|
-
attr_accessor :width, :height, :aspect
|
10
|
-
attr_reader :filters, :time
|
11
|
-
|
12
|
-
def initialize video, capturer, time
|
13
|
-
@video = video
|
14
|
-
@capturer = capturer
|
15
|
-
@time = time
|
16
|
-
@filters = []
|
17
|
-
end
|
18
|
-
|
19
|
-
def filename= file_path
|
20
|
-
@out_path = File.dirname(file_path)
|
21
|
-
@out_filename = File.basename(file_path,'.*')
|
22
|
-
end
|
23
|
-
|
24
|
-
def filename
|
25
|
-
File.join(@out_path, "#{@out_filename}.#{@capturer.format_extension}")
|
26
|
-
end
|
27
|
-
|
28
|
-
def format= fmt
|
29
|
-
@capturer.format = fmt
|
30
|
-
end
|
31
|
-
|
32
|
-
def format
|
33
|
-
@capturer.format
|
34
|
-
end
|
35
|
-
|
36
|
-
def capture
|
37
|
-
@capturer.grab @time, filename
|
38
|
-
end
|
39
|
-
|
40
|
-
def capture_and_evade interval = nil
|
41
|
-
times = [TimeIndex.new] + Configuration.instance.blank_alternatives
|
42
|
-
if interval
|
43
|
-
times.select! { |t| (t < interval / 2) and (t > interval / -2) }
|
44
|
-
end
|
45
|
-
times.map! { |t| @time + t }
|
46
|
-
|
47
|
-
times.each do |time|
|
48
|
-
@time = time
|
49
|
-
capture
|
50
|
-
break unless blank?
|
51
|
-
puts "Blank frame detected. => #{@time}" unless Configuration.instance.quiet?
|
52
|
-
puts "Giving up!" if time == times.last && !Configuration.instance.quiet?
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def blank?
|
57
|
-
image = MiniMagick::Image.open filename
|
58
|
-
image.colorspace 'Gray'
|
59
|
-
mean = image['%[fx:image.mean]'].to_f
|
60
|
-
return mean < Configuration.instance.blank_threshold
|
61
|
-
end
|
62
|
-
|
63
|
-
def apply_filters
|
64
|
-
MiniMagick::Tool::Convert.new do |convert|
|
65
|
-
convert.background 'Transparent'
|
66
|
-
convert.fill 'Transparent'
|
67
|
-
convert << filename
|
68
|
-
|
69
|
-
sorted_filters.each do |filter|
|
70
|
-
call_filter filter, convert
|
71
|
-
end
|
72
|
-
|
73
|
-
convert << filename
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
def call_filter filter, convert
|
79
|
-
if respond_to?(filter, true)
|
80
|
-
method(filter).call(convert)
|
81
|
-
else
|
82
|
-
raise "Filter '#{filter}' does not exist"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def sorted_filters
|
87
|
-
[:resize_filter, :timestamp_filter, :photoframe_filter, :polaroid_filter, :random_rotation_filter, :softshadow_filter].select{ |filter| @filters.include?(filter) }
|
88
|
-
end
|
89
|
-
|
90
|
-
def resize_filter convert
|
91
|
-
convert.resize "#{width}x#{height}!"
|
92
|
-
end
|
93
|
-
|
94
|
-
def timestamp_filter convert
|
95
|
-
convert.stack do |box|
|
96
|
-
box.box Configuration.instance.timestamp_background
|
97
|
-
box.fill Configuration.instance.timestamp_color
|
98
|
-
box.pointsize Configuration.instance.timestamp_font.size
|
99
|
-
box.gravity 'SouthEast'
|
100
|
-
if Configuration.instance.timestamp_font.exists?
|
101
|
-
box.font Configuration.instance.timestamp_font.path
|
102
|
-
end
|
103
|
-
box.annotate('+10+10', " #{@time.to_timestamp} ")
|
104
|
-
end
|
105
|
-
convert.flatten
|
106
|
-
convert.gravity 'None'
|
107
|
-
end
|
108
|
-
|
109
|
-
def softshadow_filter convert
|
110
|
-
convert.stack do |box|
|
111
|
-
box.background 'Black'
|
112
|
-
box.clone.+
|
113
|
-
box.shadow '50x2+4+4'
|
114
|
-
box.background 'None'
|
115
|
-
end
|
116
|
-
convert.swap.+
|
117
|
-
convert.flatten
|
118
|
-
convert.trim
|
119
|
-
convert.repage.+
|
120
|
-
end
|
121
|
-
|
122
|
-
def photoframe_filter convert
|
123
|
-
convert.bordercolor 'White'
|
124
|
-
convert.border 3
|
125
|
-
convert.bordercolor 'Grey60'
|
126
|
-
convert.border 1
|
127
|
-
end
|
128
|
-
|
129
|
-
def polaroid_filter convert
|
130
|
-
border = 8
|
131
|
-
convert.stack do |a|
|
132
|
-
a.fill 'White'
|
133
|
-
a.background 'White'
|
134
|
-
a.bordercolor 'White'
|
135
|
-
if Tools.magick_version.major > 6
|
136
|
-
a.alpha_color 'White'
|
137
|
-
else
|
138
|
-
a.mattecolor 'White'
|
139
|
-
end
|
140
|
-
a.frame "#{border}x#{border}"
|
141
|
-
a.stack do |b|
|
142
|
-
b.flip
|
143
|
-
b.splice "0x#{border*5}"
|
144
|
-
end
|
145
|
-
a.flip
|
146
|
-
a.bordercolor 'Grey60'
|
147
|
-
a.border 1
|
148
|
-
end
|
149
|
-
convert.repage.+
|
150
|
-
end
|
151
|
-
|
152
|
-
def random_rotation_filter convert
|
153
|
-
angle = Random::rand(-18..18)
|
154
|
-
convert.background 'None'
|
155
|
-
convert.rotate angle
|
156
|
-
end
|
157
|
-
|
158
|
-
def film_filter
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
1
|
+
#
|
2
|
+
# Frame from video
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'mini_magick'
|
6
|
+
|
7
|
+
module VCSRuby
|
8
|
+
class Frame
|
9
|
+
attr_accessor :width, :height, :aspect
|
10
|
+
attr_reader :filters, :time
|
11
|
+
|
12
|
+
def initialize video, capturer, time
|
13
|
+
@video = video
|
14
|
+
@capturer = capturer
|
15
|
+
@time = time
|
16
|
+
@filters = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def filename= file_path
|
20
|
+
@out_path = File.dirname(file_path)
|
21
|
+
@out_filename = File.basename(file_path,'.*')
|
22
|
+
end
|
23
|
+
|
24
|
+
def filename
|
25
|
+
File.join(@out_path, "#{@out_filename}.#{@capturer.format_extension}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def format= fmt
|
29
|
+
@capturer.format = fmt
|
30
|
+
end
|
31
|
+
|
32
|
+
def format
|
33
|
+
@capturer.format
|
34
|
+
end
|
35
|
+
|
36
|
+
def capture
|
37
|
+
@capturer.grab @time, filename
|
38
|
+
end
|
39
|
+
|
40
|
+
def capture_and_evade interval = nil
|
41
|
+
times = [TimeIndex.new] + Configuration.instance.blank_alternatives
|
42
|
+
if interval
|
43
|
+
times.select! { |t| (t < interval / 2) and (t > interval / -2) }
|
44
|
+
end
|
45
|
+
times.map! { |t| @time + t }
|
46
|
+
|
47
|
+
times.each do |time|
|
48
|
+
@time = time
|
49
|
+
capture
|
50
|
+
break unless blank?
|
51
|
+
puts "Blank frame detected. => #{@time}" unless Configuration.instance.quiet?
|
52
|
+
puts "Giving up!" if time == times.last && !Configuration.instance.quiet?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def blank?
|
57
|
+
image = MiniMagick::Image.open filename
|
58
|
+
image.colorspace 'Gray'
|
59
|
+
mean = image['%[fx:image.mean]'].to_f
|
60
|
+
return mean < Configuration.instance.blank_threshold
|
61
|
+
end
|
62
|
+
|
63
|
+
def apply_filters
|
64
|
+
MiniMagick::Tool::Convert.new do |convert|
|
65
|
+
convert.background 'Transparent'
|
66
|
+
convert.fill 'Transparent'
|
67
|
+
convert << filename
|
68
|
+
|
69
|
+
sorted_filters.each do |filter|
|
70
|
+
call_filter filter, convert
|
71
|
+
end
|
72
|
+
|
73
|
+
convert << filename
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def call_filter filter, convert
|
79
|
+
if respond_to?(filter, true)
|
80
|
+
method(filter).call(convert)
|
81
|
+
else
|
82
|
+
raise "Filter '#{filter}' does not exist"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def sorted_filters
|
87
|
+
[:resize_filter, :timestamp_filter, :photoframe_filter, :polaroid_filter, :random_rotation_filter, :softshadow_filter].select{ |filter| @filters.include?(filter) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def resize_filter convert
|
91
|
+
convert.resize "#{width}x#{height}!"
|
92
|
+
end
|
93
|
+
|
94
|
+
def timestamp_filter convert
|
95
|
+
convert.stack do |box|
|
96
|
+
box.box Configuration.instance.timestamp_background
|
97
|
+
box.fill Configuration.instance.timestamp_color
|
98
|
+
box.pointsize Configuration.instance.timestamp_font.size
|
99
|
+
box.gravity 'SouthEast'
|
100
|
+
if Configuration.instance.timestamp_font.exists?
|
101
|
+
box.font Configuration.instance.timestamp_font.path
|
102
|
+
end
|
103
|
+
box.annotate('+10+10', " #{@time.to_timestamp} ")
|
104
|
+
end
|
105
|
+
convert.flatten
|
106
|
+
convert.gravity 'None'
|
107
|
+
end
|
108
|
+
|
109
|
+
def softshadow_filter convert
|
110
|
+
convert.stack do |box|
|
111
|
+
box.background 'Black'
|
112
|
+
box.clone.+
|
113
|
+
box.shadow '50x2+4+4'
|
114
|
+
box.background 'None'
|
115
|
+
end
|
116
|
+
convert.swap.+
|
117
|
+
convert.flatten
|
118
|
+
convert.trim
|
119
|
+
convert.repage.+
|
120
|
+
end
|
121
|
+
|
122
|
+
def photoframe_filter convert
|
123
|
+
convert.bordercolor 'White'
|
124
|
+
convert.border 3
|
125
|
+
convert.bordercolor 'Grey60'
|
126
|
+
convert.border 1
|
127
|
+
end
|
128
|
+
|
129
|
+
def polaroid_filter convert
|
130
|
+
border = 8
|
131
|
+
convert.stack do |a|
|
132
|
+
a.fill 'White'
|
133
|
+
a.background 'White'
|
134
|
+
a.bordercolor 'White'
|
135
|
+
if Tools.magick_version.major > 6
|
136
|
+
a.alpha_color 'White'
|
137
|
+
else
|
138
|
+
a.mattecolor 'White'
|
139
|
+
end
|
140
|
+
a.frame "#{border}x#{border}"
|
141
|
+
a.stack do |b|
|
142
|
+
b.flip
|
143
|
+
b.splice "0x#{border*5}"
|
144
|
+
end
|
145
|
+
a.flip
|
146
|
+
a.bordercolor 'Grey60'
|
147
|
+
a.border 1
|
148
|
+
end
|
149
|
+
convert.repage.+
|
150
|
+
end
|
151
|
+
|
152
|
+
def random_rotation_filter convert
|
153
|
+
angle = Random::rand(-18..18)
|
154
|
+
convert.background 'None'
|
155
|
+
convert.rotate angle
|
156
|
+
end
|
157
|
+
|
158
|
+
def film_filter
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|