@poe-platform/safe-bash 0.1.839 → 0.1.840

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.
Files changed (61) hide show
  1. package/dist/mp4-ast/types.d.ts +4 -2
  2. package/dist/pdf-ast/cos/security.d.ts +10 -0
  3. package/dist/pdf-ast/edit/retained-attachment-edits.d.ts +4 -0
  4. package/dist/pdf-ast/edit/retained-graph.d.ts +1 -1
  5. package/dist/pdf-ast/image.js +1 -1
  6. package/dist/pdf-ast/index.d.ts +2 -1
  7. package/dist/pdf-ast/index.js +7 -1
  8. package/dist/pdf-ast/vendor/pdfjs-fonts.d.mts +8 -0
  9. package/dist/pdf-ast/vendor/pdfjs-fonts.mjs +28 -0
  10. package/dist/safe-bash/command-chunks/{chunk-SG2KOOD4.js → chunk-3SAYZFMD.js} +2 -2
  11. package/dist/safe-bash/command-chunks/{chunk-FH5VMAPS.js → chunk-45ZH6CNF.js} +20 -4
  12. package/dist/safe-bash/command-chunks/{chunk-T6OXG7QQ.js → chunk-AAOPVEPF.js} +66 -1
  13. package/dist/safe-bash/command-chunks/{chunk-46GA576V.js → chunk-CPKVUB7S.js} +2 -2
  14. package/dist/safe-bash/command-chunks/{chunk-MBXNG6C3.js → chunk-DLLN3C3Q.js} +1 -1
  15. package/dist/safe-bash/command-chunks/{chunk-ZSQCROFE.js → chunk-DM5T5D2K.js} +5 -1
  16. package/dist/safe-bash/command-chunks/{chunk-3EAZRKI4.js → chunk-G7HQYFIU.js} +10 -1
  17. package/dist/safe-bash/command-chunks/{chunk-GYKQ3IOV.js → chunk-LMYQMJYY.js} +94 -18
  18. package/dist/safe-bash/command-chunks/{chunk-DX7KU6YU.js → chunk-N52AHO6S.js} +1 -1
  19. package/dist/safe-bash/command-chunks/{chunk-4BSD2RGK.js → chunk-QPKL5GWH.js} +102 -42
  20. package/dist/safe-bash/command-chunks/{chunk-Z2T7E4ID.js → chunk-R7QLP5RH.js} +1212 -1119
  21. package/dist/safe-bash/command-chunks/{chunk-RSFKQE2U.js → chunk-T5WFJ44Z.js} +1 -1
  22. package/dist/safe-bash/command-chunks/{chunk-77ROD2TR.js → chunk-X3WNWRVT.js} +11 -5
  23. package/dist/safe-bash/command-chunks/{chunk-TCXM7BSZ.js → chunk-YFMNXPLP.js} +4 -4
  24. package/dist/safe-bash/command-chunks/{chunk-DQVOBGI2.js → chunk-YVFQ5I36.js} +7335 -7162
  25. package/dist/safe-bash-command-exiftool/index.js +25 -1
  26. package/dist/safe-bash-command-ffmpeg/index.js +174 -41
  27. package/dist/safe-bash-command-ffprobe/index.js +2 -2
  28. package/dist/safe-bash-command-ffprobe/media.js +2 -2
  29. package/dist/safe-bash-command-imagemagick/index.js +482 -98
  30. package/dist/safe-bash-command-mdq/index.js +2 -0
  31. package/dist/safe-bash-command-pandoc/citeproc-filters.js +7 -7
  32. package/dist/safe-bash-command-pandoc/command.js +8 -8
  33. package/dist/safe-bash-command-pandoc/index.js +51 -51
  34. package/dist/safe-bash-command-qpdf/index.js +1037 -654
  35. package/dist/safe-bash-command-qpdf/page-range.d.ts +2 -0
  36. package/dist/safe-bash-command-soffice/index.js +28 -8
  37. package/dist/safe-bash-command-sqlite3/btree.d.ts +2 -1
  38. package/dist/safe-bash-command-sqlite3/engine.d.ts +15 -1
  39. package/dist/safe-bash-command-sqlite3/index.js +294 -68
  40. package/dist/safe-bash-command-tar/create.js +2 -2
  41. package/dist/safe-bash-command-tar/extract.js +2 -2
  42. package/dist/safe-bash-command-tar/format.js +1 -1
  43. package/dist/safe-bash-command-tar/index.js +4 -4
  44. package/dist/safe-bash-command-tar/modes.js +4 -4
  45. package/dist/safe-bash-command-unzip/index.js +1 -1
  46. package/dist/safe-bash-command-unzip/unzip.js +1 -1
  47. package/dist/safe-bash-markdown-engine/commonmark-blocks.d.ts +17 -1
  48. package/dist/safe-bash-markdown-engine/commonmark-blocks.js +3 -3
  49. package/dist/safe-bash-markdown-engine/commonmark-inlines.d.ts +1 -1
  50. package/dist/safe-bash-markdown-engine/commonmark-inlines.js +3 -3
  51. package/dist/safe-bash-markdown-engine/commonmark-syntax.d.ts +5 -0
  52. package/dist/safe-bash-markdown-engine/commonmark-syntax.js +5 -1
  53. package/dist/safe-bash-markdown-engine/gfm-syntax.js +2 -2
  54. package/dist/safe-bash-markdown-engine/index.js +4 -4
  55. package/dist/safe-bash-zip-engine/index.js +1 -1
  56. package/dist/safe-bash-zip-engine/zip/repair.js +1 -1
  57. package/dist/safe-bash-zip-engine/zip/sfx.js +1 -1
  58. package/dist/safe-bash-zip-engine/zip-format.d.ts +2 -0
  59. package/dist/safe-bash-zip-engine/zip-format.js +1 -1
  60. package/package.json +2 -2
  61. package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +2 -0
