screenkit 0.0.6 → 0.0.8
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/.github/workflows/docker.yml +2 -2
- data/CHANGELOG.md +8 -0
- data/DOCUMENTATION.md +54 -16
- data/action.yml +140 -0
- data/lib/screenkit/callout/styles/base.rb +16 -0
- data/lib/screenkit/cli/episode.rb +7 -3
- data/lib/screenkit/content_type.rb +1 -1
- data/lib/screenkit/core_ext/json.rb +2 -0
- data/lib/screenkit/core_ext/string.rb +6 -0
- data/lib/screenkit/core_ext.rb +4 -0
- data/lib/screenkit/exporter/demotape.rb +2 -1
- data/lib/screenkit/exporter/episode.rb +20 -14
- data/lib/screenkit/exporter/segment.rb +5 -1
- data/lib/screenkit/exporter/video.rb +7 -2
- data/lib/screenkit/generators/episode/config.yml.erb +8 -103
- data/lib/screenkit/generators/project/.github/screenkit.yml +48 -0
- data/lib/screenkit/generators/project.rb +7 -0
- data/lib/screenkit/http.rb +58 -0
- data/lib/screenkit/shell.rb +2 -2
- data/lib/screenkit/tts/base.rb +40 -5
- data/lib/screenkit/tts/eleven_labs.rb +19 -23
- data/lib/screenkit/tts/espeak.rb +11 -11
- data/lib/screenkit/tts/say.rb +9 -9
- data/lib/screenkit/version.rb +1 -1
- data/lib/screenkit.rb +79 -1
- metadata +9 -6
- data/lib/screen_kit.rb +0 -80
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9432b0beeacae6f145c48e9935c18d92c62c08c69ea7177a3d282db689b0d9d5
|
|
4
|
+
data.tar.gz: 9cbf970a8998f0cb3bd5778489faa0ecc521b800862c1d8c8112cb11e0232bcd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b952dc0d719ccdf1496b2ee0cb430e9ba8271e5e9373f1859d8132ed68febf86daa9ff063cbc05a75a314de32d8a279eb4336021344caf70e9cc297e8eaac37
|
|
7
|
+
data.tar.gz: 4660532603407564ab3315a0d21d6af592d5fad3179cbd7bafade4438e97708a95ba2d9eab34fd61507aee02b58c90bed5d487f7affa4951351a1d3442c37d46
|
|
@@ -7,7 +7,7 @@ on:
|
|
|
7
7
|
paths:
|
|
8
8
|
- Dockerfile
|
|
9
9
|
tags:
|
|
10
|
-
- v
|
|
10
|
+
- v[0-9]+.[0-9]+.[0-9]+
|
|
11
11
|
workflow_dispatch:
|
|
12
12
|
inputs:
|
|
13
13
|
ref:
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
build:
|
|
24
24
|
runs-on: ubuntu-latest
|
|
25
25
|
steps:
|
|
26
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@v6
|
|
27
27
|
with:
|
|
28
28
|
ref: ${{ github.event.inputs.ref }}
|
|
29
29
|
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,14 @@ Prefix your message with one of the following:
|
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
|
+
# v0.0.8
|
|
15
|
+
|
|
16
|
+
- [Fixed] Further improvements to support extensions.
|
|
17
|
+
|
|
18
|
+
## v0.0.7
|
|
19
|
+
|
|
20
|
+
- [Fixed] Improve support for extensions.
|
|
21
|
+
|
|
14
22
|
## v0.0.6
|
|
15
23
|
|
|
16
24
|
- [Added] Add `--tts-preset` option to select TTS preset when exporting
|
data/DOCUMENTATION.md
CHANGED
|
@@ -719,7 +719,9 @@ tts:
|
|
|
719
719
|
|
|
720
720
|
### ElevenLabs Engine
|
|
721
721
|
|
|
722
|
-
|
|
722
|
+
The ElevenLabs TTS engine requires an API key. Set it via the `--tts-api-key`
|
|
723
|
+
option when exporting an episode. The API key must be prefixed with
|
|
724
|
+
`eleven_labs:`, e.g. `eleven_labs:sk_c195c0131de...`.
|
|
723
725
|
|
|
724
726
|
```yaml
|
|
725
727
|
tts:
|
|
@@ -764,32 +766,50 @@ module. Custom engines must implement the `generate` method:
|
|
|
764
766
|
module ScreenKit
|
|
765
767
|
module TTS
|
|
766
768
|
class CustomEngine < Base
|
|
767
|
-
include Shell
|
|
768
|
-
|
|
769
769
|
# Optional: Define schema path for validation
|
|
770
770
|
def self.schema_path
|
|
771
|
-
|
|
771
|
+
File.join(__dir__, "yourschema.json")
|
|
772
772
|
end
|
|
773
773
|
|
|
774
774
|
# This method is required.
|
|
775
|
-
|
|
776
|
-
|
|
775
|
+
# The keyword arguments will be all the configuration options, plus
|
|
776
|
+
# api_key and segments. If you don't care about those, remember to use
|
|
777
|
+
# the `**` operator to ignore them.
|
|
778
|
+
def self.available?(**)
|
|
779
|
+
# If you're running a local command:
|
|
780
|
+
# command_exist?("some-command")
|
|
781
|
+
|
|
782
|
+
# If you're using an API key:
|
|
783
|
+
# api_key.to_s.start_with?("#{engine_name}:")
|
|
777
784
|
end
|
|
778
785
|
|
|
779
786
|
# This method is required.
|
|
780
787
|
def generate(text:, output_path:, log_path: nil)
|
|
781
|
-
# Optional: validate options against JSON schema.
|
|
788
|
+
# Optional, but recommended: validate options against JSON schema.
|
|
782
789
|
self.class.validate!(options)
|
|
783
790
|
|
|
791
|
+
# If you need access to previous/next text, you can access the method
|
|
792
|
+
# `segments`.
|
|
793
|
+
# current_index = segments.index { it.script_content == text }
|
|
794
|
+
# next_text = segments[current_index + 1]&.script_content
|
|
795
|
+
|
|
784
796
|
# Generate audio file from text
|
|
785
797
|
# Write output to output_path
|
|
786
798
|
# Optionally log to log_path
|
|
787
799
|
|
|
788
|
-
# Example
|
|
789
|
-
# run_command "some-command",
|
|
790
|
-
#
|
|
791
|
-
#
|
|
792
|
-
#
|
|
800
|
+
# Example calling a command (provided by ScreenKit::Shell)
|
|
801
|
+
# self.class.run_command "some-command",
|
|
802
|
+
# "-o", output_path.sub_ext(".wav"),
|
|
803
|
+
# text,
|
|
804
|
+
# log_path:
|
|
805
|
+
|
|
806
|
+
# Example using an API (provided by ScreenKit::HTTP)
|
|
807
|
+
# response = json_post(
|
|
808
|
+
# url: "https://api.example.com/tts",
|
|
809
|
+
# headers: {authorization: "Bearer #{api_key}"},
|
|
810
|
+
# api_key:,
|
|
811
|
+
# log_path:
|
|
812
|
+
# )
|
|
793
813
|
end
|
|
794
814
|
end
|
|
795
815
|
end
|
|
@@ -802,6 +822,7 @@ end
|
|
|
802
822
|
tts:
|
|
803
823
|
- id: custom_engine
|
|
804
824
|
engine: custom_engine # Camelized to CustomEngine
|
|
825
|
+
enabled: true
|
|
805
826
|
# Add your custom options here
|
|
806
827
|
api_key: your_api_key
|
|
807
828
|
custom_option: value
|
|
@@ -811,6 +832,17 @@ The engine name is camelized (e.g., `custom_engine` → `CustomEngine`,
|
|
|
811
832
|
`google_cloud` → `GoogleCloud`) and loaded as
|
|
812
833
|
`ScreenKit::TTS::#{CamelizedName}`.
|
|
813
834
|
|
|
835
|
+
### 3rd-party TTS Engines
|
|
836
|
+
|
|
837
|
+
- [Search Github](https://github.com/topics/screenkit-tts)
|
|
838
|
+
- [Google Text to Speech](https://github.com/fnando/screenkit-tts-google)
|
|
839
|
+
- [Minimax Text to Speech](https://github.com/fnando/screenkit-tts-minimax)
|
|
840
|
+
|
|
841
|
+
> [!TIP]
|
|
842
|
+
>
|
|
843
|
+
> If you host your TTS engine on Github, use the topic `screekit-tts`, so other
|
|
844
|
+
> people can find it.
|
|
845
|
+
|
|
814
846
|
---
|
|
815
847
|
|
|
816
848
|
## Animations
|
|
@@ -897,6 +929,12 @@ Enter
|
|
|
897
929
|
Sleep 2s
|
|
898
930
|
```
|
|
899
931
|
|
|
932
|
+
When running tape files, the working directory will be the episode directory. If
|
|
933
|
+
you're importing anything from the parent directory, you must specify relative
|
|
934
|
+
paths accordingly. For instance, `episodes/001-episode-name/content/001.tape`
|
|
935
|
+
would need to reference `../../resources/some-file` to access
|
|
936
|
+
`resources/some-file` in the project's directory.
|
|
937
|
+
|
|
900
938
|
### Script Files
|
|
901
939
|
|
|
902
940
|
Plain text files for voiceover generation:
|
|
@@ -1099,26 +1137,26 @@ be processed).
|
|
|
1099
1137
|
|
|
1100
1138
|
### Common Issues
|
|
1101
1139
|
|
|
1102
|
-
|
|
1140
|
+
#### "Gem not found" error
|
|
1103
1141
|
|
|
1104
1142
|
```bash
|
|
1105
1143
|
bundle install
|
|
1106
1144
|
bundle exec screenkit ...
|
|
1107
1145
|
```
|
|
1108
1146
|
|
|
1109
|
-
|
|
1147
|
+
#### Schema validation failed
|
|
1110
1148
|
|
|
1111
1149
|
- Check YAML syntax
|
|
1112
1150
|
- Verify required fields are present
|
|
1113
1151
|
- Use schema hints with `yaml-language-server`
|
|
1114
1152
|
|
|
1115
|
-
|
|
1153
|
+
#### Missing resources
|
|
1116
1154
|
|
|
1117
1155
|
- Check `resources_dir` configuration
|
|
1118
1156
|
- Verify file paths are relative to resource directories
|
|
1119
1157
|
- Use absolute paths for system resources
|
|
1120
1158
|
|
|
1121
|
-
|
|
1159
|
+
#### TTS not working
|
|
1122
1160
|
|
|
1123
1161
|
- For ElevenLabs: Set `--tts-api-key`
|
|
1124
1162
|
- For macOS `say`: Verify voice name with `say -v ?`
|
data/action.yml
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ScreenKit Episode
|
|
3
|
+
description: Export a screencast episode using Docker
|
|
4
|
+
inputs:
|
|
5
|
+
episode:
|
|
6
|
+
description: "Episode number (e.g. 001)"
|
|
7
|
+
required: true
|
|
8
|
+
tts_preset:
|
|
9
|
+
description: "TTS preset name"
|
|
10
|
+
default: ""
|
|
11
|
+
required: false
|
|
12
|
+
tts_api_key:
|
|
13
|
+
description: "TTS API key"
|
|
14
|
+
required: false
|
|
15
|
+
overwrite:
|
|
16
|
+
description: "Regenerate files"
|
|
17
|
+
default: "false"
|
|
18
|
+
required: false
|
|
19
|
+
match:
|
|
20
|
+
description: "Match pattern for segments (e.g. 001)"
|
|
21
|
+
default: ""
|
|
22
|
+
required: false
|
|
23
|
+
github_token:
|
|
24
|
+
description: "GitHub token for cache operations"
|
|
25
|
+
required: true
|
|
26
|
+
retention:
|
|
27
|
+
description: "Retention days for artifacts"
|
|
28
|
+
default: "2"
|
|
29
|
+
|
|
30
|
+
runs:
|
|
31
|
+
using: composite
|
|
32
|
+
steps:
|
|
33
|
+
- name: Generate cache keys
|
|
34
|
+
shell: bash
|
|
35
|
+
run: |
|
|
36
|
+
VOICEOVER_HASH=$(find episodes/${{ inputs.episode }}-*/scripts/*.txt -type f 2>/dev/null | sort | xargs sha256sum 2>/dev/null | sha256sum | cut -d' ' -f1 || echo "none")
|
|
37
|
+
CONTENT_HASH=$(find episodes/${{ inputs.episode }}-*/*/*.* -type f 2>/dev/null | sort | xargs sha256sum 2>/dev/null | sha256sum | cut -d' ' -f1 || echo "none")
|
|
38
|
+
|
|
39
|
+
echo "VOICEOVER_CACHE_KEY=episode-voiceover-${{ inputs.episode }}-${VOICEOVER_HASH}" >> $GITHUB_ENV
|
|
40
|
+
echo "VOICEOVER_RESTORE_KEY=episode-voiceover-${{ inputs.episode }}-" >> $GITHUB_ENV
|
|
41
|
+
echo "VIDEO_CACHE_KEY=episode-${{ inputs.episode }}-${CONTENT_HASH}" >> $GITHUB_ENV
|
|
42
|
+
echo "VIDEO_RESTORE_KEY=episode-${{ inputs.episode }}-" >> $GITHUB_ENV
|
|
43
|
+
|
|
44
|
+
- name: Find episode dir
|
|
45
|
+
shell: bash
|
|
46
|
+
run: |
|
|
47
|
+
EPISODE_DIR=$(ls -1d episodes/${{ inputs.episode }}-* | head -n 1)
|
|
48
|
+
echo "EPISODE_DIR=$EPISODE_DIR" >> $GITHUB_ENV
|
|
49
|
+
|
|
50
|
+
if [[ ! -d "$EPISODE_DIR" ]]; then
|
|
51
|
+
echo "Episode directory not found!"
|
|
52
|
+
echo "Available episodes:"
|
|
53
|
+
ls -1 episodes
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
- name: Restore episode voice cache
|
|
58
|
+
id: cache-voice
|
|
59
|
+
uses: actions/cache/restore@v4
|
|
60
|
+
with:
|
|
61
|
+
path: episodes/${{ inputs.episode }}-*/voiceovers
|
|
62
|
+
key: ${{ env.VOICEOVER_CACHE_KEY }}
|
|
63
|
+
restore-keys: ${{ env.VOICEOVER_RESTORE_KEY }}
|
|
64
|
+
enableCrossOsArchive: true
|
|
65
|
+
|
|
66
|
+
- name: Restore videos cache
|
|
67
|
+
id: cache-videos
|
|
68
|
+
uses: actions/cache/restore@v4
|
|
69
|
+
with:
|
|
70
|
+
path: output/${{ inputs.episode }}-*/videos/*.mp4
|
|
71
|
+
key: ${{ env.VIDEO_CACHE_KEY }}
|
|
72
|
+
restore-keys: ${{ env.VIDEO_RESTORE_KEY }}
|
|
73
|
+
enableCrossOsArchive: true
|
|
74
|
+
|
|
75
|
+
- name: Export Episode
|
|
76
|
+
shell: bash
|
|
77
|
+
id: screenkit
|
|
78
|
+
run: |
|
|
79
|
+
set -e
|
|
80
|
+
docker run \
|
|
81
|
+
--rm \
|
|
82
|
+
--cap-add=SYS_ADMIN \
|
|
83
|
+
--shm-size=2g \
|
|
84
|
+
--security-opt seccomp=unconfined \
|
|
85
|
+
-v ${{ github.workspace }}:/source \
|
|
86
|
+
fnando/screenkit:latest \
|
|
87
|
+
episode export \
|
|
88
|
+
--dir="${{ env.EPISODE_DIR }}" \
|
|
89
|
+
${{ inputs.match != '' && format('--match {0}', inputs.match) || '' }} \
|
|
90
|
+
${{ inputs.overwrite == 'true' && '--overwrite' || '--no-overwrite' }} \
|
|
91
|
+
${{ inputs.tts_preset != '' && format('--tts-preset {0}', inputs.tts_preset) || '' }} \
|
|
92
|
+
${{ inputs.tts_api_key != '' && format('--tts-api-key {0}', inputs.tts_api_key) || '' }}
|
|
93
|
+
echo "result=true" >> $GITHUB_OUTPUT
|
|
94
|
+
|
|
95
|
+
- name: Delete voice cache
|
|
96
|
+
if: steps.screenkit.outputs.result == 'true'
|
|
97
|
+
shell: bash
|
|
98
|
+
run: gh cache delete "${{ env.VOICEOVER_CACHE_KEY }}" || true
|
|
99
|
+
env:
|
|
100
|
+
GH_TOKEN: ${{ inputs.github_token }}
|
|
101
|
+
|
|
102
|
+
- name: Delete videos cache
|
|
103
|
+
if: steps.screenkit.outputs.result == 'true'
|
|
104
|
+
shell: bash
|
|
105
|
+
run: gh cache delete "${{ env.VIDEO_CACHE_KEY }}" || true
|
|
106
|
+
env:
|
|
107
|
+
GH_TOKEN: ${{ inputs.github_token }}
|
|
108
|
+
|
|
109
|
+
- name: List cache entries
|
|
110
|
+
shell: bash
|
|
111
|
+
run: |
|
|
112
|
+
echo "= Voiceover Cache ="
|
|
113
|
+
ls -la episodes/${{ inputs.episode }}-*/voiceovers || true
|
|
114
|
+
echo
|
|
115
|
+
echo "= Videos Cache ="
|
|
116
|
+
ls -la output/${{ inputs.episode }}-*/videos || true
|
|
117
|
+
|
|
118
|
+
- name: Save episode voiceover cache
|
|
119
|
+
if: steps.screenkit.outputs.result == 'true'
|
|
120
|
+
uses: actions/cache/save@v4
|
|
121
|
+
with:
|
|
122
|
+
path: episodes/${{ inputs.episode }}-*/voiceovers
|
|
123
|
+
key: ${{ env.VOICEOVER_CACHE_KEY }}
|
|
124
|
+
enableCrossOsArchive: true
|
|
125
|
+
|
|
126
|
+
- name: Save video recordings cache
|
|
127
|
+
if: steps.screenkit.outputs.result == 'true'
|
|
128
|
+
uses: actions/cache/save@v4
|
|
129
|
+
with:
|
|
130
|
+
path: output/${{ inputs.episode }}-*/videos/*.mp4
|
|
131
|
+
key: ${{ env.VIDEO_CACHE_KEY }}
|
|
132
|
+
enableCrossOsArchive: true
|
|
133
|
+
|
|
134
|
+
- name: Upload output artifacts
|
|
135
|
+
uses: actions/upload-artifact@v5
|
|
136
|
+
if: always()
|
|
137
|
+
with:
|
|
138
|
+
name: output
|
|
139
|
+
path: output
|
|
140
|
+
retention-days: ${{ inputs.retention }}
|
|
@@ -4,6 +4,8 @@ module ScreenKit
|
|
|
4
4
|
class Callout
|
|
5
5
|
module Styles
|
|
6
6
|
class Base
|
|
7
|
+
require_relative "../../schema_validator"
|
|
8
|
+
|
|
7
9
|
attr_reader :source, :output_path, :log_path
|
|
8
10
|
attr_accessor :options
|
|
9
11
|
|
|
@@ -16,6 +18,11 @@ module ScreenKit
|
|
|
16
18
|
@options = options
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
# Wrap text to fit within the specified maximum width.
|
|
22
|
+
# @param text [String] The text to wrap.
|
|
23
|
+
# @param max_width [Integer] The maximum width in pixels.
|
|
24
|
+
# @param font_size [Integer] The font size in points.
|
|
25
|
+
# @return [Array<String>] The wrapped lines of text.
|
|
19
26
|
def text_wrap(text, max_width:, font_size:)
|
|
20
27
|
words = text.to_s.split(/\s+/)
|
|
21
28
|
width_factor = 0.6
|
|
@@ -42,10 +49,19 @@ module ScreenKit
|
|
|
42
49
|
text.gsub("'", "\\\\'")
|
|
43
50
|
end
|
|
44
51
|
|
|
52
|
+
# Remove a file if it exists.
|
|
53
|
+
# @param path [String] The file path to remove.
|
|
45
54
|
def remove_file(path)
|
|
46
55
|
File.unlink(path) if path && File.exist?(path)
|
|
47
56
|
end
|
|
48
57
|
|
|
58
|
+
# Render text into an image using MiniMagick.
|
|
59
|
+
# @param text [String] The text to render.
|
|
60
|
+
# @param style [TextStyle] The text style to apply.
|
|
61
|
+
# @param width [Integer] The width of the text image.
|
|
62
|
+
# @param type [String] The ImageMagick text type (e.g., "caption").
|
|
63
|
+
# @return [Array] The path to the generated text image, width, and
|
|
64
|
+
# height.
|
|
49
65
|
def render_text_image(text:, style:, width:, type:)
|
|
50
66
|
return [nil, 0, 0] if text.to_s.empty?
|
|
51
67
|
|
|
@@ -61,9 +61,13 @@ module ScreenKit
|
|
|
61
61
|
def export
|
|
62
62
|
puts Banner.banner if options.banner
|
|
63
63
|
|
|
64
|
-
episode_config =
|
|
65
|
-
|
|
66
|
-
)
|
|
64
|
+
episode_config = File.join(options.dir, "config.yml")
|
|
65
|
+
|
|
66
|
+
episode_config = if File.file?(episode_config)
|
|
67
|
+
Config::Episode.load_file(episode_config)
|
|
68
|
+
else
|
|
69
|
+
{}
|
|
70
|
+
end
|
|
67
71
|
|
|
68
72
|
options.require.each { require(it) }
|
|
69
73
|
|
|
@@ -4,7 +4,7 @@ module ScreenKit
|
|
|
4
4
|
module ContentType
|
|
5
5
|
def self.video = %w[mp4 webm mov]
|
|
6
6
|
def self.audio = %w[mp3 wav m4a aac aiff]
|
|
7
|
-
def self.image = %w[gif jpg jpeg png]
|
|
7
|
+
def self.image = %w[gif jpg jpeg png tiff]
|
|
8
8
|
def self.demotape = %w[tape]
|
|
9
9
|
|
|
10
10
|
def self.all = video + image + demotape
|
|
@@ -7,6 +7,8 @@ module ScreenKit
|
|
|
7
7
|
unicode_normalize(:nfkd)
|
|
8
8
|
.delete("'")
|
|
9
9
|
.gsub(/[^\x00-\x7F]/, "")
|
|
10
|
+
.gsub(/([a-z\d])([A-Z])/, '\1-\2')
|
|
11
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1-\2')
|
|
10
12
|
.gsub(/[^-\w]+/xim, "-")
|
|
11
13
|
.tr("_", "-")
|
|
12
14
|
.gsub(/-+/xm, "-")
|
|
@@ -14,6 +16,10 @@ module ScreenKit
|
|
|
14
16
|
.downcase
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
def underscore
|
|
20
|
+
dasherize.tr("-", "_")
|
|
21
|
+
end
|
|
22
|
+
|
|
17
23
|
def camelize(first_letter = :upper)
|
|
18
24
|
split(/_|-/).map.with_index do |part, index|
|
|
19
25
|
if index.zero? && first_letter == :lower
|
|
@@ -46,18 +46,11 @@ module ScreenKit
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def tts_engine
|
|
49
|
-
|
|
50
|
-
if options.tts_preset
|
|
51
|
-
tts_engines.find do |engine|
|
|
52
|
-
engine.id == options.tts_preset && engine.available?
|
|
53
|
-
end
|
|
54
|
-
else
|
|
55
|
-
tts_engines.find(&:available?)
|
|
56
|
-
end
|
|
49
|
+
tts_engines.first
|
|
57
50
|
end
|
|
58
51
|
|
|
59
|
-
def
|
|
60
|
-
@
|
|
52
|
+
def tts_config
|
|
53
|
+
@tts_config ||= begin
|
|
61
54
|
project_tts = if project_config.tts.is_a?(Hash)
|
|
62
55
|
[project_config.tts]
|
|
63
56
|
else
|
|
@@ -70,10 +63,23 @@ module ScreenKit
|
|
|
70
63
|
Array(config.tts)
|
|
71
64
|
end
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
episode_tts + project_tts
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def tts_engines
|
|
71
|
+
@tts_engines ||= tts_config.filter_map do |opts|
|
|
72
|
+
next unless opts[:enabled]
|
|
73
|
+
|
|
74
|
+
api_key = options.tts_api_key
|
|
75
|
+
tts_class = TTS.const_get(opts[:engine].camelize)
|
|
76
|
+
tts_preset = options.tts_preset.to_s
|
|
77
|
+
|
|
78
|
+
next if !tts_preset.empty? && tts_preset != opts[:id]
|
|
79
|
+
next unless tts_class.available?(api_key:, **opts)
|
|
80
|
+
|
|
81
|
+
opts = opts.except(:engine, :enabled)
|
|
82
|
+
tts_class.new(**opts, api_key:, segments:)
|
|
77
83
|
end
|
|
78
84
|
end
|
|
79
85
|
|
|
@@ -177,6 +177,10 @@ module ScreenKit
|
|
|
177
177
|
log_path:
|
|
178
178
|
end
|
|
179
179
|
|
|
180
|
+
def script_content
|
|
181
|
+
@script_content ||= script_path.read if script_path.file?
|
|
182
|
+
end
|
|
183
|
+
|
|
180
184
|
def create_voiceover(log_path:)
|
|
181
185
|
return if voiceover_path&.file? && !episode.options.overwrite
|
|
182
186
|
return unless script_path.file?
|
|
@@ -185,7 +189,7 @@ module ScreenKit
|
|
|
185
189
|
FileUtils.mkdir_p(voiceover_path.dirname)
|
|
186
190
|
|
|
187
191
|
episode.tts_engine.generate(
|
|
188
|
-
text:
|
|
192
|
+
text: script_content,
|
|
189
193
|
output_path: voiceover_path,
|
|
190
194
|
log_path:
|
|
191
195
|
)
|
|
@@ -9,8 +9,12 @@ module ScreenKit
|
|
|
9
9
|
# The path to the input video file.
|
|
10
10
|
attr_reader :input_path
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# The path to the log file.
|
|
13
|
+
attr_reader :log_path
|
|
14
|
+
|
|
15
|
+
def initialize(input_path:, log_path: nil)
|
|
13
16
|
@input_path = input_path
|
|
17
|
+
@log_path = log_path
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def export(output_path)
|
|
@@ -26,7 +30,8 @@ module ScreenKit
|
|
|
26
30
|
"-r", "24",
|
|
27
31
|
"-c:v", "libx264",
|
|
28
32
|
"-y",
|
|
29
|
-
output_path
|
|
33
|
+
output_path,
|
|
34
|
+
log_path:
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
37
|
end
|
|
@@ -1,106 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
# yaml-language-server: $schema=https://screenkit.dev/schemas/episode.json
|
|
3
3
|
|
|
4
|
-
# The episode title
|
|
5
|
-
# on
|
|
6
|
-
title:
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# episode configuration can define the content and timing.
|
|
13
|
-
callouts_styles:
|
|
14
|
-
- type: info
|
|
15
|
-
title: "ScreenKit"
|
|
16
|
-
body: "Visit https://github.com/fnando/screenkit to learn more."
|
|
17
|
-
starts_at: 3
|
|
18
|
-
duration: 5
|
|
19
|
-
width: 600
|
|
20
|
-
|
|
21
|
-
# The background music to be played during the episode.
|
|
22
|
-
# When not set, the backtrack will be defined by using the project's
|
|
23
|
-
# `backtrack_dir` configuration, and a track will be randomly selected.
|
|
24
|
-
# To disable the backtrack entirely, use `backtrack: false`.
|
|
25
|
-
# backtrack: resources/backtrack.mp3
|
|
26
|
-
|
|
27
|
-
# Define episode-specific scene configurations.
|
|
28
|
-
# When present, this will have higher precedence over the project's
|
|
29
|
-
# configuration, so you can have different intro/outro for each episode.
|
|
30
|
-
# scenes:
|
|
31
|
-
# intro:
|
|
32
|
-
# # The duration of the intro scene in seconds.
|
|
33
|
-
# duration: 5.5
|
|
34
|
-
#
|
|
35
|
-
# # The fade-in duration in seconds.
|
|
36
|
-
# fade_in: 0
|
|
37
|
-
#
|
|
38
|
-
# # The fade-out duration in seconds.
|
|
39
|
-
# fade_out: 0.5
|
|
40
|
-
#
|
|
41
|
-
# # The background color/image of the intro scene.
|
|
42
|
-
# background: "#100f50"
|
|
43
|
-
#
|
|
44
|
-
# # The title text to be displayed in the intro scene.
|
|
45
|
-
# title:
|
|
46
|
-
# x: 100
|
|
47
|
-
# y: 300
|
|
48
|
-
# font_path: open-sans/OpenSans-ExtraBold.ttf
|
|
49
|
-
# size: 144
|
|
50
|
-
# color: "#ffffff"
|
|
51
|
-
#
|
|
52
|
-
# # The logo to be displayed in the video intro.
|
|
53
|
-
# # Works best with a transparent PNG file in high resolution (at least 2x the
|
|
54
|
-
# # size it'll be displayed).
|
|
55
|
-
# logo:
|
|
56
|
-
# path: images/logo.png
|
|
57
|
-
# x: 100
|
|
58
|
-
# y: 200
|
|
59
|
-
# width: 200
|
|
60
|
-
# height: 34
|
|
61
|
-
#
|
|
62
|
-
# # The sound to be played along with the logo in the intro.
|
|
63
|
-
# # This must correspond to a sound file in the sounds directory, with any
|
|
64
|
-
# # audio extension (e.g. mp3, m4a, wav).
|
|
65
|
-
# sound: sounds/chime.mp3
|
|
66
|
-
#
|
|
67
|
-
# outro:
|
|
68
|
-
# # The duration of the outro scene in seconds.
|
|
69
|
-
# duration: 3.5
|
|
70
|
-
#
|
|
71
|
-
# # The fade-in duration in seconds.
|
|
72
|
-
# fade_in: 0.5
|
|
73
|
-
#
|
|
74
|
-
# # The fade-out duration in seconds.
|
|
75
|
-
# fade_out: 0.5
|
|
76
|
-
#
|
|
77
|
-
# # The background color/image of the outro scene.
|
|
78
|
-
# background: "#100f50"
|
|
79
|
-
#
|
|
80
|
-
# # The sound to be played along with the logo in the outro.
|
|
81
|
-
# sound: sounds/chime.mp3
|
|
82
|
-
#
|
|
83
|
-
# # The logo to be displayed in the video outro.
|
|
84
|
-
# logo:
|
|
85
|
-
# path: images/logo.png
|
|
86
|
-
# x: center
|
|
87
|
-
# y: center
|
|
88
|
-
# width: 500
|
|
89
|
-
#
|
|
90
|
-
# segment:
|
|
91
|
-
# # The duration of the crossover transition between segments in seconds.
|
|
92
|
-
# crossfade_duration: 0.5
|
|
93
|
-
|
|
94
|
-
# Define episode-specific voice configuration.
|
|
95
|
-
# When present, this will have higher precedence over the project's
|
|
96
|
-
# configuration, so you can have different intro/outro segments for each
|
|
97
|
-
# episode.
|
|
98
|
-
# tts:
|
|
99
|
-
# engine: elevenlabs
|
|
100
|
-
# voice_id: 56AoDkrOh6qfVPDXZ7Pt
|
|
101
|
-
# language_code: en
|
|
102
|
-
# voice_settings:
|
|
103
|
-
# speed: 0.9
|
|
104
|
-
# stability: 0.5
|
|
105
|
-
# similarity: 0.75
|
|
106
|
-
# style: 0.0
|
|
4
|
+
# The episode title (displayed in intro scene).
|
|
5
|
+
# Line breaks are inferred based on approximate text width.
|
|
6
|
+
title: <%= options.title.to_yaml[4..] %>
|
|
7
|
+
|
|
8
|
+
# Episode-specific overrides for global configuration.
|
|
9
|
+
# Use an editor with JSON Schema support to see available options and
|
|
10
|
+
# autocomplete.
|
|
11
|
+
# Schema: https://screenkit.dev/schemas/episode.json
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Screenkit
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
inputs:
|
|
7
|
+
episode:
|
|
8
|
+
type: string
|
|
9
|
+
description: "Episode number (e.g. 001)"
|
|
10
|
+
required: true
|
|
11
|
+
tts_preset:
|
|
12
|
+
description: "TTS preset name"
|
|
13
|
+
default: ""
|
|
14
|
+
type: choice
|
|
15
|
+
options:
|
|
16
|
+
- ""
|
|
17
|
+
- say
|
|
18
|
+
- espeak
|
|
19
|
+
- eleven_labs
|
|
20
|
+
- eleven_labs_mp3_192k
|
|
21
|
+
match:
|
|
22
|
+
type: string
|
|
23
|
+
description: "Match pattern for segments (e.g. 001)"
|
|
24
|
+
default: ""
|
|
25
|
+
overwrite:
|
|
26
|
+
type: boolean
|
|
27
|
+
description: "Regenerate files"
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
screenkit:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
actions: write
|
|
35
|
+
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout repository
|
|
38
|
+
uses: actions/checkout@v3
|
|
39
|
+
|
|
40
|
+
- name: Run Screenkit
|
|
41
|
+
uses: fnando/screenkit@v1
|
|
42
|
+
with:
|
|
43
|
+
episode: ${{ github.event.inputs.episode }}
|
|
44
|
+
tts_preset: ${{ github.event.inputs.tts_preset }}
|
|
45
|
+
tts_api_key: ${{ secrets.TTS_API_KEY }}
|
|
46
|
+
overwrite: ${{ github.event.inputs.overwrite }}
|
|
47
|
+
match: ${{ github.event.inputs.match }}
|
|
48
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -22,6 +22,7 @@ module ScreenKit
|
|
|
22
22
|
def copy_files
|
|
23
23
|
copy_file "screenkit.yml"
|
|
24
24
|
directory "resources", exclude_pattern: /DS_Store/
|
|
25
|
+
directory ".github", exclude_pattern: /DS_Store/
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def bundle_install
|
|
@@ -32,6 +33,12 @@ module ScreenKit
|
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def instructions
|
|
37
|
+
cmd = set_color("screenkit episode new --title TITLE", :blue)
|
|
38
|
+
path = set_color(destination_root, :blue)
|
|
39
|
+
say "\nTo create a new episode, run #{cmd} from #{path}"
|
|
40
|
+
end
|
|
41
|
+
|
|
35
42
|
no_commands do
|
|
36
43
|
# Add helper methods here
|
|
37
44
|
end
|
|
@@ -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
|
data/lib/screenkit/shell.rb
CHANGED
|
@@ -20,9 +20,9 @@ module ScreenKit
|
|
|
20
20
|
!`which #{command}`.strip.empty?
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def run_command(command, *args, log_path: nil)
|
|
23
|
+
def run_command(command, *args, log_path: nil, **)
|
|
24
24
|
args = args.flatten.compact.map(&:to_s)
|
|
25
|
-
stdout, stderr, status = Open3.capture3(command, *args)
|
|
25
|
+
stdout, stderr, status = Open3.capture3(command, *args, **)
|
|
26
26
|
exit_code = status.exitstatus
|
|
27
27
|
|
|
28
28
|
if exit_code&.nonzero?
|
data/lib/screenkit/tts/base.rb
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
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
|
|
10
|
+
require_relative "../schema_validator"
|
|
11
|
+
require_relative "../shell"
|
|
12
|
+
require_relative "../http"
|
|
13
|
+
require_relative "../core_ext"
|
|
14
|
+
require_relative "../version"
|
|
15
|
+
|
|
6
16
|
extend SchemaValidator
|
|
17
|
+
extend Shell
|
|
18
|
+
include HTTP
|
|
19
|
+
|
|
20
|
+
using CoreExt
|
|
7
21
|
|
|
8
22
|
# Additional options for the tts engine.
|
|
9
23
|
attr_reader :options
|
|
@@ -11,14 +25,35 @@ module ScreenKit
|
|
|
11
25
|
# The preset name for the tts engine.
|
|
12
26
|
attr_reader :id
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
# The list of segments.
|
|
29
|
+
# This is available so that engines can contextually generate audio, for
|
|
30
|
+
# instance, by providing previous/next text (e.g. Eleven Labs).
|
|
31
|
+
attr_reader :segments
|
|
32
|
+
|
|
33
|
+
# The API key for the tts engine, if applicable.
|
|
34
|
+
attr_reader :api_key
|
|
35
|
+
|
|
36
|
+
# Detects if the tts engine is available.
|
|
37
|
+
def self.available?(**)
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.engine_name
|
|
42
|
+
name.split("::").last.underscore
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.api_key_prefix
|
|
46
|
+
"#{engine_name}:"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize(id: nil, segments: nil, api_key: nil, **options)
|
|
50
|
+
@segments = Array(segments)
|
|
16
51
|
@options = options
|
|
17
52
|
@id = id
|
|
18
|
-
end
|
|
19
53
|
|
|
20
|
-
|
|
21
|
-
|
|
54
|
+
return unless api_key
|
|
55
|
+
|
|
56
|
+
@api_key = api_key.delete_prefix("#{self.class.engine_name}:")
|
|
22
57
|
end
|
|
23
58
|
end
|
|
24
59
|
end
|
|
@@ -3,44 +3,40 @@
|
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def initialize(api_key:, **)
|
|
15
|
-
super(**)
|
|
16
|
-
@api_key = api_key
|
|
13
|
+
def self.available?(api_key: nil, **)
|
|
14
|
+
api_key.to_s.start_with?(api_key_prefix)
|
|
17
15
|
end
|
|
18
16
|
|
|
19
|
-
def
|
|
20
|
-
|
|
17
|
+
def all_texts
|
|
18
|
+
@all_texts ||= segments.map(&:script_content)
|
|
21
19
|
end
|
|
22
20
|
|
|
23
21
|
def generate(output_path:, text:, log_path: nil)
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
voice_id = options[:voice_id]
|
|
23
|
+
|
|
24
|
+
current_index = all_texts.index { it == text }
|
|
26
25
|
|
|
27
|
-
if
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
26
|
+
if current_index
|
|
27
|
+
previous_text = all_texts[current_index - 1]
|
|
28
|
+
next_text = all_texts[current_index + 1]
|
|
31
29
|
end
|
|
32
30
|
|
|
33
|
-
|
|
31
|
+
params = options.merge(text:, previous_text:, next_text:)
|
|
32
|
+
.except(:voice_id)
|
|
34
33
|
|
|
35
|
-
response =
|
|
34
|
+
response = json_post(
|
|
36
35
|
url: "https://api.elevenlabs.io/v1/text-to-speech/#{voice_id}",
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"user-agent": "ScreenKit/#{ScreenKit::VERSION}",
|
|
42
|
-
"xi-api-key": api_key
|
|
43
|
-
}
|
|
36
|
+
params:,
|
|
37
|
+
headers: {"xi-api-key": api_key},
|
|
38
|
+
api_key:,
|
|
39
|
+
log_path:
|
|
44
40
|
)
|
|
45
41
|
|
|
46
42
|
File.binwrite(output_path, response.body)
|
data/lib/screenkit/tts/espeak.rb
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
module TTS
|
|
5
5
|
class Espeak < Base
|
|
6
|
-
|
|
6
|
+
extend Shell
|
|
7
7
|
|
|
8
|
-
def self.
|
|
9
|
-
|
|
8
|
+
def self.available?(**)
|
|
9
|
+
command_exist?("espeak")
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
|
|
12
|
+
def self.schema_path
|
|
13
|
+
ScreenKit.root_dir.join("screenkit/schemas/tts/espeak.json")
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def generate(text:, output_path:, log_path: nil)
|
|
@@ -18,12 +18,12 @@ module ScreenKit
|
|
|
18
18
|
|
|
19
19
|
{voice: nil, rate: nil}.merge(options) => {voice:, rate:}
|
|
20
20
|
|
|
21
|
-
run_command "espeak",
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
self.class.run_command "espeak",
|
|
22
|
+
(["-v", voice] if voice),
|
|
23
|
+
(["-s", rate] if rate),
|
|
24
|
+
"-w", output_path.sub_ext(".wav"),
|
|
25
|
+
text,
|
|
26
|
+
log_path:
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/screenkit/tts/say.rb
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
module TTS
|
|
5
5
|
class Say < Base
|
|
6
|
-
|
|
6
|
+
extend Shell
|
|
7
7
|
|
|
8
8
|
def self.schema_path
|
|
9
9
|
ScreenKit.root_dir.join("screenkit/schemas/tts/say.json")
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def available?
|
|
13
|
-
|
|
12
|
+
def self.available?(**)
|
|
13
|
+
command_exist?("say")
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def generate(text:, output_path:, log_path: nil)
|
|
@@ -18,12 +18,12 @@ module ScreenKit
|
|
|
18
18
|
|
|
19
19
|
{voice: nil, rate: nil}.merge(options) => {voice:, rate:}
|
|
20
20
|
|
|
21
|
-
run_command "say",
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
self.class.run_command "say",
|
|
22
|
+
(["-v", voice] if voice),
|
|
23
|
+
(["-r", rate] if rate),
|
|
24
|
+
"-o", output_path.sub_ext(".aiff"),
|
|
25
|
+
text,
|
|
26
|
+
log_path:
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/screenkit/version.rb
CHANGED
data/lib/screenkit.rb
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "thor"
|
|
4
|
+
require "thor/completion"
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "json-schema"
|
|
7
|
+
require "mini_magick"
|
|
8
|
+
require "pathname"
|
|
9
|
+
require "tty-spinner"
|
|
10
|
+
require "etc"
|
|
11
|
+
require "securerandom"
|
|
12
|
+
require "demo_tape/duration"
|
|
13
|
+
require "aitch"
|
|
14
|
+
|
|
15
|
+
module ScreenKit
|
|
16
|
+
require_relative "screenkit/version"
|
|
17
|
+
require_relative "screenkit/duration"
|
|
18
|
+
require_relative "screenkit/core_ext"
|
|
19
|
+
require_relative "screenkit/content_type"
|
|
20
|
+
require_relative "screenkit/anchor"
|
|
21
|
+
require_relative "screenkit/banner"
|
|
22
|
+
require_relative "screenkit/time_formatter"
|
|
23
|
+
require_relative "screenkit/spacing"
|
|
24
|
+
require_relative "screenkit/watermark"
|
|
25
|
+
require_relative "screenkit/spinner"
|
|
26
|
+
require_relative "screenkit/shell"
|
|
27
|
+
require_relative "screenkit/http"
|
|
28
|
+
require_relative "screenkit/schema_validator"
|
|
29
|
+
require_relative "screenkit/generators/project"
|
|
30
|
+
require_relative "screenkit/generators/episode"
|
|
31
|
+
require_relative "screenkit/config/base"
|
|
32
|
+
require_relative "screenkit/config/project"
|
|
33
|
+
require_relative "screenkit/config/episode"
|
|
34
|
+
require_relative "screenkit/callout"
|
|
35
|
+
require_relative "screenkit/callout/text_style"
|
|
36
|
+
require_relative "screenkit/callout/styles/base"
|
|
37
|
+
require_relative "screenkit/transition"
|
|
38
|
+
require_relative "screenkit/parallel_processor"
|
|
39
|
+
require_relative "screenkit/cli"
|
|
40
|
+
require_relative "screenkit/cli/base"
|
|
41
|
+
require_relative "screenkit/cli/episode"
|
|
42
|
+
require_relative "screenkit/cli/root"
|
|
43
|
+
require_relative "screenkit/animation_filters"
|
|
44
|
+
require_relative "screenkit/tts/base"
|
|
45
|
+
require_relative "screenkit/path_lookup"
|
|
46
|
+
require_relative "screenkit/sound"
|
|
47
|
+
require_relative "screenkit/utils"
|
|
48
|
+
require_relative "screenkit/logfile"
|
|
49
|
+
require_relative "screenkit/exporter/intro"
|
|
50
|
+
require_relative "screenkit/exporter/outro"
|
|
51
|
+
require_relative "screenkit/exporter/demotape"
|
|
52
|
+
require_relative "screenkit/exporter/episode"
|
|
53
|
+
require_relative "screenkit/exporter/segment"
|
|
54
|
+
require_relative "screenkit/exporter/image"
|
|
55
|
+
require_relative "screenkit/exporter/video"
|
|
56
|
+
|
|
57
|
+
require_files = lambda do |pattern|
|
|
58
|
+
Gem.find_files_from_load_path(pattern).each do |path|
|
|
59
|
+
next if path.include?("test")
|
|
60
|
+
|
|
61
|
+
require(path)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Load all files that may be available as plugins.
|
|
66
|
+
require_files.call("screenkit/callout/styles/*.rb")
|
|
67
|
+
require_files.call("screenkit/tts/*.rb")
|
|
68
|
+
|
|
69
|
+
def self.root_dir
|
|
70
|
+
@root_dir ||= Pathname(__dir__)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Raised when the configuration schema is invalid.
|
|
74
|
+
InvalidConfigSchemaError = Class.new(StandardError)
|
|
75
|
+
|
|
76
|
+
# Raised when a file is not found.
|
|
77
|
+
FileNotFoundError = Class.new(StandardError)
|
|
78
|
+
|
|
79
|
+
# Raised when a file entry is not found in the lookup.
|
|
80
|
+
FileEntryNotFoundError = Class.new(StandardError)
|
|
81
|
+
end
|
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.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nando Vieira
|
|
@@ -276,10 +276,10 @@ files:
|
|
|
276
276
|
- LICENSE.md
|
|
277
277
|
- README.md
|
|
278
278
|
- Rakefile
|
|
279
|
+
- action.yml
|
|
279
280
|
- bin/console
|
|
280
281
|
- bin/setup
|
|
281
282
|
- exe/screenkit
|
|
282
|
-
- lib/screen_kit.rb
|
|
283
283
|
- lib/screenkit.rb
|
|
284
284
|
- lib/screenkit/anchor.rb
|
|
285
285
|
- lib/screenkit/animation_filters.rb
|
|
@@ -298,6 +298,7 @@ files:
|
|
|
298
298
|
- lib/screenkit/config/episode.rb
|
|
299
299
|
- lib/screenkit/config/project.rb
|
|
300
300
|
- lib/screenkit/content_type.rb
|
|
301
|
+
- lib/screenkit/core_ext.rb
|
|
301
302
|
- lib/screenkit/core_ext/json.rb
|
|
302
303
|
- lib/screenkit/core_ext/string.rb
|
|
303
304
|
- lib/screenkit/duration.rb
|
|
@@ -314,6 +315,7 @@ files:
|
|
|
314
315
|
- lib/screenkit/generators/episode/content/001.tape
|
|
315
316
|
- lib/screenkit/generators/episode/scripts/001.txt
|
|
316
317
|
- lib/screenkit/generators/project.rb
|
|
318
|
+
- lib/screenkit/generators/project/.github/screenkit.yml
|
|
317
319
|
- lib/screenkit/generators/project/Gemfile.erb
|
|
318
320
|
- lib/screenkit/generators/project/resources/backtracks/default.aac
|
|
319
321
|
- lib/screenkit/generators/project/resources/fonts/open-sans/OFL.txt
|
|
@@ -326,6 +328,7 @@ files:
|
|
|
326
328
|
- lib/screenkit/generators/project/resources/sounds/pop.mp3
|
|
327
329
|
- lib/screenkit/generators/project/resources/sounds/whoosh.mp3
|
|
328
330
|
- lib/screenkit/generators/project/screenkit.yml
|
|
331
|
+
- lib/screenkit/http.rb
|
|
329
332
|
- lib/screenkit/logfile.rb
|
|
330
333
|
- lib/screenkit/parallel_processor.rb
|
|
331
334
|
- lib/screenkit/path_lookup.rb
|
|
@@ -385,10 +388,10 @@ metadata:
|
|
|
385
388
|
rubygems_mfa_required: 'true'
|
|
386
389
|
homepage_uri: https://github.com/fnando/screenkit
|
|
387
390
|
bug_tracker_uri: https://github.com/fnando/screenkit/issues
|
|
388
|
-
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
389
|
-
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
390
|
-
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
391
|
-
license_uri: https://github.com/fnando/screenkit/tree/v0.0.
|
|
391
|
+
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.8
|
|
392
|
+
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.8/CHANGELOG.md
|
|
393
|
+
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.8/README.md
|
|
394
|
+
license_uri: https://github.com/fnando/screenkit/tree/v0.0.8/LICENSE.md
|
|
392
395
|
rdoc_options: []
|
|
393
396
|
require_paths:
|
|
394
397
|
- lib
|
data/lib/screen_kit.rb
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "thor"
|
|
4
|
-
require "thor/completion"
|
|
5
|
-
require "yaml"
|
|
6
|
-
require "json-schema"
|
|
7
|
-
require "mini_magick"
|
|
8
|
-
require "pathname"
|
|
9
|
-
require "tty-spinner"
|
|
10
|
-
require "etc"
|
|
11
|
-
require "securerandom"
|
|
12
|
-
require "demo_tape/duration"
|
|
13
|
-
|
|
14
|
-
module ScreenKit
|
|
15
|
-
require_relative "screenkit/version"
|
|
16
|
-
require_relative "screenkit/duration"
|
|
17
|
-
require_relative "screenkit/core_ext/string"
|
|
18
|
-
require_relative "screenkit/core_ext/json"
|
|
19
|
-
require_relative "screenkit/content_type"
|
|
20
|
-
require_relative "screenkit/anchor"
|
|
21
|
-
require_relative "screenkit/banner"
|
|
22
|
-
require_relative "screenkit/time_formatter"
|
|
23
|
-
require_relative "screenkit/spacing"
|
|
24
|
-
require_relative "screenkit/watermark"
|
|
25
|
-
require_relative "screenkit/spinner"
|
|
26
|
-
require_relative "screenkit/shell"
|
|
27
|
-
require_relative "screenkit/schema_validator"
|
|
28
|
-
require_relative "screenkit/generators/project"
|
|
29
|
-
require_relative "screenkit/generators/episode"
|
|
30
|
-
require_relative "screenkit/config/base"
|
|
31
|
-
require_relative "screenkit/config/project"
|
|
32
|
-
require_relative "screenkit/config/episode"
|
|
33
|
-
require_relative "screenkit/callout"
|
|
34
|
-
require_relative "screenkit/callout/text_style"
|
|
35
|
-
require_relative "screenkit/callout/styles/base"
|
|
36
|
-
require_relative "screenkit/transition"
|
|
37
|
-
require_relative "screenkit/parallel_processor"
|
|
38
|
-
require_relative "screenkit/cli"
|
|
39
|
-
require_relative "screenkit/cli/base"
|
|
40
|
-
require_relative "screenkit/cli/episode"
|
|
41
|
-
require_relative "screenkit/cli/root"
|
|
42
|
-
require_relative "screenkit/animation_filters"
|
|
43
|
-
require_relative "screenkit/tts/base"
|
|
44
|
-
require_relative "screenkit/path_lookup"
|
|
45
|
-
require_relative "screenkit/sound"
|
|
46
|
-
require_relative "screenkit/utils"
|
|
47
|
-
require_relative "screenkit/logfile"
|
|
48
|
-
require_relative "screenkit/exporter/intro"
|
|
49
|
-
require_relative "screenkit/exporter/outro"
|
|
50
|
-
require_relative "screenkit/exporter/demotape"
|
|
51
|
-
require_relative "screenkit/exporter/episode"
|
|
52
|
-
require_relative "screenkit/exporter/segment"
|
|
53
|
-
require_relative "screenkit/exporter/image"
|
|
54
|
-
require_relative "screenkit/exporter/video"
|
|
55
|
-
|
|
56
|
-
require_files = lambda do |pattern|
|
|
57
|
-
Gem.find_files_from_load_path(pattern).each do |path|
|
|
58
|
-
next if path.include?("test")
|
|
59
|
-
|
|
60
|
-
require(path)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Load all files that may be available as plugins.
|
|
65
|
-
require_files.call("screenkit/callout/styles/*.rb")
|
|
66
|
-
require_files.call("screenkit/tts/*.rb")
|
|
67
|
-
|
|
68
|
-
def self.root_dir
|
|
69
|
-
@root_dir ||= Pathname(__dir__)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Raised when the configuration schema is invalid.
|
|
73
|
-
InvalidConfigSchemaError = Class.new(StandardError)
|
|
74
|
-
|
|
75
|
-
# Raised when a file is not found.
|
|
76
|
-
FileNotFoundError = Class.new(StandardError)
|
|
77
|
-
|
|
78
|
-
# Raised when a file entry is not found in the lookup.
|
|
79
|
-
FileEntryNotFoundError = Class.new(StandardError)
|
|
80
|
-
end
|