castaway 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.
@@ -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).
@@ -0,0 +1,5 @@
1
+ #!/bin/sh ruby
2
+
3
+ require 'castaway/cli/main'
4
+
5
+ Castaway::CLI::Main.run(ARGV)
@@ -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
@@ -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