@poe-platform/safe-bash 0.1.839 → 0.1.841
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/audio-ast/binary.d.ts +19 -0
- package/dist/audio-ast/index.d.ts +1 -0
- package/dist/audio-ast/index.js +92 -91
- package/dist/audio-ast/mpeg.d.ts +23 -0
- package/dist/mp4-ast/containers/adapters.d.ts +1 -0
- package/dist/mp4-ast/types.d.ts +8 -2
- package/dist/pdf-ast/cos/security.d.ts +10 -0
- package/dist/pdf-ast/edit/retained-attachment-edits.d.ts +4 -0
- package/dist/pdf-ast/edit/retained-graph.d.ts +1 -1
- package/dist/pdf-ast/image.js +1 -1
- package/dist/pdf-ast/index.d.ts +2 -1
- package/dist/pdf-ast/index.js +7 -1
- package/dist/pdf-ast/vendor/pdfjs-fonts.d.mts +8 -0
- package/dist/pdf-ast/vendor/pdfjs-fonts.mjs +28 -0
- package/dist/safe-bash/audio-ast.browser.js +2 -1
- package/dist/safe-bash/audio-ast.js +1 -1
- package/dist/safe-bash/command-chunks/{chunk-SG2KOOD4.js → chunk-3SAYZFMD.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-T6OXG7QQ.js → chunk-AAOPVEPF.js} +66 -1
- package/dist/safe-bash/command-chunks/{chunk-FH5VMAPS.js → chunk-BZW25LNV.js} +420 -131
- package/dist/safe-bash/command-chunks/{chunk-DQVOBGI2.js → chunk-CIV3GER2.js} +6770 -6523
- package/dist/safe-bash/command-chunks/{chunk-46GA576V.js → chunk-CPKVUB7S.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-MBXNG6C3.js → chunk-DLLN3C3Q.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ZSQCROFE.js → chunk-DM5T5D2K.js} +5 -1
- package/dist/safe-bash/command-chunks/{chunk-3EAZRKI4.js → chunk-G7HQYFIU.js} +10 -1
- package/dist/safe-bash/command-chunks/{chunk-GYKQ3IOV.js → chunk-LMYQMJYY.js} +94 -18
- package/dist/safe-bash/command-chunks/{chunk-DX7KU6YU.js → chunk-N52AHO6S.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-4BSD2RGK.js → chunk-QPKL5GWH.js} +102 -42
- package/dist/safe-bash/command-chunks/{chunk-Z2T7E4ID.js → chunk-R7QLP5RH.js} +1212 -1119
- package/dist/safe-bash/command-chunks/{chunk-77ROD2TR.js → chunk-X3WNWRVT.js} +11 -5
- package/dist/safe-bash/command-chunks/{chunk-RSFKQE2U.js → chunk-YAXP4EHP.js} +19 -10
- package/dist/safe-bash/command-chunks/{chunk-TCXM7BSZ.js → chunk-YFMNXPLP.js} +4 -4
- package/dist/safe-bash-command-exiftool/index.js +25 -1
- package/dist/safe-bash-command-ffmpeg/index.js +191 -49
- package/dist/safe-bash-command-ffprobe/index.js +2 -2
- package/dist/safe-bash-command-ffprobe/media.js +2 -2
- package/dist/safe-bash-command-imagemagick/index.js +498 -101
- package/dist/safe-bash-command-mdq/index.js +2 -0
- package/dist/safe-bash-command-pandoc/citeproc-filters.js +7 -7
- package/dist/safe-bash-command-pandoc/command.js +8 -8
- package/dist/safe-bash-command-pandoc/index.js +52 -52
- package/dist/safe-bash-command-qpdf/index.js +1037 -654
- package/dist/safe-bash-command-qpdf/page-range.d.ts +2 -0
- package/dist/safe-bash-command-soffice/index.js +28 -8
- package/dist/safe-bash-command-sqlite3/btree.d.ts +2 -1
- package/dist/safe-bash-command-sqlite3/engine.d.ts +15 -1
- package/dist/safe-bash-command-sqlite3/index.js +294 -68
- package/dist/safe-bash-command-tar/create.js +2 -2
- package/dist/safe-bash-command-tar/extract.js +2 -2
- package/dist/safe-bash-command-tar/format.js +1 -1
- package/dist/safe-bash-command-tar/index.js +4 -4
- package/dist/safe-bash-command-tar/modes.js +4 -4
- package/dist/safe-bash-command-unzip/index.js +1 -1
- package/dist/safe-bash-command-unzip/unzip.js +1 -1
- package/dist/safe-bash-markdown-engine/commonmark-blocks.d.ts +17 -1
- package/dist/safe-bash-markdown-engine/commonmark-blocks.js +3 -3
- package/dist/safe-bash-markdown-engine/commonmark-inlines.d.ts +1 -1
- package/dist/safe-bash-markdown-engine/commonmark-inlines.js +3 -3
- package/dist/safe-bash-markdown-engine/commonmark-syntax.d.ts +5 -0
- package/dist/safe-bash-markdown-engine/commonmark-syntax.js +5 -1
- package/dist/safe-bash-markdown-engine/gfm-syntax.js +2 -2
- package/dist/safe-bash-markdown-engine/index.js +4 -4
- package/dist/safe-bash-zip-engine/index.js +1 -1
- package/dist/safe-bash-zip-engine/zip/repair.js +1 -1
- package/dist/safe-bash-zip-engine/zip/sfx.js +1 -1
- package/dist/safe-bash-zip-engine/zip-format.d.ts +2 -0
- package/dist/safe-bash-zip-engine/zip-format.js +1 -1
- package/package.json +2 -2
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +2 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bareAutolink,
|
|
3
3
|
filterGfmHtml
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-N52AHO6S.js";
|
|
5
5
|
import {
|
|
6
6
|
asciiPunctuation,
|
|
7
7
|
decodeSyntax,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
readLabel,
|
|
15
15
|
readTitle,
|
|
16
16
|
whitespace
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-AAOPVEPF.js";
|
|
18
18
|
import {
|
|
19
19
|
unicodePunctuation,
|
|
20
20
|
unicodeWhitespace
|
|
@@ -156,7 +156,7 @@ function html(text, start, context) {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
async function parseCommonMarkInlines(pending, definitions, context, extensions = {}) {
|
|
159
|
+
async function parseCommonMarkInlines(pending, definitions, context, extensions = {}, footnote) {
|
|
160
160
|
context.checkpoint(0);
|
|
161
161
|
const size = pending.lines.reduce((n, line) => n + line.text.length + 1, -1);
|
|
162
162
|
context.bound("text", Math.max(0, size));
|
|
@@ -353,10 +353,16 @@ async function parseCommonMarkInlines(pending, definitions, context, extensions
|
|
|
353
353
|
continue;
|
|
354
354
|
}
|
|
355
355
|
if (char === "[" || char === "!" && text[i + 1] === "[") {
|
|
356
|
-
if (char === "[" && text[i + 1] === "^" && context.footnoteReference) {
|
|
356
|
+
if (char === "[" && text[i + 1] === "^" && (footnote || context.footnoteReference)) {
|
|
357
357
|
const end = text.indexOf("]", i + 2);
|
|
358
358
|
const label = end < 0 ? "" : text.slice(i + 2, end);
|
|
359
|
-
|
|
359
|
+
const note = label ? await footnote?.(label) : void 0;
|
|
360
|
+
if (note) {
|
|
361
|
+
append(note);
|
|
362
|
+
i = end + 1;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (label && context.footnoteReference?.(label)) {
|
|
360
366
|
append({ t: "RawInline", c: ["mdq-footnote", label] });
|
|
361
367
|
i = end + 1;
|
|
362
368
|
continue;
|
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
probeOggFlacSource,
|
|
12
12
|
probeOggStreamMetadata,
|
|
13
13
|
scanMp4Frames,
|
|
14
|
-
scanMp4Packets
|
|
15
|
-
|
|
14
|
+
scanMp4Packets,
|
|
15
|
+
wavPacketFrames
|
|
16
|
+
} from "./chunk-BZW25LNV.js";
|
|
16
17
|
|
|
17
18
|
// packages/safe-bash-command-ffprobe/src/probe-text.ts
|
|
18
19
|
import { yieldTurn } from "../../safe-bash-contracts/yield.js";
|
|
@@ -583,7 +584,7 @@ function audioProbeRows(audio, size, parsed, retainedTags = false) {
|
|
|
583
584
|
const firstFrame = audio.nodes.find((node) => node.type === "MPEG");
|
|
584
585
|
const vbr = firstFrame?.fields?.vbr;
|
|
585
586
|
const encodedSamples = Number(firstFrame?.fields?.encodedSamples);
|
|
586
|
-
const mp3Bitrate = vbr?.bytes && encodedSamples ? vbr.bytes * 8 * audio.streams[0].sampleRate / encodedSamples : audio.streams[0].bitrate;
|
|
587
|
+
const mp3Bitrate = vbr?.type === "Info" ? Number(firstFrame?.fields?.bitrate) : vbr?.bytes && encodedSamples ? vbr.bytes * 8 * audio.streams[0].sampleRate / encodedSamples : audio.streams[0].bitrate;
|
|
587
588
|
const formatTags = audio.format === "ogg" ? {} : { ...audio.tags };
|
|
588
589
|
const brands = audio.nodes.find((node) => node.type === "ftyp")?.fields;
|
|
589
590
|
if (brands)
|
|
@@ -602,6 +603,7 @@ function audioProbeRows(audio, size, parsed, retainedTags = false) {
|
|
|
602
603
|
...stream.tags && Object.keys(stream.tags).length ? { tags: stream.tags } : {},
|
|
603
604
|
codec_name: stream.codec === "pcm" ? stream.bitsPerSample === 8 ? "pcm_u8" : `pcm_s${stream.bitsPerSample}le` : stream.codec === "pcm_float" ? `pcm_f${stream.bitsPerSample}le` : stream.codec,
|
|
604
605
|
codec_type: "audio",
|
|
606
|
+
...["pcm", "pcm_float", "mp3", "flac"].includes(stream.codec) ? { sample_fmt: stream.codec === "mp3" ? "fltp" : stream.codec === "pcm_float" ? stream.bitsPerSample === 64 ? "dbl" : "flt" : stream.codec === "pcm" && stream.bitsPerSample === 8 ? "u8" : stream.bitsPerSample <= 16 ? "s16" : "s32" } : {},
|
|
605
607
|
sample_rate: String(stream.sampleRate),
|
|
606
608
|
channels: stream.channels,
|
|
607
609
|
bits_per_sample: stream.codec.startsWith("pcm") ? stream.bitsPerSample ?? 0 : 0,
|
|
@@ -1624,10 +1626,15 @@ function* formatProbeSteps(probe2, opts) {
|
|
|
1624
1626
|
if (typeOrdinal++ !== (parseInt(spec.slice(2), 10) || 0)) return;
|
|
1625
1627
|
} else if ([...spec].every((c) => c >= "0" && c <= "9") && stream.index !== parseInt(spec, 10)) return;
|
|
1626
1628
|
}
|
|
1629
|
+
const recordCount = probe2.format?.format_name === "wav" ? String(Math.ceil(stream.duration_ts / wavPacketFrames(
|
|
1630
|
+
Number(stream.sample_rate),
|
|
1631
|
+
Math.max(1, (stream.channels ?? 1) * (stream.bits_per_sample ?? 16) / 8)
|
|
1632
|
+
))) : stream.nb_frames ?? "0";
|
|
1633
|
+
const { nb_read_frames, nb_read_packets, ...fields } = stream;
|
|
1627
1634
|
return filterObject({
|
|
1628
|
-
...
|
|
1629
|
-
...opts.countFrames ? { nb_read_frames:
|
|
1630
|
-
...opts.countPackets ? { nb_read_packets:
|
|
1635
|
+
...fields,
|
|
1636
|
+
...opts.countFrames ? { nb_read_frames: nb_read_frames ?? recordCount } : {},
|
|
1637
|
+
...opts.countPackets ? { nb_read_packets: nb_read_packets ?? recordCount } : {}
|
|
1631
1638
|
}, "stream", "stream_tags");
|
|
1632
1639
|
};
|
|
1633
1640
|
const finalFormat = includeFormat && probe2.format ? filterObject(probe2.format, "format", "format_tags") : void 0;
|
|
@@ -1817,6 +1824,9 @@ function parseProbeArguments(args) {
|
|
|
1817
1824
|
showFormat = true;
|
|
1818
1825
|
showStreams = true;
|
|
1819
1826
|
}
|
|
1827
|
+
const entryFilter = parseEntryFilter(showEntries);
|
|
1828
|
+
showPackets ||= entryFilter.has("packet");
|
|
1829
|
+
showFrames ||= entryFilter.has("frame");
|
|
1820
1830
|
return {
|
|
1821
1831
|
printFormat,
|
|
1822
1832
|
showFormat,
|
|
@@ -2038,11 +2048,10 @@ function createFfprobeCommand(options = {}) {
|
|
|
2038
2048
|
inputTarget
|
|
2039
2049
|
} = parseProbeArguments(args);
|
|
2040
2050
|
const entryFilter = parseEntryFilter(showEntries);
|
|
2041
|
-
const packetRecords = showPackets || entryFilter.has("packet"), frameRecords = showFrames || entryFilter.has("frame");
|
|
2042
2051
|
const mp4Records = !options.asts ? {
|
|
2043
|
-
packets:
|
|
2044
|
-
frames:
|
|
2045
|
-
metadata: !(
|
|
2052
|
+
packets: showPackets,
|
|
2053
|
+
frames: showFrames,
|
|
2054
|
+
metadata: !(showPackets || showFrames) || showStreams || showFormat || showChapters || countFrames || countPackets || [...entryFilter.keys()].some((section) => section !== "packet" && section !== "frame")
|
|
2046
2055
|
} : void 0;
|
|
2047
2056
|
const packetPlugins = mp4Records ? [registry.findByFormatName("mp4"), registry.findByFormatName("mov")] : [];
|
|
2048
2057
|
let audioInput;
|
|
@@ -123,8 +123,8 @@ function decimalTime(value) {
|
|
|
123
123
|
return value.toFixed(9).replace(/(\.[0-9]*?)0+$/u, "$1").replace(/\.$/u, "");
|
|
124
124
|
}
|
|
125
125
|
var supportedKeywords = /* @__PURE__ */ new Set(["path", "linkpath", "size", "uid", "gid", "mtime", "atime", "ctime", "uname", "gname", "comment", "charset", "hdrcharset"]);
|
|
126
|
-
var
|
|
127
|
-
var
|
|
126
|
+
var unsupportedKeywords = /* @__PURE__ */ new Set(["SCHILY.realsize", "SCHILY.filetype", "SCHILY.offset", "SCHILY.dev", "SCHILY.ino", "SCHILY.nlink", "SUN.holesdata", "LIBARCHIVE.symlinktype"]);
|
|
127
|
+
var unsupportedKeywordPrefixes = ["GNU.sparse.", "GNU.volume."];
|
|
128
128
|
function parsePax(payload) {
|
|
129
129
|
const result = /* @__PURE__ */ new Map();
|
|
130
130
|
let offset = 0;
|
|
@@ -141,8 +141,8 @@ function parsePax(payload) {
|
|
|
141
141
|
const key = text(record.subarray(0, equals));
|
|
142
142
|
if (/[\0\n]/u.test(key)) fail("invalid PAX key/value");
|
|
143
143
|
offset += size;
|
|
144
|
-
if (
|
|
145
|
-
if (!supportedKeywords.has(key))
|
|
144
|
+
if (unsupportedKeywords.has(key) || unsupportedKeywordPrefixes.some((prefix) => key.startsWith(prefix))) fail(`unsupported PAX keyword: ${key}`);
|
|
145
|
+
if (!supportedKeywords.has(key)) continue;
|
|
146
146
|
const value = text(record.subarray(equals + 1));
|
|
147
147
|
if (value.includes("\0")) fail("invalid PAX key/value");
|
|
148
148
|
if (key === "hdrcharset" && value !== "" && value !== "ISO-IR 10646 2000 UTF-8") fail("unsupported PAX header charset");
|
|
@@ -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-
|
|
23
|
+
} from "../safe-bash/command-chunks/chunk-BZW25LNV.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
|
|
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])
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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,63 @@ 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(
|
|
306
|
-
if (clean.startsWith("ih/")) return Math.max(
|
|
307
|
-
if (clean.startsWith("iw*")) return Math.max(
|
|
308
|
-
if (clean.startsWith("ih*")) return Math.max(
|
|
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 parseFilterExpression(expression, filter, variables) {
|
|
330
|
+
let offset = 0;
|
|
331
|
+
const source = expression.trim();
|
|
332
|
+
function parse(minPrecedence = 0, depth = 0) {
|
|
333
|
+
if (depth > 64) throw new Error(`${filter}: 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(`${filter}: invalid expression`);
|
|
344
|
+
} else if (first && (first >= "a" && first <= "z" || first >= "A" && first <= "Z" || first === "_")) {
|
|
345
|
+
const start = offset++;
|
|
346
|
+
while (offset < source.length) {
|
|
347
|
+
const c = source[offset];
|
|
348
|
+
if (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_") offset++;
|
|
349
|
+
else break;
|
|
350
|
+
}
|
|
351
|
+
const name = source.slice(start, offset);
|
|
352
|
+
value = Object.hasOwn(variables, name) ? variables[name] : NaN;
|
|
353
|
+
} else {
|
|
354
|
+
const start = offset;
|
|
355
|
+
while (offset < source.length) {
|
|
356
|
+
const c = source[offset];
|
|
357
|
+
if (c >= "0" && c <= "9" || c === "." || c === "e" || c === "E" || (c === "+" || c === "-") && (source[offset - 1] === "e" || source[offset - 1] === "E")) offset++;
|
|
358
|
+
else break;
|
|
359
|
+
}
|
|
360
|
+
value = start === offset ? NaN : Number(source.slice(start, offset));
|
|
361
|
+
}
|
|
362
|
+
for (; ; ) {
|
|
363
|
+
while (source[offset] === " ") offset++;
|
|
364
|
+
const operator = source[offset];
|
|
365
|
+
const precedence = operator === "+" || operator === "-" ? 1 : operator === "*" || operator === "/" ? 2 : 0;
|
|
366
|
+
if (precedence === 0 || precedence < minPrecedence) break;
|
|
367
|
+
offset++;
|
|
368
|
+
const right = parse(precedence + 1, depth + 1);
|
|
369
|
+
if (operator === "+") value += right;
|
|
370
|
+
else if (operator === "-") value -= right;
|
|
371
|
+
else if (operator === "*") value *= right;
|
|
372
|
+
else value /= right;
|
|
373
|
+
}
|
|
374
|
+
return value;
|
|
375
|
+
}
|
|
376
|
+
const angle = parse();
|
|
377
|
+
if (offset !== source.length || !Number.isFinite(angle)) throw new Error(`${filter}: invalid or unsupported expression`);
|
|
378
|
+
return angle;
|
|
379
|
+
}
|
|
311
380
|
var GLYPH_5X7 = {
|
|
312
381
|
" ": [0, 0, 0, 0, 0, 0, 0],
|
|
313
382
|
"0": [14, 17, 19, 21, 25, 17, 14],
|
|
@@ -464,7 +533,23 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
464
533
|
let work = 0;
|
|
465
534
|
let current = frames;
|
|
466
535
|
if (current.length === 0) return current;
|
|
467
|
-
const filters =
|
|
536
|
+
const filters = [];
|
|
537
|
+
let filter = "";
|
|
538
|
+
let quoted = false;
|
|
539
|
+
for (let i = 0; i < filterChainStr.length; i++) {
|
|
540
|
+
const char = filterChainStr[i];
|
|
541
|
+
if (char === "'") {
|
|
542
|
+
quoted = !quoted;
|
|
543
|
+
} else if (!quoted && char === "\\" && i + 1 < filterChainStr.length) {
|
|
544
|
+
filter += filterChainStr[++i];
|
|
545
|
+
} else if (!quoted && char === ",") {
|
|
546
|
+
if (filter.trim()) filters.push(filter.trim());
|
|
547
|
+
filter = "";
|
|
548
|
+
} else {
|
|
549
|
+
filter += char;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (filter.trim()) filters.push(filter.trim());
|
|
468
553
|
for (const filterSpec of filters) {
|
|
469
554
|
if (++work % 1024 === 0) {
|
|
470
555
|
await yieldTurn(signal);
|
|
@@ -494,14 +579,14 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
494
579
|
let targetW = evalScaleDim(wSpec, iw, ih);
|
|
495
580
|
let targetH = evalScaleDim(hSpec, iw, ih);
|
|
496
581
|
if (targetW < 0 && targetH > 0) {
|
|
497
|
-
|
|
498
|
-
|
|
582
|
+
const multiple = wSpec.trim() === "-2" ? 2 : 1;
|
|
583
|
+
targetW = Math.max(1, Math.round(iw * targetH / (Math.max(1, ih) * multiple))) * multiple;
|
|
499
584
|
} else if (targetH < 0 && targetW > 0) {
|
|
500
|
-
|
|
501
|
-
|
|
585
|
+
const multiple = hSpec.trim() === "-2" ? 2 : 1;
|
|
586
|
+
targetH = Math.max(1, Math.round(ih * targetW / (Math.max(1, iw) * multiple))) * multiple;
|
|
502
587
|
}
|
|
503
|
-
targetW = Math.max(
|
|
504
|
-
targetH = Math.max(
|
|
588
|
+
targetW = Math.max(1, targetW);
|
|
589
|
+
targetH = Math.max(1, targetH);
|
|
505
590
|
current = await mapWork(current, async (f) => {
|
|
506
591
|
budget.recordFrame(targetW, targetH);
|
|
507
592
|
const resized = resizeImage(makeRgbaImg(f.width, f.height, f.data), {
|
|
@@ -524,15 +609,19 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
524
609
|
} else if (name === "crop") {
|
|
525
610
|
const iw = current[0].width;
|
|
526
611
|
const ih = current[0].height;
|
|
527
|
-
const cw =
|
|
528
|
-
const ch =
|
|
612
|
+
const cw = evalScaleDim(named.w ?? positional[0] ?? String(iw), iw, ih);
|
|
613
|
+
const ch = evalScaleDim(named.h ?? positional[1] ?? String(ih), iw, ih);
|
|
614
|
+
if (!Number.isFinite(cw) || !Number.isFinite(ch) || cw <= 0 || ch <= 0 || cw > iw || ch > ih) {
|
|
615
|
+
throw new Error(`Invalid too big or non positive size for width '${cw}' or height '${ch}'`);
|
|
616
|
+
}
|
|
617
|
+
const variables = { iw, ih, ow: cw, oh: ch, in_w: iw, in_h: ih, out_w: cw, out_h: ch };
|
|
529
618
|
const cx = Math.max(
|
|
530
619
|
0,
|
|
531
|
-
Math.min(iw - cw,
|
|
620
|
+
Math.min(iw - cw, Math.trunc(parseFilterExpression(named.x ?? positional[2] ?? String(Math.floor((iw - cw) / 2)), "crop", variables)))
|
|
532
621
|
);
|
|
533
622
|
const cy = Math.max(
|
|
534
623
|
0,
|
|
535
|
-
Math.min(ih - ch,
|
|
624
|
+
Math.min(ih - ch, Math.trunc(parseFilterExpression(named.y ?? positional[3] ?? String(Math.floor((ih - ch) / 2)), "crop", variables)))
|
|
536
625
|
);
|
|
537
626
|
current = await mapWork(current, async (f) => {
|
|
538
627
|
const cropped = extractImage(makeRgbaImg(f.width, f.height, f.data), {
|
|
@@ -551,10 +640,16 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
551
640
|
} else if (name === "pad") {
|
|
552
641
|
const iw = current[0].width;
|
|
553
642
|
const ih = current[0].height;
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
const
|
|
557
|
-
const
|
|
643
|
+
const width = named.w ?? named.width ?? positional[0] ?? String(iw);
|
|
644
|
+
const height = named.h ?? named.height ?? positional[1] ?? String(ih);
|
|
645
|
+
const pw = Number(width) === 0 ? iw : evalScaleDim(width, iw, ih);
|
|
646
|
+
const ph = Number(height) === 0 ? ih : evalScaleDim(height, iw, ih);
|
|
647
|
+
if (pw < iw || ph < ih) {
|
|
648
|
+
throw new Error("Padded dimensions cannot be smaller than input dimensions");
|
|
649
|
+
}
|
|
650
|
+
const variables = { iw, ih, ow: pw, oh: ph, in_w: iw, in_h: ih, out_w: pw, out_h: ph };
|
|
651
|
+
const px = Math.max(0, Math.min(pw - iw, Math.trunc(parseFilterExpression(named.x ?? positional[2] ?? "0", "pad", variables))));
|
|
652
|
+
const py = Math.max(0, Math.min(ph - ih, Math.trunc(parseFilterExpression(named.y ?? positional[3] ?? "0", "pad", variables))));
|
|
558
653
|
const col = parseColorRgba(named.color ?? positional[4] ?? "black");
|
|
559
654
|
current = await mapWork(current, async (f) => {
|
|
560
655
|
const padded = extendImage(makeRgbaImg(f.width, f.height, f.data), {
|
|
@@ -562,7 +657,7 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
562
657
|
top: py,
|
|
563
658
|
right: Math.max(0, pw - iw - px),
|
|
564
659
|
bottom: Math.max(0, ph - ih - py),
|
|
565
|
-
background: { r: col[0], g: col[1], b: col[2], a:
|
|
660
|
+
background: { r: col[0], g: col[1], b: col[2], a: col[3] },
|
|
566
661
|
extendWith: "background"
|
|
567
662
|
});
|
|
568
663
|
return {
|
|
@@ -582,11 +677,12 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
582
677
|
const out = flipImage(makeRgbaImg(f.width, f.height, f.data));
|
|
583
678
|
return { ...f, data: out.data };
|
|
584
679
|
}, signal);
|
|
585
|
-
} else if (name === "transpose"
|
|
680
|
+
} else if (name === "transpose") {
|
|
586
681
|
const dir = named.dir ?? positional[0] ?? "1";
|
|
587
|
-
const angle =
|
|
682
|
+
const angle = dir === "0" || dir === "2" ? 270 : 90;
|
|
588
683
|
current = await mapWork(current, async (f) => {
|
|
589
|
-
const
|
|
684
|
+
const rotated = rotateImage(makeRgbaImg(f.width, f.height, f.data), angle);
|
|
685
|
+
const out = dir === "0" || dir === "3" ? flipImage(rotated) : rotated;
|
|
590
686
|
return {
|
|
591
687
|
...f,
|
|
592
688
|
width: out.width,
|
|
@@ -594,6 +690,41 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
594
690
|
data: out.data
|
|
595
691
|
};
|
|
596
692
|
}, signal);
|
|
693
|
+
} else if (name === "rotate") {
|
|
694
|
+
const angle = parseFilterExpression(named.angle ?? named.a ?? (positional[0] || "0"), "rotate", { PI: Math.PI });
|
|
695
|
+
const cos = Math.cos(angle);
|
|
696
|
+
const sin = Math.sin(angle);
|
|
697
|
+
current = await mapWork(current, async (f) => {
|
|
698
|
+
budget.recordFrame(f.width, f.height);
|
|
699
|
+
const data = new Uint8Array(f.data.length);
|
|
700
|
+
const cx = (f.width - 1) / 2;
|
|
701
|
+
const cy = (f.height - 1) / 2;
|
|
702
|
+
for (let y = 0; y < f.height; y++) {
|
|
703
|
+
await yieldTurn(signal);
|
|
704
|
+
budget.checkCpu();
|
|
705
|
+
for (let x = 0; x < f.width; x++) {
|
|
706
|
+
const sx = cos * (x - cx) + sin * (y - cy) + cx;
|
|
707
|
+
const sy = -sin * (x - cx) + cos * (y - cy) + cy;
|
|
708
|
+
const dest = (y * f.width + x) * 4;
|
|
709
|
+
data[dest + 3] = 255;
|
|
710
|
+
if (sx < -1 || sx > f.width || sy < -1 || sy > f.height) continue;
|
|
711
|
+
const px = Math.max(0, Math.min(f.width - 1, sx));
|
|
712
|
+
const py = Math.max(0, Math.min(f.height - 1, sy));
|
|
713
|
+
const x0 = Math.floor(px);
|
|
714
|
+
const y0 = Math.floor(py);
|
|
715
|
+
const x1 = Math.min(x0 + 1, f.width - 1);
|
|
716
|
+
const y1 = Math.min(y0 + 1, f.height - 1);
|
|
717
|
+
const dx = px - x0;
|
|
718
|
+
const dy = py - y0;
|
|
719
|
+
for (let c = 0; c < 4; c++) {
|
|
720
|
+
const top = f.data[(y0 * f.width + x0) * 4 + c] * (1 - dx) + f.data[(y0 * f.width + x1) * 4 + c] * dx;
|
|
721
|
+
const bottom = f.data[(y1 * f.width + x0) * 4 + c] * (1 - dx) + f.data[(y1 * f.width + x1) * 4 + c] * dx;
|
|
722
|
+
data[dest + c] = Math.floor(top * (1 - dy) + bottom * dy + 1e-8);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return { ...f, data };
|
|
727
|
+
}, signal);
|
|
597
728
|
} else if (name === "negate") {
|
|
598
729
|
current = await mapWork(current, async (f) => {
|
|
599
730
|
const out = negateImage(makeRgbaImg(f.width, f.height, f.data));
|
|
@@ -609,15 +740,15 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
609
740
|
const totalDuration = current.reduce((acc, f) => acc + f.durationSeconds, 0) || 1;
|
|
610
741
|
const targetCount = Math.max(1, Math.round(totalDuration * targetFps));
|
|
611
742
|
const resampled = [];
|
|
743
|
+
let srcIdx = 0;
|
|
612
744
|
for (let i = 0; i < targetCount; i++) {
|
|
613
745
|
if (++work % 1024 === 0) {
|
|
614
746
|
await yieldTurn(signal);
|
|
615
747
|
signal?.throwIfAborted();
|
|
616
748
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
);
|
|
749
|
+
while (srcIdx + 1 < current.length && Math.round(current[srcIdx + 1].ptsSeconds * targetFps) <= i) {
|
|
750
|
+
srcIdx++;
|
|
751
|
+
}
|
|
621
752
|
const src = current[srcIdx];
|
|
622
753
|
resampled.push({
|
|
623
754
|
...src,
|
|
@@ -762,13 +893,17 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
762
893
|
return out;
|
|
763
894
|
}, signal);
|
|
764
895
|
} else if (name === "drawbox") {
|
|
765
|
-
const bx = parseInt(named.x ?? positional[0] ?? "0", 10) || 0;
|
|
766
|
-
const by = parseInt(named.y ?? positional[1] ?? "0", 10) || 0;
|
|
767
896
|
const bw = parseInt(named.w ?? named.width ?? positional[2] ?? "16", 10) || 16;
|
|
768
897
|
const bh = parseInt(named.h ?? named.height ?? positional[3] ?? "16", 10) || 16;
|
|
769
898
|
const col = parseColorRgba(named.color ?? named.c ?? positional[4] ?? "red");
|
|
899
|
+
const thicknessOption = named.t ?? named.thickness ?? positional[5] ?? "3";
|
|
900
|
+
const thickness = thicknessOption === "fill" ? Infinity : parseInt(thicknessOption, 10);
|
|
901
|
+
const replace = named.replace === "1";
|
|
770
902
|
current = await mapWork(current, async (f) => {
|
|
771
903
|
let work2 = 0;
|
|
904
|
+
const variables = { iw: f.width, ih: f.height, in_w: f.width, in_h: f.height };
|
|
905
|
+
const bx = Math.trunc(parseFilterExpression(named.x ?? positional[0] ?? "0", "drawbox", variables));
|
|
906
|
+
const by = Math.trunc(parseFilterExpression(named.y ?? positional[1] ?? "0", "drawbox", variables));
|
|
772
907
|
const nextData = new Uint8Array(f.data);
|
|
773
908
|
for (let y = Math.max(0, by); y < Math.min(f.height, by + bh); y++) {
|
|
774
909
|
if (++work2 % 1024 === 0) {
|
|
@@ -780,11 +915,15 @@ async function applyVideoFilterChain(frames, filterChainStr, budget, subtitleCue
|
|
|
780
915
|
await yieldTurn(signal);
|
|
781
916
|
signal?.throwIfAborted();
|
|
782
917
|
}
|
|
918
|
+
if (Math.min(x - bx, y - by, bx + bw - 1 - x, by + bh - 1 - y) >= thickness) continue;
|
|
783
919
|
const idx = (y * f.width + x) * 4;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
920
|
+
if (replace) {
|
|
921
|
+
nextData.set(col, idx);
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
925
|
+
nextData[idx + channel] = (nextData[idx + channel] * (255 - col[3]) + col[channel] * col[3]) / 255;
|
|
926
|
+
}
|
|
788
927
|
}
|
|
789
928
|
}
|
|
790
929
|
return { ...f, data: nextData };
|
|
@@ -837,7 +976,6 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
|
|
|
837
976
|
let rotation;
|
|
838
977
|
let noOverwrite = true;
|
|
839
978
|
const vfFilters = [];
|
|
840
|
-
const afFilters = [];
|
|
841
979
|
const metadata = {};
|
|
842
980
|
let outputTarget;
|
|
843
981
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -914,8 +1052,7 @@ function evalSyncFfmpeg(inBytesOrArgs, maybeArgs, readFileSync, writeFileSync) {
|
|
|
914
1052
|
const f = args[++i];
|
|
915
1053
|
if (f) vfFilters.push(f);
|
|
916
1054
|
} else if (arg === "-af" || arg === "-filter:a") {
|
|
917
|
-
|
|
918
|
-
if (f) afFilters.push(f);
|
|
1055
|
+
return void 0;
|
|
919
1056
|
} else if (arg === "-metadata" || arg.startsWith("-metadata:")) {
|
|
920
1057
|
const kv = args[++i] ?? "";
|
|
921
1058
|
const eq = kv.indexOf("=");
|
|
@@ -1799,7 +1936,7 @@ function createFfmpegCommand(options = {}) {
|
|
|
1799
1936
|
const origCh = t.decodedAudio?.channels ?? t.codecDescriptions[0]?.channels ?? 2;
|
|
1800
1937
|
const targetRate = audioRate ?? origRate;
|
|
1801
1938
|
const targetCh = audioChannels ?? origCh;
|
|
1802
|
-
const durSec =
|
|
1939
|
+
const durSec = (t.duration || origRate) / Math.max(1, t.timescale || origRate);
|
|
1803
1940
|
let volFactor = t.volume ?? 1;
|
|
1804
1941
|
let tempoFactor = 1;
|
|
1805
1942
|
let reverseAudio = false;
|
|
@@ -1816,11 +1953,14 @@ function createFfmpegCommand(options = {}) {
|
|
|
1816
1953
|
const trimmed = item.trim();
|
|
1817
1954
|
if (trimmed.startsWith("volume=")) {
|
|
1818
1955
|
const volSpec = trimmed.slice("volume=".length).trim();
|
|
1819
|
-
|
|
1956
|
+
const gain = parseFloat(volSpec);
|
|
1957
|
+
volFactor *= volSpec.endsWith("dB") ? Math.pow(10, (parseFloat(volSpec.slice(0, -2)) || 0) / 20) : Number.isNaN(gain) ? 1 : gain;
|
|
1820
1958
|
} else if (trimmed.startsWith("atempo=")) {
|
|
1821
1959
|
tempoFactor *= Math.max(0.25, Math.min(4, parseFloat(trimmed.slice(7)) || 1));
|
|
1822
1960
|
} else if (trimmed === "areverse") {
|
|
1823
1961
|
reverseAudio = true;
|
|
1962
|
+
} else {
|
|
1963
|
+
throw new Error(`Unsupported audio filter: ${trimmed}`);
|
|
1824
1964
|
}
|
|
1825
1965
|
}
|
|
1826
1966
|
}
|
|
@@ -1833,6 +1973,7 @@ function createFfmpegCommand(options = {}) {
|
|
|
1833
1973
|
}
|
|
1834
1974
|
const dst = new Float32Array(targetSamples);
|
|
1835
1975
|
const srcCh = t.decodedAudio?.channelData[Math.min(c, (t.decodedAudio.channelData.length || 1) - 1)];
|
|
1976
|
+
const rightCh = origCh === 2 && targetCh === 1 ? t.decodedAudio.channelData[1] : void 0;
|
|
1836
1977
|
if (srcCh && srcCh.length > 0) {
|
|
1837
1978
|
for (let i = 0; i < targetSamples; i++) {
|
|
1838
1979
|
if (++work % 1024 === 0) {
|
|
@@ -1840,7 +1981,8 @@ function createFfmpegCommand(options = {}) {
|
|
|
1840
1981
|
context.signal?.throwIfAborted();
|
|
1841
1982
|
}
|
|
1842
1983
|
const srcIdx = Math.min(srcCh.length - 1, Math.floor(i / targetSamples * srcCh.length));
|
|
1843
|
-
|
|
1984
|
+
const sample = rightCh ? (srcCh[srcIdx] + rightCh[srcIdx]) / 2 : srcCh[srcIdx];
|
|
1985
|
+
dst[i] = Math.max(-1, Math.min(1, sample * volFactor));
|
|
1844
1986
|
}
|
|
1845
1987
|
}
|
|
1846
1988
|
if (reverseAudio) dst.reverse();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createFfprobeCommand
|
|
3
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
3
|
+
} from "../safe-bash/command-chunks/chunk-YAXP4EHP.js";
|
|
4
4
|
import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
|
|
5
|
-
import "../safe-bash/command-chunks/chunk-
|
|
5
|
+
import "../safe-bash/command-chunks/chunk-BZW25LNV.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-
|
|
14
|
+
} from "../safe-bash/command-chunks/chunk-YAXP4EHP.js";
|
|
15
15
|
import "../safe-bash/command-chunks/chunk-MGBHGZEX.js";
|
|
16
|
-
import "../safe-bash/command-chunks/chunk-
|
|
16
|
+
import "../safe-bash/command-chunks/chunk-BZW25LNV.js";
|
|
17
17
|
import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
|
|
18
18
|
export {
|
|
19
19
|
createFfprobeCommand,
|