@poe-platform/safe-bash 0.1.842 → 0.1.843

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.
@@ -1292,6 +1292,24 @@ function applyStreamDispositions(tracks, dispositions) {
1292
1292
  }
1293
1293
  return tracks;
1294
1294
  }
1295
+ function selectAutomaticAudio(documents) {
1296
+ let selected;
1297
+ let bestScore = -1;
1298
+ for (const document of documents) {
1299
+ for (const track of document.tracks) {
1300
+ if (track.type !== "audio") continue;
1301
+ const score = (track.enabled ? 1 << 22 : 0) + (track.decodedAudio?.channels ?? track.codecDescriptions[0]?.channels ?? 0);
1302
+ if (score > bestScore) {
1303
+ selected = track;
1304
+ bestScore = score;
1305
+ }
1306
+ }
1307
+ }
1308
+ return documents.map((document) => ({
1309
+ ...document,
1310
+ tracks: document.tracks.filter((track) => track.type !== "audio" || track === selected)
1311
+ }));
1312
+ }
1295
1313
  function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
1296
1314
  const inBytes = Array.isArray(inBytesOrArgs) ? void 0 : inBytesOrArgs;
1297
1315
  const args = Array.isArray(inBytesOrArgs) ? inBytesOrArgs : maybeArgs ?? [];
@@ -1441,6 +1459,7 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
1441
1459
  else outputTarget = arg;
1442
1460
  }
1443
1461
  if (inputs.length === 0 || !outputTarget) return void 0;
1462
+ if (outputDuration === void 0 && outputTo !== void 0 && outputTo <= (outputSs ?? 0)) return void 0;
1444
1463
  const isNullMux = outputFormat === "null" || outputTarget === "/dev/null";
1445
1464
  if (!isNullMux && !writeFileSync) return void 0;
1446
1465
  if (noOverwrite && !isNullMux && (!readFileSync || readFileSync(outputTarget))) return void 0;
@@ -1467,9 +1486,9 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
1467
1486
  }
1468
1487
  loadedDocs.push(doc);
1469
1488
  }
1470
- let workingDoc = muxMp4(loadedDocs, { stripAudio, stripVideo, stripSubtitles, shortest, rotation });
1489
+ let workingDoc = muxMp4(selectAutomaticAudio(loadedDocs), { stripAudio, stripVideo, stripSubtitles, shortest, rotation });
1471
1490
  if (outputSs !== void 0 || outputTo !== void 0 || outputDuration !== void 0) {
1472
- workingDoc = sliceMp4(workingDoc, { startSeconds: outputSs, endSeconds: outputTo, durationSeconds: outputDuration });
1491
+ workingDoc = sliceMp4(workingDoc, { startSeconds: outputSs, endSeconds: outputDuration === void 0 ? outputTo : void 0, durationSeconds: outputDuration });
1473
1492
  }
1474
1493
  if (outputSize && videoCodec !== "copy") {
1475
1494
  const [w, h] = outputSize.toLowerCase().split("x");
@@ -1823,6 +1842,10 @@ function createFfmpegCommand(options = {}) {
1823
1842
  );
1824
1843
  return { exitCode: 1 };
1825
1844
  }
