@gentbajko/slopify 3.25.5 → 3.25.6

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.6
2
+
3
+ Released 17 October 2026.
4
+
5
+ - **Saving a softened project again no longer times the words again.** With Soften breaths or Soften S sounds on, a second save or a restore could plan the word timing, the description and the shorts' pick again, which are paid calls. Slopify now finds the narration the timing was made from in the project's earlier versions and keeps the timing.
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "id": "3.25.6",
4
+ "title": "Slopify 3.25.6",
5
+ "version": "3.25.6",
6
+ "date": "2026-10-17"
7
+ },
2
8
  {
3
9
  "id": "3.25.5",
4
10
  "title": "Slopify 3.25.5",
@@ -156,7 +156,12 @@ function planEdit(base, edit, prepared, inspected, keeping) {
156
156
  config: base.revision.config,
157
157
  content: base.revision.content,
158
158
  manifest,
159
- history: { outputs: base.outputs, pieces: base.pieces },
159
+ history: {
160
+ outputs: base.outputs,
161
+ pieces: base.pieces,
162
+ earlierJoins: base.earlierJoins,
163
+ earlierJoinPieces: base.earlierJoinPieces,
164
+ },
160
165
  resolved: {
161
166
  articleMarkdown: base.articleMarkdown,
162
167
  researchNotes: base.revision.config.provided.research ?? null,
@@ -28,10 +28,10 @@ export function unsoftenedTimelines(context, text) {
28
28
  // project from before an older fingerprint scheme its outputs carry another fingerprint
29
29
  // than its pieces.
30
30
  const fingerprint = entry.audio[0];
31
- const output = past.outputs.find((row) => row.workKey === key &&
31
+ const output = [...past.outputs, ...(past.earlierJoins ?? [])].find((row) => row.workKey === key &&
32
32
  row.fingerprint === fingerprint &&
33
33
  (row.state === "ready" || row.state === "outdated"));
34
- const piece = past.pieces.find((row) => row.key === key &&
34
+ const piece = [...past.pieces, ...(past.earlierJoinPieces ?? [])].find((row) => row.key === key &&
35
35
  row.fingerprint === fingerprint &&
36
36
  row.piece.state === "done" &&
37
37
  row.assetId !== null);
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { z } from "zod";
3
3
  import { catalogueSchema } from "../../catalog/schema.js";
4
- import { getRevisionView } from "../revisions/view.js";
4
+ import { getRevisionView, withEarlierJoins } from "../revisions/view.js";
5
5
  import { outputPath } from "../storage/layout.js";
6
6
  import { referencedAssetsAvailable, retainedNarrationPieces } from "./narration-history.js";
7
7
  import { applyProvidedReviews } from "./provided-review.js";
@@ -58,7 +58,7 @@ export function executionPlan(deps, view, catalogue) {
58
58
  .filter((row) => row.available && row.assetId !== null)
59
59
  .flatMap((row) => (row.assetId === null ? [] : [row.assetId])),
60
60
  ]);
61
- return applyProvidedReviews(deps.db, view.revision.id, planRevisionWork(view.revision, view, catalogue, available, {
61
+ return applyProvidedReviews(deps.db, view.revision.id, planRevisionWork(view.revision, withEarlierJoins(deps, view), catalogue, available, {
62
62
  ...resolved,
63
63
  ...(research === undefined ? {} : { research }),
64
64
  }, history));
@@ -64,6 +64,21 @@ export function insertManifestPiece(db, revision, piece, recordId, publicationId
64
64
  (id,project_id,revision_id,piece_key,stage_kind,asset_id,fingerprint,descriptor,publication_id,selected,created_at)
65
65
  VALUES (?,?,?,?,?,?,?,?,?,0,?)`).run(recordId, revision.projectId, revision.id, piece.key, piece.stageKind, piece.assetId, piece.fingerprint, JSON.stringify(piece.piece), publicationId, revision.createdAt);
66
66
  }
67
+ // Every narration join (intro, body, outro) any version of the project recorded.
68
+ export function narrationJoinsOfProject(db, projectId) {
69
+ return db
70
+ .prepare("SELECT * FROM revision_outputs WHERE project_id=? AND work_key IN ('audio:intro','audio:body:concat','audio:outro') ORDER BY created_at,id")
71
+ .all(projectId)
72
+ .map(toOutput);
73
+ }
74
+ // The finished pieces of those joins, for a project whose outputs and pieces carry different
75
+ // fingerprints (made before a fingerprint change).
76
+ export function narrationJoinPiecesOfProject(db, projectId) {
77
+ return db
78
+ .prepare("SELECT * FROM revision_pieces WHERE project_id=? AND piece_key IN ('audio:intro','audio:body:concat','audio:outro') ORDER BY created_at,id")
79
+ .all(projectId)
80
+ .map(toPiece);
81
+ }
67
82
  export function outputsForRevision(db, projectId, revisionId) {
68
83
  return db
69
84
  .prepare("SELECT * FROM revision_outputs WHERE project_id=? AND revision_id=? ORDER BY created_at,id")
@@ -25,6 +25,7 @@ import { insertAsset, insertRevision } from "./repo.js";
25
25
  import { bindNarrationSources, validateNarrationIntent, validateTemplateIntent } from "./rules.js";
26
26
  import { revisionEditSchema } from "./schema.js";
27
27
  import { keptSubject } from "./subject.js";
28
+ import { withEarlierJoins } from "./view.js";
28
29
  export { bindUpload } from "./mutation-assets.js";
29
30
  export { restoreRevision } from "./restore.js";
30
31
  export async function saveRevision(deps, input) {
@@ -111,7 +112,7 @@ export async function saveRevision(deps, input) {
111
112
  ],
112
113
  pieces: [],
113
114
  };
114
- let plan = planRevision(fresh, finalEdit, preparedManifest, inspected);
115
+ let plan = planRevision(withEarlierJoins(deps, fresh), finalEdit, preparedManifest, inspected);
115
116
  if (!plan.ok)
116
117
  return {
117
118
  ok: false,
@@ -129,7 +130,7 @@ export async function saveRevision(deps, input) {
129
130
  if (cuesChanged(fresh, finalEdit) && plan.content.subtitleCues !== undefined) {
130
131
  const timing = plan.recipes.find((row) => row.key === "subtitles:timing");
131
132
  if (timing !== undefined) {
132
- plan = planRevision(fresh, {
133
+ plan = planRevision(withEarlierJoins(deps, fresh), {
133
134
  ...finalEdit,
134
135
  // From the edit as sent, so the plan decides the kept shorts the same way again.
135
136
  content: {
@@ -7,7 +7,7 @@ import { checkMutation, insertReceipt, refusal, requestHash, requiredView, } fro
7
7
  import { advanceHead, logicalKeys } from "./mutations.js";
8
8
  import { cloneManifest, ensureRevisionStages, projectSelected } from "./projection.js";
9
9
  import { insertRevision } from "./repo.js";
10
- import { getRevisionView } from "./view.js";
10
+ import { getRevisionView, withEarlierJoins } from "./view.js";
11
11
  export async function restoreRevision(deps, input) {
12
12
  const identity = {
13
13
  ...input,
@@ -24,7 +24,7 @@ export async function restoreRevision(deps, input) {
24
24
  if (target === undefined)
25
25
  return refusal(deps, input.projectId, "no-revision");
26
26
  const base = requiredView(deps, input.projectId, input.baseRevisionId);
27
- const comparison = planRevision(base, {
27
+ const comparison = planRevision(withEarlierJoins(deps, base), {
28
28
  config: target.revision.config,
29
29
  content: target.revision.content,
30
30
  });
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, statSync } from "node:fs";
2
2
  import { z } from "zod";
3
3
  import { outputPath } from "../storage/layout.js";
4
+ import { narrationJoinPiecesOfProject, narrationJoinsOfProject } from "./manifest-repo.js";
4
5
  import { currentRevisionId, outputsForRevision, piecesForRevision, revisionById } from "./repo.js";
5
6
  export function getRevisionView(deps, projectId, revisionId) {
6
7
  const revision = revisionById(deps.db, projectId, revisionId);
@@ -44,3 +45,13 @@ export function resolvedArticleMarkdown(deps, revision, outputs) {
44
45
  ? ""
45
46
  : null;
46
47
  }
48
+ // The view with the narration joins of every version of the project, for planning: turning
49
+ // softening on looks up the joins a finished word timing was made from
50
+ // (`rebuild/recipe-softening.ts`), which a later version no longer holds.
51
+ export function withEarlierJoins(deps, view) {
52
+ return {
53
+ ...view,
54
+ earlierJoins: narrationJoinsOfProject(deps.db, view.revision.projectId),
55
+ earlierJoinPieces: narrationJoinPiecesOfProject(deps.db, view.revision.projectId),
56
+ };
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gentbajko/slopify",
3
- "version": "3.25.5",
3
+ "version": "3.25.6",
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",