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.
@@ -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