screenkit 0.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/.github/CODEOWNERS +4 -0
- data/.github/FUNDING.yml +4 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- data/.github/ISSUE_TEMPLATE/config.yml +5 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +38 -0
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ruby-tests.yml +51 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +16 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +80 -0
- data/DOCUMENTATION.md +972 -0
- data/Gemfile +5 -0
- data/LICENSE.md +20 -0
- data/README.md +49 -0
- data/Rakefile +15 -0
- data/bin/console +16 -0
- data/bin/setup +10 -0
- data/exe/screenkit +5 -0
- data/lib/screen_kit.rb +79 -0
- data/lib/screenkit/anchor.rb +19 -0
- data/lib/screenkit/animation_filters.rb +114 -0
- data/lib/screenkit/banner.rb +46 -0
- data/lib/screenkit/callout/styles/base.rb +101 -0
- data/lib/screenkit/callout/styles/default.rb +144 -0
- data/lib/screenkit/callout/styles/inline_block.rb +123 -0
- data/lib/screenkit/callout/text_style.rb +44 -0
- data/lib/screenkit/callout.rb +98 -0
- data/lib/screenkit/cli/base.rb +24 -0
- data/lib/screenkit/cli/episode.rb +78 -0
- data/lib/screenkit/cli/root.rb +73 -0
- data/lib/screenkit/cli.rb +9 -0
- data/lib/screenkit/config/base.rb +51 -0
- data/lib/screenkit/config/episode.rb +42 -0
- data/lib/screenkit/config/project.rb +47 -0
- data/lib/screenkit/content_type.rb +12 -0
- data/lib/screenkit/core_ext/json.rb +47 -0
- data/lib/screenkit/core_ext/string.rb +28 -0
- data/lib/screenkit/exporter/demotape.rb +26 -0
- data/lib/screenkit/exporter/episode.rb +565 -0
- data/lib/screenkit/exporter/image.rb +31 -0
- data/lib/screenkit/exporter/intro.rb +261 -0
- data/lib/screenkit/exporter/outro.rb +183 -0
- data/lib/screenkit/exporter/segment.rb +258 -0
- data/lib/screenkit/exporter/video.rb +33 -0
- data/lib/screenkit/generators/episode/config.yml.erb +106 -0
- data/lib/screenkit/generators/episode/content/001.tape +4 -0
- data/lib/screenkit/generators/episode/scripts/001.txt +1 -0
- data/lib/screenkit/generators/episode.rb +31 -0
- data/lib/screenkit/generators/project/Gemfile.erb +7 -0
- data/lib/screenkit/generators/project/resources/backtracks/default.aac +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OFL.txt +93 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-Bold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-BoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-ExtraBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-ExtraBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-Italic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-Light.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-LightItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-Medium.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-MediumItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-Regular.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-SemiBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans-SemiBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-Bold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-BoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-ExtraBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-ExtraBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-Italic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-Light.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-LightItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-Medium.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-MediumItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-Regular.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-SemiBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_Condensed-SemiBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-Bold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-BoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-ExtraBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-ExtraBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-Italic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-Light.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-LightItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-Medium.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-MediumItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-Regular.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-SemiBold.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/OpenSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- data/lib/screenkit/generators/project/resources/fonts/opensans/README.txt +100 -0
- data/lib/screenkit/generators/project/resources/images/logo.png +0 -0
- data/lib/screenkit/generators/project/resources/images/watermark.png +0 -0
- data/lib/screenkit/generators/project/resources/sounds/chime.mp3 +0 -0
- data/lib/screenkit/generators/project/resources/sounds/pop.mp3 +0 -0
- data/lib/screenkit/generators/project/resources/sounds/whoosh.mp3 +0 -0
- data/lib/screenkit/generators/project/screenkit.yml +189 -0
- data/lib/screenkit/generators/project.rb +34 -0
- data/lib/screenkit/logfile.rb +33 -0
- data/lib/screenkit/parallel_processor.rb +50 -0
- data/lib/screenkit/path_lookup.rb +27 -0
- data/lib/screenkit/resources/mute.mp3 +0 -0
- data/lib/screenkit/resources/transparent.png +0 -0
- data/lib/screenkit/schema_validator.rb +14 -0
- data/lib/screenkit/schemas/callouts/default.json +44 -0
- data/lib/screenkit/schemas/callouts/inline_block.json +20 -0
- data/lib/screenkit/schemas/episode.json +74 -0
- data/lib/screenkit/schemas/project.json +37 -0
- data/lib/screenkit/schemas/refs/anchor.json +17 -0
- data/lib/screenkit/schemas/refs/animation.json +7 -0
- data/lib/screenkit/schemas/refs/background.json +14 -0
- data/lib/screenkit/schemas/refs/callout.json +27 -0
- data/lib/screenkit/schemas/refs/color.json +7 -0
- data/lib/screenkit/schemas/refs/directory.json +20 -0
- data/lib/screenkit/schemas/refs/intro.json +30 -0
- data/lib/screenkit/schemas/refs/logo.json +26 -0
- data/lib/screenkit/schemas/refs/outro.json +26 -0
- data/lib/screenkit/schemas/refs/position.json +38 -0
- data/lib/screenkit/schemas/refs/scenes.json +27 -0
- data/lib/screenkit/schemas/refs/size.json +19 -0
- data/lib/screenkit/schemas/refs/sound.json +36 -0
- data/lib/screenkit/schemas/refs/spacing.json +22 -0
- data/lib/screenkit/schemas/refs/text_style.json +18 -0
- data/lib/screenkit/schemas/refs/transition.json +15 -0
- data/lib/screenkit/schemas/refs/tts.json +47 -0
- data/lib/screenkit/schemas/refs/watermark.json +18 -0
- data/lib/screenkit/schemas/tts/elevenlabs.json +67 -0
- data/lib/screenkit/schemas/tts/say.json +16 -0
- data/lib/screenkit/shell.rb +58 -0
- data/lib/screenkit/sound.rb +44 -0
- data/lib/screenkit/spacing.rb +23 -0
- data/lib/screenkit/spinner.rb +39 -0
- data/lib/screenkit/transition.rb +16 -0
- data/lib/screenkit/tts/eleven_labs.rb +51 -0
- data/lib/screenkit/tts/say.rb +31 -0
- data/lib/screenkit/utils.rb +87 -0
- data/lib/screenkit/version.rb +5 -0
- data/lib/screenkit/watermark.rb +34 -0
- data/lib/screenkit.rb +3 -0
- data/screenkit.gemspec +56 -0
- metadata +426 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
# yaml-language-server: $schema=../../schemas/project.json
|
|
3
|
+
|
|
4
|
+
# ScreenKit project configuration file.
|
|
5
|
+
schema: 1
|
|
6
|
+
|
|
7
|
+
# The episodes directory is where the source files for each episode are stored.
|
|
8
|
+
# A new episode can be generated with `screenkit episode new`.
|
|
9
|
+
#
|
|
10
|
+
# The episode directory name can include placeholders:
|
|
11
|
+
# - %{episode_number}: The episode number
|
|
12
|
+
# - %{episode_slug}: A URL-friendly slug for the episode title
|
|
13
|
+
# - %{date}: The current timestamp in `yyyy-mm-dd` format.
|
|
14
|
+
#
|
|
15
|
+
# You can have padded episodes number by using a format string that looks like
|
|
16
|
+
# `%<episode_number>03d` (e.g. `001`).
|
|
17
|
+
episode_dir: episodes/%<episode_number>03d-%{episode_slug}
|
|
18
|
+
|
|
19
|
+
# The resources directory contains additional resource files used in the video.
|
|
20
|
+
# These can include images, sounds, fonts, etc.
|
|
21
|
+
# When defining your configuration, you can just specify the partial path. For
|
|
22
|
+
# instance, if you have an image at `resources/images/picture.png`, you can just
|
|
23
|
+
# refer to it as `images/picture.png`.
|
|
24
|
+
#
|
|
25
|
+
# You can also specify absolute paths, like system font directories.
|
|
26
|
+
#
|
|
27
|
+
# To point to the episode directory, use the placeholder `%{episode_dir}`.
|
|
28
|
+
resources_dir:
|
|
29
|
+
- .
|
|
30
|
+
- "%{episode_dir}"
|
|
31
|
+
- resources
|
|
32
|
+
- "%{episode_dir}/resources"
|
|
33
|
+
- resources/backtracks
|
|
34
|
+
- "%{episode_dir}/resources/backtracks"
|
|
35
|
+
- resources/sounds
|
|
36
|
+
- "%{episode_dir}/resources/sounds"
|
|
37
|
+
- resources/images
|
|
38
|
+
- "%{episode_dir}/resources/images"
|
|
39
|
+
- resources/fonts
|
|
40
|
+
- "%{episode_dir}/resources/fonts"
|
|
41
|
+
- resources
|
|
42
|
+
- "%{episode_dir}/resources"
|
|
43
|
+
- ~/Library/Fonts
|
|
44
|
+
- /Library/Fonts
|
|
45
|
+
- /System/Library/Fonts
|
|
46
|
+
- ~/.fonts
|
|
47
|
+
- ~/.local/share/fonts
|
|
48
|
+
- /usr/share/fonts
|
|
49
|
+
- /usr/local/share/fonts
|
|
50
|
+
- /opt/share/fonts
|
|
51
|
+
|
|
52
|
+
# The output directory is where the generated video files will be saved.
|
|
53
|
+
# You can change this to any directory you prefer.
|
|
54
|
+
#
|
|
55
|
+
# To point to the episode directory's name, use the placeholder
|
|
56
|
+
# `%{episode_dirname}`.
|
|
57
|
+
output_dir: output/%{episode_dirname}
|
|
58
|
+
|
|
59
|
+
# Set the backtrack file or directory.
|
|
60
|
+
# When a directory is specified, a random backtrack will be chosen from the
|
|
61
|
+
# directory.
|
|
62
|
+
# When a file is specified, that specific backtrack will be used instead.
|
|
63
|
+
#
|
|
64
|
+
# The entry is searched by using the `resorces_dir` config.
|
|
65
|
+
#
|
|
66
|
+
# You can disable the backtrack by setting this to `false`.
|
|
67
|
+
backtrack: backtracks/
|
|
68
|
+
|
|
69
|
+
# Render a watermark.
|
|
70
|
+
# This is useful for branding or copyright purposes.
|
|
71
|
+
# The watermark is omitted from the intro and outro scenes.
|
|
72
|
+
# To disable the watermark, use `watermark: false`.
|
|
73
|
+
watermark:
|
|
74
|
+
path: "watermark.png"
|
|
75
|
+
anchor: [right, bottom]
|
|
76
|
+
margin: 100
|
|
77
|
+
|
|
78
|
+
# TTS configuration.
|
|
79
|
+
# To disable voiceover entirely, set `tts: false`.
|
|
80
|
+
# tts:
|
|
81
|
+
# engine: elevenlabs
|
|
82
|
+
# voice_id: 56AoDkrOh6qfVPDXZ7Pt
|
|
83
|
+
# language_code: en
|
|
84
|
+
# voice_settings:
|
|
85
|
+
# speed: 0.9
|
|
86
|
+
# stability: 0.5
|
|
87
|
+
# similarity: 0.75
|
|
88
|
+
# style: 0.0
|
|
89
|
+
tts:
|
|
90
|
+
engine: say
|
|
91
|
+
rate: 150
|
|
92
|
+
|
|
93
|
+
# Scene configurations define how different scenes in the video are rendered.
|
|
94
|
+
# There are three types of scenes: intro, outro, and segment.
|
|
95
|
+
# - Intro: The opening scene of the video, typically featuring the logo.
|
|
96
|
+
# - Outro: The closing scene of the video.
|
|
97
|
+
# - Segment: The main content scenes of the video.
|
|
98
|
+
scenes:
|
|
99
|
+
intro:
|
|
100
|
+
# The duration of the intro scene in seconds.
|
|
101
|
+
duration: 5.5
|
|
102
|
+
|
|
103
|
+
# The fade-in duration in seconds.
|
|
104
|
+
fade_in: 0
|
|
105
|
+
|
|
106
|
+
# The fade-out duration in seconds.
|
|
107
|
+
fade_out: 0.5
|
|
108
|
+
|
|
109
|
+
# The background color/image of the intro scene.
|
|
110
|
+
background: "#100f50"
|
|
111
|
+
|
|
112
|
+
# The title text to be displayed in the intro scene.
|
|
113
|
+
title:
|
|
114
|
+
x: 100
|
|
115
|
+
y: 300
|
|
116
|
+
font_path: opensans/OpenSans-ExtraBold.ttf
|
|
117
|
+
size: 144
|
|
118
|
+
color: "#ffffff"
|
|
119
|
+
|
|
120
|
+
# The logo to be displayed in the video intro.
|
|
121
|
+
# Works best with a transparent PNG file in high resolution (at least 2x the
|
|
122
|
+
# size it'll be displayed).
|
|
123
|
+
logo:
|
|
124
|
+
path: logo.png
|
|
125
|
+
x: 100
|
|
126
|
+
y: 200
|
|
127
|
+
width: 300
|
|
128
|
+
|
|
129
|
+
# The sound to be played along with the logo in the intro.
|
|
130
|
+
# This must correspond to a sound file in the sounds directory, with any
|
|
131
|
+
# audio extension (e.g. mp3, m4a, wav).
|
|
132
|
+
sound: false
|
|
133
|
+
|
|
134
|
+
outro:
|
|
135
|
+
# The duration of the outro scene in seconds.
|
|
136
|
+
duration: 5.5
|
|
137
|
+
|
|
138
|
+
# The fade-in duration in seconds.
|
|
139
|
+
fade_in: 0.5
|
|
140
|
+
|
|
141
|
+
# The fade-out duration in seconds.
|
|
142
|
+
fade_out: 0.5
|
|
143
|
+
|
|
144
|
+
# The background color/image of the outro scene.
|
|
145
|
+
background: "#100f50"
|
|
146
|
+
|
|
147
|
+
# The sound to be played along with the logo in the outro.
|
|
148
|
+
sound: chime.mp3
|
|
149
|
+
|
|
150
|
+
# The logo to be displayed in the video outro.
|
|
151
|
+
logo:
|
|
152
|
+
path: logo.png
|
|
153
|
+
x: center
|
|
154
|
+
y: center
|
|
155
|
+
width: 300
|
|
156
|
+
|
|
157
|
+
segment:
|
|
158
|
+
# The duration of the crossover transition between segments in seconds.
|
|
159
|
+
crossfade_duration: 0.5
|
|
160
|
+
|
|
161
|
+
# Callout configurations define the appearance and behavior of callouts used
|
|
162
|
+
# in the video. Callouts are visual elements that highlight important
|
|
163
|
+
# information. Each callout type can have its own settings for icon, background,
|
|
164
|
+
# text styles, animations, and sounds.
|
|
165
|
+
callouts:
|
|
166
|
+
info:
|
|
167
|
+
# yaml-language-server: $schema=../../schemas/callouts/default.json
|
|
168
|
+
background_color: "#ffff00"
|
|
169
|
+
shadow: "#2242d3"
|
|
170
|
+
title_style:
|
|
171
|
+
color: "#000000"
|
|
172
|
+
size: 40
|
|
173
|
+
font_path: opensans/OpenSans-ExtraBold.ttf
|
|
174
|
+
body_style:
|
|
175
|
+
color: "#000000"
|
|
176
|
+
size: 32
|
|
177
|
+
font_path: opensans/OpenSans-Semibold.ttf
|
|
178
|
+
margin: 100
|
|
179
|
+
padding: 50
|
|
180
|
+
anchor: [left, bottom]
|
|
181
|
+
animation: fade
|
|
182
|
+
in_transition:
|
|
183
|
+
duration: 0.4
|
|
184
|
+
sound: pop.mp3
|
|
185
|
+
out_transition:
|
|
186
|
+
duration: 0.3
|
|
187
|
+
sound:
|
|
188
|
+
path: pop.mp3
|
|
189
|
+
volume: 0.7
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
module Generators
|
|
5
|
+
class Project < Thor::Group
|
|
6
|
+
include Thor::Actions
|
|
7
|
+
|
|
8
|
+
attr_accessor :options
|
|
9
|
+
|
|
10
|
+
def self.source_root
|
|
11
|
+
File.join(__dir__, "project")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_project_dir
|
|
15
|
+
template "Gemfile.erb", "Gemfile"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def copy_files
|
|
19
|
+
copy_file "screenkit.yml"
|
|
20
|
+
directory "resources", exclude_pattern: /DS_Store/
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def bundle_install
|
|
24
|
+
in_root do
|
|
25
|
+
run "bundle install"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
no_commands do
|
|
30
|
+
# Add helper methods here
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
class Logfile
|
|
5
|
+
attr_reader :root_dir, :basename, :mutex
|
|
6
|
+
attr_accessor :index
|
|
7
|
+
|
|
8
|
+
def initialize(root_dir)
|
|
9
|
+
@root_dir = root_dir
|
|
10
|
+
@index = 0
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create(*args)
|
|
15
|
+
args.unshift(mutex.synchronize { format("%04d", index) })
|
|
16
|
+
mutex.synchronize { self.index += 1 }
|
|
17
|
+
|
|
18
|
+
Pathname(root_dir).join("#{args.join('-')}.txt")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def log(*args)
|
|
22
|
+
message = args.pop
|
|
23
|
+
path = create(*args)
|
|
24
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
25
|
+
File.open(path, "w") { it << message.to_s }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def json_log(*args)
|
|
29
|
+
message = args.pop
|
|
30
|
+
log(*args, JSON.pretty_generate(message))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
class ParallelProcessor
|
|
5
|
+
attr_reader :spinner, :list, :message, :mutex, :count, :log_path
|
|
6
|
+
attr_accessor :progress
|
|
7
|
+
|
|
8
|
+
def initialize(spinner:, list:, message:, log_path: nil)
|
|
9
|
+
@list = list
|
|
10
|
+
@message = message
|
|
11
|
+
@spinner = spinner
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
@progress = 0
|
|
14
|
+
@count = list.size
|
|
15
|
+
@log_path = log_path
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run(&block)
|
|
19
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
20
|
+
indexed_list = list.map.with_index {|item, index| [item, index] }
|
|
21
|
+
arity = block.arity
|
|
22
|
+
|
|
23
|
+
indexed_list.each_slice(Etc.nprocessors) do |slice|
|
|
24
|
+
threads = slice.map do |args|
|
|
25
|
+
thread = Thread.new do
|
|
26
|
+
yield(*args.take([1, arity].max), log_path:)
|
|
27
|
+
update_progress
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
thread.abort_on_exception = true
|
|
31
|
+
thread
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
threads.each(&:join)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
spinner.stop
|
|
38
|
+
|
|
39
|
+
ended_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
40
|
+
ended_at - started_at
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def update_progress
|
|
44
|
+
mutex.synchronize do
|
|
45
|
+
self.progress += 1
|
|
46
|
+
spinner.update(format(message, count:, progress:))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
class PathLookup
|
|
5
|
+
# The directories to search for files.
|
|
6
|
+
attr_reader :dirs
|
|
7
|
+
|
|
8
|
+
def initialize(*dirs)
|
|
9
|
+
@dirs = dirs.map { Pathname(it) }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Search for the given path in the configured directories.
|
|
13
|
+
# @param path [String, Pathname] The relative path to search for.
|
|
14
|
+
# @return [Pathname, nil] The found path or nil if not found.
|
|
15
|
+
def search(path, default: nil)
|
|
16
|
+
dirs.each do |dir|
|
|
17
|
+
candidate = dir.join(path)
|
|
18
|
+
return candidate if candidate.exist?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return default if default
|
|
22
|
+
|
|
23
|
+
raise FileEntryNotFoundError,
|
|
24
|
+
%[No file entry found for #{path.to_s.inspect}]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
module SchemaValidator
|
|
5
|
+
def validate!(attributes)
|
|
6
|
+
errors = JSON::Validator
|
|
7
|
+
.fully_validate("file://#{schema_path}", attributes)
|
|
8
|
+
|
|
9
|
+
return if errors.empty?
|
|
10
|
+
|
|
11
|
+
raise ArgumentError, "Invalid attributes: #{errors.first}"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/callouts/basic.json",
|
|
4
|
+
"title": "Basic Callout",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"background_color",
|
|
8
|
+
"body_style",
|
|
9
|
+
"padding",
|
|
10
|
+
"shadow",
|
|
11
|
+
"title_style"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"width": {
|
|
15
|
+
"type": "number",
|
|
16
|
+
"description": "The callout width"
|
|
17
|
+
},
|
|
18
|
+
"background_color": {
|
|
19
|
+
"$ref": "../refs/color.json"
|
|
20
|
+
},
|
|
21
|
+
"shadow": {
|
|
22
|
+
"oneOf": [
|
|
23
|
+
{ "type": "boolean", "enum": [false] },
|
|
24
|
+
{ "type": "string", "$ref": "../refs/color.json" },
|
|
25
|
+
{
|
|
26
|
+
"type": "object",
|
|
27
|
+
"properties": {
|
|
28
|
+
"color": { "$ref": "../refs/color.json" },
|
|
29
|
+
"offset": { "type": "integer" }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"title_style": {
|
|
35
|
+
"$ref": "../refs/text_style.json"
|
|
36
|
+
},
|
|
37
|
+
"body_style": {
|
|
38
|
+
"$ref": "../refs/text_style.json"
|
|
39
|
+
},
|
|
40
|
+
"padding": {
|
|
41
|
+
"$ref": "../refs/spacing.json"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/callouts/inline_block.json",
|
|
4
|
+
"title": "Inline Block Callout",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["background_color", "text_style"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"background_color": { "$ref": "../refs/color.json" },
|
|
9
|
+
"width": {
|
|
10
|
+
"type": "number",
|
|
11
|
+
"description": "The callout width"
|
|
12
|
+
},
|
|
13
|
+
"text_style": {
|
|
14
|
+
"$ref": "../refs/text_style.json"
|
|
15
|
+
},
|
|
16
|
+
"padding": {
|
|
17
|
+
"$ref": "../refs/spacing.json"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/episode.json",
|
|
4
|
+
"title": "ScreenKit Episode Configuration",
|
|
5
|
+
"description": "Schema for ScreenKit episode configuration file (config.yml)",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["title"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"backtrack": { "$ref": "refs/sound.json" },
|
|
10
|
+
"scenes": { "$ref": "refs/scenes.json" },
|
|
11
|
+
"tts": { "$ref": "refs/tts.json" },
|
|
12
|
+
"watermark": { "$ref": "refs/watermark.json" },
|
|
13
|
+
"title": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The episode title",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"pattern": "\\S"
|
|
18
|
+
},
|
|
19
|
+
"callouts": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"description": "List of callouts to be used in the episode",
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"required": ["type", "starts_at", "duration"],
|
|
25
|
+
"additionalProperties": true,
|
|
26
|
+
"properties": {
|
|
27
|
+
"type": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Callout type identifier",
|
|
30
|
+
"minLength": 1,
|
|
31
|
+
"pattern": "\\S"
|
|
32
|
+
},
|
|
33
|
+
"starts_at": {
|
|
34
|
+
"oneOf": [
|
|
35
|
+
{
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Callout start time in HH:MM:SS format (e.g. 00:01:30)"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "number",
|
|
41
|
+
"description": "Callout start time in seconds (e.g. 90)"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"duration": {
|
|
46
|
+
"oneOf": [
|
|
47
|
+
{
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Callout duration time (e.g. 5s)",
|
|
50
|
+
"pattern": "/^\\d+(s|m|h)$/"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"type": "number",
|
|
54
|
+
"description": "Callout duration time in seconds (e.g. 5)"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"width": {
|
|
59
|
+
"oneOf": [
|
|
60
|
+
{
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Callout width in percentage (e.g. 50%)"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"type": "number",
|
|
66
|
+
"description": "Callout width in pixels (e.g. 300)"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/project.json",
|
|
4
|
+
"title": "ScreenKit Project Configuration",
|
|
5
|
+
"description": "Schema for ScreenKit project configuration file (screenkit.yml)",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "resources_dir", "episode_dir", "output_dir"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"resources_dir": { "$ref": "refs/directory.json" },
|
|
10
|
+
"backtrack": { "$ref": "refs/sound.json" },
|
|
11
|
+
"tts": { "$ref": "refs/tts.json" },
|
|
12
|
+
"watermark": { "$ref": "refs/watermark.json" },
|
|
13
|
+
"scenes": { "$ref": "refs/scenes.json" },
|
|
14
|
+
"schema": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"description": "The schema version number",
|
|
17
|
+
"enum": [1]
|
|
18
|
+
},
|
|
19
|
+
"episode_dir": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Directory where episode source files are stored. Supports placeholders like %{episode_number}, %{episode_slug} and %{date}.",
|
|
22
|
+
"$ref": "refs/directory.json"
|
|
23
|
+
},
|
|
24
|
+
"output_dir": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Directory where generated video files will be saved. Supports placeholders like %{episode_number}, %{episode_slug} and %{date}.",
|
|
27
|
+
"format": "uri"
|
|
28
|
+
},
|
|
29
|
+
"callouts": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": {
|
|
32
|
+
"$ref": "refs/callout.json"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"additionalProperties": false
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/anchor.json",
|
|
4
|
+
"title": "Anchor",
|
|
5
|
+
"type": "array",
|
|
6
|
+
"description": "Anchor position [horizontal, vertical]",
|
|
7
|
+
"items": [
|
|
8
|
+
{
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": ["left", "center", "right"]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["top", "center", "bottom"]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/refs/background.json",
|
|
4
|
+
"title": "Background",
|
|
5
|
+
"anyOf": [
|
|
6
|
+
{
|
|
7
|
+
"type": "string",
|
|
8
|
+
"format": "uri"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"$ref": "color.json"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/callout.json",
|
|
4
|
+
"title": "Callout",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"in_transition",
|
|
8
|
+
"out_transition",
|
|
9
|
+
"anchor",
|
|
10
|
+
"margin",
|
|
11
|
+
"animation"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"in_transition": { "$ref": "transition.json" },
|
|
15
|
+
"out_transition": { "$ref": "transition.json" },
|
|
16
|
+
"animation": { "$ref": "animation.json" },
|
|
17
|
+
"margin": { "$ref": "spacing.json" },
|
|
18
|
+
"style": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Callout style name (defaults to 'default')",
|
|
21
|
+
"default": "default"
|
|
22
|
+
},
|
|
23
|
+
"anchor": {
|
|
24
|
+
"$ref": "anchor.json"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/project.json",
|
|
4
|
+
"title": "Directory",
|
|
5
|
+
"oneOf": [
|
|
6
|
+
{
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "Directory containing background music files",
|
|
9
|
+
"format": "uri"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "array",
|
|
13
|
+
"description": "List of directories containing background music files",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"format": "uri"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://github.com/fnando/screenkit/schemas/refs/intro.json",
|
|
4
|
+
"title": "Intro",
|
|
5
|
+
"properties": {
|
|
6
|
+
"duration": {
|
|
7
|
+
"type": "number",
|
|
8
|
+
"description": "Duration of the intro scene in seconds",
|
|
9
|
+
"minimum": 0
|
|
10
|
+
},
|
|
11
|
+
"fade_in": {
|
|
12
|
+
"type": "number",
|
|
13
|
+
"description": "Fade-in duration in seconds",
|
|
14
|
+
"minimum": 0
|
|
15
|
+
},
|
|
16
|
+
"fade_out": {
|
|
17
|
+
"type": "number",
|
|
18
|
+
"description": "Fade-out duration in seconds",
|
|
19
|
+
"minimum": 0
|
|
20
|
+
},
|
|
21
|
+
"title": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Title configuration",
|
|
24
|
+
"allOf": [{ "$ref": "text_style.json" }, { "$ref": "position.json" }]
|
|
25
|
+
},
|
|
26
|
+
"background": { "$ref": "background.json" },
|
|
27
|
+
"logo": { "$ref": "logo.json" },
|
|
28
|
+
"sound": { "$ref": "sound.json" }
|
|
29
|
+
}
|
|
30
|
+
}
|