castaway 1.0.0
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/MIT-LICENSE +20 -0
- data/README.md +123 -0
- data/bin/castaway +5 -0
- data/lib/castaway.rb +1 -0
- data/lib/castaway/animation.rb +37 -0
- data/lib/castaway/box.rb +77 -0
- data/lib/castaway/cli/build.rb +83 -0
- data/lib/castaway/cli/main.rb +24 -0
- data/lib/castaway/cli/script.rb +28 -0
- data/lib/castaway/cli/version.rb +17 -0
- data/lib/castaway/effect.rb +59 -0
- data/lib/castaway/element/base.rb +280 -0
- data/lib/castaway/element/matte.rb +20 -0
- data/lib/castaway/element/pointer.rb +35 -0
- data/lib/castaway/element/still.rb +31 -0
- data/lib/castaway/element/text.rb +76 -0
- data/lib/castaway/interpolation.rb +23 -0
- data/lib/castaway/interpolation/linear.rb +26 -0
- data/lib/castaway/point.rb +63 -0
- data/lib/castaway/production.rb +124 -0
- data/lib/castaway/production/audio.rb +161 -0
- data/lib/castaway/production/class_methods.rb +148 -0
- data/lib/castaway/production/scenes.rb +39 -0
- data/lib/castaway/range.rb +70 -0
- data/lib/castaway/relative_to.rb +18 -0
- data/lib/castaway/scene.rb +145 -0
- data/lib/castaway/size.rb +47 -0
- data/lib/castaway/timeline.rb +80 -0
- data/lib/castaway/times.rb +33 -0
- data/lib/castaway/version.rb +3 -0
- metadata +134 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'castaway/scene'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
class Production
|
5
|
+
module Scenes
|
6
|
+
|
7
|
+
# Returns the duration of the production, in seconds.
|
8
|
+
def duration
|
9
|
+
@scenes.last.finish
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the first scene with the given title.
|
13
|
+
def scene(title)
|
14
|
+
@scenes.find { |s| s.title == title }
|
15
|
+
end
|
16
|
+
|
17
|
+
def resource(name)
|
18
|
+
self.class.resource(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def pointers
|
22
|
+
self.class.pointers
|
23
|
+
end
|
24
|
+
|
25
|
+
def _build_scenes
|
26
|
+
@scenes = self.class.scenes.map do |(name, config)|
|
27
|
+
Castaway::Scene.new(name, self).configure(&config)
|
28
|
+
end
|
29
|
+
|
30
|
+
@scenes = @scenes.sort_by(&:start)
|
31
|
+
|
32
|
+
@scenes.each.with_index do |scene, index|
|
33
|
+
scene.update_from_next(@scenes[index + 1])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'castaway/times'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
|
5
|
+
class Range
|
6
|
+
include Castaway::Times
|
7
|
+
|
8
|
+
attr_accessor :start_frame, :end_frame
|
9
|
+
|
10
|
+
def self.at_frame(production, frame)
|
11
|
+
new(production).tap do |range|
|
12
|
+
range.start_frame = frame
|
13
|
+
range.end_frame = frame
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.at_time(production, time)
|
18
|
+
new(production).tap do |range|
|
19
|
+
range.start_time = time
|
20
|
+
range.end_time = time
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.at_scene(production, title)
|
25
|
+
new(production).tap do |range|
|
26
|
+
range.start_scene = title
|
27
|
+
range.end_scene = title
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(production)
|
32
|
+
@production = production
|
33
|
+
@start_frame = 0
|
34
|
+
self.end_time = production.duration
|
35
|
+
end
|
36
|
+
|
37
|
+
def truncated?
|
38
|
+
start_frame > 0 || end_time < @production.duration
|
39
|
+
end
|
40
|
+
|
41
|
+
def start_time=(t)
|
42
|
+
@start_frame = (_parse_time(t) * @production.fps).floor
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_time
|
46
|
+
@start_frame / @production.fps.to_f
|
47
|
+
end
|
48
|
+
|
49
|
+
def end_time=(t)
|
50
|
+
@end_frame = (_parse_time(t) * @production.fps).ceil
|
51
|
+
end
|
52
|
+
|
53
|
+
def end_time
|
54
|
+
@end_frame / @production.fps.to_f
|
55
|
+
end
|
56
|
+
|
57
|
+
def start_scene=(title)
|
58
|
+
scene = @production.scene(title)
|
59
|
+
raise ArgumentError, "no scene named #{title.inspect}" unless scene
|
60
|
+
self.start_time = scene.start
|
61
|
+
end
|
62
|
+
|
63
|
+
def end_scene=(title)
|
64
|
+
scene = @production.scene(title)
|
65
|
+
raise ArgumentError, "no scene named #{title.inspect}" unless scene
|
66
|
+
self.end_time = scene.finish
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
require 'castaway/point'
|
3
|
+
|
4
|
+
module Castaway
|
5
|
+
class RelativeTo
|
6
|
+
def initialize(image_file_name, production)
|
7
|
+
path = production.resource(image_file_name)
|
8
|
+
image = MiniMagick::Image.new(path)
|
9
|
+
@width = image.width.to_f
|
10
|
+
@height = image.height.to_f
|
11
|
+
@production = production
|
12
|
+
end
|
13
|
+
|
14
|
+
def position(x, y)
|
15
|
+
Castaway::Point.new(x / @width, y / @height) * @production.resolution
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'castaway/element/matte'
|
2
|
+
require 'castaway/element/still'
|
3
|
+
require 'castaway/element/pointer'
|
4
|
+
require 'castaway/element/text'
|
5
|
+
require 'castaway/relative_to'
|
6
|
+
require 'castaway/times'
|
7
|
+
|
8
|
+
module Castaway
|
9
|
+
class Scene
|
10
|
+
include Castaway::Times
|
11
|
+
|
12
|
+
attr_reader :title
|
13
|
+
attr_reader :production
|
14
|
+
|
15
|
+
attr_reader :finish, :duration
|
16
|
+
|
17
|
+
attr_reader :_timeline
|
18
|
+
|
19
|
+
def initialize(title, production)
|
20
|
+
@title = title
|
21
|
+
@production = production
|
22
|
+
end
|
23
|
+
|
24
|
+
def configure(&block)
|
25
|
+
instance_eval(&block)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Declares (or returns) the time value (in seconds) for the start of this
|
30
|
+
# scene. Any value parsable by Castaway::Times will be accepted.
|
31
|
+
def start(value = nil)
|
32
|
+
return @start unless value
|
33
|
+
@start = _parse_time(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Parses and returns the seconds corresponding to the given value. See
|
37
|
+
# Castaway::Times.
|
38
|
+
def time(value)
|
39
|
+
_parse_time(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a new Castaway::RelativeTo instance for the resource with the
|
43
|
+
# given file name. This is useful for positioning pointers in a
|
44
|
+
# resolution-independent way.
|
45
|
+
def relative_to_image(name)
|
46
|
+
RelativeTo.new(name, production)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets (or returns) the script corresponding to the current scene.
|
50
|
+
# This is not used, except informationally.
|
51
|
+
def script(*args)
|
52
|
+
if args.empty?
|
53
|
+
@script
|
54
|
+
elsif args.length == 1
|
55
|
+
@script = _strip(args.first)
|
56
|
+
else
|
57
|
+
raise ArgumentError, 'script expects 0 or 1 argument'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Declare the plan to be used for constructing the scene. Within the plan,
|
62
|
+
# scene elements are declared and configured.
|
63
|
+
#
|
64
|
+
# plan do
|
65
|
+
# matte(:black).enter(-0.5).in(:dissolve, speed: 0.5)
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# See #matte, #still, #text, #sprite, and #pointer.
|
69
|
+
def plan(&block)
|
70
|
+
@plan = block
|
71
|
+
end
|
72
|
+
|
73
|
+
def construct(timeline)
|
74
|
+
@_timeline = timeline
|
75
|
+
instance_eval(&@plan) if @plan
|
76
|
+
ensure
|
77
|
+
remove_instance_variable :@_timeline
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns a new Castaway::Element::Matte element with the given color, and
|
81
|
+
# adds it to the timeline.
|
82
|
+
def matte(color)
|
83
|
+
Element::Matte.new(production, self, color).tap do |element|
|
84
|
+
_timeline.add(element)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a new Castaway::Element::Still element for the given filename,
|
89
|
+
# and adds it to the timeline. It will be forced to fill the entire frame.
|
90
|
+
def still(filename)
|
91
|
+
_still(filename, true)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns a new Castaway::Element::Still element for the given filename,
|
95
|
+
# and adds it to the timeline. It's native dimensions will be preserved.
|
96
|
+
def sprite(filename)
|
97
|
+
_still(filename, false)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns a new Castaway::Element::Pointer element and adds it to the
|
101
|
+
# timeline. If an `id` is given, the pointer declared with that `id` is
|
102
|
+
# used.
|
103
|
+
def pointer(id = :default)
|
104
|
+
Element::Pointer.new(production, self, id).tap do |pointer|
|
105
|
+
_timeline.add(pointer)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns a new Castaway::Element::Text element with the given text, and
|
110
|
+
# adds it to the timeline.
|
111
|
+
def text(string)
|
112
|
+
Element::Text.new(production, self, string).tap do |text|
|
113
|
+
_timeline.add(text)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns a Castaway::Point with the given coordinates multiplied by the
|
118
|
+
# resolution. This let's you declare coordinates as fractions of the frame
|
119
|
+
# size, so that they work regardless of the final rendered resolution.
|
120
|
+
def relative_position(x, y)
|
121
|
+
Castaway::Point.new(x, y) * production.resolution
|
122
|
+
end
|
123
|
+
|
124
|
+
def update_from_next(neighbor)
|
125
|
+
@finish = neighbor.nil? ? @start : neighbor.start
|
126
|
+
@duration = @finish - @start
|
127
|
+
end
|
128
|
+
|
129
|
+
def _still(filename, full)
|
130
|
+
Element::Still.new(production, self, filename, full: full).
|
131
|
+
tap do |element|
|
132
|
+
_timeline.add(element)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def _strip(text)
|
137
|
+
if text =~ /^(\s+)\S/
|
138
|
+
indent = Regexp.last_match(1)
|
139
|
+
text.gsub(/^#{indent}/, '')
|
140
|
+
else
|
141
|
+
text
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Castaway
|
2
|
+
|
3
|
+
class Size < Struct.new(:width, :height)
|
4
|
+
def *(factor)
|
5
|
+
if factor.is_a?(Size)
|
6
|
+
Size.new(width * factor.width, height * factor.height)
|
7
|
+
else
|
8
|
+
Size.new(width * factor, height * factor)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def present?
|
13
|
+
width || height
|
14
|
+
end
|
15
|
+
|
16
|
+
def empty?
|
17
|
+
(width || 0).zero? && (height || 0).zero?
|
18
|
+
end
|
19
|
+
|
20
|
+
def aspect_ratio
|
21
|
+
@aspect_ratio ||= width.to_f / height
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_height(height)
|
25
|
+
Size.new((aspect_ratio * height).to_i, height.to_i)
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_width(width)
|
29
|
+
Size.new(width.to_i, (width / aspect_ratio).to_i)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
format('(%.2f, %.2f)', width, height)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_geometry
|
37
|
+
format('%.2fx%.2f', width, height).tap do |geometry|
|
38
|
+
geometry << '!' if width && height
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_resolution
|
43
|
+
format('%dx%d', width || 0, height || 0)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Castaway
|
5
|
+
|
6
|
+
class Timeline
|
7
|
+
attr_reader :resolution, :fps
|
8
|
+
|
9
|
+
def initialize(resolution, fps)
|
10
|
+
@resolution = resolution
|
11
|
+
@elements = []
|
12
|
+
@fps = fps
|
13
|
+
|
14
|
+
@cached_command = nil
|
15
|
+
@cached_file = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(element)
|
19
|
+
@elements << element
|
20
|
+
end
|
21
|
+
|
22
|
+
def duration
|
23
|
+
@elements.map(&:t2).max
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_frame(frame, name: 'frame')
|
27
|
+
t = frame / fps.to_f
|
28
|
+
|
29
|
+
signature = nil
|
30
|
+
tool = MiniMagick::Tool::Convert.new.tap do |convert|
|
31
|
+
convert << '-size' << resolution.to_geometry
|
32
|
+
convert.xc 'black'
|
33
|
+
|
34
|
+
@elements.sort_by(&:t1).each do |element|
|
35
|
+
next unless element.alive_at?(t)
|
36
|
+
element.render_at(t, convert)
|
37
|
+
end
|
38
|
+
|
39
|
+
convert.colorspace 'sRGB'
|
40
|
+
convert.type 'TrueColor'
|
41
|
+
convert.depth '16'
|
42
|
+
|
43
|
+
signature = convert.command
|
44
|
+
convert << "PNG48:#{name}.png"
|
45
|
+
end
|
46
|
+
|
47
|
+
if signature != @cached_command
|
48
|
+
_log frame, t, tool.command.join(' ')
|
49
|
+
@cached_command = signature
|
50
|
+
@cached_file = name
|
51
|
+
tool.call
|
52
|
+
else
|
53
|
+
old = "#{@cached_file}.png"
|
54
|
+
new = "#{name}.png"
|
55
|
+
_log frame, t, "duplicate #{old} as #{new}"
|
56
|
+
_link old, new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def _link(old, new)
|
61
|
+
# FIXME: detect whether linking is supported, and fallback to copy
|
62
|
+
# if not.
|
63
|
+
|
64
|
+
FileUtils.ln(old, new)
|
65
|
+
end
|
66
|
+
|
67
|
+
def _logger
|
68
|
+
@_logger ||= Logger.new('build.log').tap do |logger|
|
69
|
+
logger.formatter = lambda do |severity, _datetime, _progname, msg|
|
70
|
+
"#{severity}: #{msg}\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def _log(frame, t, msg)
|
76
|
+
_logger.info { format('[%d:%.2fs] %s', frame, t, msg) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Castaway
|
2
|
+
module Times
|
3
|
+
def _parse_time(spec)
|
4
|
+
_parse_numeric_time(spec) ||
|
5
|
+
_parse_timespec_time(spec) ||
|
6
|
+
raise(ArgumentError, "unsupported time #{spec.inspect}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def _parse_numeric_time(spec)
|
10
|
+
if spec.is_a?(Numeric)
|
11
|
+
spec
|
12
|
+
elsif spec =~ /^\d+(\.\d+)?s?$/
|
13
|
+
spec.to_f
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def _parse_timespec_time(spec)
|
18
|
+
return unless spec =~ /^(\d+)(?::(\d+))?(?::(\d+))?(\.\d+)?$/
|
19
|
+
|
20
|
+
a = Regexp.last_match(1)
|
21
|
+
b = Regexp.last_match(2)
|
22
|
+
c = Regexp.last_match(3)
|
23
|
+
d = Regexp.last_match(4)
|
24
|
+
|
25
|
+
time = [c, b, a].compact.each.with_index.reduce(0) do |m, (v, i)|
|
26
|
+
m + v.to_i * 60**i
|
27
|
+
end
|
28
|
+
|
29
|
+
time += d.to_f if d
|
30
|
+
time
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: castaway
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jamis Buck
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.14'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mini_magick
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.6'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-progressbar
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: chaussettes
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
description: |2
|
70
|
+
Construct screencasts in Ruby! Write your script, declare your timeline,
|
71
|
+
mix your audio, and render your video in an easily-edited, easily-repeated
|
72
|
+
DSL. (Depends on ImageMagick, Sox, and FFMPEG for the heavy-lifting.)
|
73
|
+
email:
|
74
|
+
- jamis@jamisbuck.org
|
75
|
+
executables:
|
76
|
+
- castaway
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- MIT-LICENSE
|
81
|
+
- README.md
|
82
|
+
- bin/castaway
|
83
|
+
- lib/castaway.rb
|
84
|
+
- lib/castaway/animation.rb
|
85
|
+
- lib/castaway/box.rb
|
86
|
+
- lib/castaway/cli/build.rb
|
87
|
+
- lib/castaway/cli/main.rb
|
88
|
+
- lib/castaway/cli/script.rb
|
89
|
+
- lib/castaway/cli/version.rb
|
90
|
+
- lib/castaway/effect.rb
|
91
|
+
- lib/castaway/element/base.rb
|
92
|
+
- lib/castaway/element/matte.rb
|
93
|
+
- lib/castaway/element/pointer.rb
|
94
|
+
- lib/castaway/element/still.rb
|
95
|
+
- lib/castaway/element/text.rb
|
96
|
+
- lib/castaway/interpolation.rb
|
97
|
+
- lib/castaway/interpolation/linear.rb
|
98
|
+
- lib/castaway/point.rb
|
99
|
+
- lib/castaway/production.rb
|
100
|
+
- lib/castaway/production/audio.rb
|
101
|
+
- lib/castaway/production/class_methods.rb
|
102
|
+
- lib/castaway/production/scenes.rb
|
103
|
+
- lib/castaway/range.rb
|
104
|
+
- lib/castaway/relative_to.rb
|
105
|
+
- lib/castaway/scene.rb
|
106
|
+
- lib/castaway/size.rb
|
107
|
+
- lib/castaway/timeline.rb
|
108
|
+
- lib/castaway/times.rb
|
109
|
+
- lib/castaway/version.rb
|
110
|
+
homepage: https://github.com/jamis/castaway
|
111
|
+
licenses:
|
112
|
+
- MIT
|
113
|
+
metadata: {}
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options: []
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
requirements: []
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 2.5.1
|
131
|
+
signing_key:
|
132
|
+
specification_version: 4
|
133
|
+
summary: System for building screencasts and video presentations
|
134
|
+
test_files: []
|