1845
+ if (outputDuration === void 0 && outputTo !== void 0 && outputTo <= (outputSs ?? 0)) {
1846
+ await writeBytes(context.stderr, encodeUtf8("ffmpeg: -to value smaller than -ss; aborting.\n"), context.signal);
1847
+ return { exitCode: 1 };
1848
+ }
1826
1849
  try {
1827
1850
  if (!overwrite && !isStdout(outputTarget) && outputFormat !== "null") {
1828
1851
  const outFull = resolvePath(context.cwd, outputTarget);
@@ -2248,7 +2271,11 @@ function createFfmpegCommand(options = {}) {
2248
2271
  const [input, selector, ordinal] = clean.split(":");
2249
2272
  const inputIdx = Number(input);
2250
2273
  const srcDoc = loadedDocs[inputIdx];
2251
- if (!srcDoc) continue;
2274
+ if (!srcDoc) {
2275
+ await writeBytes(context.stderr, encodeUtf8(`ffmpeg: Invalid input file index: ${inputIdx}.
2276
+ `), context.signal);
2277
+ return { exitCode: 234 };
2278
+ }
2252
2279
  const type = selector === "v" ? "video" : selector === "a" ? "audio" : selector === "s" ? "subtitle" : void 0;
2253
2280
  let matches = type ? srcDoc.tracks.filter((t) => t.type === type) : srcDoc.tracks;
2254
2281
  const index = type ? ordinal : selector;
@@ -2268,7 +2295,7 @@ To ignore this, add a trailing '?' to the map.
2268
2295
  }
2269
2296
  const filteredTracks = selectedTracks.map((entry) => entry.track);
2270
2297
  if (!mapped) {
2271
- const tracks = loadedDocs.flatMap((doc) => doc.tracks);
2298
+ const tracks = selectAutomaticAudio(loadedDocs).flatMap((doc) => doc.tracks);
2272
2299
  for (const type of ["video", "audio"]) {
2273
2300
  const track = tracks.find((track2) => track2.type === type);
2274
2301
  if (track) filteredTracks.push(track);
@@ -2280,7 +2307,7 @@ To ignore this, add a trailing '?' to the map.
2280
2307
  context.signal
2281
2308
  );
2282
2309
  } else {
2283
- workingDoc = await muxMp4Cooperatively(loadedDocs, {
2310
+ workingDoc = await muxMp4Cooperatively(selectAutomaticAudio(loadedDocs), {
2284
2311
  stripAudio,
2285
2312
  stripVideo,
2286
2313
  stripSubtitles,
@@ -2291,7 +2318,7 @@ To ignore this, add a trailing '?' to the map.
2291
2318
  if (outputSs !== void 0 || outputTo !== void 0 || outputDuration !== void 0) {
2292
2319
  workingDoc = sliceMp4(workingDoc, {
2293
2320
  startSeconds: outputSs,
2294
- endSeconds: outputTo,
2321
+ endSeconds: outputDuration === void 0 ? outputTo : void 0,
2295
2322
  durationSeconds: outputDuration
2296
2323
  });
2297
2324
  }
@@ -3651,7 +3651,7 @@ import {
3651
3651
  } from "../safe-bash-contracts/command.js";
3652
3652
  import { readBytes as readBytes2, writeBytes as writeBytes2 } from "../safe-bash-contracts/io.js";
3653
3653
  import { createOutputOperation } from "../safe-bash-contracts/output.js";
3654
- import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, encodeImage, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flattenImageSteps, flipImageSteps, flopImageSteps, grayscaleImageSteps, removeAlphaImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3654
+ import { readImageMetadataFromSource as readImageMetadataFromSource3, decodeImageToStorage as decodeImageToStorage2, transformStoredImage as transformStoredImage2, UnsupportedStoredResource as UnsupportedStoredResource4, decodeImage as decodeImage3, decodePngToCanvas, FONT_5X7, encodeImage, readImageMetadata as readImageMetadata3, parseColor as baseParseColor, applyExifOrientationSteps, compositeImageSteps, computeImageStatsSteps as computeImageStatsSteps2, ensureAlphaImageSteps, extendImageSteps, extractChannelImageSteps, extractImageSteps as extractImageSteps2, flipImageSteps, flopImageSteps, grayscaleImageSteps, resizeImageSteps, rotateImageSteps, thresholdImageSteps } from "../image-ast/portable.js";
3655
3655
  var X11_NAMED_COLORS = {
3656
3656
  aliceblue: [240, 248, 255, 255],
3657
3657
  antiquewhite: [250, 235, 215, 255],
@@ -4805,11 +4805,10 @@ function* applyMagickSpliceSteps(img, geomStr, bg, gravity = "northwest", signal
4805
4805
  const g = parseMagickGeometry(geomStr);
4806
4806
  const sw = Math.max(0, Math.round(g.width ?? 0));
4807
4807
  const sh = Math.max(0, Math.round(g.height ?? 0));
4808
- const base = resolveGravityOffset(img.width, img.height, gravity);
4809
- const isEast = gravity === "east" || gravity === "northeast" || gravity === "southeast";
4810
- const isSouth = gravity === "south" || gravity === "southwest" || gravity === "southeast";
4811
- const sx = Math.max(0, Math.min(img.width, Math.round(base.left + (isEast ? -g.x : g.x))));
4812
- const sy = Math.max(0, Math.min(img.height, Math.round(base.top + (isSouth ? -g.y : g.y))));
4808
+ const box = gravityAdjustBox(img.width, img.height, sw, sh, g.x, g.y, gravity);
4809
+ const anchor = resolveGravityOffset(sw, sh, gravity);
4810
+ const sx = Math.max(0, Math.min(img.width, box.x + anchor.left));
4811
+ const sy = Math.max(0, Math.min(img.height, box.y + anchor.top));
4813
4812
  const outW = img.width + sw;
4814
4813
  const outH = img.height + sh;
4815
4814
  const out = new Uint8Array(new ArrayBuffer(outW * outH * 4 + outH), 0, outW * outH * 4);
@@ -8061,7 +8060,7 @@ function parseMagickPixelOperation(tokens, settings, start, signal) {
8061
8060
  return { ...metadata, data: out, space: "b-w", channels: image.hasAlpha ? 2 : 1 };
8062
8061
  };
8063
8062
  } else if (token === "-gamma") {
8064
- const gamma = Math.max(0.1, Number(tokens[++i] ?? 1));
8063
+ const gamma = Math.max(0.1, parseFloat(tokens[++i] ?? "1"));
8065
8064
  const channels = state.channelExplicit ? state.channels : { r: true, g: true, b: true, a: true };
8066
8065
  const lookup = [channels.r, channels.g, channels.b, channels.a].map((active) => Uint8Array.from({ length: 256 }, (_, value) => active ? clampByteVal(255 * Math.pow(value / 255, 1 / gamma)) : value));
8067
8066
  apply = (image) => applyMagickLookup(image, lookup);
@@ -8069,7 +8068,19 @@ function parseMagickPixelOperation(tokens, settings, start, signal) {
8069
8068
  const mode = (tokens[++i] ?? "on").toLowerCase();
8070
8069
  if (mode === "remove") {
8071
8070
  apply = function* (im) {
8072
- return yield* removeAlphaImageSteps(yield* flattenImageSteps(im, state.background));
8071
+ const out = new Uint8Array(im.data.length);
8072
+ const background = [state.background.r, state.background.g, state.background.b];
8073
+ for (let idx = 0; idx < out.length; idx += 4) {
8074
+ if (idx % 65536 === 0) yield;
8075
+ const alpha = im.data[idx + 3] / 255;
8076
+ for (let channel = 0; channel < 3; channel++) {
8077
+ const bg = background[im.channels === 2 ? 0 : channel];
8078
+ out[idx + channel] = clampByteVal(im.data[idx + channel] * alpha + bg * (1 - alpha));
8079
+ }
8080
+ out[idx + 3] = 255;
8081
+ }
8082
+ const { data16: ignored, ...metadata } = im;
8083
+ return { ...metadata, data: out, hasAlpha: false, channels: im.channels === 4 ? 3 : im.channels === 2 ? 1 : im.channels };
8073
8084
  };
8074
8085
  } else if (mode === "associate" || mode === "disassociate") {
8075
8086
  apply = function* (im) {
@@ -9267,6 +9278,8 @@ function* evaluatePipelineTokensSteps(tokens, files, state, parentStack = [], st
9267
9278
  if (composed !== overlay) detachRgbaBuffer(overlay.data);
9268
9279
  }
9269
9280
  stack = [composed, ...stack.slice(2)];
9281
+ } else {
9282
+ throw new Error("image sequence is required '-composite'");
9270
9283
  }
9271
9284
  } else {
9272
9285
  if (t.startsWith("-") && t !== "-" || t.startsWith("+")) throw new Error(`unrecognized option '${t}'`);
@@ -9513,11 +9526,10 @@ async function transformStoredMagickCoordinates(image, backend, token, geometry,
9513
9526
  ys = [{ source: h - dy, target: 0, length: dy }, { source: 0, target: dy, length: h - dy }];
9514
9527
  } else if (token === "-splice") {
9515
9528
  const sw = Math.max(0, Math.round(g.width ?? 0)), sh = Math.max(0, Math.round(g.height ?? 0));
9516
- const base = resolveGravityOffset(w, h, state.gravity);
9517
- const east = state.gravity === "east" || state.gravity === "northeast" || state.gravity === "southeast";
9518
- const south = state.gravity === "south" || state.gravity === "southwest" || state.gravity === "southeast";
9519
- const x = Math.max(0, Math.min(w, Math.round(base.left + (east ? -g.x : g.x))));
9520
- const y = Math.max(0, Math.min(h, Math.round(base.top + (south ? -g.y : g.y))));
9529
+ const box = gravityAdjustBox(w, h, sw, sh, g.x, g.y, state.gravity);
9530
+ const anchor = resolveGravityOffset(sw, sh, state.gravity);
9531
+ const x = Math.max(0, Math.min(w, box.x + anchor.left));
9532
+ const y = Math.max(0, Math.min(h, box.y + anchor.top));
9521
9533
  width += sw;
9522
9534
  height += sh;
9523
9535
  xs = [{ source: 0, target: 0, length: x }, { source: x, target: x + sw, length: w - x }];
@@ -9888,7 +9900,8 @@ async function tryConvertFilesResult(argv, input, stdinBytes, signal, analysis)
9888
9900
  if (token === "-layers") i++;
9889
9901
  const settings = { ...state };
9890
9902
  stackStep(async (stack, backend) => {
9891
- if (!stack.length || token === "-composite" && stack.length < 2) return stack;
9903
+ if (token === "-composite" && stack.length < 2) throw new Error("image sequence is required '-composite'");
9904
+ if (!stack.length) return stack;
9892
9905
  let result;
9893
9906
  if (token === "-composite") {
9894
9907
  const base = await stack.get(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.842",
3
+ "version": "0.1.843",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1415,7 +1415,7 @@
1415
1415
  "access": "public"
1416
1416
  },
1417
1417
  "dependencies": {
1418
- "@poe-platform/safe-fs": "0.1.842",
1418
+ "@poe-platform/safe-fs": "0.1.843",
1419
1419
  "pdf-lib": "1.17.1",
1420
1420
  "@types/node": "^25.2.2",
1421
1421
  "@formatjs/intl-numberformat": "9.4.0",