@gentbajko/slopify 2.0.1 → 2.0.3
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/dist/adapters/alignment/cache.js +17 -1
- package/dist/adapters/alignment/prefetch.js +35 -0
- package/dist/adapters/alignment/protocol.js +11 -1
- package/dist/adapters/alignment/runner.js +4 -1
- package/dist/adapters/alignment/worker.js +28 -5
- package/dist/adapters/explain.js +93 -0
- package/dist/adapters/ffmpeg.js +3 -3
- package/dist/adapters/host-cli/index.js +9 -2
- 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/catalog/registry.js +13 -10
- package/dist/catalog/store.js +1 -1
- package/dist/catalog/validate.js +13 -4
- package/dist/edge/cli.js +37 -8
- 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 +6 -6
- package/dist/edge/docker-projects/tree.js +6 -6
- package/dist/edge/docker.js +13 -13
- 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.js +3 -3
- package/dist/edge/http/fonts.js +22 -10
- package/dist/edge/http/host-cli.js +34 -12
- 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 +3 -3
- 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/paths.js +6 -6
- 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 +2 -1
- package/dist/kernel/db/migrations/0013-schedule-topic-queue.sql +4 -0
- package/dist/kernel/errors.js +12 -0
- package/dist/kernel/lock.js +4 -3
- package/dist/kernel/paths.js +5 -0
- package/dist/kernel/ports/subtitles.js +15 -1
- package/dist/kernel/runner/attempt.js +26 -2
- package/dist/kernel/runner/index.js +16 -6
- package/dist/kernel/runner/progress.js +56 -0
- package/dist/main.js +20 -4
- package/dist/slices/admission/rules.js +32 -20
- 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/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/slots.js +2 -2
- 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 +5 -4
- package/dist/slices/play-drafts/readiness.js +12 -6
- package/dist/slices/play-drafts/review-inputs.js +7 -4
- package/dist/slices/play-drafts/review.js +3 -3
- package/dist/slices/play-drafts/service.js +3 -1
- package/dist/slices/play-drafts/uploads.js +1 -1
- package/dist/slices/rebuild/admission-repo.js +2 -2
- package/dist/slices/rebuild/recipe-legacy.js +1 -1
- package/dist/slices/rebuild/recipe-narration-text.js +1 -1
- package/dist/slices/rebuild/recipe-visual.js +1 -1
- package/dist/slices/rebuild/recovery.js +2 -2
- package/dist/slices/rebuild/repo.js +3 -3
- 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-export-inputs.js +6 -6
- package/dist/slices/rebuild/runtime-export.js +4 -4
- 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 +62 -0
- package/dist/slices/rebuild/runtime-provider.js +6 -4
- package/dist/slices/rebuild/runtime-publication.js +3 -3
- package/dist/slices/rebuild/runtime-run.js +11 -1
- package/dist/slices/rebuild/runtime-store.js +45 -15
- package/dist/slices/rebuild/runtime-subtitles.js +80 -17
- package/dist/slices/rebuild/transition-repo.js +6 -6
- 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 +1 -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/layout.js +1 -1
- 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/thumbnail/run.js +8 -8
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/ffmpeg.js +6 -4
- package/dist/slices/video/plan.js +1 -1
- package/dist/slices/video/run.js +2 -2
- 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-CZNiHm8P.css → index-Bwtmu9ei.css} +1 -1
- package/dist/web/assets/index-DjhMJqMN.js +141 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-DPgGLX5m.js +0 -137
|
@@ -19,11 +19,11 @@ export function saveSettings(deps, settings) {
|
|
|
19
19
|
if (!silenceGapValue.safeParse(settings.silenceGapSeconds).success) {
|
|
20
20
|
fields.push({
|
|
21
21
|
field: "silenceGapSeconds",
|
|
22
|
-
message: `
|
|
22
|
+
message: `Enter a whole number of seconds between 0 and ${String(silenceGapSecondsMax)}.`,
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
if (!appearanceValue.safeParse(settings.appearance).success) {
|
|
26
|
-
fields.push({ field: "appearance", message: "
|
|
26
|
+
fields.push({ field: "appearance", message: "Choose System, Light or Dark." });
|
|
27
27
|
}
|
|
28
28
|
if (fields.length > 0) {
|
|
29
29
|
return { ok: false, fields };
|
|
@@ -10,7 +10,7 @@ export function allocateAsset(deps, projectId, filename) {
|
|
|
10
10
|
const absolutePath = outputPath(deps.paths, projectId, path);
|
|
11
11
|
const directory = outputPath(deps.paths, projectId, `assets/${id}`);
|
|
12
12
|
if (dirname(absolutePath) !== directory)
|
|
13
|
-
throw new Error("
|
|
13
|
+
throw new Error("Slopify hit an internal error (a file name to save is not a plain file name). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
14
14
|
mkdirSync(outputPath(deps.paths, projectId, "assets"), { recursive: true, mode: 0o700 });
|
|
15
15
|
mkdirSync(directory, { mode: 0o700 });
|
|
16
16
|
return { id, projectId, path, absolutePath };
|
|
@@ -20,7 +20,7 @@ export function sealAsset(deps, pending) {
|
|
|
20
20
|
try {
|
|
21
21
|
const stats = fstatSync(file);
|
|
22
22
|
if (!stats.isFile())
|
|
23
|
-
throw new Error("
|
|
23
|
+
throw new Error("Slopify hit an internal error (a saved file is not a regular file). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
24
24
|
fchmodSync(file, 0o600);
|
|
25
25
|
fsyncSync(file);
|
|
26
26
|
return {
|
|
@@ -58,7 +58,7 @@ export function discardPreparedAssets(deps, assets) {
|
|
|
58
58
|
const filename = basename(asset.path);
|
|
59
59
|
requireBasename(filename);
|
|
60
60
|
if (asset.path !== `assets/${asset.id}/${filename}`) {
|
|
61
|
-
throw new Error("
|
|
61
|
+
throw new Error("Slopify hit an internal error (a prepared file is not in its allocated directory). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
62
62
|
}
|
|
63
63
|
const directory = outputPath(deps.paths, asset.projectId, `assets/${asset.id}`);
|
|
64
64
|
const registered = deps.db
|
|
@@ -68,7 +68,7 @@ export function discardPreparedAssets(deps, assets) {
|
|
|
68
68
|
if (row.id === asset.id)
|
|
69
69
|
return true;
|
|
70
70
|
if (typeof row.path !== "string")
|
|
71
|
-
throw new Error("
|
|
71
|
+
throw new Error("Slopify hit an internal error (a saved file has no path). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
72
72
|
const inside = relative(directory, outputPath(deps.paths, asset.projectId, row.path));
|
|
73
73
|
return (inside === "" ||
|
|
74
74
|
(inside !== ".." && !inside.startsWith(`..${sep}`) && !isAbsolute(inside)));
|
|
@@ -92,7 +92,7 @@ function requireBasename(filename) {
|
|
|
92
92
|
}) ||
|
|
93
93
|
/[. ]$/.test(filename) ||
|
|
94
94
|
/^(?:con|prn|aux|nul|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/i.test(filename))
|
|
95
|
-
throw new Error("
|
|
95
|
+
throw new Error("Slopify hit an internal error (a file name to save is not allowed on every system). Try again; if it happens again, use Download diagnostics in Settings and report it.");
|
|
96
96
|
}
|
|
97
97
|
function messageOf(error) {
|
|
98
98
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -14,7 +14,7 @@ function contained(root, path) {
|
|
|
14
14
|
const target = resolve(root, path);
|
|
15
15
|
const inside = relative(root, target);
|
|
16
16
|
if (inside === "" || inside.startsWith("..") || isAbsolute(inside)) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error(`Slopify hit an internal error (the saved file path ${path} resolves outside ${root}). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
18
18
|
}
|
|
19
19
|
return target;
|
|
20
20
|
}
|
|
@@ -21,7 +21,7 @@ export function captionCues(words) {
|
|
|
21
21
|
!Number.isFinite(word.end) ||
|
|
22
22
|
word.start < previousEnd - 0.001 ||
|
|
23
23
|
word.end <= word.start) {
|
|
24
|
-
throw new Error("The
|
|
24
|
+
throw new Error("The saved caption timing is damaged. Use Re-run section on Video to time them again, then Retry stage.");
|
|
25
25
|
}
|
|
26
26
|
const first = pending[0];
|
|
27
27
|
if (first !== undefined &&
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { copyFileSync, createReadStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { join, relative, sep } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { subtitleModelDir } from "../../kernel/paths.js";
|
|
5
6
|
import { projectById, setStageProgress } from "../admission/repo.js";
|
|
6
7
|
import { resolveFont } from "../fonts/index.js";
|
|
7
8
|
import { outputPath, projectDir } from "../storage/layout.js";
|
|
@@ -42,7 +43,7 @@ export async function prepareSubtitles(deps, context, audio, frame) {
|
|
|
42
43
|
const { projectId } = context.stage;
|
|
43
44
|
const project = projectById(deps.db, projectId);
|
|
44
45
|
if (project === undefined)
|
|
45
|
-
throw new Error("
|
|
46
|
+
throw new Error("This project no longer exists; it may have been deleted while it was running.");
|
|
46
47
|
const config = project.config.subtitles ?? defaultSubtitles;
|
|
47
48
|
if (config.mode === "off")
|
|
48
49
|
return undefined;
|
|
@@ -50,7 +51,7 @@ export async function prepareSubtitles(deps, context, audio, frame) {
|
|
|
50
51
|
setStageProgress(deps.db, context.stage.id, 0, 100);
|
|
51
52
|
context.emit({ type: "stage.progress", projectId, stage: "video", current: 0, total: 100 });
|
|
52
53
|
if (audio.length === 0)
|
|
53
|
-
throw new Error("
|
|
54
|
+
throw new Error("Captions need narration audio, but this project has none. Turn captions off in Edit project → Subtitles, or turn narration on, then Retry stage.");
|
|
54
55
|
const outputs = outputsOf(deps.db, projectId);
|
|
55
56
|
const segments = audio.map((segment) => ({
|
|
56
57
|
...segment,
|
|
@@ -67,7 +68,7 @@ export async function prepareSubtitles(deps, context, audio, frame) {
|
|
|
67
68
|
context.signal.throwIfAborted();
|
|
68
69
|
const cues = captionCues(words);
|
|
69
70
|
if (cues.length === 0)
|
|
70
|
-
throw new Error("
|
|
71
|
+
throw new Error("None of the narration could be matched to the article text, so captions can't be timed. Captions only work for English narration; if you uploaded your own audio, make sure it reads the article text, then Retry stage.");
|
|
71
72
|
writeFileSync(join(directory, "subtitles.srt"), serializeSrt(cues), { mode: 0o600 });
|
|
72
73
|
writeFileSync(join(directory, "subtitles.vtt"), serializeVtt(cues), { mode: 0o600 });
|
|
73
74
|
writeFileSync(join(directory, "subtitles.ass"), serializeAss(cues, {
|
|
@@ -117,7 +118,7 @@ async function timingKey(segments, signal) {
|
|
|
117
118
|
}
|
|
118
119
|
async function alignSegments(deps, context, segments, omissions) {
|
|
119
120
|
if (deps.alignSubtitles === undefined)
|
|
120
|
-
throw new Error("
|
|
121
|
+
throw new Error("Slopify hit an internal error (the caption timing tool is missing from this build). Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
|
|
121
122
|
const words = [];
|
|
122
123
|
const total = segments.reduce((sum, segment) => sum + segment.seconds, 0);
|
|
123
124
|
let offset = 0;
|
|
@@ -128,7 +129,7 @@ async function alignSegments(deps, context, segments, omissions) {
|
|
|
128
129
|
audioPath: segment.path,
|
|
129
130
|
onOmission: (omission) => omissions.push({ ...omission, start: omission.start + offset }),
|
|
130
131
|
text: segment.text,
|
|
131
|
-
cacheDir:
|
|
132
|
+
cacheDir: subtitleModelDir(deps.paths.dataDir),
|
|
132
133
|
ffmpeg: deps.ffmpeg,
|
|
133
134
|
signal: context.signal,
|
|
134
135
|
onProgress: (current, maximum) => {
|
|
@@ -145,7 +146,7 @@ async function alignSegments(deps, context, segments, omissions) {
|
|
|
145
146
|
});
|
|
146
147
|
for (const word of aligned) {
|
|
147
148
|
if (word.end > segment.seconds + 0.1)
|
|
148
|
-
throw new Error("
|
|
149
|
+
throw new Error("Caption timing came out longer than the narration audio. Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
|
|
149
150
|
words.push({
|
|
150
151
|
...word,
|
|
151
152
|
start: word.start + offset,
|
|
@@ -27,7 +27,7 @@ export function spokenText(deps, projectId, kind, outputs) {
|
|
|
27
27
|
return segment.text;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
throw new Error(`The saved ${kind
|
|
30
|
+
throw new Error(`The saved text of the ${kind === "body" ? "article" : kind} is missing, and captions need the text spoken in the narration. Use Re-run section on Article, then Retry stage.`);
|
|
31
31
|
}
|
|
32
32
|
function payload(raw) {
|
|
33
33
|
return textPayload.parse(JSON.parse(raw ?? "null"));
|
|
@@ -16,19 +16,19 @@ export async function runThumbnail(deps, context, providers) {
|
|
|
16
16
|
const { projectId } = context.stage;
|
|
17
17
|
const project = projectById(deps.db, projectId);
|
|
18
18
|
if (project === undefined) {
|
|
19
|
-
throw new Error(
|
|
19
|
+
throw new Error("This project no longer exists; it may have been deleted while it was running.");
|
|
20
20
|
}
|
|
21
21
|
const source = project.config.sources.thumbnail;
|
|
22
22
|
if (source !== "from_prompt" && source !== "prompt_by_llm") {
|
|
23
23
|
// Off is `skipped` and Provide is `provided` at project creation,
|
|
24
24
|
// so the runner never starts this stage for either.
|
|
25
|
-
throw new Error(`the thumbnail stage cannot run with its source set to ${source}
|
|
25
|
+
throw new Error(`Slopify hit an internal error (the thumbnail stage cannot run with its source set to ${source}). Retry stage; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
26
26
|
}
|
|
27
27
|
const choice = project.config.images;
|
|
28
28
|
if (choice === undefined) {
|
|
29
29
|
// Admission requires the image provider whenever the thumbnail is generated, so reaching
|
|
30
30
|
// here is a bug in admission rather than the user's.
|
|
31
|
-
throw new Error("the
|
|
31
|
+
throw new Error("No image model is set for the thumbnail. Choose one in Edit project → Providers, then Retry stage.");
|
|
32
32
|
}
|
|
33
33
|
const result = source === "from_prompt" ? undefined : await byLlm(deps, context, providers, project);
|
|
34
34
|
if (result !== undefined && !result.ok)
|
|
@@ -60,7 +60,7 @@ export async function runThumbnail(deps, context, providers) {
|
|
|
60
60
|
function fromTemplate(project) {
|
|
61
61
|
const rendered = project.config.rendered.thumbnailPrompt;
|
|
62
62
|
if (rendered === undefined) {
|
|
63
|
-
throw new Error("
|
|
63
|
+
throw new Error("Slopify hit an internal error (the thumbnail prompt was never filled in). Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
|
|
64
64
|
}
|
|
65
65
|
return rendered;
|
|
66
66
|
}
|
|
@@ -83,7 +83,7 @@ async function byLlm(deps, context, providers, project) {
|
|
|
83
83
|
}
|
|
84
84
|
if (llm === undefined) {
|
|
85
85
|
// Admission requires the LLM row when the thumbnail source is Prompt by LLM.
|
|
86
|
-
throw new Error("the
|
|
86
|
+
throw new Error("No AI model is set for writing the thumbnail prompt. Choose one in Edit project → Providers, then Retry stage.");
|
|
87
87
|
}
|
|
88
88
|
const messages = thumbnailMessages(brief(deps, project));
|
|
89
89
|
const answer = await providers.llm({
|
|
@@ -117,7 +117,7 @@ function named(choice) {
|
|
|
117
117
|
function brief(deps, project) {
|
|
118
118
|
const instruction = project.config.rendered.thumbnailPrompt;
|
|
119
119
|
if (instruction === undefined) {
|
|
120
|
-
throw new Error("
|
|
120
|
+
throw new Error("Slopify hit an internal error (the thumbnail prompt was never filled in). Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
|
|
121
121
|
}
|
|
122
122
|
return {
|
|
123
123
|
instruction,
|
|
@@ -133,7 +133,7 @@ function brief(deps, project) {
|
|
|
133
133
|
function articleText(deps, projectId) {
|
|
134
134
|
const article = outputsOf(deps.db, projectId).find((output) => output.role === "article_txt");
|
|
135
135
|
if (article === undefined) {
|
|
136
|
-
throw new Error("the
|
|
136
|
+
throw new Error("The thumbnail prompt is written from the article, but the article isn't ready. Let the Article stage finish, then Retry stage.");
|
|
137
137
|
}
|
|
138
138
|
return readFileSync(outputPath(deps.paths, projectId, article.path), "utf8");
|
|
139
139
|
}
|
|
@@ -212,7 +212,7 @@ function instructionsText(messages) {
|
|
|
212
212
|
}
|
|
213
213
|
function payloadOf(piece) {
|
|
214
214
|
if (piece.payload === null) {
|
|
215
|
-
throw new Error(
|
|
215
|
+
throw new Error("Slopify hit an internal error (the saved thumbnail prompt is empty). Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
|
|
216
216
|
}
|
|
217
217
|
return writtenPayload.parse(JSON.parse(piece.payload));
|
|
218
218
|
}
|
|
@@ -5,9 +5,9 @@ export async function audioInputs(deps, projectId, outputs, signal) {
|
|
|
5
5
|
const dir = projectDir(deps.paths, projectId);
|
|
6
6
|
const body = await audio(deps, outputs, "audio_body", dir, signal);
|
|
7
7
|
if (!body)
|
|
8
|
-
throw new Error("
|
|
8
|
+
throw new Error("The video has no narration audio to play. Let the Narration stage finish, then Retry stage.");
|
|
9
9
|
if (body.seconds <= 0)
|
|
10
|
-
throw new Error("the narration audio
|
|
10
|
+
throw new Error("The narration audio contains no sound; the file may be damaged or empty. Regenerate the narration (or upload a different audio file in Edit project → Narration), then Retry stage.");
|
|
11
11
|
const intro = await audio(deps, outputs, "audio_intro", dir, signal);
|
|
12
12
|
const outro = await audio(deps, outputs, "audio_outro", dir, signal);
|
|
13
13
|
return { body, intro, outro };
|
|
@@ -24,8 +24,9 @@ export function resolveFfmpeg(env, bundled) {
|
|
|
24
24
|
}
|
|
25
25
|
// Never PATH: a Slopify that quietly rendered with whichever ffmpeg the machine
|
|
26
26
|
// happens to carry would not be testing the binary it ships with.
|
|
27
|
-
throw new Error("
|
|
28
|
-
"
|
|
27
|
+
throw new Error("Slopify can't find ffmpeg, the tool it uses to make audio and video: its bundled copy is " +
|
|
28
|
+
"missing for this computer. Reinstall Slopify, or set the SLOPIFY_FFMPEG environment " +
|
|
29
|
+
"variable to the path of an ffmpeg program.");
|
|
29
30
|
}
|
|
30
31
|
export function renderArgs(plan, burnSubtitles = false) {
|
|
31
32
|
const inputs = [];
|
|
@@ -180,7 +181,7 @@ export function runFfmpeg(run) {
|
|
|
180
181
|
run.signal.addEventListener("abort", stop, { once: true });
|
|
181
182
|
child.on("error", (error) => {
|
|
182
183
|
run.signal.removeEventListener("abort", stop);
|
|
183
|
-
reject(new Error(`
|
|
184
|
+
reject(new Error(`Slopify could not start ffmpeg, the tool it uses to make audio and video (${error.message}). Reinstall Slopify, or if you set SLOPIFY_FFMPEG, check that it points to a working ffmpeg program.`));
|
|
184
185
|
});
|
|
185
186
|
child.on("close", (code, signal) => {
|
|
186
187
|
run.signal.removeEventListener("abort", stop);
|
|
@@ -240,7 +241,8 @@ function messageOf(error) {
|
|
|
240
241
|
}
|
|
241
242
|
function failure(code, signal, errors) {
|
|
242
243
|
const ended = code === null ? `killed by ${signal ?? "a signal"}` : `exited with code ${code}`;
|
|
243
|
-
|
|
244
|
+
const said = errors.length === 0
|
|
244
245
|
? `ffmpeg ${ended} and wrote nothing to its error stream`
|
|
245
246
|
: `ffmpeg ${ended}: ${errors.join(" / ")}`;
|
|
247
|
+
return `The audio/video export failed (${said}). Retry stage; if it fails again, make sure your disk has free space (see Settings → Backup & storage), then use Download diagnostics in Settings and report it.`;
|
|
246
248
|
}
|
|
@@ -13,7 +13,7 @@ export const zoomBy = 0.225;
|
|
|
13
13
|
export function planRender(input) {
|
|
14
14
|
if (input.images.length === 0) {
|
|
15
15
|
// Admission permits Video only with an image source, so an empty set is a bug upstream.
|
|
16
|
-
throw new Error("
|
|
16
|
+
throw new Error("The video needs at least one image, but there are none. Check the Images section in Edit project, then Retry stage.");
|
|
17
17
|
}
|
|
18
18
|
const frame = frames[input.format];
|
|
19
19
|
const audio = input.body === undefined ? [] : audioTimeline({ ...input, body: input.body });
|
package/dist/slices/video/run.js
CHANGED
|
@@ -12,10 +12,10 @@ export async function renderVideo(deps, context) {
|
|
|
12
12
|
const { projectId } = context.stage;
|
|
13
13
|
const project = projectById(deps.db, projectId);
|
|
14
14
|
if (!project)
|
|
15
|
-
throw new Error(
|
|
15
|
+
throw new Error("This project no longer exists; it may have been deleted while it was running.");
|
|
16
16
|
if (project.config.sources.video === "off") {
|
|
17
17
|
if (project.config.sources.audio === "off")
|
|
18
|
-
throw new Error("
|
|
18
|
+
throw new Error("There is nothing to export because both video and narration are turned off for this project. Turn one on in Edit project, then Retry stage.");
|
|
19
19
|
await exportAudioWav(deps, context, project.config.silenceGapSeconds);
|
|
20
20
|
return;
|
|
21
21
|
}
|
package/dist/updater/plan.js
CHANGED
|
@@ -147,7 +147,7 @@ export async function npmCommand() {
|
|
|
147
147
|
/* PATH can include directories without npm. */
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
throw new Error("npm
|
|
150
|
+
throw new Error("npm was not found, so in-app updates are off. Install Node.js with npm (https://nodejs.org), or update from the terminal: npx @gentbajko/slopify@latest");
|
|
151
151
|
}
|
|
152
152
|
export async function updateCommitted(dataDir, version, token) {
|
|
153
153
|
try {
|
package/dist/updater/service.js
CHANGED
|
@@ -7,7 +7,7 @@ export function createUpdater(deps) {
|
|
|
7
7
|
let status = deps.candidate?.pending === true ? "restarting" : "idle";
|
|
8
8
|
let activated = deps.candidate?.pending !== true;
|
|
9
9
|
let installError = deps.previousUpdateFailed
|
|
10
|
-
? "The
|
|
10
|
+
? "The new version did not start, so Slopify put back the previous version and database. You can try again; the reason is in logs/updates.log inside your Slopify data folder."
|
|
11
11
|
: undefined;
|
|
12
12
|
let error = installError;
|
|
13
13
|
let checking;
|
|
@@ -56,7 +56,8 @@ export function createUpdater(deps) {
|
|
|
56
56
|
}
|
|
57
57
|
catch {
|
|
58
58
|
status = "error";
|
|
59
|
-
error =
|
|
59
|
+
error =
|
|
60
|
+
"Could not check for updates: the npm registry (registry.npmjs.org) did not answer properly. Check your internet connection and try again.";
|
|
60
61
|
deps.report(error);
|
|
61
62
|
}
|
|
62
63
|
finally {
|
|
@@ -143,7 +144,7 @@ export function createUpdater(deps) {
|
|
|
143
144
|
.catch(() => {
|
|
144
145
|
status = "error";
|
|
145
146
|
error =
|
|
146
|
-
"The update could not be
|
|
147
|
+
"The update could not be installed, so Slopify kept your current version. Check your internet connection and try again; the reason is in logs/updates.log inside your Slopify data folder.";
|
|
147
148
|
installError = error;
|
|
148
149
|
deps.report(error);
|
|
149
150
|
});
|
package/dist/updater/worker.js
CHANGED
|
@@ -111,15 +111,17 @@ function installPackage(plan, directory) {
|
|
|
111
111
|
timeout: 15 * 60_000,
|
|
112
112
|
env: { ...process.env, npm_config_update_notifier: "false" },
|
|
113
113
|
});
|
|
114
|
-
child.once("error", () => reject(new Error(
|
|
115
|
-
child.once("close", (code) => code === 0
|
|
114
|
+
child.once("error", () => reject(new Error(`npm (${plan.npm.file}) could not be started. Check that Node.js and npm are installed.`)));
|
|
115
|
+
child.once("close", (code) => code === 0
|
|
116
|
+
? resolve()
|
|
117
|
+
: reject(new Error(`npm could not install Slopify ${plan.version} (exit code ${code}). Check your internet connection (npm must reach registry.npmjs.org) and try again.`)));
|
|
116
118
|
});
|
|
117
119
|
}
|
|
118
120
|
async function waitForServer(plan, version, exited) {
|
|
119
121
|
const until = Date.now() + 60_000;
|
|
120
122
|
while (Date.now() < until) {
|
|
121
123
|
if (exited())
|
|
122
|
-
throw new Error(
|
|
124
|
+
throw new Error(`Slopify ${version} stopped while starting up, so the previous version was kept.`);
|
|
123
125
|
try {
|
|
124
126
|
if (await candidateReady(serverOrigin(plan), version, plan.token, exited, globalThis.fetch))
|
|
125
127
|
return;
|
|
@@ -129,7 +131,7 @@ async function waitForServer(plan, version, exited) {
|
|
|
129
131
|
}
|
|
130
132
|
await delay(250);
|
|
131
133
|
}
|
|
132
|
-
throw new Error(
|
|
134
|
+
throw new Error(`Slopify ${version} did not answer within 60 seconds of starting.`);
|
|
133
135
|
}
|
|
134
136
|
function serverOrigin(plan) {
|
|
135
137
|
const host = plan.host === "0.0.0.0" ? "127.0.0.1" : plan.host === "::" ? "::1" : plan.host;
|