castaway 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a112c82c587c36cded9e5a12357b32afccda2b23
|
4
|
+
data.tar.gz: 9d309a240a441890b9cd8fc21d7385d96c28de67
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d8c360c1446343694d4ad4cf7b869dd60f338759d81e2d5101d4bae9ba7eebf393b83b4afc48565c7839d0a6341a7a15bf59f31660074ef02b8adc99ce7347b2
|
7
|
+
data.tar.gz: 6ee19bbf94daa6a12d7155c4219be4e96bb84d613df584d5c9d567b73a40153b05999beebec6c48db58a819bc2667d463c1f470493be786e122a9988b1d8b45c
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Jamis Buck
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Castaway
|
2
|
+
|
3
|
+
Want to create an online presentation or screencast, but are frustrated by
|
4
|
+
complicated interfaces or expensive tools? Maybe you're using one that _almost_
|
5
|
+
does what you need, but that one feature it's missing is a deal-breaker?
|
6
|
+
|
7
|
+
_Castaway_ to the rescue! Write your scripts, mix your audio, and render your
|
8
|
+
video, all via a simple-yet-powerful DSL.
|
9
|
+
|
10
|
+
Want to re-render your video with a different resolution or frame rate? No
|
11
|
+
problem--just run the script with different parameters.
|
12
|
+
|
13
|
+
Is that arrow pointing at the wrong point, or does that animation start at the
|
14
|
+
wrong time? Easy-peasy. Change the position or timing in your script, and rerun
|
15
|
+
it.
|
16
|
+
|
17
|
+
Screen-casting just got a whole lot easier.
|
18
|
+
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Castaway depends on a few external tools to do the heavy lifting. You'll need to
|
23
|
+
make sure you have the following tools installed:
|
24
|
+
|
25
|
+
* [ImageMagick](https://www.imagemagick.org/script/binary-releases.php) is used
|
26
|
+
to render video frames and special effects. _(Tested with version 6.9.5)_
|
27
|
+
* [Sox](https://sourceforge.net/projects/sox/files/sox/) is used to edit and mix
|
28
|
+
audio. _(Tested with version 14.4.2)_
|
29
|
+
* [FFmpeg](https://ffmpeg.org/download.html) is used to combine the frames and
|
30
|
+
audio into a single video file. _(Tested with version 3.2.2)_
|
31
|
+
|
32
|
+
Once you've met those requirements, installing Castaway itself is simple:
|
33
|
+
|
34
|
+
$ gem install castaway
|
35
|
+
|
36
|
+
And you're good to go!
|
37
|
+
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
_Watch a four-minute introduction to Castaway--created with Castaway!--on YouTube, here: [https://www.youtube.com/watch?v=F5ShAdLvVIk](https://www.youtube.com/watch?v=F5ShAdLvVIk) ._
|
42
|
+
|
43
|
+
Scripts are written in a DSL in Ruby. You declare scenes and sound clips,
|
44
|
+
and describe what comprises those scenes and sound clips. Here's a simple
|
45
|
+
example:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
soundclip :theme, resource('music.wav')
|
49
|
+
|
50
|
+
soundtrack do |clip|
|
51
|
+
clip.in soundclip(:theme)
|
52
|
+
# fade in the theme music
|
53
|
+
clip.chain.fade(5, type: :linear)
|
54
|
+
end
|
55
|
+
|
56
|
+
scene 'Title Screen' do
|
57
|
+
start '0:00'
|
58
|
+
script 'Hello, and welcome to our new screencast!'
|
59
|
+
|
60
|
+
plan do
|
61
|
+
# start with a black screen
|
62
|
+
matte(:black).
|
63
|
+
exit(1)
|
64
|
+
|
65
|
+
# dissolve-in our title screen
|
66
|
+
still('title.png').
|
67
|
+
enter(0.5).
|
68
|
+
in(:dissolve, speed: 0.5)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
finish '0:10'
|
73
|
+
```
|
74
|
+
|
75
|
+
This declares a sound track that fades in over five seconds, as well as a
|
76
|
+
single scene that displays a still frame, dissolved in at the 0.5 second mark.
|
77
|
+
The whole finishes at the ten second mark. If this were saved as `script.rb`,
|
78
|
+
you could generate the video like so:
|
79
|
+
|
80
|
+
$ castaway build script.rb
|
81
|
+
|
82
|
+
This will generate the frames, mix the audio, and compose the whole together
|
83
|
+
into a video called `script.mp4` (it uses the name of the script file as the
|
84
|
+
default for naming the video).
|
85
|
+
|
86
|
+
To name it something else:
|
87
|
+
|
88
|
+
$ castaway build -o movie.mp4 script.rb
|
89
|
+
|
90
|
+
By default, the video will be rendered at 540p (960x540 pixels). Change this
|
91
|
+
with the `--resolution` parameter:
|
92
|
+
|
93
|
+
$ castaway build --resolution 1080p script.rb
|
94
|
+
|
95
|
+
You can specify either HD-style resolutions (1080p, 540p, etc.) or WIDTHxHEIGHT
|
96
|
+
resolutions (e.g. 960x540).
|
97
|
+
|
98
|
+
Also by default, video will be rendered at NTSC-standard 29.97 frames/second.
|
99
|
+
To change the number of frames per second, use the `--fps` parameter:
|
100
|
+
|
101
|
+
$ castaway build --fps 10 script.rb
|
102
|
+
|
103
|
+
This can be useful for previewing a build quickly, before building the final
|
104
|
+
movie.
|
105
|
+
|
106
|
+
|
107
|
+
## Caveats
|
108
|
+
|
109
|
+
This is a work in progress, and will probably not do everything you need just
|
110
|
+
yet. Documentation and examples are severely lacking.
|
111
|
+
|
112
|
+
But stay tuned!
|
113
|
+
|
114
|
+
|
115
|
+
## Author
|
116
|
+
|
117
|
+
Castaway was written by Jamis Buck (jamis@jamisbuck.org).
|
118
|
+
|
119
|
+
|
120
|
+
## License
|
121
|
+
|
122
|
+
Castaway is distributed under the MIT license. (See the `MIT-LICENSE` file for
|
123
|
+
details).
|
data/bin/castaway
ADDED
data/lib/castaway.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'castaway/production'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'castaway/interpolation'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
|
5
|
+
class Animation
|
6
|
+
attr_reader :from, :to, :duration
|
7
|
+
|
8
|
+
def self.from_options(options)
|
9
|
+
factory = Castaway::Interpolation.lookup(options)
|
10
|
+
initial = options[:initial] || 0.0
|
11
|
+
final = options[:final] || (initial + 1.0)
|
12
|
+
interpolator = factory.new(initial, final)
|
13
|
+
|
14
|
+
from = options[:from] ||
|
15
|
+
raise(ArgumentError, 'animations require `from` time')
|
16
|
+
to = options[:to] ||
|
17
|
+
(options[:length] ? from + options[:length] : nil) ||
|
18
|
+
raise(ArgumentError, 'animations require `to` or `length`')
|
19
|
+
|
20
|
+
new(interpolator, from, to)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(interpolator, from, to)
|
24
|
+
@interpolator = interpolator
|
25
|
+
@from = from.to_f
|
26
|
+
@to = to.to_f
|
27
|
+
@duration = @to - @from
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](t)
|
31
|
+
# adjust t to 0..1
|
32
|
+
adjusted_t = duration.zero? ? 1.0 : (t - from) / duration.to_f
|
33
|
+
@interpolator[adjusted_t]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/castaway/box.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'castaway/point'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
class Box
|
5
|
+
def self.from_size(size)
|
6
|
+
new(
|
7
|
+
Point.new(0, 0),
|
8
|
+
Point.new(size.width - 1, 0),
|
9
|
+
Point.new(size.width - 1, size.height - 1),
|
10
|
+
Point.new(0, size.height - 1))
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(a, b, c, d, other = {})
|
14
|
+
@a = a
|
15
|
+
@b = b
|
16
|
+
@c = c
|
17
|
+
@d = d
|
18
|
+
@other_points = other.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(id, point_of_interest)
|
22
|
+
@other_points[id] = point_of_interest
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](id)
|
27
|
+
@other_points[id]
|
28
|
+
end
|
29
|
+
|
30
|
+
def scale(factor)
|
31
|
+
a = @a * factor
|
32
|
+
b = @b * factor
|
33
|
+
c = @c * factor
|
34
|
+
d = @d * factor
|
35
|
+
|
36
|
+
others = @other_points.each_with_object({}) do |(k, v), h|
|
37
|
+
h[k] = v * factor
|
38
|
+
end
|
39
|
+
|
40
|
+
Box.new(a, b, c, d, others)
|
41
|
+
end
|
42
|
+
|
43
|
+
def rotate(degrees)
|
44
|
+
rads = degrees * Math::PI / 180.0
|
45
|
+
|
46
|
+
a = @a.rotate(rads)
|
47
|
+
b = @b.rotate(rads)
|
48
|
+
c = @c.rotate(rads)
|
49
|
+
d = @d.rotate(rads)
|
50
|
+
|
51
|
+
others = @other_points.each_with_object({}) do |(k, v), h|
|
52
|
+
h[k] = v.rotate(rads)
|
53
|
+
end
|
54
|
+
|
55
|
+
Box.new(a, b, c, d, others)
|
56
|
+
end
|
57
|
+
|
58
|
+
def bounds
|
59
|
+
x1, x2 = [ @a.x, @b.x, @c.x, @d.x ].minmax
|
60
|
+
y1, y2 = [ @a.y, @b.y, @c.y, @d.y ].minmax
|
61
|
+
|
62
|
+
# translate to origin
|
63
|
+
x2 -= x1
|
64
|
+
y2 -= y1
|
65
|
+
others = @other_points.each_with_object({}) do |(k, v), h|
|
66
|
+
h[k] = v.translate(-x1, -y1)
|
67
|
+
end
|
68
|
+
|
69
|
+
Box.new(Point.new(0, 0), Point.new(x2, 0),
|
70
|
+
Point.new(x2, y2), Point.new(0, y2), others)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
"#{@a}-#{@b}-#{@c}-#{@d}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'castaway/production'
|
2
|
+
require 'castaway/range'
|
3
|
+
|
4
|
+
module Castaway
|
5
|
+
module CLI
|
6
|
+
class Build
|
7
|
+
extend GLI::App
|
8
|
+
|
9
|
+
def self.description
|
10
|
+
'Builds the given castaway production'
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.define(command)
|
14
|
+
command.desc 'The resolution at which to generate the frames'
|
15
|
+
command.flag %i(r resolution), default_value: '540p'
|
16
|
+
|
17
|
+
command.desc 'How many frames per second to generate'
|
18
|
+
command.flag %i(f fps), default_value: 29.97, type: Float
|
19
|
+
|
20
|
+
command.desc 'The frame from which to start producing frames'
|
21
|
+
command.flag %i(start-frame), default_value: 0, type: Integer
|
22
|
+
|
23
|
+
command.desc 'The frame after which to stop producing frames'
|
24
|
+
command.flag %i(end-frame), type: Integer
|
25
|
+
|
26
|
+
command.desc 'The scene from which to start producing frames'
|
27
|
+
command.flag %i(start-scene)
|
28
|
+
|
29
|
+
command.desc 'The scene after which to stop producing frames'
|
30
|
+
command.flag %i(end-scene)
|
31
|
+
|
32
|
+
command.desc 'The time from which to start producing frames'
|
33
|
+
command.flag %i(start-time)
|
34
|
+
|
35
|
+
command.desc 'The time after which to stop producing frames'
|
36
|
+
command.flag %i(end-time)
|
37
|
+
|
38
|
+
command.desc 'What to call the resulting movie'
|
39
|
+
command.flag %i(o output)
|
40
|
+
|
41
|
+
command.action do |_globals, options, args|
|
42
|
+
exit_now!('you have to supply a castaway program') if args.empty?
|
43
|
+
|
44
|
+
definition = Castaway::Production.from_script(args.first)
|
45
|
+
new(definition, args.first, options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(definition, name, options)
|
50
|
+
deliverable = File.basename(name, File.extname(name)) + '.mp4'
|
51
|
+
|
52
|
+
production = definition.new(
|
53
|
+
resolution: options[:resolution],
|
54
|
+
fps: options[:fps],
|
55
|
+
deliverable: options[:output] || deliverable)
|
56
|
+
|
57
|
+
range = Castaway::Range.new(production)
|
58
|
+
|
59
|
+
if options['start-time']
|
60
|
+
range.start_time = options['start-time']
|
61
|
+
elsif options['start-scene']
|
62
|
+
range.start_scene = options['start-scene']
|
63
|
+
elsif options['start-frame']
|
64
|
+
range.start_frame = options['start-frame']
|
65
|
+
end
|
66
|
+
|
67
|
+
if options['end-time']
|
68
|
+
range.end_time = options['end-time']
|
69
|
+
elsif options['end-scene']
|
70
|
+
range.end_scene = options['end-scene']
|
71
|
+
elsif options['end-frame']
|
72
|
+
range.end_frame = options['end-frame']
|
73
|
+
end
|
74
|
+
|
75
|
+
production.produce(range)
|
76
|
+
rescue Exception => e
|
77
|
+
puts "#{e.class} (#{e.message})"
|
78
|
+
puts e.backtrace
|
79
|
+
abort
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'gli'
|
2
|
+
require 'castaway'
|
3
|
+
require 'castaway/cli/build'
|
4
|
+
require 'castaway/cli/script'
|
5
|
+
require 'castaway/cli/version'
|
6
|
+
|
7
|
+
module Castaway
|
8
|
+
module CLI
|
9
|
+
class Main
|
10
|
+
extend GLI::App
|
11
|
+
|
12
|
+
program_desc 'Build screencasts from a script'
|
13
|
+
|
14
|
+
desc Castaway::CLI::Script.description
|
15
|
+
command(:build) { |c| Castaway::CLI::Build.define(c) }
|
16
|
+
|
17
|
+
desc Castaway::CLI::Script.description
|
18
|
+
command(:script) { |c| Castaway::CLI::Script.define(c) }
|
19
|
+
|
20
|
+
desc Castaway::CLI::Version.description
|
21
|
+
command(:version) { |c| Castaway::CLI::Version.define(c) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'castaway/production'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
module CLI
|
5
|
+
class Script
|
6
|
+
extend GLI::App
|
7
|
+
|
8
|
+
def self.description
|
9
|
+
'Display the given program as a script'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.define(command)
|
13
|
+
command.action do |_globals, _options, args|
|
14
|
+
exit_now!('you have to supply a castaway program') if args.empty?
|
15
|
+
|
16
|
+
production = Castaway::Production.from_script(args.first)
|
17
|
+
|
18
|
+
production.new.scenes.each.with_index do |scene, idx|
|
19
|
+
mark = scene.start || "##{idx}"
|
20
|
+
puts "[#{mark}] #{scene.title}"
|
21
|
+
puts scene.script if scene.script
|
22
|
+
puts
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'castaway/version'
|
2
|
+
|
3
|
+
module Castaway
|
4
|
+
module CLI
|
5
|
+
class Version
|
6
|
+
def self.description
|
7
|
+
"Report the current version (#{Castaway::VERSION})"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.define(command)
|
11
|
+
command.action do |_globals, _options, _args|
|
12
|
+
puts "v#{Castaway::VERSION}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Castaway
|
2
|
+
module Effect
|
3
|
+
|
4
|
+
@effects = {}
|
5
|
+
def self.register(name, &implementation)
|
6
|
+
@effects[name] = implementation
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.invoke(name, element, options)
|
10
|
+
@effects.fetch(name).call(element, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
register :dissolve_in do |element, options|
|
14
|
+
element.animate(:alpha,
|
15
|
+
from: 0.0, to: options[:speed],
|
16
|
+
initial: 0.0, final: 1.0)
|
17
|
+
end
|
18
|
+
|
19
|
+
register :dissolve_out do |element, options|
|
20
|
+
element.animate(:alpha,
|
21
|
+
from: element.tail(options[:speed]), to: element.tail,
|
22
|
+
initial: 1.0, final: 0.0)
|
23
|
+
end
|
24
|
+
|
25
|
+
register :pan do |element, options|
|
26
|
+
dx = case options[:horizontal]
|
27
|
+
when nil then 0
|
28
|
+
when true, 1, :right then
|
29
|
+
element.production.resolution.width - element.size.width
|
30
|
+
when false, -1, :left then
|
31
|
+
element.size.width - element.production.resolution.width
|
32
|
+
else
|
33
|
+
raise ArgumentError,
|
34
|
+
"unsupported horizontal: #{options[:horizontal].inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
dy = case options[:vertical]
|
38
|
+
when nil then 0
|
39
|
+
when true, 1, :down then
|
40
|
+
element.production.resolution.height - element.size.height
|
41
|
+
when false, -1, :up then
|
42
|
+
element.size.height - element.production.resolution.height
|
43
|
+
else
|
44
|
+
raise ArgumentError,
|
45
|
+
"unsupported vertical: #{options[:vertical].inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
type = options[:type] || :linear
|
49
|
+
from = options[:from] || 0.0
|
50
|
+
to = options[:to] || element.duration
|
51
|
+
|
52
|
+
p0 = Castaway::Point.new(0, 0)
|
53
|
+
p1 = Castaway::Point.new(dx, dy)
|
54
|
+
|
55
|
+
element.animate(:position, type: type, from: from, to: to,
|
56
|
+
initial: p0, final: p1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|