@gentbajko/slopify 3.25.2 → 3.25.4

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.
@@ -0,0 +1,5 @@
1
+ # Slopify 3.25.3
2
+
3
+ Released 17 October 2026.
4
+
5
+ - **Older projects keep their description and shorts when you turn softening on.** A project made before late September still asked for its YouTube description and shorts' pick again when Soften breaths or Soften S sounds was turned on. It now keeps them, like newer projects.
@@ -0,0 +1,5 @@
1
+ # Slopify 3.25.4
2
+
3
+ Released 17 October 2026.
4
+
5
+ - **The shorts of an older project render after you change it.** On a project made before late September, a saved change (such as turning on Soften breaths) left its shorts waiting forever: they showed as waiting their turn and never started. They now render.
@@ -1,4 +1,16 @@
1
1
  [
2
+ {
3
+ "id": "3.25.4",
4
+ "title": "Slopify 3.25.4",
5
+ "version": "3.25.4",
6
+ "date": "2026-10-17"
7
+ },
8
+ {
9
+ "id": "3.25.3",
10
+ "title": "Slopify 3.25.3",
11
+ "version": "3.25.3",
12
+ "date": "2026-10-17"
13
+ },
2
14
  {
3
15
  "id": "3.25.2",
4
16
  "title": "Slopify 3.25.2",
@@ -53,9 +53,17 @@ unsoftened = () => []) {
53
53
  }, audio.keys);
54
54
  const timedNow = timed ? timingOf(audio.timeline) : undefined;
55
55
  // A finished timing made before the narration was softened is kept: softening moves no word.
56
- const doneTiming = (fingerprint) => (context.history ?? context.manifest).pieces.some((row) => row.key === "subtitles:timing" &&
57
- row.piece.state === "done" &&
58
- row.fingerprint === fingerprint);
56
+ // A timing is finished when a done piece or an output of it has the fingerprint (a project
57
+ // from before an older fingerprint scheme carries different ones on the two).
58
+ const doneTiming = (fingerprint) => {
59
+ const past = context.history ?? context.manifest;
60
+ return (past.pieces.some((row) => row.key === "subtitles:timing" &&
61
+ row.piece.state === "done" &&
62
+ row.fingerprint === fingerprint) ||
63
+ past.outputs.some((row) => row.workKey === "subtitles:timing" &&
64
+ row.fingerprint === fingerprint &&
65
+ (row.state === "ready" || row.state === "outdated")));
66
+ };
59
67
  const timing = timedNow === undefined || doneTiming(timedNow.fingerprint)
60
68
  ? timedNow
61
69
  : (unsoftened()
@@ -220,10 +220,18 @@ export function shortRemakeKey(number) {
220
220
  }
221
221
  // What the pick saved, when its saved answer is the one this plan asks for.
222
222
  export function savedPicked(context, pick) {
223
- const piece = context.manifest.pieces.find((row) => row.key === pick.key &&
224
- selectedReference(row) &&
225
- row.piece.state === "done" &&
226
- row.fingerprint === pick.fingerprint);
223
+ const done = context.manifest.pieces.filter((row) => row.key === pick.key && selectedReference(row) && row.piece.state === "done");
224
+ const piece = done.find((row) => row.fingerprint === pick.fingerprint) ??
225
+ // A project from before an older fingerprint scheme: its output carries the plan's
226
+ // fingerprint and its piece another one. The version's one selected pick belongs with its
227
+ // selected output, so that output's fingerprint vouches for it.
228
+ (done.length === 1 &&
229
+ context.manifest.outputs.some((row) => row.workKey === pick.key &&
230
+ selectedReference(row) &&
231
+ row.fingerprint === pick.fingerprint &&
232
+ (row.state === "ready" || row.state === "outdated"))
233
+ ? done[0]
234
+ : undefined);
227
235
  return pickedShortsOf(piece?.piece.payload);
228
236
  }
229
237
  // A clip's saved image prompts, when they answer this plan's request. The request carries
@@ -23,19 +23,27 @@ export function unsoftenedTimelines(context, text) {
23
23
  const entries = plain.timeline;
24
24
  const timeline = entries.map((entry, at) => {
25
25
  const key = plain.keys[at];
26
- const made = past.outputs.find(
27
- // Found by its fingerprint: once the softened version is saved, the join it will make
28
- // again is marked outdated.
29
- (row) => row.workKey === key &&
30
- row.fingerprint === entry.audio[0] &&
26
+ // Found by its fingerprint, as an output or as the finished piece that made it: once the
27
+ // softened version is saved, the join it will make again is marked outdated, and on a
28
+ // project from before an older fingerprint scheme its outputs carry another fingerprint
29
+ // than its pieces.
30
+ const fingerprint = entry.audio[0];
31
+ const output = past.outputs.find((row) => row.workKey === key &&
32
+ row.fingerprint === fingerprint &&
31
33
  (row.state === "ready" || row.state === "outdated"));
32
- return made === undefined
33
- ? undefined
34
- : {
35
- audio: [entry.audio[0], made.assetId],
36
- transcript: entry.transcript,
37
- durationMs: made.output.durationMs ?? null,
38
- };
34
+ const piece = past.pieces.find((row) => row.key === key &&
35
+ row.fingerprint === fingerprint &&
36
+ row.piece.state === "done" &&
37
+ row.assetId !== null);
38
+ const assetId = output?.assetId ?? piece?.assetId ?? undefined;
39
+ if (assetId === undefined || assetId === null)
40
+ return undefined;
41
+ const made = output ?? past.outputs.find((row) => row.workKey === key && row.assetId === assetId);
42
+ return {
43
+ audio: [fingerprint, assetId],
44
+ transcript: entry.transcript,
45
+ durationMs: made?.output.durationMs ?? null,
46
+ };
39
47
  });
40
48
  return timeline.every((entry) => entry !== undefined) && timeline.length > 0 ? [timeline] : [];
41
49
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gentbajko/slopify",
3
- "version": "3.25.2",
3
+ "version": "3.25.4",
4
4
  "description": "Turn a prompt into a finished video, locally, with your own provider keys",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Gent Bajko",