opencaptions 1.2.0 → 1.2.1

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/captions.js CHANGED
@@ -9,7 +9,7 @@ import { basename, dirname, extname, join, resolve } from "node:path";
9
9
  import { Readable } from "node:stream";
10
10
  import { pipeline } from "node:stream/promises";
11
11
  import { OpenCaptionsError, unwrap, } from "./client.js";
12
- import { captionLines, setWord, shuffleVariants } from "./engine.js";
12
+ import { captionLines, mergeWords, setWord, shuffleVariants } from "./engine.js";
13
13
  import { findLook, LOOKS } from "./looks.js";
14
14
  /** How often a job is asked about, in milliseconds. */
15
15
  const pollMs = () => Number(process.env.OPENCAPTIONS_POLL_MS) || 1000;
@@ -121,15 +121,17 @@ export async function readTranscript(client, projectId) {
121
121
  const lines = await captionLines(project.transcript, project.style_config?.words_per_line ?? 3, project.caption_offset_ms);
122
122
  return { lines, words };
123
123
  }
124
- /** Sets words by their index; empty text removes a word. The indices are the ones read before. */
125
124
  export async function fixWords(client, projectId, fixes) {
126
125
  const project = await getProject(client, projectId);
127
126
  if (!project.transcript)
128
127
  throw new OpenCaptionsError("This project has no captions yet.");
129
- // Last first: removing a word moves the ones after it, never the ones before.
128
+ // Last first: removing or joining words moves the ones after them, never the ones before.
130
129
  let transcript = project.transcript;
131
130
  for (const fix of [...fixes].sort((a, b) => b.index - a.index)) {
132
- transcript = await setWord(transcript, fix.index, fix.text);
131
+ transcript =
132
+ (fix.count ?? 1) > 1
133
+ ? await mergeWords(transcript, fix.index, fix.count ?? 1, fix.text)
134
+ : await setWord(transcript, fix.index, fix.text);
133
135
  }
134
136
  await update(client, projectId, { transcript });
135
137
  return fixes.length;
package/dist/engine.js CHANGED
@@ -36,6 +36,12 @@ export async function setWord(t, index, text) {
36
36
  const x = await exports();
37
37
  return result(x, x.oc_set_word(...json(x, t), index, ...copyIn(x, encoder.encode(text))));
38
38
  }
39
+ /** `count` words from `index` replaced by one word `text`, spanning them all. */
40
+ export async function mergeWords(t, index, count, text) {
41
+ const x = await exports();
42
+ const words = copyIn(x, encoder.encode(text));
43
+ return result(x, x.oc_merge_words(...json(x, t), index, count, ...words));
44
+ }
39
45
  /** Deals every word one of `count` looks, or the style's own, as picking such a look does. */
40
46
  export async function shuffleVariants(t, count) {
41
47
  const x = await exports();
package/dist/mcp.js CHANGED
@@ -88,7 +88,8 @@ const TOOLS = [
88
88
  {
89
89
  name: "fix_words",
90
90
  description: "Sets words by the index get_transcript gave them, keeping their timing; empty text removes " +
91
- "a word. Then save_video writes the video again.",
91
+ "a word, and count joins that many words into one spanning them all (two words heard for " +
92
+ "one). Then save_video writes the video again.",
92
93
  inputSchema: {
93
94
  type: "object",
94
95
  properties: {
@@ -97,7 +98,15 @@ const TOOLS = [
97
98
  type: "array",
98
99
  items: {
99
100
  type: "object",
100
- properties: { index: { type: "integer", minimum: 0 }, text: { type: "string" } },
101
+ properties: {
102
+ index: { type: "integer", minimum: 0 },
103
+ text: { type: "string" },
104
+ count: {
105
+ type: "integer",
106
+ minimum: 1,
107
+ description: "How many words from index the text replaces, as one word (default 1)",
108
+ },
109
+ },
101
110
  required: ["index", "text"],
102
111
  },
103
112
  },
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencaptions",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Animated word-by-word captions for your videos, from Claude, Cursor or any MCP client, or the command line. Runs on your own OpenCaptions server.",
5
5
  "mcpName": "io.github.leogaudin/opencaptions",
6
6
  "license": "AGPL-3.0-only",
package/server.json CHANGED
@@ -9,13 +9,13 @@
9
9
  "source": "github",
10
10
  "subfolder": "apps/agent"
11
11
  },
12
- "version": "1.2.0",
12
+ "version": "1.2.1",
13
13
  "packages": [
14
14
  {
15
15
  "registryType": "npm",
16
16
  "registryBaseUrl": "https://registry.npmjs.org",
17
17
  "identifier": "opencaptions",
18
- "version": "1.2.0",
18
+ "version": "1.2.1",
19
19
  "transport": { "type": "stdio" },
20
20
  "packageArguments": [{ "type": "positional", "value": "mcp" }],
21
21
  "environmentVariables": [