@gentbajko/slopify 2.0.2 → 2.1.0
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.
- package/SUBTITLES.md +1 -1
- package/dist/adapters/alignment/protocol.js +11 -1
- package/dist/adapters/alignment/runner.js +4 -1
- package/dist/adapters/alignment/threads.js +11 -0
- package/dist/adapters/alignment/worker.js +66 -9
- package/dist/adapters/explain.js +93 -0
- package/dist/adapters/ffmpeg.js +3 -3
- package/dist/adapters/host-cli/index.js +34 -3
- package/dist/adapters/host-cli/transport.js +4 -4
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +12 -12
- package/dist/adapters/image/codex.js +24 -8
- package/dist/adapters/image/fal.js +12 -6
- package/dist/adapters/image/google.js +18 -8
- package/dist/adapters/image/models.js +8 -2
- package/dist/adapters/image/openai.js +15 -6
- package/dist/adapters/image/replicate.js +16 -7
- package/dist/adapters/llm/claude-code-models.js +1 -1
- package/dist/adapters/llm/claude-code.js +16 -7
- package/dist/adapters/llm/cli-login-error.js +2 -1
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/codex.js +5 -6
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-models.js +1 -1
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +15 -11
- package/dist/adapters/llm/openrouter.js +21 -9
- package/dist/adapters/llm/run-cli.js +14 -11
- package/dist/adapters/tts/cartesia.js +10 -3
- package/dist/adapters/tts/elevenlabs.js +18 -6
- package/dist/adapters/tts/inworld-async.js +47 -16
- package/dist/adapters/tts/inworld.js +25 -15
- package/dist/adapters/tts/openai.js +18 -6
- package/dist/assets/document/SOURCE.txt +7 -0
- package/dist/assets/document/background.jpg +0 -0
- package/dist/assets/document/fonts/Cinzel-Black.ttf +0 -0
- package/dist/assets/document/fonts/Cinzel-Bold.ttf +0 -0
- package/dist/assets/document/fonts/Cinzel-Medium.ttf +0 -0
- package/dist/assets/document/fonts/Cinzel-Regular.ttf +0 -0
- package/dist/assets/document/fonts/OFL.txt +93 -0
- package/dist/assets/document/fonts/SOURCE.txt +11 -0
- package/dist/catalog/registry.js +13 -10
- package/dist/catalog/store.js +1 -1
- package/dist/catalog/validate.js +16 -6
- package/dist/edge/cli.js +33 -7
- package/dist/edge/docker-launch.js +6 -6
- package/dist/edge/docker-projects/engine.js +34 -13
- package/dist/edge/docker-projects/install.js +8 -8
- package/dist/edge/docker-projects/state.js +11 -7
- package/dist/edge/docker-projects/tree.js +6 -6
- package/dist/edge/docker.js +13 -13
- package/dist/edge/host-cli.js +14 -0
- package/dist/edge/http/actions.js +27 -25
- package/dist/edge/http/app.js +3 -3
- package/dist/edge/http/audio-preview.js +1 -1
- package/dist/edge/http/checkpoints.js +5 -1
- package/dist/edge/http/diagnostics.js +3 -1
- package/dist/edge/http/draft-files.js +2 -2
- package/dist/edge/http/draft-problem.js +11 -1
- package/dist/edge/http/files.js +4 -4
- package/dist/edge/http/folder-location-schema.js +5 -8
- package/dist/edge/http/folder-location.js +13 -8
- package/dist/edge/http/fonts.js +22 -10
- package/dist/edge/http/host-cli.js +61 -14
- package/dist/edge/http/multipart.js +7 -5
- package/dist/edge/http/open-folder.js +2 -2
- package/dist/edge/http/planning.js +4 -4
- package/dist/edge/http/problem.js +2 -2
- package/dist/edge/http/project-templates.js +7 -3
- package/dist/edge/http/projects.js +8 -8
- package/dist/edge/http/prompts.js +9 -4
- package/dist/edge/http/providers.js +15 -7
- package/dist/edge/http/revision-files.js +8 -4
- package/dist/edge/http/revisions.js +30 -4
- package/dist/edge/http/schedules.js +9 -9
- package/dist/edge/http/settings.js +15 -9
- package/dist/edge/http/staging.js +5 -5
- package/dist/edge/http/storage.js +4 -4
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/tutorial.js +2 -2
- package/dist/edge/http/update.js +3 -3
- package/dist/host-cli/install.js +28 -6
- package/dist/host-cli/open-folder.js +95 -0
- package/dist/host-cli/paths.js +6 -6
- package/dist/host-cli/runtime.js +2 -0
- package/dist/host-cli/server.js +1 -1
- package/dist/host-cli/service.js +11 -11
- package/dist/host-cli/status.js +7 -3
- package/dist/kernel/config/index.js +6 -4
- package/dist/kernel/db/migrate.js +27 -2
- package/dist/kernel/db/migrations/0013-schedule-topic-queue.sql +4 -0
- package/dist/kernel/db/migrations/0014-document-stage.sql +44 -0
- package/dist/kernel/db/migrations/0015-description-prompts.sql +13 -0
- package/dist/kernel/lock.js +4 -3
- package/dist/kernel/pipeline.js +11 -1
- package/dist/kernel/ports/host-cli.js +14 -0
- package/dist/kernel/ports/subtitles.js +15 -1
- package/dist/kernel/runner/attempt.js +26 -2
- package/dist/kernel/runner/graph.js +6 -1
- package/dist/main.js +8 -4
- package/dist/slices/admission/model.js +7 -1
- package/dist/slices/admission/rules.js +98 -22
- package/dist/slices/admission/schema.js +16 -3
- package/dist/slices/admission/start.js +3 -2
- package/dist/slices/article/continuation.js +4 -2
- package/dist/slices/article/plain.js +2 -2
- package/dist/slices/article/provided-entries.js +3 -3
- package/dist/slices/article/run.js +2 -2
- package/dist/slices/article/segments.js +5 -3
- package/dist/slices/control/providers.js +20 -7
- package/dist/slices/document/blocks.js +129 -0
- package/dist/slices/document/flow.js +117 -0
- package/dist/slices/document/fonts.js +54 -0
- package/dist/slices/document/model.js +11 -0
- package/dist/slices/document/pages.js +198 -0
- package/dist/slices/document/render.js +242 -0
- package/dist/slices/document/sources.js +35 -0
- package/dist/slices/document/theme.js +158 -0
- package/dist/slices/document/writer.js +66 -0
- package/dist/slices/estimate/index.js +14 -2
- package/dist/slices/fonts/catalog.js +1 -1
- package/dist/slices/fonts/upload.js +1 -1
- package/dist/slices/images/run.js +5 -5
- package/dist/slices/library/lint.js +13 -7
- package/dist/slices/library/model.js +1 -1
- package/dist/slices/library/slots.js +6 -3
- package/dist/slices/narration/concat.js +1 -1
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +2 -2
- package/dist/slices/narration/run.js +7 -7
- package/dist/slices/narration/steering.js +3 -3
- package/dist/slices/play-drafts/convert.js +35 -5
- package/dist/slices/play-drafts/readiness.js +15 -8
- package/dist/slices/play-drafts/review-inputs.js +7 -4
- package/dist/slices/play-drafts/review.js +3 -3
- package/dist/slices/play-drafts/schema.js +22 -5
- package/dist/slices/play-drafts/service.js +3 -1
- package/dist/slices/play-drafts/uploads.js +1 -1
- package/dist/slices/project-templates/from-project.js +10 -0
- package/dist/slices/project-templates/setup.js +1 -0
- package/dist/slices/rebuild/admission-repo.js +2 -2
- package/dist/slices/rebuild/preview-details.js +11 -1
- package/dist/slices/rebuild/recipe-audio.js +3 -0
- package/dist/slices/rebuild/recipe-build.js +6 -1
- package/dist/slices/rebuild/recipe-document.js +36 -0
- package/dist/slices/rebuild/recipe-exports.js +14 -2
- package/dist/slices/rebuild/recipe-fixture.js +4 -0
- package/dist/slices/rebuild/recipe-legacy.js +6 -1
- package/dist/slices/rebuild/recipe-model.js +2 -0
- package/dist/slices/rebuild/recipe-narration-text.js +1 -1
- package/dist/slices/rebuild/recipe-provider-choice.js +3 -0
- package/dist/slices/rebuild/recipe-save.js +15 -3
- package/dist/slices/rebuild/recipe-validation.js +15 -2
- package/dist/slices/rebuild/recipe-visual.js +11 -2
- package/dist/slices/rebuild/recipe-work.js +15 -0
- package/dist/slices/rebuild/recipe-youtube.js +33 -0
- package/dist/slices/rebuild/recovery-selection.js +2 -0
- package/dist/slices/rebuild/recovery.js +12 -7
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/runtime-actions.js +2 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-checkpoints.js +4 -4
- package/dist/slices/rebuild/runtime-document.js +85 -0
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -6
- package/dist/slices/rebuild/runtime-export.js +39 -23
- package/dist/slices/rebuild/runtime-local.js +6 -6
- package/dist/slices/rebuild/runtime-narration-text.js +46 -7
- package/dist/slices/rebuild/runtime-piece-label.js +66 -0
- package/dist/slices/rebuild/runtime-provider.js +6 -4
- package/dist/slices/rebuild/runtime-publication.js +4 -3
- package/dist/slices/rebuild/runtime-run.js +22 -4
- package/dist/slices/rebuild/runtime-store.js +7 -2
- package/dist/slices/rebuild/runtime-subtitles.js +82 -16
- package/dist/slices/rebuild/runtime-youtube.js +84 -0
- package/dist/slices/rebuild/transition-repo.js +8 -6
- package/dist/slices/reruns/cascade.js +6 -4
- package/dist/slices/reruns/index.js +1 -1
- package/dist/slices/research/run.js +9 -7
- package/dist/slices/research/synthesis.js +4 -2
- package/dist/slices/revisions/mutation-assets.js +26 -8
- package/dist/slices/revisions/mutation-cues.js +4 -2
- package/dist/slices/revisions/projection.js +2 -1
- package/dist/slices/revisions/rules.js +18 -9
- package/dist/slices/revisions/schema.js +1 -1
- package/dist/slices/schedules/repo.js +55 -11
- package/dist/slices/schedules/scheduler.js +25 -8
- package/dist/slices/schedules/schema.js +19 -4
- package/dist/slices/schedules/service.js +2 -0
- package/dist/slices/settings/cli-paths.js +7 -4
- package/dist/slices/settings/models.js +4 -4
- package/dist/slices/settings/playback.js +2 -2
- package/dist/slices/storage/assets.js +5 -5
- package/dist/slices/storage/downloads.js +1 -0
- package/dist/slices/storage/layout.js +7 -1
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/subtitles/captions.js +1 -1
- package/dist/slices/subtitles/prepare.js +7 -6
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/telemetry/model.js +1 -1
- package/dist/slices/thumbnail/run.js +8 -8
- package/dist/slices/video/audio-export.js +11 -3
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/edit-list.js +73 -0
- package/dist/slices/video/ffmpeg.js +129 -61
- package/dist/slices/video/motion.js +51 -0
- package/dist/slices/video/plan.js +62 -28
- package/dist/slices/video/reuse-audio.js +2 -0
- package/dist/slices/video/run.js +23 -12
- package/dist/slices/video/slideshow.js +48 -0
- package/dist/slices/video/write-export.js +17 -25
- package/dist/slices/youtube/answer.js +199 -0
- package/dist/slices/youtube/model.js +27 -0
- package/dist/slices/youtube/timestamps.js +24 -0
- package/dist/slices/youtube/transcript.js +47 -0
- package/dist/updater/plan.js +1 -1
- package/dist/updater/service.js +4 -3
- package/dist/updater/worker.js +6 -4
- package/dist/web/assets/index-B6NbhpjD.css +1 -0
- package/dist/web/assets/index-ComF5gwP.js +142 -0
- package/dist/web/index.html +2 -2
- package/package.json +4 -2
- package/dist/web/assets/index-CZNiHm8P.css +0 -1
- package/dist/web/assets/index-DPgGLX5m.js +0 -137
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { cliCommand } from "../../kernel/cli-command.js";
|
|
4
4
|
import { redact } from "../../kernel/log.js";
|
|
5
5
|
import { providerError } from "../../kernel/ports/model.js";
|
|
6
|
+
import { cliName, commandOf, quoted } from "../explain.js";
|
|
6
7
|
// ceiling: the last 8 KiB of stderr is kept. A CLI that writes megabytes of progress there
|
|
7
8
|
// would otherwise be held in memory for the length of a run, and the tail is the part that
|
|
8
9
|
// says why it stopped. Streaming stderr to the log is the upgrade if a provider ever
|
|
@@ -117,7 +118,7 @@ export async function deliveredInput(run) {
|
|
|
117
118
|
catch {
|
|
118
119
|
throw providerError({
|
|
119
120
|
kind: "unavailable",
|
|
120
|
-
message: "
|
|
121
|
+
message: "Slopify could not hand the whole prompt to the CLI, so it cannot tell what the CLI worked on (it may already have used your quota). Use Retry stage to run it again.",
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
}
|
|
@@ -126,7 +127,7 @@ export function cliInput(text) {
|
|
|
126
127
|
if (Buffer.byteLength(text) >= 8 * 1024 * 1024)
|
|
127
128
|
throw providerError({
|
|
128
129
|
kind: "unsupported",
|
|
129
|
-
message: "
|
|
130
|
+
message: "This prompt is larger than the CLI accepts (8 MB), so nothing was sent. Make the inputs shorter in Edit project, or choose an API provider such as OpenRouter in its Providers section, then use Retry stage.",
|
|
130
131
|
});
|
|
131
132
|
return text;
|
|
132
133
|
}
|
|
@@ -151,15 +152,17 @@ export function promptOf(messages) {
|
|
|
151
152
|
.join("\n\n");
|
|
152
153
|
}
|
|
153
154
|
// A bare non-zero exit tells the user nothing, so the stage shows whatever the CLI put on
|
|
154
|
-
// stderr - the provider's error text, verbatim.
|
|
155
|
+
// stderr - the provider's error text, verbatim - after a sentence saying what to do.
|
|
155
156
|
export function endedWithout(binary, ended, stderr) {
|
|
156
157
|
const said = redact(stderr.trim());
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return said
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
if (ended.error !== null)
|
|
159
|
+
return `The ${cliName(binary)} could not be started${quoted("", redact(ended.error.message))}. Check it is installed and set up in Settings → Providers, then use Retry stage.`;
|
|
160
|
+
const how = ended.code === null ? "killed by the system" : `exit code ${String(ended.code)}`;
|
|
161
|
+
return `The ${cliName(binary)} stopped without answering${quoted(how, said)}. Run ${commandOf(binary)} in a terminal to check it works and is signed in, then use Retry stage.`;
|
|
162
|
+
}
|
|
163
|
+
// The run outlived both the polite and the forced stop.
|
|
164
|
+
export function stuckCli(binary) {
|
|
165
|
+
return `The ${cliName(binary)} stopped responding and could not be shut down. Close any leftover ${commandOf(binary)} processes or restart your computer, then use Retry stage.`;
|
|
163
166
|
}
|
|
164
167
|
// Unreachable while stdio names `pipe` for fd 1; it is here so the type carries no null
|
|
165
168
|
// and no non-null assertion is needed to read the stream.
|
|
@@ -180,7 +183,7 @@ export function cliEvent(binary, line) {
|
|
|
180
183
|
// know. The text is not echoed back: half a JSON object is noise to the user.
|
|
181
184
|
throw providerError({
|
|
182
185
|
kind: "other",
|
|
183
|
-
message: `
|
|
186
|
+
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use Retry stage.`,
|
|
184
187
|
});
|
|
185
188
|
}
|
|
186
189
|
return { type: parsed.data.type, value };
|
|
@@ -190,7 +193,7 @@ export function cliShaped(binary, schema, value) {
|
|
|
190
193
|
if (!parsed.success) {
|
|
191
194
|
throw providerError({
|
|
192
195
|
kind: "other",
|
|
193
|
-
message: `
|
|
196
|
+
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use Retry stage.`,
|
|
194
197
|
});
|
|
195
198
|
}
|
|
196
199
|
return parsed.data;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { httpFailure, missingKey, noAudio, voiceFix } from "../explain.js";
|
|
4
5
|
import { retryAfter } from "../retry-after.js";
|
|
5
6
|
// The HTTP gateway adapter for Cartesia. `fetch` and
|
|
6
7
|
// nothing else; `/tts/bytes` streams the audio it renders, so the response body is
|
|
@@ -54,7 +55,7 @@ export function cartesiaTts(deps) {
|
|
|
54
55
|
throw await failure(response, req.voiceId);
|
|
55
56
|
}
|
|
56
57
|
if (response.body === null) {
|
|
57
|
-
throw providerError({ kind: "other", message: "Cartesia
|
|
58
|
+
throw providerError({ kind: "other", message: noAudio("Cartesia") });
|
|
58
59
|
}
|
|
59
60
|
return { audio: response.body, container: "mp3" };
|
|
60
61
|
},
|
|
@@ -64,7 +65,7 @@ function keyOf(deps) {
|
|
|
64
65
|
const key = deps.key();
|
|
65
66
|
// An absent key is terminal, so it never becomes a request.
|
|
66
67
|
if (key === undefined || key === "") {
|
|
67
|
-
throw providerError({ kind: "missing_key", message: "
|
|
68
|
+
throw providerError({ kind: "missing_key", message: missingKey("Cartesia") });
|
|
68
69
|
}
|
|
69
70
|
return key;
|
|
70
71
|
}
|
|
@@ -88,7 +89,13 @@ async function failure(response, voiceId) {
|
|
|
88
89
|
kind: kindOf(response.status),
|
|
89
90
|
// `voice_not_found` has to say which voice was asked for; Cartesia's
|
|
90
91
|
// own message does not repeat the id.
|
|
91
|
-
message:
|
|
92
|
+
message: httpFailure({
|
|
93
|
+
provider: "Cartesia",
|
|
94
|
+
status: response.status,
|
|
95
|
+
detail: message,
|
|
96
|
+
subject: `narration request for voice "${voiceId}"`,
|
|
97
|
+
fix: voiceFix(voiceId),
|
|
98
|
+
}),
|
|
92
99
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
93
100
|
});
|
|
94
101
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { httpFailure, missingKey, noAudio, refreshList, unreadable, voiceFix } from "../explain.js";
|
|
4
5
|
import { retryAfter } from "../retry-after.js";
|
|
5
6
|
// The HTTP gateway adapter for ElevenLabs: the platform's own `fetch` and nothing else. The
|
|
6
7
|
// response body already is the `ReadableStream<Uint8Array>` the port asks for, so an SDK
|
|
@@ -44,12 +45,12 @@ export function elevenLabsTts(deps) {
|
|
|
44
45
|
signal: AbortSignal.timeout(10_000),
|
|
45
46
|
});
|
|
46
47
|
if (!response.ok)
|
|
47
|
-
throw await failure(response);
|
|
48
|
+
throw await failure(response, undefined, refreshList);
|
|
48
49
|
const parsed = modelList.safeParse(safeJson(await response.text()));
|
|
49
50
|
if (!parsed.success) {
|
|
50
51
|
throw providerError({
|
|
51
52
|
kind: "other",
|
|
52
|
-
message: "ElevenLabs
|
|
53
|
+
message: unreadable("ElevenLabs"),
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
56
|
return parsed.data
|
|
@@ -70,7 +71,7 @@ export function elevenLabsTts(deps) {
|
|
|
70
71
|
throw await failure(response, req.voiceId);
|
|
71
72
|
}
|
|
72
73
|
if (response.body === null) {
|
|
73
|
-
throw providerError({ kind: "other", message: "ElevenLabs
|
|
74
|
+
throw providerError({ kind: "other", message: noAudio("ElevenLabs") });
|
|
74
75
|
}
|
|
75
76
|
return { audio: response.body, container: "mp3" };
|
|
76
77
|
},
|
|
@@ -80,7 +81,7 @@ function keyOf(deps) {
|
|
|
80
81
|
const key = deps.key();
|
|
81
82
|
// `missing_key`, not `auth`, because that rule makes it terminal.
|
|
82
83
|
if (key === undefined || key === "") {
|
|
83
|
-
throw providerError({ kind: "missing_key", message: "
|
|
84
|
+
throw providerError({ kind: "missing_key", message: missingKey("ElevenLabs") });
|
|
84
85
|
}
|
|
85
86
|
return key;
|
|
86
87
|
}
|
|
@@ -97,7 +98,7 @@ function kindOf(status) {
|
|
|
97
98
|
// port's error contract first, which is not this adapter's to widen.
|
|
98
99
|
return "other";
|
|
99
100
|
}
|
|
100
|
-
async function failure(response, voiceId) {
|
|
101
|
+
async function failure(response, voiceId, next) {
|
|
101
102
|
const text = await response.text().catch(() => "");
|
|
102
103
|
// The provider's own words, through the redactor - an error body may quote the key back.
|
|
103
104
|
const message = redact(detailOf(text) || response.statusText);
|
|
@@ -106,7 +107,18 @@ async function failure(response, voiceId) {
|
|
|
106
107
|
kind: kindOf(response.status),
|
|
107
108
|
// A rejected voice ID has to be named, and it is the one part of the
|
|
108
109
|
// request the user chose. It is not secret, unlike everything else on the wire.
|
|
109
|
-
message:
|
|
110
|
+
message: httpFailure({
|
|
111
|
+
provider: "ElevenLabs",
|
|
112
|
+
status: response.status,
|
|
113
|
+
detail: message,
|
|
114
|
+
next,
|
|
115
|
+
...(voiceId === undefined
|
|
116
|
+
? {}
|
|
117
|
+
: {
|
|
118
|
+
subject: `narration request for voice "${voiceId}"`,
|
|
119
|
+
fix: voiceFix(voiceId),
|
|
120
|
+
}),
|
|
121
|
+
}),
|
|
110
122
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
111
123
|
});
|
|
112
124
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { httpFailure, internalError, noAudio, providerSaid, unreadable, voiceFix, } from "../explain.js";
|
|
4
5
|
import { retryAfter } from "../retry-after.js";
|
|
5
6
|
const operationName = /^workspaces\/[A-Za-z0-9_-]+\/ttsAsyncJobs\/[A-Za-z0-9_-]+\/operations\/[A-Za-z0-9_-]+$/;
|
|
6
7
|
const operationSchema = z.object({
|
|
@@ -15,11 +16,14 @@ const operationSchema = z.object({
|
|
|
15
16
|
export async function* inworldAsync(deps, request, key) {
|
|
16
17
|
const saved = request.continuation?.read();
|
|
17
18
|
if (saved !== undefined && !operationName.test(saved))
|
|
18
|
-
throw
|
|
19
|
+
throw providerError({
|
|
20
|
+
kind: "other",
|
|
21
|
+
message: internalError("the saved Inworld job reference is damaged"),
|
|
22
|
+
});
|
|
19
23
|
if (saved === undefined && request.text.length > 100_000) {
|
|
20
24
|
throw providerError({
|
|
21
25
|
kind: "unsupported",
|
|
22
|
-
message: "
|
|
26
|
+
message: "This narration part is longer than the 100,000 characters Inworld takes at once (10,000 on On-Demand accounts). Set Chunking to Paragraph in the Providers section of Edit project, then use Retry stage.",
|
|
23
27
|
});
|
|
24
28
|
}
|
|
25
29
|
const headers = { Authorization: `Basic ${key}`, "Content-Type": "application/json" };
|
|
@@ -45,10 +49,10 @@ export async function* inworldAsync(deps, request, key) {
|
|
|
45
49
|
let expected = saved;
|
|
46
50
|
for (;;) {
|
|
47
51
|
request.signal.throwIfAborted();
|
|
48
|
-
await check(response, key);
|
|
52
|
+
await check(response, key, request.voiceId);
|
|
49
53
|
const parsed = operationSchema.safeParse(await response.json().catch(() => null));
|
|
50
54
|
if (!parsed.success || (expected && parsed.data.name !== expected))
|
|
51
|
-
throw
|
|
55
|
+
throw unreadableAnswer();
|
|
52
56
|
const operation = parsed.data;
|
|
53
57
|
expected = operation.name;
|
|
54
58
|
request.continuation?.write(operation.name);
|
|
@@ -65,11 +69,11 @@ export async function* inworldAsync(deps, request, key) {
|
|
|
65
69
|
: code === 3 || code === 5 || code === 9
|
|
66
70
|
? "unsupported"
|
|
67
71
|
: "other",
|
|
68
|
-
message:
|
|
72
|
+
message: streamFailure(code, clean(message ?? "", key), request.voiceId),
|
|
69
73
|
});
|
|
70
74
|
}
|
|
71
75
|
if (!operation.response)
|
|
72
|
-
throw
|
|
76
|
+
throw providerError({ kind: "other", message: noAudio("Inworld") });
|
|
73
77
|
yield* download(deps.fetch, operation.response.audioUri, request.signal);
|
|
74
78
|
return;
|
|
75
79
|
}
|
|
@@ -85,7 +89,7 @@ export async function* inworldAsync(deps, request, key) {
|
|
|
85
89
|
async function* download(fetch, uri, signal) {
|
|
86
90
|
const url = URL.parse(uri);
|
|
87
91
|
if (url?.protocol !== "https:" || url.username || url.password)
|
|
88
|
-
throw
|
|
92
|
+
throw unreadableAnswer();
|
|
89
93
|
// This is a signed storage URL, not an Inworld API endpoint. Never forward Basic auth.
|
|
90
94
|
let response;
|
|
91
95
|
try {
|
|
@@ -94,12 +98,12 @@ async function* download(fetch, uri, signal) {
|
|
|
94
98
|
catch {
|
|
95
99
|
signal.throwIfAborted();
|
|
96
100
|
// Signed query parameters must not become a persisted error or log entry.
|
|
97
|
-
throw
|
|
101
|
+
throw downloadFailed("");
|
|
98
102
|
}
|
|
99
103
|
if (!response.ok)
|
|
100
|
-
throw
|
|
104
|
+
throw downloadFailed(` (error ${String(response.status)})`);
|
|
101
105
|
if (!response.body)
|
|
102
|
-
throw
|
|
106
|
+
throw providerError({ kind: "other", message: noAudio("Inworld") });
|
|
103
107
|
const reader = response.body.getReader();
|
|
104
108
|
let heard = false;
|
|
105
109
|
try {
|
|
@@ -116,16 +120,16 @@ async function* download(fetch, uri, signal) {
|
|
|
116
120
|
}
|
|
117
121
|
catch {
|
|
118
122
|
signal.throwIfAborted();
|
|
119
|
-
throw
|
|
123
|
+
throw downloadFailed("");
|
|
120
124
|
}
|
|
121
125
|
finally {
|
|
122
126
|
await reader.cancel().catch(() => { });
|
|
123
127
|
reader.releaseLock();
|
|
124
128
|
}
|
|
125
129
|
if (!heard)
|
|
126
|
-
throw
|
|
130
|
+
throw providerError({ kind: "other", message: noAudio("Inworld") });
|
|
127
131
|
}
|
|
128
|
-
async function check(response, key) {
|
|
132
|
+
async function check(response, key, voiceId) {
|
|
129
133
|
if (response.ok)
|
|
130
134
|
return;
|
|
131
135
|
const detail = clean(await response.text().catch(() => ""), key);
|
|
@@ -138,13 +142,40 @@ async function check(response, key) {
|
|
|
138
142
|
: response.status === 400 || response.status === 404
|
|
139
143
|
? "unsupported"
|
|
140
144
|
: "other",
|
|
141
|
-
message:
|
|
145
|
+
message: httpFailure({
|
|
146
|
+
provider: "Inworld",
|
|
147
|
+
status: response.status,
|
|
148
|
+
detail: detail || response.statusText,
|
|
149
|
+
subject: `narration request for voice "${voiceId}"`,
|
|
150
|
+
fix: voiceFix(voiceId),
|
|
151
|
+
}),
|
|
142
152
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
143
153
|
});
|
|
144
154
|
}
|
|
145
155
|
function clean(message, key) {
|
|
146
156
|
return redact(message.replaceAll(key, "[redacted]"));
|
|
147
157
|
}
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
// Signed query parameters must not become a persisted error or log entry, so the link is
|
|
159
|
+
// never quoted.
|
|
160
|
+
function downloadFailed(status) {
|
|
161
|
+
return providerError({
|
|
162
|
+
kind: "other",
|
|
163
|
+
message: `Inworld made the narration, but Slopify could not download it${status}. Check your internet connection, then use Retry stage.`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function unreadableAnswer() {
|
|
167
|
+
return providerError({ kind: "other", message: unreadable("Inworld") });
|
|
168
|
+
}
|
|
169
|
+
// Inworld's streamed and async errors carry a gRPC code instead of an HTTP status.
|
|
170
|
+
export function streamFailure(code, detail, voiceId) {
|
|
171
|
+
const label = `code ${String(code)}`;
|
|
172
|
+
if (code === 16)
|
|
173
|
+
return httpFailure({ provider: "Inworld", status: 401, detail, label });
|
|
174
|
+
if (code === 7)
|
|
175
|
+
return httpFailure({ provider: "Inworld", status: 403, detail, label });
|
|
176
|
+
if (code === 8)
|
|
177
|
+
return httpFailure({ provider: "Inworld", status: 429, detail, label });
|
|
178
|
+
if (code === 3 || code === 5 || code === 9)
|
|
179
|
+
return providerSaid("Inworld", "could not narrate this text", detail, voiceFix(voiceId));
|
|
180
|
+
return providerSaid("Inworld", "stopped before the narration was finished", detail, "This is usually temporary: wait a few minutes, then use Retry stage.");
|
|
150
181
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { httpFailure, missingKey, noAudio, unreadable, voiceFix } from "../explain.js";
|
|
4
5
|
import { retryAfter } from "../retry-after.js";
|
|
5
|
-
import { inworldAsync } from "./inworld-async.js";
|
|
6
|
+
import { inworldAsync, streamFailure } from "./inworld-async.js";
|
|
6
7
|
export const inworldModels = [
|
|
7
8
|
{ id: "inworld-tts-2", name: "Realtime TTS-2" },
|
|
8
9
|
{ id: "inworld-tts-2-flash", name: "Realtime TTS-2 Flash" },
|
|
@@ -22,7 +23,7 @@ export function inworldTts(deps) {
|
|
|
22
23
|
?.trim()
|
|
23
24
|
.replace(/^Basic\s+/i, "");
|
|
24
25
|
if (!key)
|
|
25
|
-
throw providerError({ kind: "missing_key", message: "
|
|
26
|
+
throw providerError({ kind: "missing_key", message: missingKey("Inworld") });
|
|
26
27
|
const cancel = new AbortController();
|
|
27
28
|
const signal = AbortSignal.any([request.signal, cancel.signal]);
|
|
28
29
|
const iterator = synthesizeRequest(deps, { ...request, signal }, key);
|
|
@@ -56,7 +57,10 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
56
57
|
return;
|
|
57
58
|
}
|
|
58
59
|
if (request.text.trim().length === 0)
|
|
59
|
-
throw providerError({
|
|
60
|
+
throw providerError({
|
|
61
|
+
kind: "unsupported",
|
|
62
|
+
message: "Inworld was sent an empty piece of narration. Check the article has text in the Article section of Edit project, then use Retry stage.",
|
|
63
|
+
});
|
|
60
64
|
if ((request.model ?? "inworld-tts-2") === "inworld-tts-2" && request.text.length > 4000) {
|
|
61
65
|
yield* inworldAsync(deps, request, key);
|
|
62
66
|
return;
|
|
@@ -64,7 +68,7 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
64
68
|
if (request.text.length > 4000)
|
|
65
69
|
throw providerError({
|
|
66
70
|
kind: "unsupported",
|
|
67
|
-
message: "
|
|
71
|
+
message: "This narration part is longer than the 4,000 characters Inworld's Flash model takes at once. In the Providers section of Edit project, set Chunking to Paragraph or a smaller character count, or switch to the Realtime TTS-2 model.",
|
|
68
72
|
});
|
|
69
73
|
request.signal.throwIfAborted();
|
|
70
74
|
const response = await deps.fetch("https://api.inworld.ai/tts/v1/voice:stream", {
|
|
@@ -90,12 +94,18 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
90
94
|
: response.status === 400 || response.status === 404
|
|
91
95
|
? "unsupported"
|
|
92
96
|
: "other",
|
|
93
|
-
message: clean(
|
|
97
|
+
message: clean(httpFailure({
|
|
98
|
+
provider: "Inworld",
|
|
99
|
+
status: response.status,
|
|
100
|
+
detail: detail || response.statusText,
|
|
101
|
+
subject: `narration request for voice "${request.voiceId}"`,
|
|
102
|
+
fix: voiceFix(request.voiceId),
|
|
103
|
+
}), key),
|
|
94
104
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
95
105
|
});
|
|
96
106
|
}
|
|
97
107
|
if (response.body === null)
|
|
98
|
-
throw
|
|
108
|
+
throw providerError({ kind: "other", message: noAudio("Inworld") });
|
|
99
109
|
let heard = false;
|
|
100
110
|
for await (const line of lines(response.body)) {
|
|
101
111
|
request.signal.throwIfAborted();
|
|
@@ -104,11 +114,11 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
104
114
|
raw = JSON.parse(line);
|
|
105
115
|
}
|
|
106
116
|
catch {
|
|
107
|
-
throw
|
|
117
|
+
throw unreadableAudio();
|
|
108
118
|
}
|
|
109
119
|
const parsed = envelope.safeParse(raw);
|
|
110
120
|
if (!parsed.success || (!parsed.data.result && !parsed.data.error))
|
|
111
|
-
throw
|
|
121
|
+
throw unreadableAudio();
|
|
112
122
|
if (parsed.data.error) {
|
|
113
123
|
const { code, message } = parsed.data.error;
|
|
114
124
|
throw providerError({
|
|
@@ -119,14 +129,14 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
119
129
|
: code === 3 || code === 5
|
|
120
130
|
? "unsupported"
|
|
121
131
|
: "other",
|
|
122
|
-
message:
|
|
132
|
+
message: streamFailure(code, clean(message ?? "", key), request.voiceId),
|
|
123
133
|
});
|
|
124
134
|
}
|
|
125
135
|
const encoded = parsed.data.result?.audioContent;
|
|
126
136
|
if (!encoded)
|
|
127
137
|
continue;
|
|
128
138
|
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.length % 4 === 1)
|
|
129
|
-
throw
|
|
139
|
+
throw unreadableAudio();
|
|
130
140
|
const audio = Buffer.from(encoded, "base64");
|
|
131
141
|
if (audio.length === 0)
|
|
132
142
|
continue;
|
|
@@ -134,7 +144,7 @@ async function* synthesizeRequest(deps, request, key) {
|
|
|
134
144
|
yield audio;
|
|
135
145
|
}
|
|
136
146
|
if (!heard)
|
|
137
|
-
throw
|
|
147
|
+
throw providerError({ kind: "other", message: noAudio("Inworld") });
|
|
138
148
|
}
|
|
139
149
|
async function* lines(body) {
|
|
140
150
|
const reader = body.getReader();
|
|
@@ -147,7 +157,7 @@ async function* lines(body) {
|
|
|
147
157
|
let end = pending.indexOf("\n");
|
|
148
158
|
while (end !== -1) {
|
|
149
159
|
if (end > 8 * 1024 * 1024)
|
|
150
|
-
throw
|
|
160
|
+
throw unreadableAudio();
|
|
151
161
|
const line = pending.slice(0, end).trim();
|
|
152
162
|
pending = pending.slice(end + 1);
|
|
153
163
|
if (line)
|
|
@@ -155,7 +165,7 @@ async function* lines(body) {
|
|
|
155
165
|
end = pending.indexOf("\n");
|
|
156
166
|
}
|
|
157
167
|
if (pending.length > 8 * 1024 * 1024)
|
|
158
|
-
throw
|
|
168
|
+
throw unreadableAudio();
|
|
159
169
|
if (done)
|
|
160
170
|
break;
|
|
161
171
|
}
|
|
@@ -170,6 +180,6 @@ async function* lines(body) {
|
|
|
170
180
|
function clean(message, key) {
|
|
171
181
|
return redact(message.replaceAll(key, "[redacted]"));
|
|
172
182
|
}
|
|
173
|
-
function
|
|
174
|
-
return providerError({ kind: "other", message:
|
|
183
|
+
function unreadableAudio() {
|
|
184
|
+
return providerError({ kind: "other", message: unreadable("Inworld") });
|
|
175
185
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { httpFailure, missingKey, noAudio, refreshList, unreadable, voiceFix } from "../explain.js";
|
|
4
5
|
import { retryAfter } from "../retry-after.js";
|
|
5
6
|
// The HTTP gateway adapter for OpenAI's speech endpoint. `fetch` and nothing else; the response
|
|
6
7
|
// body is already the stream the port asks for.
|
|
@@ -34,12 +35,12 @@ export function openAiTts(deps) {
|
|
|
34
35
|
signal: AbortSignal.timeout(10_000),
|
|
35
36
|
});
|
|
36
37
|
if (!response.ok)
|
|
37
|
-
throw await failure(response);
|
|
38
|
+
throw await failure(response, undefined, refreshList);
|
|
38
39
|
const parsed = modelList.safeParse(safeJson(await response.text()));
|
|
39
40
|
if (!parsed.success) {
|
|
40
41
|
throw providerError({
|
|
41
42
|
kind: "other",
|
|
42
|
-
message: "OpenAI
|
|
43
|
+
message: unreadable("OpenAI"),
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
return parsed.data.data
|
|
@@ -68,7 +69,7 @@ export function openAiTts(deps) {
|
|
|
68
69
|
throw await failure(response, req.voiceId);
|
|
69
70
|
}
|
|
70
71
|
if (response.body === null) {
|
|
71
|
-
throw providerError({ kind: "other", message: "OpenAI
|
|
72
|
+
throw providerError({ kind: "other", message: noAudio("OpenAI") });
|
|
72
73
|
}
|
|
73
74
|
return { audio: response.body, container: "mp3" };
|
|
74
75
|
},
|
|
@@ -81,7 +82,7 @@ function keyOf(deps) {
|
|
|
81
82
|
const key = deps.key();
|
|
82
83
|
// An absent key is terminal, so it never becomes a request.
|
|
83
84
|
if (key === undefined || key === "") {
|
|
84
|
-
throw providerError({ kind: "missing_key", message: "
|
|
85
|
+
throw providerError({ kind: "missing_key", message: missingKey("OpenAI") });
|
|
85
86
|
}
|
|
86
87
|
return key;
|
|
87
88
|
}
|
|
@@ -96,7 +97,7 @@ function kindOf(status) {
|
|
|
96
97
|
// port's error contract has no terminal kind for a request that cannot be repaired.
|
|
97
98
|
return "other";
|
|
98
99
|
}
|
|
99
|
-
async function failure(response, voiceId) {
|
|
100
|
+
async function failure(response, voiceId, next) {
|
|
100
101
|
const text = await response.text().catch(() => "");
|
|
101
102
|
const parsed = errorBody.safeParse(safeJson(text));
|
|
102
103
|
// Verbatim, through the same redactor the wrapper uses: OpenAI's own 401 quotes the
|
|
@@ -107,7 +108,18 @@ async function failure(response, voiceId) {
|
|
|
107
108
|
kind: kindOf(response.status),
|
|
108
109
|
// The voice ID is named, so a rejected voice reads differently from
|
|
109
110
|
// a rejected key.
|
|
110
|
-
message:
|
|
111
|
+
message: httpFailure({
|
|
112
|
+
provider: "OpenAI",
|
|
113
|
+
status: response.status,
|
|
114
|
+
detail: message,
|
|
115
|
+
next,
|
|
116
|
+
...(voiceId === undefined
|
|
117
|
+
? {}
|
|
118
|
+
: {
|
|
119
|
+
subject: `narration request for voice "${voiceId}"`,
|
|
120
|
+
fix: voiceFix(voiceId),
|
|
121
|
+
}),
|
|
122
|
+
}),
|
|
111
123
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
112
124
|
});
|
|
113
125
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
background.jpg
|
|
2
|
+
The parchment page texture of the DiceMaster document theme, from the lore2script2 PDF
|
|
3
|
+
generator (same owner).
|
|
4
|
+
SHA-256: 18f9701b476f299e942cbb99c05ba3267d8341df2b6136e3aebbafad83b03b31
|
|
5
|
+
|
|
6
|
+
fonts/
|
|
7
|
+
Cinzel, SIL Open Font License 1.1: see fonts/SOURCE.txt and fonts/OFL.txt.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The Cinzel Project Authors (https://github.com/NDISCOVER/Cinzel)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Cinzel-Regular.ttf, Cinzel-Medium.ttf, Cinzel-Bold.ttf, Cinzel-Black.ttf
|
|
2
|
+
Cinzel 2.000 by Natanael Gama, from the embedded font data of the lore2script2 PDF generator
|
|
3
|
+
(same owner). Copyright and licence are read from each file's own name table.
|
|
4
|
+
Upstream: https://github.com/NDISCOVER/Cinzel
|
|
5
|
+
|
|
6
|
+
OFL.txt
|
|
7
|
+
SIL Open Font License 1.1, with the copyright line from the fonts' name table.
|
|
8
|
+
7445469e9f302618200b15b1645d6aa3565b93a6634a0f9e5cd069c7fc2343fe Cinzel-Black.ttf
|
|
9
|
+
cbe8f6a665cc58d4540b6ce2a29e6f1b7e096fa6835aea1d2ffb40c088c5cdf6 Cinzel-Bold.ttf
|
|
10
|
+
3d50001a451ba6cfd3ef07a2dfb0c564980a5229f70decdfa23f08f4a30e1bed Cinzel-Medium.ttf
|
|
11
|
+
540b628614b728fc87e50b883189a524dac4d8163ca4a0f8e49b499910762e86 Cinzel-Regular.ttf
|