screenkit 0.0.7 → 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 +9 -0
- data/DOCUMENTATION.md +131 -12
- 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/http.rb +58 -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/base.rb +16 -7
- data/lib/screenkit/tts/eleven_labs.rb +8 -23
- 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 +8 -1
- data/screenkit.gemspec +1 -0
- metadata +36 -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,6 +11,15 @@ Prefix your message with one of the following:
|
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
|
+
## v0.0.9
|
|
15
|
+
|
|
16
|
+
- [Added] Add social callouts.
|
|
17
|
+
- [Added] Add align support for inline block callouts.
|
|
18
|
+
|
|
19
|
+
## v0.0.8
|
|
20
|
+
|
|
21
|
+
- [Fixed] Further improvements to support extensions.
|
|
22
|
+
|
|
14
23
|
## v0.0.7
|
|
15
24
|
|
|
16
25
|
- [Fixed] Improve support for extensions.
|
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
|
|
@@ -719,7 +822,9 @@ tts:
|
|
|
719
822
|
|
|
720
823
|
### ElevenLabs Engine
|
|
721
824
|
|
|
722
|
-
|
|
825
|
+
The ElevenLabs TTS engine requires an API key. Set it via the `--tts-api-key`
|
|
826
|
+
option when exporting an episode. The API key must be prefixed with
|
|
827
|
+
`eleven_labs:`, e.g. `eleven_labs:sk_c195c0131de...`.
|
|
723
828
|
|
|
724
829
|
```yaml
|
|
725
830
|
tts:
|
|
@@ -764,21 +869,26 @@ module. Custom engines must implement the `generate` method:
|
|
|
764
869
|
module ScreenKit
|
|
765
870
|
module TTS
|
|
766
871
|
class CustomEngine < Base
|
|
767
|
-
extend Shell
|
|
768
|
-
|
|
769
872
|
# Optional: Define schema path for validation
|
|
770
873
|
def self.schema_path
|
|
771
|
-
|
|
874
|
+
File.join(__dir__, "yourschema.json")
|
|
772
875
|
end
|
|
773
876
|
|
|
774
877
|
# This method is required.
|
|
878
|
+
# The keyword arguments will be all the configuration options, plus
|
|
879
|
+
# api_key and segments. If you don't care about those, remember to use
|
|
880
|
+
# the `**` operator to ignore them.
|
|
775
881
|
def self.available?(**)
|
|
776
|
-
|
|
882
|
+
# If you're running a local command:
|
|
883
|
+
# command_exist?("some-command")
|
|
884
|
+
|
|
885
|
+
# If you're using an API key:
|
|
886
|
+
# api_key.to_s.start_with?("#{engine_name}:")
|
|
777
887
|
end
|
|
778
888
|
|
|
779
889
|
# This method is required.
|
|
780
890
|
def generate(text:, output_path:, log_path: nil)
|
|
781
|
-
# Optional: validate options against JSON schema.
|
|
891
|
+
# Optional, but recommended: validate options against JSON schema.
|
|
782
892
|
self.class.validate!(options)
|
|
783
893
|
|
|
784
894
|
# If you need access to previous/next text, you can access the method
|
|
@@ -790,11 +900,19 @@ module ScreenKit
|
|
|
790
900
|
# Write output to output_path
|
|
791
901
|
# Optionally log to log_path
|
|
792
902
|
|
|
793
|
-
# Example
|
|
903
|
+
# Example calling a command (provided by ScreenKit::Shell)
|
|
794
904
|
# self.class.run_command "some-command",
|
|
795
905
|
# "-o", output_path.sub_ext(".wav"),
|
|
796
906
|
# text,
|
|
797
907
|
# log_path:
|
|
908
|
+
|
|
909
|
+
# Example using an API (provided by ScreenKit::HTTP)
|
|
910
|
+
# response = json_post(
|
|
911
|
+
# url: "https://api.example.com/tts",
|
|
912
|
+
# headers: {authorization: "Bearer #{api_key}"},
|
|
913
|
+
# api_key:,
|
|
914
|
+
# log_path:
|
|
915
|
+
# )
|
|
798
916
|
end
|
|
799
917
|
end
|
|
800
918
|
end
|
|
@@ -821,6 +939,7 @@ The engine name is camelized (e.g., `custom_engine` → `CustomEngine`,
|
|
|
821
939
|
|
|
822
940
|
- [Search Github](https://github.com/topics/screenkit-tts)
|
|
823
941
|
- [Google Text to Speech](https://github.com/fnando/screenkit-tts-google)
|
|
942
|
+
- [Minimax Text to Speech](https://github.com/fnando/screenkit-tts-minimax)
|
|
824
943
|
|
|
825
944
|
> [!TIP]
|
|
826
945
|
>
|
|
@@ -1121,26 +1240,26 @@ be processed).
|
|
|
1121
1240
|
|
|
1122
1241
|
### Common Issues
|
|
1123
1242
|
|
|
1124
|
-
|
|
1243
|
+
#### "Gem not found" error
|
|
1125
1244
|
|
|
1126
1245
|
```bash
|
|
1127
1246
|
bundle install
|
|
1128
1247
|
bundle exec screenkit ...
|
|
1129
1248
|
```
|
|
1130
1249
|
|
|
1131
|
-
|
|
1250
|
+
#### Schema validation failed
|
|
1132
1251
|
|
|
1133
1252
|
- Check YAML syntax
|
|
1134
1253
|
- Verify required fields are present
|
|
1135
1254
|
- Use schema hints with `yaml-language-server`
|
|
1136
1255
|
|
|
1137
|
-
|
|
1256
|
+
#### Missing resources
|
|
1138
1257
|
|
|
1139
1258
|
- Check `resources_dir` configuration
|
|
1140
1259
|
- Verify file paths are relative to resource directories
|
|
1141
1260
|
- Use absolute paths for system resources
|
|
1142
1261
|
|
|
1143
|
-
|
|
1262
|
+
#### TTS not working
|
|
1144
1263
|
|
|
1145
1264
|
- For ElevenLabs: Set `--tts-api-key`
|
|
1146
1265
|
- For macOS `say`: Verify voice name with `say -v ?`
|
|
@@ -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
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScreenKit
|
|
4
|
+
module HTTP
|
|
5
|
+
# Sends a POST request.
|
|
6
|
+
# @param url [String] The request URL.
|
|
7
|
+
# @param params [Hash] The request parameters.
|
|
8
|
+
# @param headers [Hash] The request headers.
|
|
9
|
+
# @param api_key [String] The API key to redact from logs.
|
|
10
|
+
# @param log_path [String, nil] The path to log the request details.
|
|
11
|
+
# @return [Aitch::Response] The response.
|
|
12
|
+
def post(url:, params:, headers:, api_key:, log_path: nil)
|
|
13
|
+
if log_path
|
|
14
|
+
File.open(log_path, "w") do |f|
|
|
15
|
+
f << JSON.pretty_generate(url:, params:, headers:)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
client = Aitch::Namespace.new
|
|
20
|
+
client.configure do |config|
|
|
21
|
+
config.logger = Logger.new(log_path) if log_path
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
client.post(
|
|
25
|
+
url:,
|
|
26
|
+
params:,
|
|
27
|
+
options: {expect: 200},
|
|
28
|
+
headers: headers.merge(user_agent: "ScreenKit/#{ScreenKit::VERSION}")
|
|
29
|
+
)
|
|
30
|
+
ensure
|
|
31
|
+
redact_file(log_path, api_key)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Sends a JSON POST request.
|
|
35
|
+
# @param url [String] The request URL.
|
|
36
|
+
# @param params [Hash] The request parameters.
|
|
37
|
+
# @param headers [Hash] The request headers.
|
|
38
|
+
# @param api_key [String] The API key to redact from logs.
|
|
39
|
+
# @param log_path [String, nil] The path to log the request details.
|
|
40
|
+
# @return [Aitch::Response] The response.
|
|
41
|
+
def json_post(headers:, **)
|
|
42
|
+
headers = headers.merge(content_type: "application/json")
|
|
43
|
+
post(headers:, **)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Redacts sensitive text from a file.
|
|
47
|
+
# @param path [String] The file path.
|
|
48
|
+
# @param text [String] The text to redact.
|
|
49
|
+
# @return [void]
|
|
50
|
+
def redact_file(path, text)
|
|
51
|
+
return unless path
|
|
52
|
+
return unless File.file?(path)
|
|
53
|
+
|
|
54
|
+
content = File.read(path).gsub(text, "[REDACTED]")
|
|
55
|
+
File.write(path, content)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
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/base.rb
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json-schema"
|
|
4
|
+
require "aitch"
|
|
5
|
+
require "logger"
|
|
6
|
+
|
|
3
7
|
module ScreenKit
|
|
4
8
|
module TTS
|
|
5
9
|
class Base
|
|
6
|
-
require_relative "../core_ext"
|
|
7
10
|
require_relative "../schema_validator"
|
|
11
|
+
require_relative "../shell"
|
|
12
|
+
require_relative "../http"
|
|
13
|
+
require_relative "../core_ext"
|
|
14
|
+
require_relative "../version"
|
|
8
15
|
|
|
9
16
|
extend SchemaValidator
|
|
17
|
+
extend Shell
|
|
18
|
+
include HTTP
|
|
10
19
|
|
|
11
20
|
using CoreExt
|
|
12
21
|
|
|
@@ -33,18 +42,18 @@ module ScreenKit
|
|
|
33
42
|
name.split("::").last.underscore
|
|
34
43
|
end
|
|
35
44
|
|
|
45
|
+
def self.api_key_prefix
|
|
46
|
+
"#{engine_name}:"
|
|
47
|
+
end
|
|
48
|
+
|
|
36
49
|
def initialize(id: nil, segments: nil, api_key: nil, **options)
|
|
37
50
|
@segments = Array(segments)
|
|
38
51
|
@options = options
|
|
39
52
|
@id = id
|
|
40
|
-
@api_key = api_key
|
|
41
|
-
end
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
return unless File.file?(path)
|
|
54
|
+
return unless api_key
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
File.write(path, content)
|
|
56
|
+
@api_key = api_key.delete_prefix("#{self.class.engine_name}:")
|
|
48
57
|
end
|
|
49
58
|
end
|
|
50
59
|
end
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
module TTS
|
|
5
5
|
class ElevenLabs < Base
|
|
6
|
+
include HTTP
|
|
7
|
+
|
|
6
8
|
def self.schema_path
|
|
7
9
|
ScreenKit.root_dir
|
|
8
10
|
.join("screenkit/schemas/tts/elevenlabs.json")
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
def self.available?(api_key: nil, **)
|
|
12
|
-
api_key.to_s.
|
|
14
|
+
api_key.to_s.start_with?(api_key_prefix)
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def all_texts
|
|
@@ -19,18 +21,6 @@ module ScreenKit
|
|
|
19
21
|
def generate(output_path:, text:, log_path: nil)
|
|
20
22
|
voice_id = options[:voice_id]
|
|
21
23
|
|
|
22
|
-
if log_path
|
|
23
|
-
File.open(log_path, "w") do |f|
|
|
24
|
-
f << JSON.pretty_generate(options.merge(text:))
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
require "aitch"
|
|
29
|
-
|
|
30
|
-
Aitch.configure do |config|
|
|
31
|
-
config.logger = Logger.new(log_path) if log_path
|
|
32
|
-
end
|
|
33
|
-
|
|
34
24
|
current_index = all_texts.index { it == text }
|
|
35
25
|
|
|
36
26
|
if current_index
|
|
@@ -41,20 +31,15 @@ module ScreenKit
|
|
|
41
31
|
params = options.merge(text:, previous_text:, next_text:)
|
|
42
32
|
.except(:voice_id)
|
|
43
33
|
|
|
44
|
-
response =
|
|
34
|
+
response = json_post(
|
|
45
35
|
url: "https://api.elevenlabs.io/v1/text-to-speech/#{voice_id}",
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"user-agent": "ScreenKit/#{ScreenKit::VERSION}",
|
|
51
|
-
"xi-api-key": api_key
|
|
52
|
-
}
|
|
36
|
+
params:,
|
|
37
|
+
headers: {"xi-api-key": api_key},
|
|
38
|
+
api_key:,
|
|
39
|
+
log_path:
|
|
53
40
|
)
|
|
54
41
|
|
|
55
42
|
File.binwrite(output_path, response.body)
|
|
56
|
-
ensure
|
|
57
|
-
redact_file(log_path, api_key)
|
|
58
43
|
end
|
|
59
44
|
end
|
|
60
45
|
end
|
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
|
@@ -10,6 +10,8 @@ require "tty-spinner"
|
|
|
10
10
|
require "etc"
|
|
11
11
|
require "securerandom"
|
|
12
12
|
require "demo_tape/duration"
|
|
13
|
+
require "aitch"
|
|
14
|
+
require "erb"
|
|
13
15
|
|
|
14
16
|
module ScreenKit
|
|
15
17
|
require_relative "screenkit/version"
|
|
@@ -23,6 +25,7 @@ module ScreenKit
|
|
|
23
25
|
require_relative "screenkit/watermark"
|
|
24
26
|
require_relative "screenkit/spinner"
|
|
25
27
|
require_relative "screenkit/shell"
|
|
28
|
+
require_relative "screenkit/http"
|
|
26
29
|
require_relative "screenkit/schema_validator"
|
|
27
30
|
require_relative "screenkit/generators/project"
|
|
28
31
|
require_relative "screenkit/generators/episode"
|
|
@@ -65,7 +68,11 @@ module ScreenKit
|
|
|
65
68
|
require_files.call("screenkit/tts/*.rb")
|
|
66
69
|
|
|
67
70
|
def self.root_dir
|
|
68
|
-
@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")
|
|
69
76
|
end
|
|
70
77
|
|
|
71
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
|
|
@@ -328,15 +343,31 @@ files:
|
|
|
328
343
|
- lib/screenkit/generators/project/resources/sounds/pop.mp3
|
|
329
344
|
- lib/screenkit/generators/project/resources/sounds/whoosh.mp3
|
|
330
345
|
- lib/screenkit/generators/project/screenkit.yml
|
|
346
|
+
- lib/screenkit/http.rb
|
|
331
347
|
- lib/screenkit/logfile.rb
|
|
332
348
|
- lib/screenkit/parallel_processor.rb
|
|
333
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
|
|
334
364
|
- lib/screenkit/resources/mute.mp3
|
|
335
365
|
- lib/screenkit/resources/transparent.png
|
|
336
366
|
- lib/screenkit/schema_validator.rb
|
|
337
367
|
- lib/screenkit/schemas/callout_styles/file_copy.json
|
|
338
368
|
- lib/screenkit/schemas/callout_styles/inline_block.json
|
|
339
369
|
- lib/screenkit/schemas/callout_styles/shadow_block.json
|
|
370
|
+
- lib/screenkit/schemas/callout_styles/social.json
|
|
340
371
|
- lib/screenkit/schemas/callouts/inline_block.json
|
|
341
372
|
- lib/screenkit/schemas/callouts/shadow_block.json
|
|
342
373
|
- lib/screenkit/schemas/episode.json
|
|
@@ -387,10 +418,10 @@ metadata:
|
|
|
387
418
|
rubygems_mfa_required: 'true'
|
|
388
419
|
homepage_uri: https://github.com/fnando/screenkit
|
|
389
420
|
bug_tracker_uri: https://github.com/fnando/screenkit/issues
|
|
390
|
-
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
391
|
-
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
392
|
-
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
393
|
-
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
|
|
394
425
|
rdoc_options: []
|
|
395
426
|
require_paths:
|
|
396
427
|
- lib
|