@@ -2208,6 +2208,29 @@ function selected(tags, names, duplicates, resources) {
2208
2208
  }
2209
2209
  return values;
2210
2210
  }
2211
+ var jpegPrintConversions = {
2212
+ Orientation: {
2213
+ "1": "Horizontal (normal)",
2214
+ "2": "Mirror horizontal",
2215
+ "3": "Rotate 180",
2216
+ "4": "Mirror vertical",
2217
+ "5": "Mirror horizontal and rotate 270 CW",
2218
+ "6": "Rotate 90 CW",
2219
+ "7": "Mirror horizontal and rotate 90 CW",
2220
+ "8": "Rotate 270 CW"
2221
+ },
2222
+ ResolutionUnit: { "1": "None", "2": "inches", "3": "cm" }
2223
+ };
2224
+ function printConverted(tags, invocation, resources) {
2225
+ if (invocation.numeric || invocation.binary) return tags;
2226
+ resources.admit("work", tags.length * (invocation.valueConvTags.length + 1) * 64);
2227
+ resources.admit("retained", tags.length * 512);
2228
+ return tags.map((tag) => {
2229
+ const conversions = tag.chunkType === "JPEG" && tag.group === "IFD0" ? jpegPrintConversions[tag.name] : void 0;
2230
+ if (!conversions || invocation.valueConvTags.includes(tag.name.toLowerCase())) return tag;
2231
+ return { ...tag, value: conversions[tag.value] ?? "Unknown (" + tag.value + ")" };
2232
+ });
2233
+ }
2211
2234
  function createExiftoolCommand(options = {}) {
2212
2235
  const limits = Object.freeze({ ...options.limits });
2213
2236
  return Object.freeze({
@@ -2436,6 +2459,7 @@ Limits:
2436
2459
  continue;
2437
2460
  }
2438
2461
  read++;
2462
+ tags = printConverted(tags, invocation, resources);
2439
2463
  const chosen = selected(tags, invocation.tags, invocation.json ? invocation.groupFamily === 4 : invocation.duplicates && !invocation.csv, resources);
2440
2464
  if (invocation.xml || invocation.tabular || invocation.template !== void 0) {
2441
2465
  const values = invocation.template !== void 0 ? selected(tags, [], false, resources) : chosen;
@@ -2634,7 +2658,7 @@ function evalSyncExiftool(inBytes, opArgs, readFileSync, writeFileSync) {
2634
2658
  continue;
2635
2659
  }
2636
2660
  const metadata = jpeg ? inspectJpeg(bytes, resources).tags : png ? inspectPng(bytes, resources).tags : inspectPdf(bytes, resources).tags;
2637
- const tags = [...systemTags(file, bytes.length, metadata, invocation, resources), ...metadata];
2661
+ const tags = printConverted([...systemTags(file, bytes.length, metadata, invocation, resources), ...metadata], invocation, resources);
2638
2662
  const chosen = selected(tags, invocation.tags, invocation.json ? invocation.groupFamily === 4 : invocation.duplicates && !invocation.csv, resources);
2639
2663
  if (invocation.xml || invocation.tabular || invocation.template !== void 0) {
2640
2664
  const values = invocation.template !== void 0 ? selected(tags, [], false, resources) : chosen;
@@ -20,7 +20,7 @@ import {
20
20
  sliceMp4,
21
21
  srtAst,
22
22
  webvttAst
23
- } from "../safe-bash/command-chunks/chunk-FH5VMAPS.js";
23
+ } from "../safe-bash/command-chunks/chunk-45ZH6CNF.js";
24
24
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
25
25
 
26
26
  // packages/safe-bash-command-ffmpeg/src/index.ts
@@ -87,8 +87,21 @@ function parseFfmpegTimestamp(spec) {
87
87
  return parseFloat(trimmed) || 0;
88
88
  }
89
89
  function parseColorRgba(colorStr) {
90
- const s = colorStr.trim().toLowerCase();
90
+ const value = colorStr.trim().toLowerCase();
91
+ const separator = value.indexOf("@");
92
+ const s = separator < 0 ? value : value.slice(0, separator);
93
+ let alpha;
94
+ if (separator >= 0) {
95
+ const suffix = value.slice(separator + 1);
96
+ const opacity = Number(suffix);
97
+ const maximum = suffix.startsWith("0x") ? 255 : 1;
98
+ if (!suffix || !Number.isFinite(opacity) || opacity < 0 || opacity > maximum) {
99
+ throw new Error(`Invalid color alpha: ${colorStr}`);
100
+ }
101
+ alpha = Math.floor(opacity * 255 / maximum);
102
+ }
91
103
  const named = {
104
+ aliceblue: [240, 248, 255, 255],
92
105
  black: [0, 0, 0, 255],
93
106
  white: [255, 255, 255, 255],
94
107
  red: [255, 0, 0, 255],
@@ -103,17 +116,21 @@ function parseColorRgba(colorStr) {
103
116
  orange: [255, 165, 0, 255],
104
117
  purple: [128, 0, 128, 255]
105
118
  };
106
- if (named[s]) return named[s];
107
- const hex = s.startsWith("#") ? s.slice(1) : s.startsWith("0x") ? s.slice(2) : "";
108
- if (hex.length === 6) {
119
+ if (named[s]) {
120
+ const color = named[s];
121
+ color[3] = alpha ?? color[3];
122
+ return color;
123
+ }
124
+ const hex = s.startsWith("#") ? s.slice(1) : s.startsWith("0x") ? s.slice(2) : s;
125
+ if ((hex.length === 6 || hex.length === 8) && [...hex].every((digit) => "0123456789abcdef".includes(digit))) {
109
126
  return [
110
127
  parseInt(hex.slice(0, 2), 16) || 0,
111
128
  parseInt(hex.slice(2, 4), 16) || 0,
112
129
  parseInt(hex.slice(4, 6), 16) || 0,
113
- 255
130
+ alpha ?? (hex.length === 8 ? parseInt(hex.slice(6, 8), 16) : 255)
114
131
  ];
115
132
  }
116
- return [40, 120, 220, 255];
133
+ throw new Error(`Invalid color: ${colorStr}`);
117
134
  }
118
135
  function detachBytesBuffer(bytes) {
119
136
  if (!bytes) return;
@@ -155,11 +172,12 @@ function* lavfiSteps(spec, budget, durationOverride) {
155
172
  }
156
173
  if (filterName === "sine" || filterName === "anullsrc") {
157
174
  const sampleRate = parseInt(kv.sample_rate ?? kv.r ?? "44100", 10) || 44100;
158
- const freq = parseFloat(kv.frequency ?? kv.f ?? "440") || 440;
175
+ const parsedFrequency = parseFloat(kv.frequency ?? kv.f ?? "440");
176
+ const freq = Number.isNaN(parsedFrequency) ? 440 : parsedFrequency;
159
177
  const duration2 = durationOverride ?? (parseFloat(kv.duration ?? kv.d ?? "1") || 1);
160
178
  const layout = kv.channel_layout ?? kv.cl;
161
179
  const channels = layout === "mono" || layout === void 0 && filterName === "sine" ? 1 : 2;
162
- const totalSamples = Math.max(1024, Math.round(duration2 * sampleRate));
180
+ const totalSamples = Math.max(0, Math.round(duration2 * sampleRate));
163
181
  budget.checkDuration(duration2);
164
182
  const channelData = [];
165
183
  for (let channel = 0; channel < channels; channel++) {
@@ -167,7 +185,7 @@ function* lavfiSteps(spec, budget, durationOverride) {
167
185
  if (filterName === "sine") {
168
186
  for (let i = 0; i < totalSamples; i++) {
169
187
  if (i % 4096 === 0) yield;
170
- arr[i] = Math.sin(2 * Math.PI * freq * i / sampleRate) * 0.5;
188
+ arr[i] = Math.sin(2 * Math.PI * freq * i / sampleRate) * 0.125;
171
189
  }
172
190
  }
173
191
  channelData.push(arr);
@@ -302,12 +320,57 @@ function evalScaleDim(expr, iw, ih) {
302
320
  if (clean === "-1" || clean === "-2") return -1;
303
321
  if (clean === "iw" || clean === "in_w") return iw;
304
322
  if (clean === "ih" || clean === "in_h") return ih;
305
- if (clean.startsWith("iw/")) return Math.max(2, Math.round(iw / (parseFloat(clean.slice(3)) || 1)));
306
- if (clean.startsWith("ih/")) return Math.max(2, Math.round(ih / (parseFloat(clean.slice(3)) || 1)));
307
- if (clean.startsWith("iw*")) return Math.max(2, Math.round(iw * (parseFloat(clean.slice(3)) || 1)));
308
- if (clean.startsWith("ih*")) return Math.max(2, Math.round(ih * (parseFloat(clean.slice(3)) || 1)));
323
+ if (clean.startsWith("iw/")) return Math.max(1, Math.round(iw / (parseFloat(clean.slice(3)) || 1)));
324
+ if (clean.startsWith("ih/")) return Math.max(1, Math.round(ih / (parseFloat(clean.slice(3)) || 1)));
325
+ if (clean.startsWith("iw*")) return Math.max(1, Math.round(iw * (parseFloat(clean.slice(3)) || 1)));
326
+ if (clean.startsWith("ih*")) return Math.max(1, Math.round(ih * (parseFloat(clean.slice(3)) || 1)));
309
327
  return parseInt(clean, 10) || iw;
310
328
  }
329
+ function parseRotateAngle(expression) {
330
+ let offset = 0;
331
+ const source = expression.trim();
332
+ function parse(minPrecedence = 0, depth = 0) {
333
+ if (depth > 64) throw new Error("rotate: angle expression is too deeply nested");
334
+ while (source[offset] === " ") offset++;
335
+ let value;
336
+ const first = source[offset];
337
+ if (first === "+" || first === "-") {
338
+ offset++;
339
+ value = parse(3, depth + 1) * (first === "-" ? -1 : 1);
340
+ } else if (first === "(") {
341
+ offset++;
342
+ value = parse(0, depth + 1);
343
+ if (source[offset++] !== ")") throw new Error("rotate: invalid angle expression");
344
+ } else if (source.slice(offset, offset + 2) === "PI") {
345
+ offset += 2;
346
+ value = Math.PI;
347
+ } else {
348
+ const start = offset;
349
+ while (offset < source.length) {
350
+ const c = source[offset];
351
+ if (c >= "0" && c <= "9" || c === "." || c === "e" || c === "E" || (c === "+" || c === "-") && (source[offset - 1] === "e" || source[offset - 1] === "E")) offset++;
352
+ else break;
353
+ }
354
+ value = start === offset ? NaN : Number(source.slice(start, offset));
355
+ }
356
+ for (; ; ) {
357
+ while (source[offset] === " ") offset++;
358
+ const operator = source[offset];
359
+ const precedence = operator === "+" || operator === "-" ? 1 : operator === "*" || operator === "/" ? 2 : 0;
360
+ if (precedence === 0 || precedence < minPrecedence) break;
361
+ offset++;
362
+ const right = parse(precedence + 1, depth + 1);
363
+ if (operator === "+") value += right;
364
+ else if (operator === "-") value -= right;
365
+ else if (operator === "*") value *= right;
366
+ else value /= right;
367
+ }
368
+ return value;
369
+ }
370
+ const angle = parse();
371
+ if (offset !== source.length || !Number.isFinite(angle)) throw new Error("rotate: invalid or unsupported angle expression");
372
+ return angle;
373
+ }
311
374
  var GLYPH_5X7 = {
312
375
  " ": [0, 0, 0, 0, 0, 0, 0],
313
376
  "0": [14, 17, 19, 21, 25, 17, 14],
@@ -464,7 +527,23 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
464
527
  let work = 0;
465
528
  let current = frames;
466
529
  if (current.length === 0) return current;
467
- const filters = filterChainStr.split(",").map((f) => f.trim()).filter(Boolean);
530
+ const filters = [];
531
+ let filter = "";
532
+ let quoted = false;
533
+ for (let i = 0; i < filterChainStr.length; i++) {
534
+ const char = filterChainStr[i];
535
+ if (char === "'") {
536
+ quoted = !quoted;
537
+ } else if (!quoted && char === "\\" && i + 1 < filterChainStr.length) {
538
+ filter += filterChainStr[++i];
539
+ } else if (!quoted && char === ",") {
540
+ if (filter.trim()) filters.push(filter.trim());
541
+ filter = "";
542
+ } else {
543
+ filter += char;
544
+ }
545
+ }
546
+ if (filter.trim()) filters.push(filter.trim());
468
547
  for (const filterSpec of filters) {
469
548
  if (++work % 1024 === 0) {
470
549
  await yieldTurn(signal);
@@ -494,14 +573,14 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
494
573
  let targetW = evalScaleDim(wSpec, iw, ih);
495
574
  let targetH = evalScaleDim(hSpec, iw, ih);
496
575
  if (targetW < 0 && targetH > 0) {
497
- targetW = Math.max(2, Math.round(iw * targetH / Math.max(1, ih)));
576
+ targetW = Math.max(1, Math.round(iw * targetH / Math.max(1, ih)));
498
577
  if (wSpec.trim() === "-2" && targetW % 2 !== 0) targetW++;
499
578
  } else if (targetH < 0 && targetW > 0) {
500
- targetH = Math.max(2, Math.round(ih * targetW / Math.max(1, iw)));
579
+ targetH = Math.max(1, Math.round(ih * targetW / Math.max(1, iw)));
501
580
  if (hSpec.trim() === "-2" && targetH % 2 !== 0) targetH++;
502
581
  }
503
- targetW = Math.max(2, targetW);
504
- targetH = Math.max(2, targetH);
582
+ targetW = Math.max(1, targetW);
583
+ targetH = Math.max(1, targetH);
505
584
  current = await mapWork(current, async (f) => {
506
585
  budget.recordFrame(targetW, targetH);
507
586
  const resized = resizeImage(makeRgbaImg(f.width, f.height, f.data), {
@@ -524,8 +603,11 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
524
603
  } else if (name === "crop") {
525
604
  const iw = current[0].width;
526
605
  const ih = current[0].height;
527
- const cw = Math.min(iw, Math.max(1, evalScaleDim(named.w ?? positional[0] ?? String(iw), iw, ih)));
528
- const ch = Math.min(ih, Math.max(1, evalScaleDim(named.h ?? positional[1] ?? String(ih), iw, ih)));
606
+ const cw = evalScaleDim(named.w ?? positional[0] ?? String(iw), iw, ih);
607
+ const ch = evalScaleDim(named.h ?? positional[1] ?? String(ih), iw, ih);
608
+ if (!Number.isFinite(cw) || !Number.isFinite(ch) || cw <= 0 || ch <= 0 || cw > iw || ch > ih) {
609
+ throw new Error(`Invalid too big or non positive size for width '${cw}' or height '${ch}'`);
610
+ }
529
611
  const cx = Math.max(
530
612
  0,
531
613
  Math.min(iw - cw, parseInt(named.x ?? positional[2] ?? String(Math.floor((iw - cw) / 2)), 10) || 0)
@@ -551,8 +633,13 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
551
633
  } else if (name === "pad") {
552
634
  const iw = current[0].width;
553
635
  const ih = current[0].height;
554
- const pw = Math.max(iw, evalScaleDim(named.w ?? named.width ?? positional[0] ?? String(iw), iw, ih));
555
- const ph = Math.max(ih, evalScaleDim(named.h ?? named.height ?? positional[1] ?? String(ih), iw, ih));
636
+ const width = named.w ?? named.width ?? positional[0] ?? String(iw);
637
+ const height = named.h ?? named.height ?? positional[1] ?? String(ih);
638
+ const pw = Number(width) === 0 ? iw : evalScaleDim(width, iw, ih);
639
+ const ph = Number(height) === 0 ? ih : evalScaleDim(height, iw, ih);
640
+ if (pw < iw || ph < ih) {
641
+ throw new Error("Padded dimensions cannot be smaller than input dimensions");
642
+ }
556
643
  const px = Math.max(0, Math.min(pw - iw, parseInt(named.x ?? positional[2] ?? "0", 10) || 0));
557
644
  const py = Math.max(0, Math.min(ph - ih, parseInt(named.y ?? positional[3] ?? "0", 10) || 0));
558
645
  const col = parseColorRgba(named.color ?? positional[4] ?? "black");
@@ -562,7 +649,7 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
562
649
  top: py,
563
650
  right: Math.max(0, pw - iw - px),
564
651
  bottom: Math.max(0, ph - ih - py),
565
- background: { r: col[0], g: col[1], b: col[2], a: 255 },
652
+ background: { r: col[0], g: col[1], b: col[2], a: col[3] },
566
653
  extendWith: "background"
567
654
  });
568
655
  return {
@@ -582,11 +669,12 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
582
669
  const out = flipImage(makeRgbaImg(f.width, f.height, f.data));
583
670
  return { ...f, data: out.data };
584
671
  }, signal);
585
- } else if (name === "transpose" || name === "rotate") {
672
+ } else if (name === "transpose") {
586
673
  const dir = named.dir ?? positional[0] ?? "1";
587
- const angle = name === "transpose" ? dir === "2" ? 270 : 90 : 90;
674
+ const angle = dir === "0" || dir === "2" ? 270 : 90;
588
675
  current = await mapWork(current, async (f) => {
589
- const out = rotateImage(makeRgbaImg(f.width, f.height, f.data), angle);
676
+ const rotated = rotateImage(makeRgbaImg(f.width, f.height, f.data), angle);
677
+ const out = dir === "0" || dir === "3" ? flipImage(rotated) : rotated;
590
678
  return {
591
679
  ...f,
592
680
  width: out.width,
@@ -594,6 +682,41 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
594
682
  data: out.data
595
683
  };
596
684
  }, signal);
685
+ } else if (name === "rotate") {
686
+ const angle = parseRotateAngle(named.angle ?? named.a ?? (positional[0] || "0"));
687
+ const cos = Math.cos(angle);
688
+ const sin = Math.sin(angle);
689
+ current = await mapWork(current, async (f) => {
690
+ budget.recordFrame(f.width, f.height);
691
+ const data = new Uint8Array(f.data.length);
692
+ const cx = (f.width - 1) / 2;
693
+ const cy = (f.height - 1) / 2;
694
+ for (let y = 0; y < f.height; y++) {
695
+ await yieldTurn(signal);
696
+ budget.checkCpu();
697
+ for (let x = 0; x < f.width; x++) {
698
+ const sx = cos * (x - cx) + sin * (y - cy) + cx;
699
+ const sy = -sin * (x - cx) + cos * (y - cy) + cy;
700
+ const dest = (y * f.width + x) * 4;
701
+ data[dest + 3] = 255;
702
+ if (sx < -1 || sx > f.width || sy < -1 || sy > f.height) continue;
703
+ const px = Math.max(0, Math.min(f.width - 1, sx));
704
+ const py = Math.max(0, Math.min(f.height - 1, sy));
705
+ const x0 = Math.floor(px);
706
+ const y0 = Math.floor(py);
707
+ const x1 = Math.min(x0 + 1, f.width - 1);
708
+ const y1 = Math.min(y0 + 1, f.height - 1);
709
+ const dx = px - x0;
710
+ const dy = py - y0;
711
+ for (let c = 0; c < 4; c++) {
712
+ const top = f.data[(y0 * f.width + x0) * 4 + c] * (1 - dx) + f.data[(y0 * f.width + x1) * 4 + c] * dx;
713
+ const bottom = f.data[(y1 * f.width + x0) * 4 + c] * (1 - dx) + f.data[(y1 * f.width + x1) * 4 + c] * dx;
714
+ data[dest + c] = Math.floor(top * (1 - dy) + bottom * dy + 1e-8);
715
+ }
716
+ }
717
+ }
718
+ return { ...f, data };
719
+ }, signal);
597
720
  } else if (name === "negate") {
598
721
  current = await mapWork(current, async (f) => {
599
722
  const out = negateImage(makeRgbaImg(f.width, f.height, f.data));
@@ -609,15 +732,15 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
609
732
  const totalDuration = current.reduce((acc, f) => acc + f.durationSeconds, 0) || 1;
610
733
  const targetCount = Math.max(1, Math.round(totalDuration * targetFps));
611
734
  const resampled = [];
735
+ let srcIdx = 0;
612
736
  for (let i = 0; i < targetCount; i++) {
613
737
  if (++work % 1024 === 0) {
614
738
  await yieldTurn(signal);
615
739
  signal?.throwIfAborted();
616
740
  }
617
- const srcIdx = Math.min(
618
- current.length - 1,
619
- Math.floor(i / targetCount * current.length)
620
- );
741
+ while (srcIdx + 1 < current.length && Math.round(current[srcIdx + 1].ptsSeconds * targetFps) <= i) {
742
+ srcIdx++;
743
+ }
621
744
  const src = current[srcIdx];
622
745
  resampled.push({
623
746
  ...src,
@@ -767,6 +890,9 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
767
890
  const bw = parseInt(named.w ?? named.width ?? positional[2] ?? "16", 10) || 16;
768
891
  const bh = parseInt(named.h ?? named.height ?? positional[3] ?? "16", 10) || 16;
769
892
  const col = parseColorRgba(named.color ?? named.c ?? positional[4] ?? "red");
893
+ const thicknessOption = named.t ?? named.thickness ?? positional[5] ?? "3";
894
+ const thickness = thicknessOption === "fill" ? Infinity : parseInt(thicknessOption, 10);
895
+ const replace = named.replace === "1";
770
896
  current = await mapWork(current, async (f) => {
771
897
  let work2 = 0;
772
898
  const nextData = new Uint8Array(f.data);
@@ -780,11 +906,15 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
780
906
  await yieldTurn(signal);
781
907
  signal?.throwIfAborted();
782
908
  }
909
+ if (Math.min(x - bx, y - by, bx + bw - 1 - x, by + bh - 1 - y) >= thickness) continue;
783
910
  const idx = (y * f.width + x) * 4;
784
- nextData[idx] = col[0];
785
- nextData[idx + 1] = col[1];
786
- nextData[idx + 2] = col[2];
787
- nextData[idx + 3] = 255;
911
+ if (replace) {
912
+ nextData.set(col, idx);
913
+ continue;
914
+ }
915
+ for (let channel = 0; channel < 3; channel++) {
916
+ nextData[idx + channel] = (nextData[idx + channel] * (255 - col[3]) + col[channel] * col[3]) / 255;
917
+ }
788
918
  }
789
919
  }
790
920
  return { ...f, data: nextData };
@@ -837,7 +967,6 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
837
967
  let rotation;
838
968
  let noOverwrite = true;
839
969
  const vfFilters = [];
840
- const afFilters = [];
841
970
  const metadata = {};
842
971
  let outputTarget;
843
972
  for (let i = 0; i < args.length; i++) {
@@ -914,8 +1043,7 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
914
1043
  const f = args[++i];
915
1044
  if (f) vfFilters.push(f);
916
1045
  } else if (arg === "-af" || arg === "-filter:a") {
917
- const f = args[++i];
918
- if (f) afFilters.push(f);
1046
+ return void 0;
919
1047
  } else if (arg === "-metadata" || arg.startsWith("-metadata:")) {
920
1048
  const kv = args[++i] ?? "";
921
1049
  const eq = kv.indexOf("=");
@@ -1799,7 +1927,7 @@ function createFfmpegCommand(options = {}) {
1799
1927
  const origCh = t.decodedAudio?.channels ?? t.codecDescriptions[0]?.channels ?? 2;
1800
1928
  const targetRate = audioRate ?? origRate;
1801
1929
  const targetCh = audioChannels ?? origCh;
1802
- const durSec = Math.max(0.05, (t.duration || origRate) / Math.max(1, t.timescale || origRate));
1930
+ const durSec = (t.duration || origRate) / Math.max(1, t.timescale || origRate);
1803
1931
  let volFactor = t.volume ?? 1;
1804
1932
  let tempoFactor = 1;
1805
1933
  let reverseAudio = false;
@@ -1816,11 +1944,14 @@ function createFfmpegCommand(options = {}) {
1816
1944
  const trimmed = item.trim();
1817
1945
  if (trimmed.startsWith("volume=")) {
1818
1946
  const volSpec = trimmed.slice("volume=".length).trim();
1819
- volFactor *= volSpec.endsWith("dB") ? Math.pow(10, (parseFloat(volSpec.slice(0, -2)) || 0) / 20) : parseFloat(volSpec) || 1;
1947
+ const gain = parseFloat(volSpec);
1948
+ volFactor *= volSpec.endsWith("dB") ? Math.pow(10, (parseFloat(volSpec.slice(0, -2)) || 0) / 20) : Number.isNaN(gain) ? 1 : gain;
1820
1949
  } else if (trimmed.startsWith("atempo=")) {
1821
1950
  tempoFactor *= Math.max(0.25, Math.min(4, parseFloat(trimmed.slice(7)) || 1));
1822
1951
  } else if (trimmed === "areverse") {
1823
1952
  reverseAudio = true;
1953
+ } else {
1954
+ throw new Error(`Unsupported audio filter: ${trimmed}`);
1824
1955
  }
1825
1956
  }
1826
1957
  }
@@ -1833,6 +1964,7 @@ function createFfmpegCommand(options = {}) {
1833
1964
  }
1834
1965
  const dst = new Float32Array(targetSamples);
1835
1966
  const srcCh = t.decodedAudio?.channelData[Math.min(c, (t.decodedAudio.channelData.length || 1) - 1)];
1967
+ const rightCh = origCh === 2 && targetCh === 1 ? t.decodedAudio.channelData[1] : void 0;
1836
1968
  if (srcCh && srcCh.length > 0) {
1837
1969
  for (let i = 0; i < targetSamples; i++) {
1838
1970
  if (++work % 1024 === 0) {
@@ -1840,7 +1972,8 @@ function createFfmpegCommand(options = {}) {
1840
1972
  context.signal?.throwIfAborted();
1841
1973
  }
1842
1974
  const srcIdx = Math.min(srcCh.length - 1, Math.floor(i / targetSamples * srcCh.length));
1843
- dst[i] = Math.max(-1, Math.min(1, srcCh[srcIdx] * volFactor));
1975
+ const sample = rightCh ? (srcCh[srcIdx] + rightCh[srcIdx]) / 2 : srcCh[srcIdx];
1976
+ dst[i] = Math.max(-1, Math.min(1, sample * volFactor));
1844
1977
  }
1845
1978
  }
1846
1979
  if (reverseAudio) dst.reverse();
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  createFfprobeCommand
3
- } from "../safe-bash/command-chunks/chunk-RSFKQE2U.js";
3
+ } from "../safe-bash/command-chunks/chunk-T5WFJ44Z.js";
4
4
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
5
- import "../safe-bash/command-chunks/chunk-FH5VMAPS.js";
5
+ import "../safe-bash/command-chunks/chunk-45ZH6CNF.js";
6
6
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
7
7
 
8
8
  // packages/safe-bash-command-ffprobe/src/index.ts
@@ -11,9 +11,9 @@ import {
11
11
  loadManifestResources,
12
12
  resolvePath,
13
13
  rethrowRuntimeError
14
- } from "../safe-bash/command-chunks/chunk-RSFKQE2U.js";
14
+ } from "../safe-bash/command-chunks/chunk-T5WFJ44Z.js";
15
15
  import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
16
- import "../safe-bash/command-chunks/chunk-FH5VMAPS.js";
16
+ import "../safe-bash/command-chunks/chunk-45ZH6CNF.js";
17
17
  import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
18
18
  export {
19
19
  createFfprobeCommand,