screenkit 0.0.8 → 0.0.9
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 +6 -1
- data/DOCUMENTATION.md +104 -1
- data/lib/screenkit/callout/styles/file_copy.rb +1 -2
- data/lib/screenkit/callout/styles/inline_block.rb +13 -5
- data/lib/screenkit/callout/styles/shadow_block.rb +1 -2
- data/lib/screenkit/callout/styles/social.rb +304 -0
- data/lib/screenkit/callout/text_style.rb +2 -2
- data/lib/screenkit/callout.rb +1 -1
- data/lib/screenkit/config/base.rb +8 -1
- data/lib/screenkit/config/episode.rb +1 -1
- data/lib/screenkit/config/project.rb +5 -1
- data/lib/screenkit/exporter/intro.rb +2 -3
- data/lib/screenkit/exporter/outro.rb +14 -4
- data/lib/screenkit/exporter/video.rb +6 -4
- data/lib/screenkit/generators/project/screenkit.yml +16 -0
- data/lib/screenkit/resources/callout_styles/social/blog.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/bsky.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/discord.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/dribbble.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/github.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/instagram.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/linkedin.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/mastodon.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/snap.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/soundcloud.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/spotify.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/tiktok.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/twitch.png +0 -0
- data/lib/screenkit/resources/callout_styles/social/youtube.png +0 -0
- data/lib/screenkit/schemas/callout_styles/social.json +55 -0
- data/lib/screenkit/schemas/refs/text_style.json +4 -0
- data/lib/screenkit/sound.rb +1 -1
- data/lib/screenkit/tts/espeak.rb +1 -1
- data/lib/screenkit/tts/say.rb +1 -1
- data/lib/screenkit/version.rb +1 -1
- data/lib/screenkit.rb +6 -1
- data/screenkit.gemspec +1 -0
- metadata +35 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60085249659092fa92d7fab2c913f5e8d8fc3b8d9ce0ac467322ef590a08e5f9
|
|
4
|
+
data.tar.gz: 318d53f5ba88c1b8f2edd6d19ac263f2fcee0df7e7b5cb639f760b867be6e4ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 565c78ea1e6701ee66087d22f4e62614ab765e1e625b50bdbeb9a953b3b4d7980dc6f8e7cad87250e1760f2923b10747c83c867f59347d1b4c37089d5ac87bb0
|
|
7
|
+
data.tar.gz: df2439bbb02d0bf47ecc80abdeafdd285f5c1f63782ce2a4411cb326a7d6b5edb6ddc79b36969555ad699439f4acea557265f52e1fc4a52be1d8e2d75e614a73
|
data/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,12 @@ Prefix your message with one of the following:
|
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## v0.0.9
|
|
15
|
+
|
|
16
|
+
- [Added] Add social callouts.
|
|
17
|
+
- [Added] Add align support for inline block callouts.
|
|
18
|
+
|
|
19
|
+
## v0.0.8
|
|
15
20
|
|
|
16
21
|
- [Fixed] Further improvements to support extensions.
|
|
17
22
|
|
data/DOCUMENTATION.md
CHANGED
|
@@ -474,7 +474,109 @@ during the video.
|
|
|
474
474
|
|
|
475
475
|
### Callout Styles
|
|
476
476
|
|
|
477
|
-
ScreenKit provides
|
|
477
|
+
ScreenKit provides a few built-in callout styles:
|
|
478
|
+
|
|
479
|
+
#### Social Style
|
|
480
|
+
|
|
481
|
+
The social callout style displays social media information with icons.
|
|
482
|
+
|
|
483
|
+
```yaml
|
|
484
|
+
callout_styles:
|
|
485
|
+
# Basic setup to use presets
|
|
486
|
+
social:
|
|
487
|
+
style: social
|
|
488
|
+
anchor: [left, bottom]
|
|
489
|
+
margin: 100
|
|
490
|
+
animation: fade
|
|
491
|
+
in_transition:
|
|
492
|
+
duration: 0.4s
|
|
493
|
+
sound: pop.mp3
|
|
494
|
+
out_transition:
|
|
495
|
+
duration: 0.4s
|
|
496
|
+
sound: pop.mp3
|
|
497
|
+
|
|
498
|
+
# Custom setup
|
|
499
|
+
custom_social:
|
|
500
|
+
style: social
|
|
501
|
+
background_color: "#E0D300"
|
|
502
|
+
label_style:
|
|
503
|
+
size: 24
|
|
504
|
+
color: "#00000088"
|
|
505
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
506
|
+
text_style:
|
|
507
|
+
size: 50
|
|
508
|
+
color: "#000000"
|
|
509
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
510
|
+
label: "CUSTOM"
|
|
511
|
+
icon:
|
|
512
|
+
path: "images/custom.png" # Must be a 160x160 image
|
|
513
|
+
background_color: "#000000"
|
|
514
|
+
anchor: [left, bottom]
|
|
515
|
+
margin: 100
|
|
516
|
+
animation: fade
|
|
517
|
+
in_transition:
|
|
518
|
+
duration: 0.4s
|
|
519
|
+
sound: pop.mp3
|
|
520
|
+
out_transition:
|
|
521
|
+
duration: 0.4s
|
|
522
|
+
sound: pop.mp3
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
##### Usage in episode
|
|
526
|
+
|
|
527
|
+
ScreenKit comes with some presets where you don't need to set up anything, but
|
|
528
|
+
you can also create custom callouts.
|
|
529
|
+
|
|
530
|
+
Available presets:
|
|
531
|
+
|
|
532
|
+
- `blog`
|
|
533
|
+
- `bsky`
|
|
534
|
+
- `discord`
|
|
535
|
+
- `dribbble`
|
|
536
|
+
- `github`
|
|
537
|
+
- `linkedin`
|
|
538
|
+
- `mastodon`
|
|
539
|
+
- `snap`
|
|
540
|
+
- `soundcloud`
|
|
541
|
+
- `spotify`
|
|
542
|
+
- `tiktok`
|
|
543
|
+
- `twitch`
|
|
544
|
+
- `youtube`
|
|
545
|
+
|
|
546
|
+
```yaml
|
|
547
|
+
callouts:
|
|
548
|
+
# Using a preset
|
|
549
|
+
- type: social
|
|
550
|
+
preset: github
|
|
551
|
+
text: "@fnando"
|
|
552
|
+
starts: 1
|
|
553
|
+
duration: 5s
|
|
554
|
+
|
|
555
|
+
# Using a custom callout style
|
|
556
|
+
- type: custom_social
|
|
557
|
+
text: "@fnando"
|
|
558
|
+
starts: 1
|
|
559
|
+
duration: 5s
|
|
560
|
+
|
|
561
|
+
# Defining a custom callout inline
|
|
562
|
+
- type: social
|
|
563
|
+
text: "@fnando"
|
|
564
|
+
starts: 1
|
|
565
|
+
duration: 5s
|
|
566
|
+
background_color: "#E0D300"
|
|
567
|
+
label_style:
|
|
568
|
+
size: 24
|
|
569
|
+
color: "#00000088"
|
|
570
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
571
|
+
text_style:
|
|
572
|
+
size: 50
|
|
573
|
+
color: "#000000"
|
|
574
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
575
|
+
label: "CUSTOM"
|
|
576
|
+
icon:
|
|
577
|
+
path: "images/custom.png" # Must be a 160x160 image
|
|
578
|
+
background_color: "#000000"
|
|
579
|
+
```
|
|
478
580
|
|
|
479
581
|
#### Shadow Block Style
|
|
480
582
|
|
|
@@ -552,6 +654,7 @@ callouts_styles:
|
|
|
552
654
|
color: "#ffffff"
|
|
553
655
|
size: 40
|
|
554
656
|
font_path: open-sans/OpenSans-ExtraBold.ttf
|
|
657
|
+
align: left # Text alignment: left, center, right
|
|
555
658
|
|
|
556
659
|
# Layout
|
|
557
660
|
padding: 20
|
|
@@ -5,8 +5,7 @@ module ScreenKit
|
|
|
5
5
|
module Styles
|
|
6
6
|
class FileCopy < Base
|
|
7
7
|
def self.schema_path
|
|
8
|
-
ScreenKit.root_dir
|
|
9
|
-
.join("screenkit/schemas/callout_styles/file_copy.json")
|
|
8
|
+
ScreenKit.root_dir.join("schemas/callout_styles/file_copy.json")
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
def initialize(source:, **kwargs)
|
|
@@ -10,8 +10,7 @@ module ScreenKit
|
|
|
10
10
|
:padding, :text, :width
|
|
11
11
|
|
|
12
12
|
def self.schema_path
|
|
13
|
-
ScreenKit.root_dir
|
|
14
|
-
.join("screenkit/schemas/callout_styles/inline_block.json")
|
|
13
|
+
ScreenKit.root_dir.join("schemas/callout_styles/inline_block.json")
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def initialize(source:, **kwargs)
|
|
@@ -88,18 +87,27 @@ module ScreenKit
|
|
|
88
87
|
image << "xc:none"
|
|
89
88
|
|
|
90
89
|
line_images.each do |path, width, height|
|
|
90
|
+
offset_x = case text_style.align
|
|
91
|
+
when "right"
|
|
92
|
+
max_line_width - width
|
|
93
|
+
when "center"
|
|
94
|
+
(max_line_width - width) / 2
|
|
95
|
+
else
|
|
96
|
+
0
|
|
97
|
+
end
|
|
98
|
+
|
|
91
99
|
# Draw rectangle background
|
|
92
100
|
image << "-fill"
|
|
93
101
|
image << background_color
|
|
94
102
|
image << "-draw"
|
|
95
|
-
image << "rectangle
|
|
96
|
-
"#{width + padding_x}," \
|
|
103
|
+
image << "rectangle #{offset_x},#{offset_y}," \
|
|
104
|
+
"#{width + padding_x + offset_x}," \
|
|
97
105
|
"#{offset_y + height + padding_y}"
|
|
98
106
|
|
|
99
107
|
# Composite line text
|
|
100
108
|
image << path
|
|
101
109
|
image << "-geometry"
|
|
102
|
-
image << "+#{padding.
|
|
110
|
+
image << "+#{padding.left + offset_x}+#{offset_y + padding.top}"
|
|
103
111
|
image << "-composite"
|
|
104
112
|
offset_y += padding_y + height
|
|
105
113
|
end
|
|
@@ -10,8 +10,7 @@ module ScreenKit
|
|
|
10
10
|
:title, :title_style, :width
|
|
11
11
|
|
|
12
12
|
def self.schema_path
|
|
13
|
-
ScreenKit.root_dir
|
|
14
|
-
.join("screenkit/schemas/callout_styles/shadow_block.json")
|
|
13
|
+
ScreenKit.root_dir.join("schemas/callout_styles/shadow_block.json")
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def initialize(source:, **kwargs)
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mini_magick"
|
|
4
|
+
|
|
5
|
+
module ScreenKit
|
|
6
|
+
class Callout
|
|
7
|
+
module Styles
|
|
8
|
+
class Social < Base
|
|
9
|
+
attr_reader :text, :label, :icon, :label_style, :text_style,
|
|
10
|
+
:background_color
|
|
11
|
+
|
|
12
|
+
LABEL_STYLE = {
|
|
13
|
+
color: "#ffffff88",
|
|
14
|
+
size: 24,
|
|
15
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
16
|
+
}.freeze
|
|
17
|
+
|
|
18
|
+
TEXT_STYLE = {
|
|
19
|
+
color: "#ffffff",
|
|
20
|
+
size: 50,
|
|
21
|
+
font_path: "open-sans/OpenSans-ExtraBold.ttf"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
def self.presets
|
|
25
|
+
@presets ||= {
|
|
26
|
+
instagram: {
|
|
27
|
+
label: "INSTAGRAM",
|
|
28
|
+
icon: {
|
|
29
|
+
path: "callout_styles/social/instagram.png",
|
|
30
|
+
background_color: "#C13684"
|
|
31
|
+
},
|
|
32
|
+
label_style: LABEL_STYLE,
|
|
33
|
+
text_style: TEXT_STYLE,
|
|
34
|
+
background_color: "#693050"
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
spotify: {
|
|
38
|
+
label: "SPOTIFY",
|
|
39
|
+
icon: {
|
|
40
|
+
path: "callout_styles/social/spotify.png",
|
|
41
|
+
background_color: "#1cd760"
|
|
42
|
+
},
|
|
43
|
+
label_style: LABEL_STYLE,
|
|
44
|
+
text_style: TEXT_STYLE,
|
|
45
|
+
background_color: "#205C36"
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
bsky: {
|
|
49
|
+
label: "BSKY",
|
|
50
|
+
icon: {
|
|
51
|
+
path: "callout_styles/social/bsky.png",
|
|
52
|
+
background_color: "#0085FF"
|
|
53
|
+
},
|
|
54
|
+
label_style: LABEL_STYLE,
|
|
55
|
+
text_style: TEXT_STYLE,
|
|
56
|
+
background_color: "#004483"
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
linkedin: {
|
|
60
|
+
label: "LINKEDIN",
|
|
61
|
+
icon: {
|
|
62
|
+
path: "callout_styles/social/linkedin.png",
|
|
63
|
+
background_color: "#007EBB"
|
|
64
|
+
},
|
|
65
|
+
label_style: LABEL_STYLE,
|
|
66
|
+
text_style: TEXT_STYLE,
|
|
67
|
+
background_color: "#00486A"
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
dribbble: {
|
|
71
|
+
label: "DRIBBBLE",
|
|
72
|
+
icon: {
|
|
73
|
+
path: "callout_styles/social/dribbble.png",
|
|
74
|
+
background_color: "#EC4989"
|
|
75
|
+
},
|
|
76
|
+
label_style: LABEL_STYLE,
|
|
77
|
+
text_style: TEXT_STYLE,
|
|
78
|
+
background_color: "#7A2245"
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
tiktok: {
|
|
82
|
+
label: "TIKTOK",
|
|
83
|
+
icon: {
|
|
84
|
+
path: "callout_styles/social/tiktok.png",
|
|
85
|
+
background_color: "#000000"
|
|
86
|
+
},
|
|
87
|
+
label_style: LABEL_STYLE,
|
|
88
|
+
text_style: TEXT_STYLE,
|
|
89
|
+
background_color: "#252525"
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
youtube: {
|
|
93
|
+
label: "YOUTUBE",
|
|
94
|
+
icon: {
|
|
95
|
+
path: "callout_styles/social/youtube.png",
|
|
96
|
+
background_color: "#FF0000"
|
|
97
|
+
},
|
|
98
|
+
label_style: LABEL_STYLE,
|
|
99
|
+
text_style: TEXT_STYLE,
|
|
100
|
+
background_color: "#7B1212"
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
snap: {
|
|
104
|
+
label: "SNAP",
|
|
105
|
+
icon: {
|
|
106
|
+
path: "callout_styles/social/snap.png",
|
|
107
|
+
background_color: "#FEFC05"
|
|
108
|
+
},
|
|
109
|
+
label_style: LABEL_STYLE,
|
|
110
|
+
text_style: TEXT_STYLE,
|
|
111
|
+
background_color: "#51511A"
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
mastodon: {
|
|
115
|
+
label: "MASTODON",
|
|
116
|
+
icon: {
|
|
117
|
+
path: "callout_styles/social/mastodon.png",
|
|
118
|
+
background_color: "#3188D4"
|
|
119
|
+
},
|
|
120
|
+
label_style: LABEL_STYLE,
|
|
121
|
+
text_style: TEXT_STYLE,
|
|
122
|
+
background_color: "#1F4566"
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
blog: {
|
|
126
|
+
label: "BLOG",
|
|
127
|
+
icon: {
|
|
128
|
+
path: "callout_styles/social/blog.png",
|
|
129
|
+
background_color: "#5831D4"
|
|
130
|
+
},
|
|
131
|
+
label_style: LABEL_STYLE,
|
|
132
|
+
text_style: TEXT_STYLE,
|
|
133
|
+
background_color: "#321E6F"
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
twitch: {
|
|
137
|
+
label: "TWITCH",
|
|
138
|
+
icon: {
|
|
139
|
+
path: "callout_styles/social/twitch.png",
|
|
140
|
+
background_color: "#9146FF"
|
|
141
|
+
},
|
|
142
|
+
label_style: LABEL_STYLE,
|
|
143
|
+
text_style: TEXT_STYLE,
|
|
144
|
+
background_color: "#461B85"
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
github: {
|
|
148
|
+
label: "GITHUB",
|
|
149
|
+
icon: {
|
|
150
|
+
path: "callout_styles/social/github.png",
|
|
151
|
+
background_color: "#161514"
|
|
152
|
+
},
|
|
153
|
+
label_style: LABEL_STYLE,
|
|
154
|
+
text_style: TEXT_STYLE,
|
|
155
|
+
background_color: "#373737"
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
soundcloud: {
|
|
159
|
+
label: "SOUNDCLOUD",
|
|
160
|
+
icon: {
|
|
161
|
+
path: "callout_styles/social/soundcloud.png",
|
|
162
|
+
background_color: "#FE2401"
|
|
163
|
+
},
|
|
164
|
+
label_style: LABEL_STYLE,
|
|
165
|
+
text_style: TEXT_STYLE,
|
|
166
|
+
background_color: "#6D1507"
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
discord: {
|
|
170
|
+
label: "DISCORD",
|
|
171
|
+
icon: {
|
|
172
|
+
path: "callout_styles/social/discord.png",
|
|
173
|
+
background_color: "#5865F2"
|
|
174
|
+
},
|
|
175
|
+
label_style: LABEL_STYLE,
|
|
176
|
+
text_style: TEXT_STYLE,
|
|
177
|
+
background_color: "#2E389D"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def self.schema_path
|
|
183
|
+
ScreenKit.root_dir.join("schemas/callout_styles/social.json")
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def initialize(source:, text:, preset: nil, **kwargs)
|
|
187
|
+
@text = text
|
|
188
|
+
self.class.validate!(kwargs)
|
|
189
|
+
super
|
|
190
|
+
self.options = self.class.presets.fetch(preset.to_sym) if preset
|
|
191
|
+
|
|
192
|
+
options.each do |key, value|
|
|
193
|
+
value = case key
|
|
194
|
+
when :label_style, :text_style
|
|
195
|
+
TextStyle.new(source:, **hi_res(**value))
|
|
196
|
+
else
|
|
197
|
+
value
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
instance_variable_set(:"@#{key}", value)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def as_json(*)
|
|
205
|
+
{}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def render
|
|
209
|
+
label_path, label_width, label_height = render_text_image(
|
|
210
|
+
type: "label",
|
|
211
|
+
text: label,
|
|
212
|
+
style: label_style,
|
|
213
|
+
width: 600
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
text_path, text_width, _ = render_text_image(
|
|
217
|
+
type: "label",
|
|
218
|
+
text:,
|
|
219
|
+
style: text_style,
|
|
220
|
+
width: 600
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
sizes = hi_res(
|
|
224
|
+
icon: 132,
|
|
225
|
+
padding: 20,
|
|
226
|
+
content_width: [label_width, text_width].max,
|
|
227
|
+
image_height: 172,
|
|
228
|
+
icon_radius: 30,
|
|
229
|
+
panel_radius: 40
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
image_width = (sizes[:icon] + (sizes[:padding] * 2)) +
|
|
233
|
+
sizes[:padding] +
|
|
234
|
+
(sizes[:content_width] / 2) +
|
|
235
|
+
(sizes[:padding] * 2)
|
|
236
|
+
image_height = sizes[:image_height]
|
|
237
|
+
offset_x = sizes[:icon] + sizes[:padding]
|
|
238
|
+
offset_y = sizes[:padding]
|
|
239
|
+
icon_y = offset_y + sizes[:icon]
|
|
240
|
+
|
|
241
|
+
MiniMagick.convert do |image|
|
|
242
|
+
# Create transparent canvas
|
|
243
|
+
image << "-size"
|
|
244
|
+
image << "#{image_width}x#{image_height}"
|
|
245
|
+
image << "xc:none"
|
|
246
|
+
|
|
247
|
+
# Draw main background
|
|
248
|
+
image << "-fill"
|
|
249
|
+
image << options[:background_color]
|
|
250
|
+
image << "-draw"
|
|
251
|
+
image << "roundrectangle 0,0,#{image_width},#{image_height}," \
|
|
252
|
+
"#{sizes[:panel_radius]},#{sizes[:panel_radius]}"
|
|
253
|
+
|
|
254
|
+
# Draw icon background
|
|
255
|
+
image << "-fill"
|
|
256
|
+
image << icon[:background_color]
|
|
257
|
+
image << "-draw"
|
|
258
|
+
image << "roundrectangle #{sizes[:padding]},#{sizes[:padding]}," \
|
|
259
|
+
"#{offset_x},#{icon_y}," \
|
|
260
|
+
"#{sizes[:icon_radius]},#{sizes[:icon_radius]}"
|
|
261
|
+
|
|
262
|
+
# Draw icon
|
|
263
|
+
icon_path = source.search(icon[:path])
|
|
264
|
+
icon_image = MiniMagick::Image.open(icon_path)
|
|
265
|
+
icon_x = sizes[:padding] + ((sizes[:icon] - icon_image.width) / 2)
|
|
266
|
+
icon_y = offset_y + ((sizes[:icon] - icon_image.height) / 2)
|
|
267
|
+
|
|
268
|
+
image << icon_path
|
|
269
|
+
image << "-geometry"
|
|
270
|
+
image << "+#{icon_x}+#{icon_y}"
|
|
271
|
+
image << "-composite"
|
|
272
|
+
|
|
273
|
+
offset_x += sizes[:padding]
|
|
274
|
+
offset_y += sizes[:padding] / 2
|
|
275
|
+
|
|
276
|
+
# Composite label
|
|
277
|
+
image << label_path
|
|
278
|
+
image << "-geometry"
|
|
279
|
+
image << "+#{offset_x}+#{offset_y}"
|
|
280
|
+
image << "-composite"
|
|
281
|
+
|
|
282
|
+
offset_y += label_height
|
|
283
|
+
|
|
284
|
+
# Composite text
|
|
285
|
+
image << text_path
|
|
286
|
+
image << "-geometry"
|
|
287
|
+
image << "+#{offset_x}+#{offset_y}"
|
|
288
|
+
image << "-composite"
|
|
289
|
+
|
|
290
|
+
image << "PNG:#{output_path}"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
output_path
|
|
294
|
+
rescue MiniMagick::Error => error
|
|
295
|
+
retry if error.message.include?("No such file or directory")
|
|
296
|
+
raise
|
|
297
|
+
ensure
|
|
298
|
+
remove_file(label_path)
|
|
299
|
+
remove_file(text_path)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
class Callout
|
|
5
5
|
class TextStyle
|
|
6
|
-
attr_reader :color, :size, :font_path
|
|
6
|
+
attr_reader :color, :size, :font_path, :align
|
|
7
7
|
|
|
8
8
|
def initialize(source:, **kwargs)
|
|
9
9
|
@source = source
|
|
@@ -37,7 +37,7 @@ module ScreenKit
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def as_json(*)
|
|
40
|
-
{color:, size:, font_path:, rgb_color:, opacity:}
|
|
40
|
+
{color:, size:, font_path:, rgb_color:, opacity:, align:}
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
data/lib/screenkit/callout.rb
CHANGED
|
@@ -32,7 +32,7 @@ module ScreenKit
|
|
|
32
32
|
:style_props, :style_class, :animation, :source, :log_path
|
|
33
33
|
|
|
34
34
|
def self.schema_path
|
|
35
|
-
ScreenKit.root_dir.join("
|
|
35
|
+
ScreenKit.root_dir.join("schemas/refs/callout_style.json")
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def initialize(
|
|
@@ -5,12 +5,17 @@ module ScreenKit
|
|
|
5
5
|
class Base
|
|
6
6
|
extend SchemaValidator
|
|
7
7
|
|
|
8
|
+
attr_reader :raw_options
|
|
9
|
+
|
|
8
10
|
def self.load_file(path)
|
|
9
11
|
unless File.file?(path)
|
|
10
12
|
raise FileNotFoundError, "Config file not found: #{path}"
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
template = File.read(path)
|
|
16
|
+
contents = ERB.new(template).result
|
|
17
|
+
|
|
18
|
+
config = YAML.load(contents, symbolize_names: true)
|
|
14
19
|
load(config)
|
|
15
20
|
end
|
|
16
21
|
|
|
@@ -21,6 +26,8 @@ module ScreenKit
|
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def initialize(**kwargs)
|
|
29
|
+
@raw_options = kwargs
|
|
30
|
+
|
|
24
31
|
kwargs.each do |key, value|
|
|
25
32
|
value = process(key, value)
|
|
26
33
|
instance_variable_set(:"@#{key}", value)
|
|
@@ -32,7 +32,7 @@ module ScreenKit
|
|
|
32
32
|
|
|
33
33
|
def self.schema_path
|
|
34
34
|
@schema_path ||=
|
|
35
|
-
ScreenKit.root_dir.join("
|
|
35
|
+
ScreenKit.root_dir.join("schemas/project.json")
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
private def process(key, value)
|
|
@@ -45,6 +45,10 @@ module ScreenKit
|
|
|
45
45
|
value
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
def to_h
|
|
50
|
+
raw_options
|
|
51
|
+
end
|
|
48
52
|
end
|
|
49
53
|
end
|
|
50
54
|
end
|
|
@@ -8,7 +8,7 @@ module ScreenKit
|
|
|
8
8
|
extend SchemaValidator
|
|
9
9
|
|
|
10
10
|
def self.schema_path
|
|
11
|
-
ScreenKit.root_dir.join("
|
|
11
|
+
ScreenKit.root_dir.join("schemas/refs/intro.json")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# The intro scene configuration.
|
|
@@ -109,11 +109,10 @@ module ScreenKit
|
|
|
109
109
|
# Background layer
|
|
110
110
|
if background_path&.file?
|
|
111
111
|
if video_file?(background_path)
|
|
112
|
-
# Ensure video is 24fps
|
|
113
112
|
extname = background_path.extname
|
|
114
113
|
optimized_path = background_path.sub_ext("_24fps#{extname}")
|
|
115
114
|
|
|
116
|
-
if
|
|
115
|
+
if Video.right_fps?(background_path)
|
|
117
116
|
optimized_path = background_path
|
|
118
117
|
end
|
|
119
118
|
|
|
@@ -8,7 +8,7 @@ module ScreenKit
|
|
|
8
8
|
extend SchemaValidator
|
|
9
9
|
|
|
10
10
|
def self.schema_path
|
|
11
|
-
ScreenKit.root_dir.join("
|
|
11
|
+
ScreenKit.root_dir.join("schemas/refs/outro.json")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# The outro scene configuration.
|
|
@@ -65,7 +65,6 @@ module ScreenKit
|
|
|
65
65
|
cmd = [
|
|
66
66
|
"ffmpeg",
|
|
67
67
|
*inputs,
|
|
68
|
-
"-sws_flags", "lanczos+accurate_rnd+full_chroma_int",
|
|
69
68
|
"-filter_complex", filters,
|
|
70
69
|
*maps,
|
|
71
70
|
"-c:v", "libx264", "-crf", "0", "-pix_fmt", "yuv444p",
|
|
@@ -95,15 +94,26 @@ module ScreenKit
|
|
|
95
94
|
# Background layer
|
|
96
95
|
if background_path&.file?
|
|
97
96
|
if video_file?(background_path)
|
|
97
|
+
extname = background_path.extname
|
|
98
|
+
optimized_path = background_path.sub_ext("_24fps#{extname}")
|
|
99
|
+
|
|
100
|
+
if Video.right_fps?(background_path)
|
|
101
|
+
optimized_path = background_path
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
unless optimized_path.file?
|
|
105
|
+
Video.new(input_path: background_path).export(optimized_path)
|
|
106
|
+
end
|
|
107
|
+
|
|
98
108
|
# Video background
|
|
99
|
-
video_duration = duration(
|
|
109
|
+
video_duration = duration(optimized_path)
|
|
100
110
|
|
|
101
111
|
# Calculate how many loops we need
|
|
102
112
|
loops_needed = (duration / video_duration).ceil
|
|
103
113
|
|
|
104
114
|
inputs += [
|
|
105
115
|
"-stream_loop", (loops_needed - 1).to_s, "-i",
|
|
106
|
-
|
|
116
|
+
optimized_path
|
|
107
117
|
]
|
|
108
118
|
|
|
109
119
|
# Scale, crop, then trim to exact duration needed
|
|
@@ -4,7 +4,7 @@ module ScreenKit
|
|
|
4
4
|
module Exporter
|
|
5
5
|
class Video
|
|
6
6
|
include Shell
|
|
7
|
-
|
|
7
|
+
extend Utils
|
|
8
8
|
|
|
9
9
|
# The path to the input video file.
|
|
10
10
|
attr_reader :input_path
|
|
@@ -17,10 +17,12 @@ module ScreenKit
|
|
|
17
17
|
@log_path = log_path
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def
|
|
21
|
-
|
|
20
|
+
def self.right_fps?(path)
|
|
21
|
+
(-0.02..0.02).cover?(24 - fps(path))
|
|
22
|
+
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
def export(output_path)
|
|
25
|
+
if self.class.right_fps?(input_path)
|
|
24
26
|
FileUtils.cp(input_path, output_path)
|
|
25
27
|
return
|
|
26
28
|
end
|
|
@@ -41,6 +41,7 @@ resources_dir:
|
|
|
41
41
|
- "%{episode_dir}/resources/fonts"
|
|
42
42
|
- resources
|
|
43
43
|
- "%{episode_dir}/resources"
|
|
44
|
+
- "<%= ScreenKit.resources_dir %>"
|
|
44
45
|
- ~/Library/Fonts
|
|
45
46
|
- /Library/Fonts
|
|
46
47
|
- /System/Library/Fonts
|
|
@@ -272,6 +273,8 @@ demotape:
|
|
|
272
273
|
# in the video. Callouts are visual elements that highlight important
|
|
273
274
|
# information. Each callout type can have its own settings for icon, background,
|
|
274
275
|
# text styles, animations, and sounds.
|
|
276
|
+
#
|
|
277
|
+
# https://github.com/fnando/screenkit/blob/main/DOCUMENTATION.md#callouts
|
|
275
278
|
callout_styles:
|
|
276
279
|
shadow_block:
|
|
277
280
|
# yaml-language-server: $schema=https://screenkit.dev/schemas/callout_styles/default.json
|
|
@@ -311,3 +314,16 @@ callout_styles:
|
|
|
311
314
|
out_transition:
|
|
312
315
|
duration: 0.4s
|
|
313
316
|
sound: pop.mp3
|
|
317
|
+
|
|
318
|
+
social:
|
|
319
|
+
# yaml-language-server: $schema=https://screenkit.dev/schemas/callout_styles/social.json
|
|
320
|
+
style: social
|
|
321
|
+
anchor: [left, bottom]
|
|
322
|
+
margin: 100
|
|
323
|
+
animation: fade
|
|
324
|
+
in_transition:
|
|
325
|
+
duration: 0.4s
|
|
326
|
+
sound: pop.mp3
|
|
327
|
+
out_transition:
|
|
328
|
+
duration: 0.4s
|
|
329
|
+
sound: pop.mp3
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
3
|
+
"$id": "https://screenkit.dev/schemas/callout_styles/shadow_block.json",
|
|
4
|
+
"title": "Social Callout",
|
|
5
|
+
"oneOf": [
|
|
6
|
+
{
|
|
7
|
+
"preset": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": [
|
|
10
|
+
"spotify",
|
|
11
|
+
"youtube",
|
|
12
|
+
"tiktok",
|
|
13
|
+
"snap",
|
|
14
|
+
"mastodon",
|
|
15
|
+
"blog",
|
|
16
|
+
"linkedin",
|
|
17
|
+
"discord",
|
|
18
|
+
"twitch",
|
|
19
|
+
"github",
|
|
20
|
+
"soundcloud",
|
|
21
|
+
"dribbble"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["label", "icon", "label_style", "account_style"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"icon": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["path", "background_color"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"path": { "type": "string", "format": "uri" },
|
|
34
|
+
"background_color": { "$ref": "../refs/color.json" }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"background_color": {
|
|
38
|
+
"$ref": "../refs/color.json"
|
|
39
|
+
},
|
|
40
|
+
"label_style": {
|
|
41
|
+
"$ref": "../refs/text_style.json"
|
|
42
|
+
},
|
|
43
|
+
"account_style": {
|
|
44
|
+
"$ref": "../refs/text_style.json"
|
|
45
|
+
},
|
|
46
|
+
"label": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"text": {
|
|
50
|
+
"type": "string"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
data/lib/screenkit/sound.rb
CHANGED
|
@@ -20,7 +20,7 @@ module ScreenKit
|
|
|
20
20
|
|
|
21
21
|
case input
|
|
22
22
|
when FalseClass, nil
|
|
23
|
-
@path = ScreenKit.
|
|
23
|
+
@path = ScreenKit.resources_dir.join("mute.mp3")
|
|
24
24
|
when Hash
|
|
25
25
|
{path: nil, volume: 1.0}.merge(input) => {path:, volume:}
|
|
26
26
|
@path = Pathname(path)
|
data/lib/screenkit/tts/espeak.rb
CHANGED
data/lib/screenkit/tts/say.rb
CHANGED
data/lib/screenkit/version.rb
CHANGED
data/lib/screenkit.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "etc"
|
|
|
11
11
|
require "securerandom"
|
|
12
12
|
require "demo_tape/duration"
|
|
13
13
|
require "aitch"
|
|
14
|
+
require "erb"
|
|
14
15
|
|
|
15
16
|
module ScreenKit
|
|
16
17
|
require_relative "screenkit/version"
|
|
@@ -67,7 +68,11 @@ module ScreenKit
|
|
|
67
68
|
require_files.call("screenkit/tts/*.rb")
|
|
68
69
|
|
|
69
70
|
def self.root_dir
|
|
70
|
-
@root_dir ||= Pathname(__dir__)
|
|
71
|
+
@root_dir ||= Pathname(__dir__).join("screenkit")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.resources_dir
|
|
75
|
+
@resources_dir ||= root_dir.join("resources")
|
|
71
76
|
end
|
|
72
77
|
|
|
73
78
|
# Raised when the configuration schema is invalid.
|
data/screenkit.gemspec
CHANGED
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.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nando Vieira
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: erb
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: etc
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -289,6 +303,7 @@ files:
|
|
|
289
303
|
- lib/screenkit/callout/styles/file_copy.rb
|
|
290
304
|
- lib/screenkit/callout/styles/inline_block.rb
|
|
291
305
|
- lib/screenkit/callout/styles/shadow_block.rb
|
|
306
|
+
- lib/screenkit/callout/styles/social.rb
|
|
292
307
|
- lib/screenkit/callout/text_style.rb
|
|
293
308
|
- lib/screenkit/cli.rb
|
|
294
309
|
- lib/screenkit/cli/base.rb
|
|
@@ -332,12 +347,27 @@ files:
|
|
|
332
347
|
- lib/screenkit/logfile.rb
|
|
333
348
|
- lib/screenkit/parallel_processor.rb
|
|
334
349
|
- lib/screenkit/path_lookup.rb
|
|
350
|
+
- lib/screenkit/resources/callout_styles/social/blog.png
|
|
351
|
+
- lib/screenkit/resources/callout_styles/social/bsky.png
|
|
352
|
+
- lib/screenkit/resources/callout_styles/social/discord.png
|
|
353
|
+
- lib/screenkit/resources/callout_styles/social/dribbble.png
|
|
354
|
+
- lib/screenkit/resources/callout_styles/social/github.png
|
|
355
|
+
- lib/screenkit/resources/callout_styles/social/instagram.png
|
|
356
|
+
- lib/screenkit/resources/callout_styles/social/linkedin.png
|
|
357
|
+
- lib/screenkit/resources/callout_styles/social/mastodon.png
|
|
358
|
+
- lib/screenkit/resources/callout_styles/social/snap.png
|
|
359
|
+
- lib/screenkit/resources/callout_styles/social/soundcloud.png
|
|
360
|
+
- lib/screenkit/resources/callout_styles/social/spotify.png
|
|
361
|
+
- lib/screenkit/resources/callout_styles/social/tiktok.png
|
|
362
|
+
- lib/screenkit/resources/callout_styles/social/twitch.png
|
|
363
|
+
- lib/screenkit/resources/callout_styles/social/youtube.png
|
|
335
364
|
- lib/screenkit/resources/mute.mp3
|
|
336
365
|
- lib/screenkit/resources/transparent.png
|
|
337
366
|
- lib/screenkit/schema_validator.rb
|
|
338
367
|
- lib/screenkit/schemas/callout_styles/file_copy.json
|
|
339
368
|
- lib/screenkit/schemas/callout_styles/inline_block.json
|
|
340
369
|
- lib/screenkit/schemas/callout_styles/shadow_block.json
|
|
370
|
+
- lib/screenkit/schemas/callout_styles/social.json
|
|
341
371
|
- lib/screenkit/schemas/callouts/inline_block.json
|
|
342
372
|
- lib/screenkit/schemas/callouts/shadow_block.json
|
|
343
373
|
- lib/screenkit/schemas/episode.json
|
|
@@ -388,10 +418,10 @@ metadata:
|
|
|
388
418
|
rubygems_mfa_required: 'true'
|
|
389
419
|
homepage_uri: https://github.com/fnando/screenkit
|
|
390
420
|
bug_tracker_uri: https://github.com/fnando/screenkit/issues
|
|
391
|
-
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
392
|
-
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
393
|
-
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
394
|
-
license_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
421
|
+
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.9
|
|
422
|
+
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.9/CHANGELOG.md
|
|
423
|
+
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.9/README.md
|
|
424
|
+
license_uri: https://github.com/fnando/screenkit/tree/v0.0.9/LICENSE.md
|
|
395
425
|
rdoc_options: []
|
|
396
426
|
require_paths:
|
|
397
427
|
- lib
|