screenkit 0.0.10 → 0.0.12
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/DOCUMENTATION.md +75 -9
- data/Dockerfile +12 -2
- data/lib/screenkit/callout/styles/base.rb +1 -100
- data/lib/screenkit/cli/base.rb +1 -1
- data/lib/screenkit/cli/episode.rb +8 -4
- data/lib/screenkit/config.rb +92 -0
- data/lib/screenkit/content_type.rb +2 -1
- data/lib/screenkit/core_ext/hash.rb +27 -0
- data/lib/screenkit/core_ext/json.rb +6 -0
- data/lib/screenkit/core_ext.rb +1 -0
- data/lib/screenkit/exporter/episode.rb +26 -47
- data/lib/screenkit/exporter/intro.rb +94 -60
- data/lib/screenkit/exporter/playwright.rb +33 -0
- data/lib/screenkit/exporter/segment.rb +13 -1
- data/lib/screenkit/generators/episode/config.yml.erb +2 -2
- data/lib/screenkit/generators/project/screenkit.yml +12 -7
- data/lib/screenkit/image_magick.rb +106 -0
- data/lib/screenkit/schemas/{project.json → config.json} +9 -2
- data/lib/screenkit/schemas/refs/intro.json +7 -3
- data/lib/screenkit/schemas/refs/playwright.json +13 -0
- data/lib/screenkit/text_style.rb +42 -0
- data/lib/screenkit/version.rb +1 -1
- data/lib/screenkit.rb +5 -5
- metadata +13 -12
- data/lib/screenkit/callout/text_style.rb +0 -44
- data/lib/screenkit/config/base.rb +0 -58
- data/lib/screenkit/config/episode.rb +0 -48
- data/lib/screenkit/config/project.rb +0 -54
- data/lib/screenkit/schemas/episode.json +0 -26
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
module Exporter
|
|
5
5
|
class Intro
|
|
6
|
+
using CoreExt
|
|
7
|
+
|
|
6
8
|
include Shell
|
|
7
9
|
include Utils
|
|
10
|
+
include ImageMagick
|
|
8
11
|
extend SchemaValidator
|
|
9
12
|
|
|
10
13
|
def self.schema_path
|
|
@@ -14,19 +17,15 @@ module ScreenKit
|
|
|
14
17
|
# The intro scene configuration.
|
|
15
18
|
attr_reader :config
|
|
16
19
|
|
|
17
|
-
# The title text.
|
|
18
|
-
attr_reader :text
|
|
19
|
-
|
|
20
20
|
# The source path lookup instance.
|
|
21
21
|
attr_reader :source
|
|
22
22
|
|
|
23
23
|
# The log path.
|
|
24
24
|
attr_reader :log_path
|
|
25
25
|
|
|
26
|
-
def initialize(config:,
|
|
26
|
+
def initialize(config:, source:, log_path: nil)
|
|
27
27
|
self.class.validate!(config)
|
|
28
28
|
@config = config
|
|
29
|
-
@text = text
|
|
30
29
|
@source = source
|
|
31
30
|
@log_path = log_path
|
|
32
31
|
end
|
|
@@ -75,8 +74,89 @@ module ScreenKit
|
|
|
75
74
|
@font_path ||= source.search(title_config[:font_path])
|
|
76
75
|
end
|
|
77
76
|
|
|
77
|
+
def render_elements(overlay_path)
|
|
78
|
+
padding = Spacing.new(hi_res(config.fetch(:padding, 100)))
|
|
79
|
+
|
|
80
|
+
hi_res(
|
|
81
|
+
image_width: 1920,
|
|
82
|
+
image_height: 1080,
|
|
83
|
+
content_width: 1920 - (padding.horizontal / 2)
|
|
84
|
+
) => {
|
|
85
|
+
image_width:,
|
|
86
|
+
image_height:,
|
|
87
|
+
content_width:,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logo = hi_res({width: 350, x: 0, y: 0}.deep_merge(config[:logo]))
|
|
91
|
+
title = hi_res({size: 144, x: 0, y: 0}.deep_merge(config[:title]))
|
|
92
|
+
url = hi_res({size: 42, x: 0, y: 0}.deep_merge(config[:url]))
|
|
93
|
+
url_height = 0
|
|
94
|
+
|
|
95
|
+
logo_intermediary_path = overlay_path.sub_ext("_logo.png")
|
|
96
|
+
title_intermediary_path = overlay_path.sub_ext("_title.png")
|
|
97
|
+
url_intermediary_path = overlay_path.sub_ext("_url.png")
|
|
98
|
+
|
|
99
|
+
MiniMagick.convert do |image|
|
|
100
|
+
image << logo_path
|
|
101
|
+
image << "-resize"
|
|
102
|
+
image << "#{logo[:width]}x"
|
|
103
|
+
image << "PNG:#{logo_intermediary_path}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
title_style = TextStyle.new(source:, **title)
|
|
107
|
+
url_style = TextStyle.new(source:, **url)
|
|
108
|
+
|
|
109
|
+
_, _, title_height =
|
|
110
|
+
*render_text_image(path: title_intermediary_path,
|
|
111
|
+
text: title[:text],
|
|
112
|
+
style: title_style,
|
|
113
|
+
width: content_width,
|
|
114
|
+
type: "caption")
|
|
115
|
+
|
|
116
|
+
if url[:text]
|
|
117
|
+
_, _, url_height =
|
|
118
|
+
*render_text_image(path: url_intermediary_path,
|
|
119
|
+
text: url[:text],
|
|
120
|
+
style: url_style,
|
|
121
|
+
width: content_width,
|
|
122
|
+
type: "caption")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
MiniMagick.convert do |image|
|
|
126
|
+
image << "-size"
|
|
127
|
+
image << "#{image_width}x#{image_height}"
|
|
128
|
+
image << "xc:none"
|
|
129
|
+
|
|
130
|
+
image << logo_intermediary_path
|
|
131
|
+
image << "-geometry"
|
|
132
|
+
image << "+#{padding.left}+#{padding.top}"
|
|
133
|
+
image << "-composite"
|
|
134
|
+
|
|
135
|
+
offset_y = (image_height - title_height) / 2
|
|
136
|
+
|
|
137
|
+
image << title_intermediary_path
|
|
138
|
+
image << "-geometry"
|
|
139
|
+
image << "+#{padding.left}+#{offset_y}"
|
|
140
|
+
image << "-composite"
|
|
141
|
+
|
|
142
|
+
offset_y = image_height - padding.bottom - url_height
|
|
143
|
+
|
|
144
|
+
if url[:text]
|
|
145
|
+
image << url_intermediary_path
|
|
146
|
+
image << "-geometry"
|
|
147
|
+
image << "+#{padding.left}+#{offset_y}"
|
|
148
|
+
image << "-composite"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
image << "PNG:#{overlay_path}"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
78
155
|
def export(path)
|
|
79
|
-
|
|
156
|
+
overlay_path = path.sub_ext(".png")
|
|
157
|
+
render_elements(overlay_path)
|
|
158
|
+
|
|
159
|
+
ffmpeg_params(overlay_path) => {inputs:, filters:, maps:}
|
|
80
160
|
|
|
81
161
|
cmd = [
|
|
82
162
|
"ffmpeg",
|
|
@@ -95,11 +175,11 @@ module ScreenKit
|
|
|
95
175
|
run_command(*cmd, log_path:)
|
|
96
176
|
end
|
|
97
177
|
|
|
98
|
-
private def ffmpeg_params
|
|
178
|
+
private def ffmpeg_params(overlay_path)
|
|
99
179
|
duration = Duration.parse(config[:duration])
|
|
100
180
|
fade_in = Duration.parse(config.fetch(:fade_in, 0.0))
|
|
101
181
|
fade_out = Duration.parse(config.fetch(:fade_out, 0.5))
|
|
102
|
-
fade_out_start = duration - fade_out - 0.
|
|
182
|
+
fade_out_start = duration - fade_out - 0.2
|
|
103
183
|
|
|
104
184
|
# Build filter chain
|
|
105
185
|
inputs = []
|
|
@@ -155,61 +235,15 @@ module ScreenKit
|
|
|
155
235
|
|
|
156
236
|
current_layer = "bg"
|
|
157
237
|
|
|
158
|
-
#
|
|
159
|
-
if
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
overlay_y = logo_y == "center" ? "(H-h)/2" : logo_y
|
|
165
|
-
|
|
166
|
-
inputs += ["-loop", "1", "-i", logo_path]
|
|
167
|
-
filters << "[#{stream_index}:v]scale=#{logo_width}:" \
|
|
168
|
-
"-1:flags=lanczos[logo]"
|
|
169
|
-
filters << "[#{current_layer}][logo]overlay=#{overlay_x}:" \
|
|
170
|
-
"#{overlay_y}[with_logo]"
|
|
171
|
-
current_layer = "with_logo"
|
|
238
|
+
# Overlay layer (if present)
|
|
239
|
+
if overlay_path.file?
|
|
240
|
+
inputs += ["-loop", "1", "-i", overlay_path]
|
|
241
|
+
filters << "[#{stream_index}:v]scale=1920:1080[overlay]"
|
|
242
|
+
filters << "[#{current_layer}][overlay]overlay=0:0[with_overlay]"
|
|
243
|
+
current_layer = "with_overlay"
|
|
172
244
|
stream_index += 1
|
|
173
245
|
end
|
|
174
246
|
|
|
175
|
-
# Title layer (if present)
|
|
176
|
-
if title_config
|
|
177
|
-
title_x = title_config.fetch(:x, "center")
|
|
178
|
-
title_y = title_config.fetch(:y, "center")
|
|
179
|
-
title_size = title_config.fetch(:size, 72)
|
|
180
|
-
title_color = title_config.fetch(:color, "white")
|
|
181
|
-
|
|
182
|
-
# Calculate max width based on x offset
|
|
183
|
-
max_width = if title_x == "center"
|
|
184
|
-
1720 # 1920 - (2 * 100)
|
|
185
|
-
else
|
|
186
|
-
1920 - (2 * title_x.to_i)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# Rough estimate characters per line based on font size
|
|
190
|
-
avg_char_width = title_size * 0.7
|
|
191
|
-
max_chars_per_line = (max_width / avg_char_width).floor
|
|
192
|
-
|
|
193
|
-
# Auto-wrap text
|
|
194
|
-
wrapped_text = wrap_text(text, max_chars_per_line)
|
|
195
|
-
|
|
196
|
-
# Convert position to drawtext coordinates
|
|
197
|
-
drawtext_x = title_x == "center" ? "(w-text_w)/2" : title_x
|
|
198
|
-
drawtext_y = title_y == "center" ? "(h-text_h)/2" : title_y
|
|
199
|
-
|
|
200
|
-
# Center align text when x is centered
|
|
201
|
-
text_align = title_x == "center" ? ":text_align=center" : ""
|
|
202
|
-
|
|
203
|
-
# Escape special characters in text
|
|
204
|
-
wrapped_text = wrapped_text.gsub("'", "'\\\\\\''").gsub(":", "\\:")
|
|
205
|
-
|
|
206
|
-
filters << "[#{current_layer}]drawtext=text='#{wrapped_text}':" \
|
|
207
|
-
"fontfile=#{font_path}:fontsize=#{title_size}:" \
|
|
208
|
-
"fontcolor=#{title_color}:x=#{drawtext_x}:" \
|
|
209
|
-
"y=#{drawtext_y}#{text_align}[with_title]"
|
|
210
|
-
current_layer = "with_title"
|
|
211
|
-
end
|
|
212
|
-
|
|
213
247
|
# Apply fades to final video layer
|
|
214
248
|
# Use black for fade color when background is an image file
|
|
215
249
|
fade_color = background_path&.file? ? "black" : background
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
module Exporter
|
|
5
|
+
class Playwright
|
|
6
|
+
include Shell
|
|
7
|
+
|
|
8
|
+
attr_reader :script_path, :log_path, :options
|
|
9
|
+
|
|
10
|
+
def initialize(script_path:, options: {}, log_path: nil)
|
|
11
|
+
@script_path = script_path
|
|
12
|
+
@log_path = log_path
|
|
13
|
+
@options = options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def export(output_path)
|
|
17
|
+
run_command "playwright-video",
|
|
18
|
+
"export",
|
|
19
|
+
script_path,
|
|
20
|
+
options_to_args(options),
|
|
21
|
+
"--output-path", output_path,
|
|
22
|
+
log_path:,
|
|
23
|
+
chdir: script_path.parent.parent
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def options_to_args(options)
|
|
27
|
+
(options || {}).flat_map do |key, value|
|
|
28
|
+
[key.to_s.tr("_", "-").prepend("--"), value]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -64,7 +64,19 @@ module ScreenKit
|
|
|
64
64
|
|
|
65
65
|
log_path = format(log_path.to_s, prefix:) if log_path
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
# Consider all extnames, after the first dot.
|
|
68
|
+
# E.g. "video.playwright.js" -> "playwright.js"
|
|
69
|
+
extname = content_path.basename.to_s.downcase.split(".")[1..].join(".")
|
|
70
|
+
|
|
71
|
+
case extname
|
|
72
|
+
when *ContentType.playwright
|
|
73
|
+
Exporter::Playwright
|
|
74
|
+
.new(
|
|
75
|
+
script_path: content_path,
|
|
76
|
+
options: episode.playwright_options,
|
|
77
|
+
log_path:,
|
|
78
|
+
)
|
|
79
|
+
.export(video_path)
|
|
68
80
|
when *ContentType.video
|
|
69
81
|
Exporter::Video
|
|
70
82
|
.new(input_path: content_path, log_path:)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
# yaml-language-server: $schema=https://screenkit.dev/schemas/
|
|
2
|
+
# yaml-language-server: $schema=https://screenkit.dev/schemas/config.json
|
|
3
3
|
|
|
4
4
|
# The episode title (displayed in intro scene).
|
|
5
5
|
# Line breaks are inferred based on approximate text width.
|
|
@@ -8,4 +8,4 @@ title: <%= options.title.to_yaml[4..] %>
|
|
|
8
8
|
# Episode-specific overrides for global configuration.
|
|
9
9
|
# Use an editor with JSON Schema support to see available options and
|
|
10
10
|
# autocomplete.
|
|
11
|
-
# Schema: https://screenkit.dev/schemas/
|
|
11
|
+
# Schema: https://screenkit.dev/schemas/config.json
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
# yaml-language-server: $schema
|
|
3
|
-
## yaml-language-server: $schema=https://screenkit.dev/schemas/project.json
|
|
2
|
+
# yaml-language-server: $schema=https://screenkit.dev/schemas/config.json
|
|
4
3
|
|
|
5
|
-
# ScreenKit
|
|
4
|
+
# ScreenKit configuration version.
|
|
6
5
|
schema: 1
|
|
7
6
|
|
|
8
7
|
# The episodes directory is where the source files for each episode are stored.
|
|
@@ -155,10 +154,18 @@ scenes:
|
|
|
155
154
|
# The background color/image of the intro scene.
|
|
156
155
|
background: "#100f50"
|
|
157
156
|
|
|
157
|
+
# The padding around the intro.
|
|
158
|
+
padding: 100
|
|
159
|
+
|
|
160
|
+
# Show the url text in the intro scene.
|
|
161
|
+
url:
|
|
162
|
+
text: "https://screenkit.dev"
|
|
163
|
+
font_path: open-sans/OpenSans-SemiBold.ttf
|
|
164
|
+
size: 42
|
|
165
|
+
color: "#ffffff80"
|
|
166
|
+
|
|
158
167
|
# The title text to be displayed in the intro scene.
|
|
159
168
|
title:
|
|
160
|
-
x: 100
|
|
161
|
-
y: 300
|
|
162
169
|
font_path: open-sans/OpenSans-ExtraBold.ttf
|
|
163
170
|
size: 144
|
|
164
171
|
color: "#ffffff"
|
|
@@ -168,8 +175,6 @@ scenes:
|
|
|
168
175
|
# size it'll be displayed).
|
|
169
176
|
logo:
|
|
170
177
|
path: logo.png
|
|
171
|
-
x: 100
|
|
172
|
-
y: 200
|
|
173
178
|
width: 300
|
|
174
179
|
|
|
175
180
|
# The sound to be played along with the logo in the intro.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
module ImageMagick
|
|
5
|
+
# Wrap text to fit within the specified maximum width.
|
|
6
|
+
# @param text [String] The text to wrap.
|
|
7
|
+
# @param max_width [Integer] The maximum width in pixels.
|
|
8
|
+
# @param font_size [Integer] The font size in points.
|
|
9
|
+
# @return [Array<String>] The wrapped lines of text.
|
|
10
|
+
def text_wrap(text, max_width:, font_size:)
|
|
11
|
+
words = text.to_s.split(/\s+/)
|
|
12
|
+
width_factor = 0.6
|
|
13
|
+
|
|
14
|
+
[].tap do |lines|
|
|
15
|
+
words.each do |word|
|
|
16
|
+
line = lines.pop.to_s
|
|
17
|
+
word_width = word.size * (font_size * width_factor)
|
|
18
|
+
line_width = line.size * (font_size * width_factor)
|
|
19
|
+
|
|
20
|
+
if line_width + word_width <= max_width
|
|
21
|
+
line = [(line unless line.empty?), word].compact.join(" ")
|
|
22
|
+
lines << line
|
|
23
|
+
else
|
|
24
|
+
lines << line
|
|
25
|
+
lines << word
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Escape text for use in ImageMagick caption.
|
|
32
|
+
def escape_text_for_image(text)
|
|
33
|
+
text.gsub("'", "\\\\'")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Render text into an image using MiniMagick.
|
|
37
|
+
# @param text [String] The text to render.
|
|
38
|
+
# @param style [TextStyle] The text style to apply.
|
|
39
|
+
# @param width [Integer] The width of the text image.
|
|
40
|
+
# @param type [String] The ImageMagick text type (e.g., "caption").
|
|
41
|
+
# @param path [String, nil] The output path.
|
|
42
|
+
# @return [Array] The path to the generated text image, width, and
|
|
43
|
+
# height.
|
|
44
|
+
def render_text_image(text:, style:, width:, type:, path: nil)
|
|
45
|
+
return [nil, 0, 0] if text.to_s.empty?
|
|
46
|
+
|
|
47
|
+
image = MiniMagick::Image.open(
|
|
48
|
+
create_text_image(text:, style:, width:, type:, path:)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
[image.path, image.width, image.height]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Convert values to high resolution (2x).
|
|
55
|
+
# @param value [Object] The value to convert.
|
|
56
|
+
# @return [Object] The converted value.
|
|
57
|
+
def hi_res(value)
|
|
58
|
+
case value
|
|
59
|
+
when Array
|
|
60
|
+
value.map { hi_res(it) }
|
|
61
|
+
when Hash
|
|
62
|
+
value.transform_values { hi_res(it) }
|
|
63
|
+
when Numeric
|
|
64
|
+
value * 2
|
|
65
|
+
else
|
|
66
|
+
value
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Create a text image using MiniMagick.
|
|
71
|
+
# @param text [String] The text to render.
|
|
72
|
+
# @param style [TextStyle] The text style to apply.
|
|
73
|
+
# @param width [Integer] The width of the text image.
|
|
74
|
+
# @param type [String] The ImageMagick text type (e.g., "caption").
|
|
75
|
+
# @param path [String, nil] The output path.
|
|
76
|
+
# @return [Array] The path to the generated text image, and the actual
|
|
77
|
+
# `Tempfile` instance.
|
|
78
|
+
def create_text_image(text:, style:, width:, type:, path: nil)
|
|
79
|
+
path ||= File.join(Dir.tmpdir, "callout-text-#{SecureRandom.hex(10)}.png")
|
|
80
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
81
|
+
|
|
82
|
+
MiniMagick.convert do |image|
|
|
83
|
+
unless type == "label"
|
|
84
|
+
image << "-size"
|
|
85
|
+
image << "#{width}x"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
image << "-background"
|
|
89
|
+
image << "none"
|
|
90
|
+
image << "-fill"
|
|
91
|
+
image << style.color
|
|
92
|
+
image << "-font"
|
|
93
|
+
image << style.font_path.to_s
|
|
94
|
+
image << "-pointsize"
|
|
95
|
+
image << style.size.to_s
|
|
96
|
+
image << "#{type}:#{escape_text_for_image(text)}"
|
|
97
|
+
image << "PNG:#{path}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
path
|
|
101
|
+
rescue MiniMagick::Error => error
|
|
102
|
+
retry if error.message.include?("No such file or directory")
|
|
103
|
+
raise
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
"$id": "https://screenkit.dev/schemas/project.json",
|
|
4
4
|
"title": "ScreenKit Project Configuration (screenkit.yml)",
|
|
5
5
|
"type": "object",
|
|
6
|
-
"required": ["schema"
|
|
6
|
+
"required": ["schema"],
|
|
7
7
|
"properties": {
|
|
8
|
+
"title": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The episode title",
|
|
11
|
+
"minLength": 1,
|
|
12
|
+
"pattern": "\\S"
|
|
13
|
+
},
|
|
8
14
|
"resources_dir": { "$ref": "refs/directory.json" },
|
|
9
15
|
"backtrack": { "$ref": "refs/sound.json" },
|
|
10
16
|
"tts": { "$ref": "refs/tts.json" },
|
|
@@ -31,7 +37,8 @@
|
|
|
31
37
|
"$ref": "refs/callout_style.json"
|
|
32
38
|
}
|
|
33
39
|
},
|
|
34
|
-
"demotape": { "$ref": "refs/demotape.json" }
|
|
40
|
+
"demotape": { "$ref": "refs/demotape.json" },
|
|
41
|
+
"playwright": { "$ref": "refs/playwright.json" }
|
|
35
42
|
},
|
|
36
43
|
"additionalProperties": false
|
|
37
44
|
}
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
"fade_in": { "$ref": "duration.json" },
|
|
8
8
|
"fade_out": { "$ref": "duration.json" },
|
|
9
9
|
"title": {
|
|
10
|
-
"type": "object",
|
|
11
10
|
"description": "Title configuration",
|
|
12
|
-
"
|
|
11
|
+
"$ref": "text_style.json"
|
|
13
12
|
},
|
|
14
13
|
"background": { "$ref": "background.json" },
|
|
15
14
|
"logo": { "$ref": "logo.json" },
|
|
16
|
-
"sound": { "$ref": "sound.json" }
|
|
15
|
+
"sound": { "$ref": "sound.json" },
|
|
16
|
+
"padding": { "$ref": "spacing.json" },
|
|
17
|
+
"url": {
|
|
18
|
+
"description": "URL configuration",
|
|
19
|
+
"$ref": "text_style.json"
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://screenkit.dev/schemas/refs/playwright.json",
|
|
4
|
+
"title": "playwright-video settings (https://github.com/fnando/playwright-video)",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"color_scheme": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Color scheme to use when capturing the video.",
|
|
10
|
+
"enum": ["light", "dark"]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
class TextStyle
|
|
5
|
+
attr_reader :color, :size, :font_path, :align
|
|
6
|
+
|
|
7
|
+
def initialize(source:, **kwargs)
|
|
8
|
+
@source = source
|
|
9
|
+
|
|
10
|
+
kwargs.each do |key, value|
|
|
11
|
+
value = case key.to_sym
|
|
12
|
+
when :font_path
|
|
13
|
+
@source.search(value)
|
|
14
|
+
else
|
|
15
|
+
value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
instance_variable_set(:"@#{key}", value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Convert hex color (with optional alpha) to RGB + opacity
|
|
23
|
+
# #RRGGBB or #RRGGBBAA
|
|
24
|
+
def rgb_color
|
|
25
|
+
color.match(/#([0-9a-fA-F]{6})/) {|m| m[1] }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def opacity
|
|
29
|
+
if color.length == 9
|
|
30
|
+
color.match(/#[0-9a-fA-F]{6}([0-9a-fA-F]{2})/) do |m|
|
|
31
|
+
m[1].to_i(16) / 255.0
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
1.0
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def as_json(*)
|
|
39
|
+
{color:, size:, font_path:, rgb_color:, opacity:, align:}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/screenkit/version.rb
CHANGED
data/lib/screenkit.rb
CHANGED
|
@@ -26,14 +26,13 @@ module ScreenKit
|
|
|
26
26
|
require_relative "screenkit/spinner"
|
|
27
27
|
require_relative "screenkit/shell"
|
|
28
28
|
require_relative "screenkit/http"
|
|
29
|
+
require_relative "screenkit/image_magick"
|
|
29
30
|
require_relative "screenkit/schema_validator"
|
|
30
31
|
require_relative "screenkit/generators/project"
|
|
31
32
|
require_relative "screenkit/generators/episode"
|
|
32
|
-
require_relative "screenkit/config
|
|
33
|
-
require_relative "screenkit/config/project"
|
|
34
|
-
require_relative "screenkit/config/episode"
|
|
33
|
+
require_relative "screenkit/config"
|
|
35
34
|
require_relative "screenkit/callout"
|
|
36
|
-
require_relative "screenkit/
|
|
35
|
+
require_relative "screenkit/text_style"
|
|
37
36
|
require_relative "screenkit/callout/styles/base"
|
|
38
37
|
require_relative "screenkit/transition"
|
|
39
38
|
require_relative "screenkit/parallel_processor"
|
|
@@ -54,9 +53,10 @@ module ScreenKit
|
|
|
54
53
|
require_relative "screenkit/exporter/segment"
|
|
55
54
|
require_relative "screenkit/exporter/image"
|
|
56
55
|
require_relative "screenkit/exporter/video"
|
|
56
|
+
require_relative "screenkit/exporter/playwright"
|
|
57
57
|
|
|
58
58
|
require_files = lambda do |pattern|
|
|
59
|
-
Gem.
|
|
59
|
+
Gem.find_files(pattern).each do |path|
|
|
60
60
|
next if path.include?("test")
|
|
61
61
|
|
|
62
62
|
require(path)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: screenkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nando Vieira
|
|
@@ -304,16 +304,14 @@ files:
|
|
|
304
304
|
- lib/screenkit/callout/styles/inline_block.rb
|
|
305
305
|
- lib/screenkit/callout/styles/shadow_block.rb
|
|
306
306
|
- lib/screenkit/callout/styles/social.rb
|
|
307
|
-
- lib/screenkit/callout/text_style.rb
|
|
308
307
|
- lib/screenkit/cli.rb
|
|
309
308
|
- lib/screenkit/cli/base.rb
|
|
310
309
|
- lib/screenkit/cli/episode.rb
|
|
311
310
|
- lib/screenkit/cli/root.rb
|
|
312
|
-
- lib/screenkit/config
|
|
313
|
-
- lib/screenkit/config/episode.rb
|
|
314
|
-
- lib/screenkit/config/project.rb
|
|
311
|
+
- lib/screenkit/config.rb
|
|
315
312
|
- lib/screenkit/content_type.rb
|
|
316
313
|
- lib/screenkit/core_ext.rb
|
|
314
|
+
- lib/screenkit/core_ext/hash.rb
|
|
317
315
|
- lib/screenkit/core_ext/json.rb
|
|
318
316
|
- lib/screenkit/core_ext/string.rb
|
|
319
317
|
- lib/screenkit/duration.rb
|
|
@@ -322,6 +320,7 @@ files:
|
|
|
322
320
|
- lib/screenkit/exporter/image.rb
|
|
323
321
|
- lib/screenkit/exporter/intro.rb
|
|
324
322
|
- lib/screenkit/exporter/outro.rb
|
|
323
|
+
- lib/screenkit/exporter/playwright.rb
|
|
325
324
|
- lib/screenkit/exporter/segment.rb
|
|
326
325
|
- lib/screenkit/exporter/video.rb
|
|
327
326
|
- lib/screenkit/generators/episode.rb
|
|
@@ -345,6 +344,7 @@ files:
|
|
|
345
344
|
- lib/screenkit/generators/project/resources/sounds/whoosh.mp3
|
|
346
345
|
- lib/screenkit/generators/project/screenkit.yml
|
|
347
346
|
- lib/screenkit/http.rb
|
|
347
|
+
- lib/screenkit/image_magick.rb
|
|
348
348
|
- lib/screenkit/logfile.rb
|
|
349
349
|
- lib/screenkit/parallel_processor.rb
|
|
350
350
|
- lib/screenkit/path_lookup.rb
|
|
@@ -371,8 +371,7 @@ files:
|
|
|
371
371
|
- lib/screenkit/schemas/callout_styles/social.json
|
|
372
372
|
- lib/screenkit/schemas/callouts/inline_block.json
|
|
373
373
|
- lib/screenkit/schemas/callouts/shadow_block.json
|
|
374
|
-
- lib/screenkit/schemas/
|
|
375
|
-
- lib/screenkit/schemas/project.json
|
|
374
|
+
- lib/screenkit/schemas/config.json
|
|
376
375
|
- lib/screenkit/schemas/refs/anchor.json
|
|
377
376
|
- lib/screenkit/schemas/refs/animation.json
|
|
378
377
|
- lib/screenkit/schemas/refs/background.json
|
|
@@ -385,6 +384,7 @@ files:
|
|
|
385
384
|
- lib/screenkit/schemas/refs/intro.json
|
|
386
385
|
- lib/screenkit/schemas/refs/logo.json
|
|
387
386
|
- lib/screenkit/schemas/refs/outro.json
|
|
387
|
+
- lib/screenkit/schemas/refs/playwright.json
|
|
388
388
|
- lib/screenkit/schemas/refs/position.json
|
|
389
389
|
- lib/screenkit/schemas/refs/scenes.json
|
|
390
390
|
- lib/screenkit/schemas/refs/size.json
|
|
@@ -402,6 +402,7 @@ files:
|
|
|
402
402
|
- lib/screenkit/sound.rb
|
|
403
403
|
- lib/screenkit/spacing.rb
|
|
404
404
|
- lib/screenkit/spinner.rb
|
|
405
|
+
- lib/screenkit/text_style.rb
|
|
405
406
|
- lib/screenkit/time_formatter.rb
|
|
406
407
|
- lib/screenkit/transition.rb
|
|
407
408
|
- lib/screenkit/tts/base.rb
|
|
@@ -419,10 +420,10 @@ metadata:
|
|
|
419
420
|
rubygems_mfa_required: 'true'
|
|
420
421
|
homepage_uri: https://github.com/fnando/screenkit
|
|
421
422
|
bug_tracker_uri: https://github.com/fnando/screenkit/issues
|
|
422
|
-
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
423
|
-
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
424
|
-
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
425
|
-
license_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
423
|
+
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.12
|
|
424
|
+
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.12/CHANGELOG.md
|
|
425
|
+
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.12/README.md
|
|
426
|
+
license_uri: https://github.com/fnando/screenkit/tree/v0.0.12/LICENSE.md
|
|
426
427
|
rdoc_options: []
|
|
427
428
|
require_paths:
|
|
428
429
|
- lib
|
|
@@ -437,7 +438,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
437
438
|
- !ruby/object:Gem::Version
|
|
438
439
|
version: '0'
|
|
439
440
|
requirements: []
|
|
440
|
-
rubygems_version:
|
|
441
|
+
rubygems_version: 4.0.0
|
|
441
442
|
specification_version: 4
|
|
442
443
|
summary: Terminal to screencast, simplified
|
|
443
444
|
test_files: []
|