storyboard 0.3.1 → 0.4.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.
- data/CHANGELOG +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +9 -9
- data/bin/storyboard +22 -5
- data/lib/storyboard/subtitles.rb +3 -8
- data/lib/storyboard/version.rb +1 -1
- data/lib/storyboard.rb +21 -4
- data/setup/osx.sh +1 -1
- metadata +150 -133
data/CHANGELOG
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
0.4.0
|
2
|
+
|
3
|
+
Added two new options to Storyboard:
|
4
|
+
--preview [NUMBER] will output only the first NUMBER of frames that have subtitles.
|
5
|
+
if --preview is used with -c (forcing a scene change scan), it may include more than
|
6
|
+
NUMBER of frames, as the scene changes will also be included.
|
7
|
+
|
8
|
+
-n [NUMBER] nudges the timestamps forward or back in time by NUMBER seconds.
|
9
|
+
Combined with --preview this lets you more quickly line up the subtitles. Error detection
|
10
|
+
on nudging is nonexistant, and so if you nudge them too far forward or back they could
|
11
|
+
cause an error.
|
12
|
+
|
13
|
+
The following example will move the subtitles back 2 seconds, and render a 25 frame PDF:
|
14
|
+
|
15
|
+
storyboard -n -2 --preview 25 Video.mkv
|
16
|
+
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -18,17 +18,17 @@ Storyboard will try to generate a file at `/path/to/video-file.pdf` containing t
|
|
18
18
|
$ ls .
|
19
19
|
ShowName.1x01.EpisodeName.pdf ShowName.1x02.AnotherEpisode.pdf ShowName.1x03.ThirdEp.pdf
|
20
20
|
|
21
|
-
|
21
|
+
To quickly test if the subtitles that are used look ok, you can use the `--preview NUMBER` option, which generates a PDF with as many pages as you specify, defaulting to 10.
|
22
22
|
|
23
|
-
|
24
|
-
-v, --[no-]verbose Run verbosely
|
25
|
-
--[no-]scenes Detect scene changes. This increases the time it takes to generate a file.
|
26
|
-
-ct FLOAT Scene detection threshold. 0.2 is too low, 0.8 is too high. Play with it!
|
27
|
-
-s, --subs FILE SRT subtitle file to use. Will skip extracting/downloading one.
|
28
|
-
--make x,y,z Filetypes to output
|
29
|
-
(pdf, mobi, epub)
|
30
|
-
-h, --help Show this message
|
23
|
+
storyboard --preview /path/to/video-file.mkv
|
31
24
|
|
25
|
+
If the subtitles are off, you can nudge them back or forward with the `-n TIME` option. This can be positive or negative, and if you make it too large it can cause Storyboard to throw an error. This would nudge the subtitles back 2 seconds, and generate just the preview PDF.
|
26
|
+
|
27
|
+
storyboard -n -2 --preview /path/to/video-file.mkv
|
28
|
+
|
29
|
+
You can see all the available options by using the help option:
|
30
|
+
|
31
|
+
storyboard -h
|
32
32
|
|
33
33
|
## Requirements
|
34
34
|
|
data/bin/storyboard
CHANGED
@@ -22,12 +22,14 @@ unless Storyboard.magick_installed?
|
|
22
22
|
exit
|
23
23
|
end
|
24
24
|
|
25
|
-
options = {:
|
25
|
+
options = {:nudge => 0, :verbose => true, :types => ['pdf'], :scene_threshold => 0.4}
|
26
26
|
|
27
27
|
# think about them
|
28
28
|
options[:consolidate_frame_threshold] = 0.4
|
29
29
|
options[:max_width] = 600
|
30
30
|
|
31
|
+
puts "Running Storyboard #{Storyboard::VERSION}"
|
32
|
+
|
31
33
|
LOG = Logger.new(STDOUT)
|
32
34
|
LOG.level = Logger::INFO
|
33
35
|
|
@@ -43,16 +45,27 @@ opts.on("-c", "--[no-]scenes", "Detect scene changes. This increases the time it
|
|
43
45
|
options[:scenes] = v
|
44
46
|
end
|
45
47
|
|
46
|
-
opts.on("
|
47
|
-
options[:
|
48
|
+
opts.on("--preview [FRAMES]", Integer, "Only render the specified number (default 10) of subtitled images.", "Unless the -c flag is used, scene detection will be skipped") do |n|
|
49
|
+
options[:preview] = n || 10
|
50
|
+
options[:scenes] ||= false
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-n", "--nudge TIME", Float, "Nudge the subtitles forward or backward. TIME is the number of seconds.", "Use this with the --preview option to quickly check and adjust the subtitle timings.") do |time|
|
54
|
+
options[:nudge] = time
|
48
55
|
end
|
49
56
|
|
57
|
+
#opts.on("--make x,y,z", Array, "Filetypes to output", '(pdf, mobi, epub)') do |types|
|
58
|
+
# options[:types] = types
|
59
|
+
#end
|
60
|
+
|
50
61
|
opts.on("-s", "--subs FILE", "SRT subtitle file to use. Will skip extracting/downloading one.") do |s|
|
51
62
|
options[:subs] = s
|
52
63
|
end
|
53
64
|
|
54
|
-
opts.on(
|
55
|
-
|
65
|
+
opts.on('--update', "Update Storyboard to the latest available version") do |u|
|
66
|
+
# This execs in the current environment, I believe.
|
67
|
+
puts `gem update storyboard`
|
68
|
+
exit
|
56
69
|
end
|
57
70
|
|
58
71
|
opts.on_tail("-h", "--help", "Show this message") do
|
@@ -67,6 +80,10 @@ rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => e
|
|
67
80
|
exit 1
|
68
81
|
end
|
69
82
|
|
83
|
+
options[:scenes] = true if options[:scenes].nil?
|
84
|
+
|
85
|
+
p options
|
86
|
+
|
70
87
|
if ARGV.size < 1
|
71
88
|
puts "videofile required"
|
72
89
|
puts opts.to_s
|
data/lib/storyboard/subtitles.rb
CHANGED
@@ -35,12 +35,7 @@ class Storyboard
|
|
35
35
|
#downloader = Suby::Downloader::OpenSubtitles.new(suby_file, 'en')
|
36
36
|
# try Addic7ed first, as, on average, it seems a bit better.
|
37
37
|
downloader = nil
|
38
|
-
|
39
|
-
LOG.debug("Searching for subtitles on Addic7ed")
|
40
|
-
#downloader = Suby::Downloader::Addic7ed.new(suby_file, 'en')
|
41
|
-
rescue Exception => e
|
42
|
-
LOG.debug(e)
|
43
|
-
end
|
38
|
+
|
44
39
|
|
45
40
|
if downloader.nil?
|
46
41
|
LOG.info("Searching for subtitles on OpenSubtitles")
|
@@ -89,8 +84,8 @@ class Storyboard
|
|
89
84
|
end
|
90
85
|
when :time
|
91
86
|
if l =~ /^(#{TIME_REGEX}) --> (#{TIME_REGEX})$/
|
92
|
-
page[:start_time] = STRTime.parse($1)
|
93
|
-
page[:end_time] = STRTime.parse($2)
|
87
|
+
page[:start_time] = STRTime.parse($1) + @options[:nudge]
|
88
|
+
page[:end_time] = STRTime.parse($2) + @options[:nudge]
|
94
89
|
phase = :text
|
95
90
|
else
|
96
91
|
raise "Bad SRT File: Should have time range but got '#{l}'"
|
data/lib/storyboard/version.rb
CHANGED
data/lib/storyboard.rb
CHANGED
@@ -34,12 +34,22 @@ class Storyboard
|
|
34
34
|
# bit of a temp hack so I don't have to wait all the time.
|
35
35
|
@subtitles.save if options[:verbose]
|
36
36
|
|
37
|
-
|
38
37
|
@renderers << Storyboard::PDFRenderer.new(self) if options[:types].include?('pdf')
|
39
38
|
|
40
39
|
run_scene_detection if options[:scenes]
|
41
40
|
consolidate_frames
|
41
|
+
|
42
|
+
|
43
|
+
# If the preview flag is set, do a quick count of how many subtitled frames fall before it.
|
44
|
+
if @options[:preview]
|
45
|
+
stop_time = @subtitles.pages[@options[:preview]].start_time.value
|
46
|
+
@stop_frame = @capture_points.count {|f| stop_time > f.value }
|
47
|
+
else
|
48
|
+
@stop_frame = @capture_points.count
|
49
|
+
end
|
50
|
+
|
42
51
|
extract_frames
|
52
|
+
|
43
53
|
render_output
|
44
54
|
|
45
55
|
cleanup
|
@@ -58,7 +68,7 @@ class Storyboard
|
|
58
68
|
end while !stdout.eof?
|
59
69
|
}
|
60
70
|
pbar.finish
|
61
|
-
LOG.info("#{@capture_points.count} scenes
|
71
|
+
LOG.info("#{@capture_points.count} scenes found and added to the timeline")
|
62
72
|
end
|
63
73
|
|
64
74
|
def consolidate_frames
|
@@ -80,9 +90,14 @@ class Storyboard
|
|
80
90
|
|
81
91
|
def extract_frames
|
82
92
|
pool = Thread::Pool.new(2)
|
83
|
-
pbar = ProgressBar.create(:title => " Extracting Frames", :format => '%t [%c/%C|%B] %e', :total => @
|
93
|
+
pbar = ProgressBar.create(:title => " Extracting Frames", :format => '%t [%c/%C|%B] %e', :total => @stop_frame )
|
84
94
|
|
85
95
|
@capture_points.each_with_index {|f,i|
|
96
|
+
if i >= @stop_frame
|
97
|
+
pool.shutdown
|
98
|
+
return
|
99
|
+
end
|
100
|
+
|
86
101
|
# It's *massively* quicker to jump to a bit before where we want to be, and then make the incrimental jump to
|
87
102
|
# exactly where we want to be.
|
88
103
|
seek_primer = (f.value < 1.000) ? 0 : -1.000
|
@@ -103,8 +118,9 @@ class Storyboard
|
|
103
118
|
end
|
104
119
|
|
105
120
|
def render_output
|
106
|
-
pbar = ProgressBar.create(:title => " Rendering Output", :format => '%t [%c/%C|%B] %e', :total => @capture_points.count)
|
121
|
+
pbar = ProgressBar.create(:title => " Rendering Output", :format => '%t [%c/%C|%B] %e', :total => ( @options[:preview] || @capture_points.count ))
|
107
122
|
@capture_points.each_with_index {|f,i|
|
123
|
+
next if i >= @stop_frame
|
108
124
|
image_name = File.join(@options[:save_directory], "%04d.jpg" % [i])
|
109
125
|
capture_point_subtitles = @subtitles.pages.select { |page| f.value >= page.start_time.value and f.value <= page.end_time.value }.first
|
110
126
|
begin
|
@@ -138,6 +154,7 @@ class Storyboard
|
|
138
154
|
def setup
|
139
155
|
@options[:basename] = File.basename(options[:file], ".*")
|
140
156
|
@options[:work_dir] = Dir.mktmpdir
|
157
|
+
raise "Unable to create temporary directory" unless File.directory?(@options[:work_dir])
|
141
158
|
Dir.mkdir(@options[:write_to]) unless File.directory?(@options[:write_to])
|
142
159
|
@options[:save_directory] = File.join(@options[:work_dir], 'raw_frames')
|
143
160
|
Dir.mkdir(@options[:save_directory]) unless File.directory?(@options[:save_directory])
|
data/setup/osx.sh
CHANGED
@@ -78,7 +78,7 @@ if [ -x /usr/local/bin/convert ]; then
|
|
78
78
|
printf '%s ok %s\n' "$(tput setaf 2)" "$(tput op)"
|
79
79
|
else
|
80
80
|
printf '%s have to install %s\n' "$(tput setaf 1)" "$(tput op)"
|
81
|
-
brew install imagemagick
|
81
|
+
brew install imagemagick libtool
|
82
82
|
fi
|
83
83
|
|
84
84
|
echo -n "Checking for Ghostscript"
|
metadata
CHANGED
@@ -1,145 +1,171 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: storyboard
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 3
|
8
|
-
- 1
|
9
|
-
version: 0.3.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Mark Olson
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-01-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: nokogiri
|
22
|
-
|
23
|
-
|
24
|
-
requirements:
|
25
|
-
- -
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
- 0
|
29
|
-
version: "0"
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
30
22
|
type: :runtime
|
31
|
-
version_requirements: *id001
|
32
|
-
- !ruby/object:Gem::Dependency
|
33
|
-
name: mini_magick
|
34
23
|
prerelease: false
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mini_magick
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
42
38
|
type: :runtime
|
43
|
-
version_requirements: *id002
|
44
|
-
- !ruby/object:Gem::Dependency
|
45
|
-
name: prawn
|
46
39
|
prerelease: false
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: prawn
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
54
|
type: :runtime
|
55
|
-
version_requirements: *id003
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: ruby-progressbar
|
58
55
|
prerelease: false
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: ruby-progressbar
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
66
70
|
type: :runtime
|
67
|
-
version_requirements: *id004
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
name: levenshtein-ffi
|
70
71
|
prerelease: false
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: levenshtein-ffi
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
78
86
|
type: :runtime
|
79
|
-
version_requirements: *id005
|
80
|
-
- !ruby/object:Gem::Dependency
|
81
|
-
name: path
|
82
87
|
prerelease: false
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: path
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
91
101
|
version: 1.3.0
|
92
102
|
type: :runtime
|
93
|
-
version_requirements: *id006
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
|
-
name: rubyzip
|
96
103
|
prerelease: false
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.3.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rubyzip
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
104
118
|
type: :runtime
|
105
|
-
version_requirements: *id007
|
106
|
-
- !ruby/object:Gem::Dependency
|
107
|
-
name: term-ansicolor
|
108
119
|
prerelease: false
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: term-ansicolor
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
116
134
|
type: :runtime
|
117
|
-
version_requirements: *id008
|
118
|
-
- !ruby/object:Gem::Dependency
|
119
|
-
name: mime-types
|
120
135
|
prerelease: false
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: mime-types
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '1.19'
|
129
150
|
type: :runtime
|
130
|
-
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '1.19'
|
131
158
|
description: Generate PDFs and eBooks from video files
|
132
|
-
email:
|
133
|
-
- "
|
134
|
-
executables:
|
159
|
+
email:
|
160
|
+
- ! '"theothermarkolson@gmail.com"'
|
161
|
+
executables:
|
135
162
|
- storyboard
|
136
163
|
extensions: []
|
137
|
-
|
138
164
|
extra_rdoc_files: []
|
139
|
-
|
140
|
-
files:
|
165
|
+
files:
|
141
166
|
- .gitignore
|
142
167
|
- .rvmrc
|
168
|
+
- CHANGELOG
|
143
169
|
- Gemfile
|
144
170
|
- Gemfile.lock
|
145
171
|
- INSTALL.md
|
@@ -179,38 +205,29 @@ files:
|
|
179
205
|
- vendor/suby/spec/suby/filename_parser_spec.rb
|
180
206
|
- vendor/suby/spec/suby_spec.rb
|
181
207
|
- vendor/suby/suby.gemspec
|
182
|
-
has_rdoc: true
|
183
208
|
homepage: http://github.com/markolson/storyboard
|
184
209
|
licenses: []
|
185
|
-
|
186
210
|
post_install_message:
|
187
211
|
rdoc_options: []
|
188
|
-
|
189
|
-
require_paths:
|
212
|
+
require_paths:
|
190
213
|
- lib
|
191
214
|
- vendor/suby
|
192
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
- 1
|
198
|
-
- 9
|
199
|
-
- 3
|
215
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ! '>='
|
219
|
+
- !ruby/object:Gem::Version
|
200
220
|
version: 1.9.3
|
201
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
version: "0"
|
221
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
222
|
+
none: false
|
223
|
+
requirements:
|
224
|
+
- - ! '>='
|
225
|
+
- !ruby/object:Gem::Version
|
226
|
+
version: '0'
|
208
227
|
requirements: []
|
209
|
-
|
210
228
|
rubyforge_project:
|
211
|
-
rubygems_version: 1.
|
229
|
+
rubygems_version: 1.8.23
|
212
230
|
signing_key:
|
213
231
|
specification_version: 3
|
214
232
|
summary: Video to PDF/ePub generator
|
215
233
|
test_files: []
|
216
|
-
|