screenkit 0.0.6 → 0.0.7
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 +4 -0
- data/DOCUMENTATION.md +29 -7
- 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/shell.rb +2 -2
- data/lib/screenkit/tts/base.rb +30 -4
- data/lib/screenkit/tts/eleven_labs.rb +22 -11
- 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 +77 -1
- metadata +8 -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: 3dc4a6ebfa5fb6725aa40e4bbe699bcafca860135ef8cca01fc48ab66731392f
|
|
4
|
+
data.tar.gz: 42df45d24c11f800e20fff4dfc030102bef5747f758ee47378d98a56dd8732f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c0eac47f990100559123217b0a54f1ae84629ea6ff7039c38d6212d72284cce07b10552f92b6504588ab74a013bb3242d8bc19fe66a7a06b0cb3a8014822248b
|
|
7
|
+
data.tar.gz: 5c43aae473d552c8f0d233c0ab19b8ca1a1eab91b26225b7533c53eb4caeb44e5909cbc5e20c04b66bad939acee49219d0a75f61edb1617e00e209e498546567
|
|
@@ -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,10 @@ Prefix your message with one of the following:
|
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
|
+
## v0.0.7
|
|
15
|
+
|
|
16
|
+
- [Fixed] Improve support for extensions.
|
|
17
|
+
|
|
14
18
|
## v0.0.6
|
|
15
19
|
|
|
16
20
|
- [Added] Add `--tts-preset` option to select TTS preset when exporting
|
data/DOCUMENTATION.md
CHANGED
|
@@ -764,7 +764,7 @@ module. Custom engines must implement the `generate` method:
|
|
|
764
764
|
module ScreenKit
|
|
765
765
|
module TTS
|
|
766
766
|
class CustomEngine < Base
|
|
767
|
-
|
|
767
|
+
extend Shell
|
|
768
768
|
|
|
769
769
|
# Optional: Define schema path for validation
|
|
770
770
|
def self.schema_path
|
|
@@ -772,8 +772,8 @@ module ScreenKit
|
|
|
772
772
|
end
|
|
773
773
|
|
|
774
774
|
# This method is required.
|
|
775
|
-
def available?
|
|
776
|
-
|
|
775
|
+
def self.available?(**)
|
|
776
|
+
command_exist?("some-command")
|
|
777
777
|
end
|
|
778
778
|
|
|
779
779
|
# This method is required.
|
|
@@ -781,15 +781,20 @@ module ScreenKit
|
|
|
781
781
|
# Optional: validate options against JSON schema.
|
|
782
782
|
self.class.validate!(options)
|
|
783
783
|
|
|
784
|
+
# If you need access to previous/next text, you can access the method
|
|
785
|
+
# `segments`.
|
|
786
|
+
# current_index = segments.index { it.script_content == text }
|
|
787
|
+
# next_text = segments[current_index + 1]&.script_content
|
|
788
|
+
|
|
784
789
|
# Generate audio file from text
|
|
785
790
|
# Write output to output_path
|
|
786
791
|
# Optionally log to log_path
|
|
787
792
|
|
|
788
793
|
# Example implementation:
|
|
789
|
-
# run_command "some-command",
|
|
790
|
-
#
|
|
791
|
-
#
|
|
792
|
-
#
|
|
794
|
+
# self.class.run_command "some-command",
|
|
795
|
+
# "-o", output_path.sub_ext(".wav"),
|
|
796
|
+
# text,
|
|
797
|
+
# log_path:
|
|
793
798
|
end
|
|
794
799
|
end
|
|
795
800
|
end
|
|
@@ -802,6 +807,7 @@ end
|
|
|
802
807
|
tts:
|
|
803
808
|
- id: custom_engine
|
|
804
809
|
engine: custom_engine # Camelized to CustomEngine
|
|
810
|
+
enabled: true
|
|
805
811
|
# Add your custom options here
|
|
806
812
|
api_key: your_api_key
|
|
807
813
|
custom_option: value
|
|
@@ -811,6 +817,16 @@ The engine name is camelized (e.g., `custom_engine` → `CustomEngine`,
|
|
|
811
817
|
`google_cloud` → `GoogleCloud`) and loaded as
|
|
812
818
|
`ScreenKit::TTS::#{CamelizedName}`.
|
|
813
819
|
|
|
820
|
+
### 3rd-party TTS Engines
|
|
821
|
+
|
|
822
|
+
- [Search Github](https://github.com/topics/screenkit-tts)
|
|
823
|
+
- [Google Text to Speech](https://github.com/fnando/screenkit-tts-google)
|
|
824
|
+
|
|
825
|
+
> [!TIP]
|
|
826
|
+
>
|
|
827
|
+
> If you host your TTS engine on Github, use the topic `screekit-tts`, so other
|
|
828
|
+
> people can find it.
|
|
829
|
+
|
|
814
830
|
---
|
|
815
831
|
|
|
816
832
|
## Animations
|
|
@@ -897,6 +913,12 @@ Enter
|
|
|
897
913
|
Sleep 2s
|
|
898
914
|
```
|
|
899
915
|
|
|
916
|
+
When running tape files, the working directory will be the episode directory. If
|
|
917
|
+
you're importing anything from the parent directory, you must specify relative
|
|
918
|
+
paths accordingly. For instance, `episodes/001-episode-name/content/001.tape`
|
|
919
|
+
would need to reference `../../resources/some-file` to access
|
|
920
|
+
`resources/some-file` in the project's directory.
|
|
921
|
+
|
|
900
922
|
### Script Files
|
|
901
923
|
|
|
902
924
|
Plain text files for voiceover generation:
|
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
|
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
|
@@ -3,22 +3,48 @@
|
|
|
3
3
|
module ScreenKit
|
|
4
4
|
module TTS
|
|
5
5
|
class Base
|
|
6
|
+
require_relative "../core_ext"
|
|
7
|
+
require_relative "../schema_validator"
|
|
8
|
+
|
|
6
9
|
extend SchemaValidator
|
|
7
10
|
|
|
11
|
+
using CoreExt
|
|
12
|
+
|
|
8
13
|
# Additional options for the tts engine.
|
|
9
14
|
attr_reader :options
|
|
10
15
|
|
|
11
16
|
# The preset name for the tts engine.
|
|
12
17
|
attr_reader :id
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
# The list of segments.
|
|
20
|
+
# This is available so that engines can contextually generate audio, for
|
|
21
|
+
# instance, by providing previous/next text (e.g. Eleven Labs).
|
|
22
|
+
attr_reader :segments
|
|
23
|
+
|
|
24
|
+
# The API key for the tts engine, if applicable.
|
|
25
|
+
attr_reader :api_key
|
|
26
|
+
|
|
27
|
+
# Detects if the tts engine is available.
|
|
28
|
+
def self.available?(**)
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.engine_name
|
|
33
|
+
name.split("::").last.underscore
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(id: nil, segments: nil, api_key: nil, **options)
|
|
37
|
+
@segments = Array(segments)
|
|
16
38
|
@options = options
|
|
17
39
|
@id = id
|
|
40
|
+
@api_key = api_key
|
|
18
41
|
end
|
|
19
42
|
|
|
20
|
-
def
|
|
21
|
-
|
|
43
|
+
def redact_file(path, text)
|
|
44
|
+
return unless File.file?(path)
|
|
45
|
+
|
|
46
|
+
content = File.read(path).gsub(text, "[REDACTED]")
|
|
47
|
+
File.write(path, content)
|
|
22
48
|
end
|
|
23
49
|
end
|
|
24
50
|
end
|
|
@@ -8,21 +8,16 @@ module ScreenKit
|
|
|
8
8
|
.join("screenkit/schemas/tts/elevenlabs.json")
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def initialize(api_key:, **)
|
|
15
|
-
super(**)
|
|
16
|
-
@api_key = api_key
|
|
11
|
+
def self.available?(api_key: nil, **)
|
|
12
|
+
api_key.to_s.empty?
|
|
17
13
|
end
|
|
18
14
|
|
|
19
|
-
def
|
|
20
|
-
|
|
15
|
+
def all_texts
|
|
16
|
+
@all_texts ||= segments.map(&:script_content)
|
|
21
17
|
end
|
|
22
18
|
|
|
23
19
|
def generate(output_path:, text:, log_path: nil)
|
|
24
|
-
|
|
25
|
-
voice_id = options.delete(:voice_id)
|
|
20
|
+
voice_id = options[:voice_id]
|
|
26
21
|
|
|
27
22
|
if log_path
|
|
28
23
|
File.open(log_path, "w") do |f|
|
|
@@ -32,9 +27,23 @@ module ScreenKit
|
|
|
32
27
|
|
|
33
28
|
require "aitch"
|
|
34
29
|
|
|
30
|
+
Aitch.configure do |config|
|
|
31
|
+
config.logger = Logger.new(log_path) if log_path
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
current_index = all_texts.index { it == text }
|
|
35
|
+
|
|
36
|
+
if current_index
|
|
37
|
+
previous_text = all_texts[current_index - 1]
|
|
38
|
+
next_text = all_texts[current_index + 1]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
params = options.merge(text:, previous_text:, next_text:)
|
|
42
|
+
.except(:voice_id)
|
|
43
|
+
|
|
35
44
|
response = Aitch.post(
|
|
36
45
|
url: "https://api.elevenlabs.io/v1/text-to-speech/#{voice_id}",
|
|
37
|
-
body: JSON.dump(
|
|
46
|
+
body: JSON.dump(params),
|
|
38
47
|
options: {expect: 200},
|
|
39
48
|
headers: {
|
|
40
49
|
"content-type": "application/json",
|
|
@@ -44,6 +53,8 @@ module ScreenKit
|
|
|
44
53
|
)
|
|
45
54
|
|
|
46
55
|
File.binwrite(output_path, response.body)
|
|
56
|
+
ensure
|
|
57
|
+
redact_file(log_path, api_key)
|
|
47
58
|
end
|
|
48
59
|
end
|
|
49
60
|
end
|
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,79 @@
|
|
|
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
|
+
|
|
14
|
+
module ScreenKit
|
|
15
|
+
require_relative "screenkit/version"
|
|
16
|
+
require_relative "screenkit/duration"
|
|
17
|
+
require_relative "screenkit/core_ext"
|
|
18
|
+
require_relative "screenkit/content_type"
|
|
19
|
+
require_relative "screenkit/anchor"
|
|
20
|
+
require_relative "screenkit/banner"
|
|
21
|
+
require_relative "screenkit/time_formatter"
|
|
22
|
+
require_relative "screenkit/spacing"
|
|
23
|
+
require_relative "screenkit/watermark"
|
|
24
|
+
require_relative "screenkit/spinner"
|
|
25
|
+
require_relative "screenkit/shell"
|
|
26
|
+
require_relative "screenkit/schema_validator"
|
|
27
|
+
require_relative "screenkit/generators/project"
|
|
28
|
+
require_relative "screenkit/generators/episode"
|
|
29
|
+
require_relative "screenkit/config/base"
|
|
30
|
+
require_relative "screenkit/config/project"
|
|
31
|
+
require_relative "screenkit/config/episode"
|
|
32
|
+
require_relative "screenkit/callout"
|
|
33
|
+
require_relative "screenkit/callout/text_style"
|
|
34
|
+
require_relative "screenkit/callout/styles/base"
|
|
35
|
+
require_relative "screenkit/transition"
|
|
36
|
+
require_relative "screenkit/parallel_processor"
|
|
37
|
+
require_relative "screenkit/cli"
|
|
38
|
+
require_relative "screenkit/cli/base"
|
|
39
|
+
require_relative "screenkit/cli/episode"
|
|
40
|
+
require_relative "screenkit/cli/root"
|
|
41
|
+
require_relative "screenkit/animation_filters"
|
|
42
|
+
require_relative "screenkit/tts/base"
|
|
43
|
+
require_relative "screenkit/path_lookup"
|
|
44
|
+
require_relative "screenkit/sound"
|
|
45
|
+
require_relative "screenkit/utils"
|
|
46
|
+
require_relative "screenkit/logfile"
|
|
47
|
+
require_relative "screenkit/exporter/intro"
|
|
48
|
+
require_relative "screenkit/exporter/outro"
|
|
49
|
+
require_relative "screenkit/exporter/demotape"
|
|
50
|
+
require_relative "screenkit/exporter/episode"
|
|
51
|
+
require_relative "screenkit/exporter/segment"
|
|
52
|
+
require_relative "screenkit/exporter/image"
|
|
53
|
+
require_relative "screenkit/exporter/video"
|
|
54
|
+
|
|
55
|
+
require_files = lambda do |pattern|
|
|
56
|
+
Gem.find_files_from_load_path(pattern).each do |path|
|
|
57
|
+
next if path.include?("test")
|
|
58
|
+
|
|
59
|
+
require(path)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Load all files that may be available as plugins.
|
|
64
|
+
require_files.call("screenkit/callout/styles/*.rb")
|
|
65
|
+
require_files.call("screenkit/tts/*.rb")
|
|
66
|
+
|
|
67
|
+
def self.root_dir
|
|
68
|
+
@root_dir ||= Pathname(__dir__)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Raised when the configuration schema is invalid.
|
|
72
|
+
InvalidConfigSchemaError = Class.new(StandardError)
|
|
73
|
+
|
|
74
|
+
# Raised when a file is not found.
|
|
75
|
+
FileNotFoundError = Class.new(StandardError)
|
|
76
|
+
|
|
77
|
+
# Raised when a file entry is not found in the lookup.
|
|
78
|
+
FileEntryNotFoundError = Class.new(StandardError)
|
|
79
|
+
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.7
|
|
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
|
|
@@ -385,10 +387,10 @@ metadata:
|
|
|
385
387
|
rubygems_mfa_required: 'true'
|
|
386
388
|
homepage_uri: https://github.com/fnando/screenkit
|
|
387
389
|
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.
|
|
390
|
+
source_code_uri: https://github.com/fnando/screenkit/tree/v0.0.7
|
|
391
|
+
changelog_uri: https://github.com/fnando/screenkit/tree/v0.0.7/CHANGELOG.md
|
|
392
|
+
documentation_uri: https://github.com/fnando/screenkit/tree/v0.0.7/README.md
|
|
393
|
+
license_uri: https://github.com/fnando/screenkit/tree/v0.0.7/LICENSE.md
|
|
392
394
|
rdoc_options: []
|
|
393
395
|
require_paths:
|
|
394
396
|
- 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
|