@poe-platform/safe-bash 0.1.836 → 0.1.837
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/pdf-ast/image.js +1 -1
- package/dist/pdf-ast/index.js +1 -1
- package/dist/pdf-ast/render/raster.d.ts +4 -3
- package/dist/pdf-ast/render/retained-page-pixels.d.ts +3 -2
- package/dist/safe-bash/command-chunks/{chunk-YHRESVJ7.js → chunk-4BSD2RGK.js} +132 -70
- package/dist/safe-bash-command-ffmpeg/index.js +32 -20
- package/package.json +2 -2
package/dist/pdf-ast/image.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
PdfRetainedReader,
|
|
6
6
|
renderPdfPageToPng,
|
|
7
7
|
renderRetainedPagePixels
|
|
8
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
8
|
+
} from "../safe-bash/command-chunks/chunk-4BSD2RGK.js";
|
|
9
9
|
import "../safe-bash/command-chunks/chunk-E6SK3OU2.js";
|
|
10
10
|
import "../safe-bash/command-chunks/chunk-NUGQQDGE.js";
|
|
11
11
|
import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
|
package/dist/pdf-ast/index.js
CHANGED
|
@@ -257,7 +257,7 @@ import {
|
|
|
257
257
|
transformNodeStringsAndStreamsSteps,
|
|
258
258
|
transformPathSegment,
|
|
259
259
|
transformPoint
|
|
260
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
260
|
+
} from "../safe-bash/command-chunks/chunk-4BSD2RGK.js";
|
|
261
261
|
import "../safe-bash/command-chunks/chunk-E6SK3OU2.js";
|
|
262
262
|
import "../safe-bash/command-chunks/chunk-NUGQQDGE.js";
|
|
263
263
|
import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
|
|
@@ -3,7 +3,7 @@ import { type PdfRetainedPixelOptions } from "./retained-page-pixels.js";
|
|
|
3
3
|
import type { PdfIndexStorage } from "../cos/object-index.js";
|
|
4
4
|
import type { PdfRetainedPngOptions } from "./retained-png.js";
|
|
5
5
|
import { type ParsedCosDocument } from "../cos/parser.js";
|
|
6
|
-
import type { PdfDisplayList, PdfPaintOperation, PdfRgbColor, PdfSoftMask } from "../ast.js";
|
|
6
|
+
import type { PdfStoredOperations, PdfDisplayList, PdfPaintOperation, PdfRgbColor, PdfSoftMask } from "../ast.js";
|
|
7
7
|
export interface RgbaBitmap {
|
|
8
8
|
readonly width: number;
|
|
9
9
|
readonly height: number;
|
|
@@ -48,10 +48,11 @@ export type PdfRasterWindowOptions = Pick<RenderToPngOptions, "scale" | "dpi" |
|
|
|
48
48
|
* by the caller. Crop, rotation and anisotropic resampling belong to the driver. */
|
|
49
49
|
export declare function renderDisplayListWindowSteps(displayList: PdfDisplayList, window: PdfCropRect, options?: PdfRasterWindowOptions): Generator<void, RgbaBitmap, void>;
|
|
50
50
|
/** Consume a replayable operation source without collecting a page display list.
|
|
51
|
-
* Opaque output
|
|
51
|
+
* Opaque output uses stored effect flags or a compositing prepass to preserve
|
|
52
|
+
* background quantization.
|
|
52
53
|
* Each operation is painted before advancing its source. Individual paths,
|
|
53
54
|
* resources and nested captures still have the evaluator's memory ownership. */
|
|
54
|
-
export declare function renderOperationStreamWindow(page: Pick<PdfDisplayList, "width" | "height" | "origin">, operations: () => AsyncIterable<PdfPaintOperation
|
|
55
|
+
export declare function renderOperationStreamWindow(page: Pick<PdfDisplayList, "width" | "height" | "origin">, operations: (() => AsyncIterable<PdfPaintOperation>) | PdfStoredOperations, window: PdfCropRect, options?: PdfRasterWindowOptions & {
|
|
55
56
|
readonly signal?: AbortSignal;
|
|
56
57
|
}): Promise<RgbaBitmap>;
|
|
57
58
|
export declare function renderDisplayListToBitmapSteps(displayList: PdfDisplayList, options?: RenderToPngOptions): Generator<void, RgbaBitmap, void>;
|
|
@@ -15,6 +15,7 @@ export interface PdfRetainedPixels {
|
|
|
15
15
|
}
|
|
16
16
|
/** Render one bounded window at a time into caller-backed tile storage, then
|
|
17
17
|
* read transformed pixels without allocating the full page or collecting paints.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* With caller-owned image backing, evaluate once and replay stored paints per
|
|
19
|
+
* tile. Otherwise replay evaluation. Nested captures/resources retain their own
|
|
20
|
+
* admission contracts. Tile storage is acquired lazily and owned by the iterator. */
|
|
20
21
|
export declare function renderRetainedPagePixels(page: PdfRetainedPage, storage: PdfIndexStorage, options?: PdfRetainedPixelOptions): Promise<PdfRetainedPixels>;
|
|
@@ -26663,6 +26663,71 @@ var PdfStagingStorage = class {
|
|
|
26663
26663
|
}
|
|
26664
26664
|
};
|
|
26665
26665
|
|
|
26666
|
+
// packages/pdf-ast/src/content/stored-operations.ts
|
|
26667
|
+
function operationEffects(operation) {
|
|
26668
|
+
const value = operation.value;
|
|
26669
|
+
let effects = (value.softMask ? 2 : 0) | (value.blendMode && value.blendMode !== "Normal" && value.blendMode !== "Compatible" ? 1 : 0);
|
|
26670
|
+
if (operation.kind === "group") {
|
|
26671
|
+
let children = operation.value.storedOperations?.effects ?? 0;
|
|
26672
|
+
for (const child of operation.value.operations) children |= operationEffects(child);
|
|
26673
|
+
if (operation.value.isolated === false && children & 3) effects |= 4;
|
|
26674
|
+
effects |= children;
|
|
26675
|
+
}
|
|
26676
|
+
return effects;
|
|
26677
|
+
}
|
|
26678
|
+
var StoredOperationsWriter = class {
|
|
26679
|
+
constructor(storage, signal) {
|
|
26680
|
+
this.storage = storage;
|
|
26681
|
+
this.signal = signal;
|
|
26682
|
+
}
|
|
26683
|
+
storage;
|
|
26684
|
+
signal;
|
|
26685
|
+
first = -1;
|
|
26686
|
+
last = -1;
|
|
26687
|
+
count = 0;
|
|
26688
|
+
effects = 0;
|
|
26689
|
+
async append(operation) {
|
|
26690
|
+
const position = await writeStoredRecord(this.storage, operation, -1, this.signal);
|
|
26691
|
+
const header2 = new Uint8Array(8), view = new DataView(header2.buffer);
|
|
26692
|
+
if (this.last >= 0) {
|
|
26693
|
+
view.setFloat64(0, position, true);
|
|
26694
|
+
await this.storage.write(
|
|
26695
|
+
this.last,
|
|
26696
|
+
header2.subarray(0, 8),
|
|
26697
|
+
this.signal ? { signal: this.signal } : void 0
|
|
26698
|
+
);
|
|
26699
|
+
}
|
|
26700
|
+
this.signal?.throwIfAborted();
|
|
26701
|
+
if (this.first < 0) this.first = position;
|
|
26702
|
+
this.last = position;
|
|
26703
|
+
this.count++;
|
|
26704
|
+
this.effects |= operationEffects(operation);
|
|
26705
|
+
}
|
|
26706
|
+
snapshot() {
|
|
26707
|
+
return {
|
|
26708
|
+
kind: "stored-operations",
|
|
26709
|
+
storage: this.storage,
|
|
26710
|
+
position: this.first,
|
|
26711
|
+
count: this.count,
|
|
26712
|
+
effects: this.effects
|
|
26713
|
+
};
|
|
26714
|
+
}
|
|
26715
|
+
};
|
|
26716
|
+
async function* readStoredOperations(source, signal) {
|
|
26717
|
+
if (!Number.isSafeInteger(source.count) || source.count < 0)
|
|
26718
|
+
throw new RangeError("Invalid capture count");
|
|
26719
|
+
let position = source.position;
|
|
26720
|
+
for (let record = 0; record < source.count; record++) {
|
|
26721
|
+
const { value, next } = await readStoredRecord(
|
|
26722
|
+
source.storage,
|
|
26723
|
+
position,
|
|
26724
|
+
signal
|
|
26725
|
+
);
|
|
26726
|
+
yield value;
|
|
26727
|
+
position = next;
|
|
26728
|
+
}
|
|
26729
|
+
}
|
|
26730
|
+
|
|
26666
26731
|
// packages/pdf-ast/src/render/raster.ts
|
|
26667
26732
|
import { PagedStorage as PagedStorage3, IntegerTable as IntegerTable3 } from "@poe-platform/safe-fs/storage";
|
|
26668
26733
|
|
|
@@ -27077,71 +27142,6 @@ async function* encodeSvgPathDataChunks(input, height, options = {}) {
|
|
|
27077
27142
|
if (used) yield buffer.slice(0, used);
|
|
27078
27143
|
}
|
|
27079
27144
|
|
|
27080
|
-
// packages/pdf-ast/src/content/stored-operations.ts
|
|
27081
|
-
function operationEffects(operation) {
|
|
27082
|
-
const value = operation.value;
|
|
27083
|
-
let effects = (value.softMask ? 2 : 0) | (value.blendMode && value.blendMode !== "Normal" && value.blendMode !== "Compatible" ? 1 : 0);
|
|
27084
|
-
if (operation.kind === "group") {
|
|
27085
|
-
let children = operation.value.storedOperations?.effects ?? 0;
|
|
27086
|
-
for (const child of operation.value.operations) children |= operationEffects(child);
|
|
27087
|
-
if (operation.value.isolated === false && children & 3) effects |= 4;
|
|
27088
|
-
effects |= children;
|
|
27089
|
-
}
|
|
27090
|
-
return effects;
|
|
27091
|
-
}
|
|
27092
|
-
var StoredOperationsWriter = class {
|
|
27093
|
-
constructor(storage, signal) {
|
|
27094
|
-
this.storage = storage;
|
|
27095
|
-
this.signal = signal;
|
|
27096
|
-
}
|
|
27097
|
-
storage;
|
|
27098
|
-
signal;
|
|
27099
|
-
first = -1;
|
|
27100
|
-
last = -1;
|
|
27101
|
-
count = 0;
|
|
27102
|
-
effects = 0;
|
|
27103
|
-
async append(operation) {
|
|
27104
|
-
const position = await writeStoredRecord(this.storage, operation, -1, this.signal);
|
|
27105
|
-
const header2 = new Uint8Array(8), view = new DataView(header2.buffer);
|
|
27106
|
-
if (this.last >= 0) {
|
|
27107
|
-
view.setFloat64(0, position, true);
|
|
27108
|
-
await this.storage.write(
|
|
27109
|
-
this.last,
|
|
27110
|
-
header2.subarray(0, 8),
|
|
27111
|
-
this.signal ? { signal: this.signal } : void 0
|
|
27112
|
-
);
|
|
27113
|
-
}
|
|
27114
|
-
this.signal?.throwIfAborted();
|
|
27115
|
-
if (this.first < 0) this.first = position;
|
|
27116
|
-
this.last = position;
|
|
27117
|
-
this.count++;
|
|
27118
|
-
this.effects |= operationEffects(operation);
|
|
27119
|
-
}
|
|
27120
|
-
snapshot() {
|
|
27121
|
-
return {
|
|
27122
|
-
kind: "stored-operations",
|
|
27123
|
-
storage: this.storage,
|
|
27124
|
-
position: this.first,
|
|
27125
|
-
count: this.count,
|
|
27126
|
-
effects: this.effects
|
|
27127
|
-
};
|
|
27128
|
-
}
|
|
27129
|
-
};
|
|
27130
|
-
async function* readStoredOperations(source, signal) {
|
|
27131
|
-
if (!Number.isSafeInteger(source.count) || source.count < 0)
|
|
27132
|
-
throw new RangeError("Invalid capture count");
|
|
27133
|
-
let position = source.position;
|
|
27134
|
-
for (let record = 0; record < source.count; record++) {
|
|
27135
|
-
const { value, next } = await readStoredRecord(
|
|
27136
|
-
source.storage,
|
|
27137
|
-
position,
|
|
27138
|
-
signal
|
|
27139
|
-
);
|
|
27140
|
-
yield value;
|
|
27141
|
-
position = next;
|
|
27142
|
-
}
|
|
27143
|
-
}
|
|
27144
|
-
|
|
27145
27145
|
// packages/pdf-ast/src/content/stored-clips.ts
|
|
27146
27146
|
var PAGE_BYTES = 4096;
|
|
27147
27147
|
var RECORD_BYTES2 = 64;
|
|
@@ -48242,8 +48242,46 @@ function strokeEdges(contours) {
|
|
|
48242
48242
|
} };
|
|
48243
48243
|
}
|
|
48244
48244
|
var SUB_OFFSETS_4X4 = [0.125, 0.375, 0.625, 0.875];
|
|
48245
|
+
function* cacheSmallEdgesSteps(edges) {
|
|
48246
|
+
if (Array.isArray(edges)) return edges;
|
|
48247
|
+
const cached = [];
|
|
48248
|
+
for (const edge of edges) {
|
|
48249
|
+
if (!edge) {
|
|
48250
|
+
yield;
|
|
48251
|
+
continue;
|
|
48252
|
+
}
|
|
48253
|
+
if (cached.length === 128) return edges;
|
|
48254
|
+
cached.push(edge);
|
|
48255
|
+
}
|
|
48256
|
+
return cached;
|
|
48257
|
+
}
|
|
48245
48258
|
function* computeSubScanlineCountsSteps(edges, scanY, fillRule, deltas, winding, rowCounts) {
|
|
48246
48259
|
const samples = winding.length;
|
|
48260
|
+
if (Array.isArray(edges)) {
|
|
48261
|
+
const crossings = [];
|
|
48262
|
+
for (const edge of edges) {
|
|
48263
|
+
if (edge.y0 <= scanY && edge.y1 > scanY || edge.y1 <= scanY && edge.y0 > scanY) {
|
|
48264
|
+
crossings.push({ x: edge.x0 + (scanY - edge.y0) / (edge.y1 - edge.y0) * (edge.x1 - edge.x0), direction: edge.y0 < edge.y1 ? 1 : -1 });
|
|
48265
|
+
}
|
|
48266
|
+
}
|
|
48267
|
+
crossings.sort((a, b) => a.x - b.x);
|
|
48268
|
+
let value2 = 0, start = 0;
|
|
48269
|
+
for (const crossing of crossings) {
|
|
48270
|
+
const before = value2;
|
|
48271
|
+
value2 = fillRule === "evenodd" ? 1 - value2 : value2 + crossing.direction;
|
|
48272
|
+
if (before === 0 && value2 !== 0) start = crossing.x;
|
|
48273
|
+
else if (before !== 0 && value2 === 0) {
|
|
48274
|
+
const first = Math.max(0, Math.ceil((start - 0.125) * 4));
|
|
48275
|
+
const end = Math.min(samples, Math.floor((crossing.x - 0.125) * 4) + 1);
|
|
48276
|
+
for (let index2 = first; index2 < end; ) {
|
|
48277
|
+
const count = Math.min(end - index2, 4 - index2 % 4);
|
|
48278
|
+
rowCounts[index2 >>> 2] = rowCounts[index2 >>> 2] + count;
|
|
48279
|
+
index2 += count;
|
|
48280
|
+
}
|
|
48281
|
+
}
|
|
48282
|
+
}
|
|
48283
|
+
return;
|
|
48284
|
+
}
|
|
48247
48285
|
deltas.fill(0);
|
|
48248
48286
|
let work = 0;
|
|
48249
48287
|
for (const edge of edges) {
|
|
@@ -48279,6 +48317,7 @@ function* computeSubScanlineCountsSteps(edges, scanY, fillRule, deltas, winding,
|
|
|
48279
48317
|
}
|
|
48280
48318
|
}
|
|
48281
48319
|
function* fillEdgesScanline4x4Steps(rgba, width, height, edges, color, alpha = 1, fillRule = "nonzero", clipScreen, antialias = true, blendMode, clipMask, groupAlpha) {
|
|
48320
|
+
edges = yield* cacheSmallEdgesSteps(edges);
|
|
48282
48321
|
let work = 0;
|
|
48283
48322
|
let minY = Infinity;
|
|
48284
48323
|
let maxY = -Infinity;
|
|
@@ -48318,6 +48357,7 @@ function* fillEdgesScanline4x4Steps(rgba, width, height, edges, color, alpha = 1
|
|
|
48318
48357
|
}
|
|
48319
48358
|
}
|
|
48320
48359
|
function* fillMaskScanline4x4Steps(mask, width, height, edges, fillRule = "nonzero", intersect = false) {
|
|
48360
|
+
edges = yield* cacheSmallEdgesSteps(edges);
|
|
48321
48361
|
let work = 0;
|
|
48322
48362
|
let minY = Infinity;
|
|
48323
48363
|
let maxY = -Infinity;
|
|
@@ -48536,6 +48576,21 @@ function* renderDisplayListLayerSteps(displayList, options, scale, backdrop, win
|
|
|
48536
48576
|
if (++work % 16384 === 0) yield;
|
|
48537
48577
|
if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
|
|
48538
48578
|
let operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
48579
|
+
let fillEdges;
|
|
48580
|
+
if (operation.kind === "path" && operation.value.fillColor && !operation.value.strokeColor) {
|
|
48581
|
+
const path = operation.value;
|
|
48582
|
+
fillEdges = yield* cacheSmallEdgesSteps(screenFillEdges(path.segments, toScreen, path.storedSegments, images));
|
|
48583
|
+
if (Array.isArray(fillEdges)) {
|
|
48584
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
48585
|
+
for (const edge of fillEdges) {
|
|
48586
|
+
minX = Math.min(minX, edge.x0, edge.x1);
|
|
48587
|
+
minY = Math.min(minY, edge.y0, edge.y1);
|
|
48588
|
+
maxX = Math.max(maxX, edge.x0, edge.x1);
|
|
48589
|
+
maxY = Math.max(maxY, edge.y0, edge.y1);
|
|
48590
|
+
}
|
|
48591
|
+
if (maxX < 0 || maxY < 0 || minX > width || minY > height) continue;
|
|
48592
|
+
}
|
|
48593
|
+
}
|
|
48539
48594
|
if (operation.kind === "group") {
|
|
48540
48595
|
const group = operation.value;
|
|
48541
48596
|
const bitmap = yield* renderDisplayListLayerSteps(
|
|
@@ -48637,7 +48692,7 @@ function* renderDisplayListLayerSteps(displayList, options, scale, backdrop, win
|
|
|
48637
48692
|
if (operation.kind === "path") {
|
|
48638
48693
|
const path = operation.value;
|
|
48639
48694
|
if (path.fillColor) {
|
|
48640
|
-
const edges = screenFillEdges(path.segments, toScreen, path.storedSegments, images);
|
|
48695
|
+
const edges = fillEdges ?? screenFillEdges(path.segments, toScreen, path.storedSegments, images);
|
|
48641
48696
|
const clipScreen = path.clipRect ? [(path.clipRect[0] - originX) * scale - offsetX, (pageTop - path.clipRect[3]) * scale - offsetY, (path.clipRect[2] - originX) * scale - offsetX, (pageTop - path.clipRect[1]) * scale - offsetY] : void 0;
|
|
48642
48697
|
yield* fillEdgesScanline4x4Steps(rgba, width, height, edges, path.fillColor, path.fillAlpha ?? 1, path.fillRule ?? "nonzero", clipScreen, original.kind === "glyph" ? aaTxt : aaVec, path.blendMode, clipMask, groupAlpha);
|
|
48643
48698
|
}
|
|
@@ -48797,7 +48852,7 @@ async function renderOperationStreamWindow(page2, operations, window, options =
|
|
|
48797
48852
|
const { signal } = options;
|
|
48798
48853
|
signal?.throwIfAborted();
|
|
48799
48854
|
async function* cursor() {
|
|
48800
|
-
const source2 = operations()[Symbol.asyncIterator]();
|
|
48855
|
+
const source2 = (typeof operations === "function" ? operations() : readStoredOperations(operations, signal))[Symbol.asyncIterator]();
|
|
48801
48856
|
let failed2 = false, pulls = 0;
|
|
48802
48857
|
try {
|
|
48803
48858
|
while (true) {
|
|
@@ -48819,8 +48874,8 @@ async function renderOperationStreamWindow(page2, operations, window, options =
|
|
|
48819
48874
|
}
|
|
48820
48875
|
}
|
|
48821
48876
|
}
|
|
48822
|
-
const input = { requested: false, next: void 0, compositing:
|
|
48823
|
-
if (!options.transparent) for await (const operation of cursor()) {
|
|
48877
|
+
const input = { requested: false, next: void 0, compositing: typeof operations !== "function" && !!(operations.effects & 1) };
|
|
48878
|
+
if (!options.transparent && typeof operations === "function") for await (const operation of cursor()) {
|
|
48824
48879
|
if (hasCompositingEffects([operation])) {
|
|
48825
48880
|
input.compositing = true;
|
|
48826
48881
|
break;
|
|
@@ -49671,10 +49726,17 @@ async function renderRetainedPagePixels(page2, storage, options = {}) {
|
|
|
49671
49726
|
let source, failed = false;
|
|
49672
49727
|
async function* tiles() {
|
|
49673
49728
|
let work = 0;
|
|
49729
|
+
const paints = options.imageStorage ? new StoredOperationsWriter(options.imageStorage, signal) : void 0;
|
|
49730
|
+
if (paints) for await (const event of page2.evaluateSteps(shared, options)) {
|
|
49731
|
+
if (!event.captured) await paints.append(event.operation);
|
|
49732
|
+
if (++work % 256 === 0) await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
49733
|
+
signal?.throwIfAborted();
|
|
49734
|
+
}
|
|
49735
|
+
const stored = paints?.snapshot();
|
|
49674
49736
|
for (let y = 0; y < height; y += tileSize) for (let x = 0; x < width; x += tileSize) {
|
|
49675
49737
|
if (++work % 32 === 0) await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
49676
49738
|
signal?.throwIfAborted();
|
|
49677
|
-
const tile = await renderOperationStreamWindow(geometry, async function* () {
|
|
49739
|
+
const tile = await renderOperationStreamWindow(geometry, stored ?? async function* () {
|
|
49678
49740
|
for await (const event of page2.evaluateSteps(shared, options)) if (!event.captured) yield event.operation;
|
|
49679
49741
|
}, { x, y, width: Math.min(tileSize, width - x), height: Math.min(tileSize, height - y) }, { ...options, scale: scaleX });
|
|
49680
49742
|
yield tile.data;
|
|
@@ -278,19 +278,22 @@ function* lavfiSteps(spec, budget, durationOverride) {
|
|
|
278
278
|
)
|
|
279
279
|
};
|
|
280
280
|
}
|
|
281
|
-
async function ensureDecodedFrames(track, budget, signal) {
|
|
282
|
-
if (track.decodedVideoFrames && track.decodedVideoFrames.length > 0) {
|
|
283
|
-
return [...track.decodedVideoFrames];
|
|
284
|
-
}
|
|
281
|
+
async function ensureDecodedFrames(track, movieTimescale, budget, signal, maxFrames) {
|
|
285
282
|
const width = track.width ?? track.codecDescriptions[0]?.width ?? 64;
|
|
286
283
|
const height = track.height ?? track.codecDescriptions[0]?.height ?? 64;
|
|
287
284
|
const ts = track.timescale || 9e4;
|
|
288
285
|
const frames = [];
|
|
289
|
-
|
|
286
|
+
const decoded = track.decodedVideoFrames ?? decodeH264Samples(track.samples, track.codecDescriptions, width, height, ts);
|
|
287
|
+
for (const frame of decoded) {
|
|
290
288
|
await yieldTurn(signal);
|
|
291
289
|
signal?.throwIfAborted();
|
|
292
|
-
budget.recordFrame(frame.width, frame.height);
|
|
290
|
+
if (!track.decodedVideoFrames) budget.recordFrame(frame.width, frame.height);
|
|
291
|
+
const pts = Math.round(frame.ptsSeconds * ts);
|
|
292
|
+
if (track.editList?.length && !track.editList.some(
|
|
293
|
+
(edit) => edit.mediaTime >= 0 && pts >= edit.mediaTime && pts < edit.mediaTime + edit.segmentDuration * ts / movieTimescale
|
|
294
|
+
)) continue;
|
|
293
295
|
frames.push(frame);
|
|
296
|
+
if (maxFrames !== void 0 && frames.length >= maxFrames) break;
|
|
294
297
|
}
|
|
295
298
|
return frames;
|
|
296
299
|
}
|
|
@@ -942,6 +945,8 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
|
|
|
942
945
|
const plugin = registry.detect(rawBytes, inp.path, inp.format);
|
|
943
946
|
if (!plugin || !plugin.canDemux) return void 0;
|
|
944
947
|
doc = plugin.parse(rawBytes, { filename: inp.path, budget });
|
|
948
|
+
const imageOutput = (outputFormat ? registry.findByFormatName(outputFormat) : registry.findByFilename(outputTarget))?.id === "image2";
|
|
949
|
+
if ((imageOutput || maxVideoFrames !== void 0) && doc.tracks.some((track) => track.type === "video" && !track.decodedVideoFrames)) return void 0;
|
|
945
950
|
}
|
|
946
951
|
if (inp.startSeconds !== void 0 || inp.endSeconds !== void 0 || inp.durationSeconds !== void 0) {
|
|
947
952
|
doc = sliceMp4(doc, { startSeconds: inp.startSeconds, endSeconds: inp.endSeconds, durationSeconds: inp.durationSeconds });
|
|
@@ -1449,11 +1454,13 @@ function createFfmpegCommand(options = {}) {
|
|
|
1449
1454
|
} else if (filterComplex.includes("xfade") && loadedDocs.length >= 2) {
|
|
1450
1455
|
const f0 = await ensureDecodedFrames(
|
|
1451
1456
|
loadedDocs[0].tracks.find((t) => t.type === "video"),
|
|
1457
|
+
loadedDocs[0].timescale,
|
|
1452
1458
|
budget,
|
|
1453
1459
|
context.signal
|
|
1454
1460
|
);
|
|
1455
1461
|
const f1 = await ensureDecodedFrames(
|
|
1456
1462
|
loadedDocs[1].tracks.find((t) => t.type === "video"),
|
|
1463
|
+
loadedDocs[1].timescale,
|
|
1457
1464
|
budget,
|
|
1458
1465
|
context.signal
|
|
1459
1466
|
);
|
|
@@ -1507,11 +1514,13 @@ function createFfmpegCommand(options = {}) {
|
|
|
1507
1514
|
} else if (filterComplex.includes("vstack") && loadedDocs.length >= 2) {
|
|
1508
1515
|
const f0 = await ensureDecodedFrames(
|
|
1509
1516
|
loadedDocs[0].tracks.find((t) => t.type === "video"),
|
|
1517
|
+
loadedDocs[0].timescale,
|
|
1510
1518
|
budget,
|
|
1511
1519
|
context.signal
|
|
1512
1520
|
);
|
|
1513
1521
|
const f1 = await ensureDecodedFrames(
|
|
1514
1522
|
loadedDocs[1].tracks.find((t) => t.type === "video"),
|
|
1523
|
+
loadedDocs[1].timescale,
|
|
1515
1524
|
budget,
|
|
1516
1525
|
context.signal
|
|
1517
1526
|
);
|
|
@@ -1553,11 +1562,13 @@ function createFfmpegCommand(options = {}) {
|
|
|
1553
1562
|
} else if (filterComplex.includes("hstack") && loadedDocs.length >= 2) {
|
|
1554
1563
|
const f0 = await ensureDecodedFrames(
|
|
1555
1564
|
loadedDocs[0].tracks.find((t) => t.type === "video"),
|
|
1565
|
+
loadedDocs[0].timescale,
|
|
1556
1566
|
budget,
|
|
1557
1567
|
context.signal
|
|
1558
1568
|
);
|
|
1559
1569
|
const f1 = await ensureDecodedFrames(
|
|
1560
1570
|
loadedDocs[1].tracks.find((t) => t.type === "video"),
|
|
1571
|
+
loadedDocs[1].timescale,
|
|
1561
1572
|
budget,
|
|
1562
1573
|
context.signal
|
|
1563
1574
|
);
|
|
@@ -1599,11 +1610,13 @@ function createFfmpegCommand(options = {}) {
|
|
|
1599
1610
|
} else if (filterComplex.includes("overlay") && loadedDocs.length >= 2) {
|
|
1600
1611
|
const f0 = await ensureDecodedFrames(
|
|
1601
1612
|
loadedDocs[0].tracks.find((t) => t.type === "video"),
|
|
1613
|
+
loadedDocs[0].timescale,
|
|
1602
1614
|
budget,
|
|
1603
1615
|
context.signal
|
|
1604
1616
|
);
|
|
1605
1617
|
const f1 = await ensureDecodedFrames(
|
|
1606
1618
|
loadedDocs[1].tracks.find((t) => t.type === "video"),
|
|
1619
|
+
loadedDocs[1].timescale,
|
|
1607
1620
|
budget,
|
|
1608
1621
|
context.signal
|
|
1609
1622
|
);
|
|
@@ -1689,20 +1702,16 @@ function createFfmpegCommand(options = {}) {
|
|
|
1689
1702
|
if (outputFps && videoCodec !== "copy") {
|
|
1690
1703
|
vfFilters.push(`fps=${outputFps}`);
|
|
1691
1704
|
}
|
|
1692
|
-
|
|
1705
|
+
const imageOutput = (outputFormat ? registry.findByFormatName(outputFormat) : registry.findByFilename(outputTarget))?.id === "image2";
|
|
1706
|
+
if (imageOutput || maxVideoFrames !== void 0 && videoCodec !== "copy") {
|
|
1693
1707
|
workingDoc = {
|
|
1694
1708
|
...workingDoc,
|
|
1695
|
-
tracks: workingDoc.tracks
|
|
1696
|
-
if (
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
}
|
|
1700
|
-
|
|
1701
|
-
const slicedSamples = t.samples.slice(0, maxVideoFrames);
|
|
1702
|
-
return { ...t, duration: slicedSamples.reduce((acc, s) => acc + s.duration, 0), samples: slicedSamples };
|
|
1703
|
-
}
|
|
1704
|
-
return t;
|
|
1705
|
-
})
|
|
1709
|
+
tracks: await mapWork(workingDoc.tracks, async (track) => {
|
|
1710
|
+
if (track.type !== "video") return track;
|
|
1711
|
+
const limit = vfFilters.length === 0 ? maxVideoFrames : void 0;
|
|
1712
|
+
const frames = await ensureDecodedFrames(track, workingDoc.timescale, budget, context.signal, limit);
|
|
1713
|
+
return { ...track, samples: [], decodedVideoFrames: frames };
|
|
1714
|
+
}, context.signal)
|
|
1706
1715
|
};
|
|
1707
1716
|
}
|
|
1708
1717
|
if (vfFilters.length > 0) {
|
|
@@ -1740,7 +1749,7 @@ function createFfmpegCommand(options = {}) {
|
|
|
1740
1749
|
}
|
|
1741
1750
|
}
|
|
1742
1751
|
const offsetTicks = Math.round(presentationOffset * t.timescale);
|
|
1743
|
-
const decoded = await ensureDecodedFrames(t, budget, context.signal);
|
|
1752
|
+
const decoded = await ensureDecodedFrames(t, workingDoc.timescale, budget, context.signal);
|
|
1744
1753
|
const displayed = decoded.map((frame) => ({ ...frame, ptsSeconds: (Math.round(frame.ptsSeconds * t.timescale) - offsetTicks) / t.timescale }));
|
|
1745
1754
|
const filtered = await applyVideoFilterChain(displayed, combinedChain, budget, loadedSubtitleCues, context.signal);
|
|
1746
1755
|
const first = filtered[0];
|
|
@@ -1954,6 +1963,9 @@ function createFfmpegCommand(options = {}) {
|
|
|
1954
1963
|
options.onMetrics?.(budget.getStats());
|
|
1955
1964
|
return { exitCode: 0 };
|
|
1956
1965
|
}
|
|
1966
|
+
if (imageOutput && !workingDoc.tracks.some((track) => track.type === "video" && track.decodedVideoFrames?.length)) {
|
|
1967
|
+
throw new Error("No video frame in the requested presentation interval");
|
|
1968
|
+
}
|
|
1957
1969
|
if (/%0?\d*d/.test(outputTarget)) {
|
|
1958
1970
|
const outExt2 = outputTarget.split(".").pop()?.toLowerCase() ?? "png";
|
|
1959
1971
|
const imgPlugin = registry.findByFilename(`x.${outExt2}`);
|
|
@@ -1961,7 +1973,7 @@ function createFfmpegCommand(options = {}) {
|
|
|
1961
1973
|
throw new Error(`Unsupported output format for '${outputTarget}' (AST not registered)`);
|
|
1962
1974
|
}
|
|
1963
1975
|
const vTrack = workingDoc.tracks.find((t) => t.type === "video");
|
|
1964
|
-
const frames = vTrack ? await ensureDecodedFrames(vTrack, budget, context.signal) : [];
|
|
1976
|
+
const frames = vTrack ? await ensureDecodedFrames(vTrack, workingDoc.timescale, budget, context.signal) : [];
|
|
1965
1977
|
const limit = maxVideoFrames ? Math.min(frames.length, maxVideoFrames) : frames.length;
|
|
1966
1978
|
const imgFmt = outExt2 === "jpg" || outExt2 === "jpeg" ? "jpeg" : outExt2 === "webp" ? "webp" : outExt2 === "gif" ? "gif" : outExt2 === "bmp" ? "bmp" : outExt2 === "ppm" ? "ppm" : "png";
|
|
1967
1979
|
for (let idx = 0; idx < limit; idx++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poe-platform/safe-bash",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.837",
|
|
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.
|
|
1418
|
+
"@poe-platform/safe-fs": "0.1.837",
|
|
1419
1419
|
"pdf-lib": "1.17.1",
|
|
1420
1420
|
"@types/node": "^25.2.2",
|
|
1421
1421
|
"@formatjs/intl-numberformat": "9.4.0",
|