@lumy-pack/scene-sieve 0.0.9 → 0.0.11
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/cli.mjs +336 -98
- package/dist/commands/Sieve.d.ts +2 -0
- package/dist/errors.d.ts +10 -0
- package/dist/index.cjs +31 -11
- package/dist/index.mjs +31 -11
- package/dist/pipeline-worker.mjs +31 -11
- package/dist/utils/command-registry.d.ts +24 -0
- package/dist/utils/parse-options.d.ts +21 -0
- package/package.json +3 -2
package/dist/cli.mjs
CHANGED
|
@@ -9,42 +9,6 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/utils/logger.ts
|
|
13
|
-
import pc from "picocolors";
|
|
14
|
-
function setDebugMode(enabled) {
|
|
15
|
-
debugMode = enabled;
|
|
16
|
-
}
|
|
17
|
-
function timestamp() {
|
|
18
|
-
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
19
|
-
}
|
|
20
|
-
var debugMode, logger;
|
|
21
|
-
var init_logger = __esm({
|
|
22
|
-
"src/utils/logger.ts"() {
|
|
23
|
-
"use strict";
|
|
24
|
-
debugMode = false;
|
|
25
|
-
logger = {
|
|
26
|
-
info(message) {
|
|
27
|
-
console.log(`${pc.blue("info")} ${message}`);
|
|
28
|
-
},
|
|
29
|
-
success(message) {
|
|
30
|
-
console.log(`
|
|
31
|
-
${pc.green("done")} ${message}`);
|
|
32
|
-
},
|
|
33
|
-
warn(message) {
|
|
34
|
-
console.warn(`${pc.yellow("warn")} ${message}`);
|
|
35
|
-
},
|
|
36
|
-
error(message) {
|
|
37
|
-
console.error(`${pc.red("error")} ${message}`);
|
|
38
|
-
},
|
|
39
|
-
debug(message) {
|
|
40
|
-
if (debugMode) {
|
|
41
|
-
console.log(`${pc.gray(`[${timestamp()}] debug`)} ${message}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
12
|
// src/constants.ts
|
|
49
13
|
import { tmpdir } from "os";
|
|
50
14
|
import { join } from "path";
|
|
@@ -86,6 +50,42 @@ var init_constants = __esm({
|
|
|
86
50
|
}
|
|
87
51
|
});
|
|
88
52
|
|
|
53
|
+
// src/utils/logger.ts
|
|
54
|
+
import pc from "picocolors";
|
|
55
|
+
function setDebugMode(enabled) {
|
|
56
|
+
debugMode = enabled;
|
|
57
|
+
}
|
|
58
|
+
function timestamp() {
|
|
59
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
60
|
+
}
|
|
61
|
+
var debugMode, logger;
|
|
62
|
+
var init_logger = __esm({
|
|
63
|
+
"src/utils/logger.ts"() {
|
|
64
|
+
"use strict";
|
|
65
|
+
debugMode = false;
|
|
66
|
+
logger = {
|
|
67
|
+
info(message) {
|
|
68
|
+
console.log(`${pc.blue("info")} ${message}`);
|
|
69
|
+
},
|
|
70
|
+
success(message) {
|
|
71
|
+
console.log(`
|
|
72
|
+
${pc.green("done")} ${message}`);
|
|
73
|
+
},
|
|
74
|
+
warn(message) {
|
|
75
|
+
console.warn(`${pc.yellow("warn")} ${message}`);
|
|
76
|
+
},
|
|
77
|
+
error(message) {
|
|
78
|
+
console.error(`${pc.red("error")} ${message}`);
|
|
79
|
+
},
|
|
80
|
+
debug(message) {
|
|
81
|
+
if (debugMode) {
|
|
82
|
+
console.log(`${pc.gray(`[${timestamp()}] debug`)} ${message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
89
|
// src/core/dbscan.ts
|
|
90
90
|
function dbscan(points, imageWidth, imageHeight, alpha, minPts) {
|
|
91
91
|
if (points.length === 0) {
|
|
@@ -1248,10 +1248,7 @@ function computeSegmentPlan(totalDuration, maxSegmentDuration, maxFrames, fps) {
|
|
|
1248
1248
|
}
|
|
1249
1249
|
return segments;
|
|
1250
1250
|
}
|
|
1251
|
-
function
|
|
1252
|
-
if (segmentResults.length === 0) {
|
|
1253
|
-
return { frames: [], edges: [], animations: [] };
|
|
1254
|
-
}
|
|
1251
|
+
function collectAllFrames(segmentResults) {
|
|
1255
1252
|
const allFrames = [];
|
|
1256
1253
|
for (const result of segmentResults) {
|
|
1257
1254
|
for (const frame of result.frames) {
|
|
@@ -1266,19 +1263,24 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1266
1263
|
});
|
|
1267
1264
|
}
|
|
1268
1265
|
}
|
|
1269
|
-
allFrames
|
|
1270
|
-
|
|
1266
|
+
return allFrames;
|
|
1267
|
+
}
|
|
1268
|
+
function deduplicateFrames(frames, effectiveFps) {
|
|
1269
|
+
frames.sort((a, b) => a.frame.timestamp - b.frame.timestamp);
|
|
1271
1270
|
const dupThreshold = 1 / (effectiveFps * 2);
|
|
1272
|
-
const
|
|
1273
|
-
for (const entry of
|
|
1274
|
-
if (
|
|
1275
|
-
const last =
|
|
1271
|
+
const unique = [];
|
|
1272
|
+
for (const entry of frames) {
|
|
1273
|
+
if (unique.length > 0) {
|
|
1274
|
+
const last = unique[unique.length - 1];
|
|
1276
1275
|
if (Math.abs(entry.frame.timestamp - last.frame.timestamp) < dupThreshold) {
|
|
1277
1276
|
continue;
|
|
1278
1277
|
}
|
|
1279
1278
|
}
|
|
1280
|
-
|
|
1279
|
+
unique.push(entry);
|
|
1281
1280
|
}
|
|
1281
|
+
return unique;
|
|
1282
|
+
}
|
|
1283
|
+
function remapFrameIds(uniqueFrames) {
|
|
1282
1284
|
const globalIdMap = /* @__PURE__ */ new Map();
|
|
1283
1285
|
const frames = uniqueFrames.map((entry, globalId) => {
|
|
1284
1286
|
globalIdMap.set(`${entry.segmentIndex}:${entry.localId}`, globalId);
|
|
@@ -1288,6 +1290,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1288
1290
|
extractPath: entry.frame.extractPath
|
|
1289
1291
|
};
|
|
1290
1292
|
});
|
|
1293
|
+
return { frames, globalIdMap };
|
|
1294
|
+
}
|
|
1295
|
+
function remapEdges(segmentResults, globalIdMap) {
|
|
1291
1296
|
const edges = [];
|
|
1292
1297
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
1293
1298
|
for (const result of segmentResults) {
|
|
@@ -1319,6 +1324,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1319
1324
|
}
|
|
1320
1325
|
}
|
|
1321
1326
|
}
|
|
1327
|
+
return edges;
|
|
1328
|
+
}
|
|
1329
|
+
function remapAnimations(segmentResults, globalIdMap) {
|
|
1322
1330
|
const animations = [];
|
|
1323
1331
|
for (const result of segmentResults) {
|
|
1324
1332
|
for (const anim of result.animations) {
|
|
@@ -1336,6 +1344,18 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1336
1344
|
});
|
|
1337
1345
|
}
|
|
1338
1346
|
}
|
|
1347
|
+
return animations;
|
|
1348
|
+
}
|
|
1349
|
+
function mergeSegmentFrames(segmentResults) {
|
|
1350
|
+
if (segmentResults.length === 0) {
|
|
1351
|
+
return { frames: [], edges: [], animations: [] };
|
|
1352
|
+
}
|
|
1353
|
+
const effectiveFps = segmentResults[0].segment.effectiveFps;
|
|
1354
|
+
const allFrames = collectAllFrames(segmentResults);
|
|
1355
|
+
const uniqueFrames = deduplicateFrames(allFrames, effectiveFps);
|
|
1356
|
+
const { frames, globalIdMap } = remapFrameIds(uniqueFrames);
|
|
1357
|
+
const edges = remapEdges(segmentResults, globalIdMap);
|
|
1358
|
+
const animations = remapAnimations(segmentResults, globalIdMap);
|
|
1339
1359
|
return { frames, edges, animations };
|
|
1340
1360
|
}
|
|
1341
1361
|
function buildSegmentContext(segment, frames, segmentWorkspacePath, resolvedOptions, onProgress) {
|
|
@@ -1637,12 +1657,41 @@ var init_orchestrator = __esm({
|
|
|
1637
1657
|
// src/cli.ts
|
|
1638
1658
|
import { createRequire as createRequire2 } from "module";
|
|
1639
1659
|
import { Command } from "commander";
|
|
1640
|
-
|
|
1641
|
-
|
|
1660
|
+
|
|
1661
|
+
// ../shared/src/respond.ts
|
|
1662
|
+
function respond(command, data, startTime, version2) {
|
|
1663
|
+
const response = {
|
|
1664
|
+
ok: true,
|
|
1665
|
+
command,
|
|
1666
|
+
data,
|
|
1667
|
+
meta: {
|
|
1668
|
+
version: version2,
|
|
1669
|
+
durationMs: Date.now() - startTime,
|
|
1670
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
process.stdout.write(JSON.stringify(response) + "\n");
|
|
1674
|
+
}
|
|
1675
|
+
function respondError(command, code, message, startTime, version2, details) {
|
|
1676
|
+
const response = {
|
|
1677
|
+
ok: false,
|
|
1678
|
+
command,
|
|
1679
|
+
error: { code, message, ...details !== void 0 ? { details } : {} },
|
|
1680
|
+
meta: {
|
|
1681
|
+
version: version2,
|
|
1682
|
+
durationMs: Date.now() - startTime,
|
|
1683
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
process.stdout.write(JSON.stringify(response) + "\n");
|
|
1687
|
+
process.exitCode = 1;
|
|
1688
|
+
}
|
|
1642
1689
|
|
|
1643
1690
|
// src/commands/Sieve.tsx
|
|
1691
|
+
import { existsSync } from "fs";
|
|
1644
1692
|
import { Box as Box2, Text as Text3, useApp } from "ink";
|
|
1645
|
-
import {
|
|
1693
|
+
import { render } from "ink";
|
|
1694
|
+
import React, { useEffect, useState } from "react";
|
|
1646
1695
|
|
|
1647
1696
|
// src/components/PhaseStep.tsx
|
|
1648
1697
|
import { Box, Text as Text2 } from "ink";
|
|
@@ -1703,6 +1752,10 @@ var PhaseStep = ({ phase }) => {
|
|
|
1703
1752
|
] });
|
|
1704
1753
|
};
|
|
1705
1754
|
|
|
1755
|
+
// src/commands/Sieve.tsx
|
|
1756
|
+
init_constants();
|
|
1757
|
+
init_orchestrator();
|
|
1758
|
+
|
|
1706
1759
|
// src/core/run-in-worker.ts
|
|
1707
1760
|
import { dirname, join as join6 } from "path";
|
|
1708
1761
|
import { fileURLToPath } from "url";
|
|
@@ -1741,6 +1794,145 @@ async function runPipelineInWorker(options, onProgress) {
|
|
|
1741
1794
|
|
|
1742
1795
|
// src/commands/Sieve.tsx
|
|
1743
1796
|
init_workspace();
|
|
1797
|
+
|
|
1798
|
+
// src/errors.ts
|
|
1799
|
+
var SieveErrorCode = {
|
|
1800
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
1801
|
+
FILE_NOT_FOUND: "FILE_NOT_FOUND",
|
|
1802
|
+
INVALID_FORMAT: "INVALID_FORMAT",
|
|
1803
|
+
PIPELINE_ERROR: "PIPELINE_ERROR",
|
|
1804
|
+
WORKER_ERROR: "WORKER_ERROR",
|
|
1805
|
+
UNKNOWN: "UNKNOWN"
|
|
1806
|
+
};
|
|
1807
|
+
function classifyError(error) {
|
|
1808
|
+
const msg = error.message.toLowerCase();
|
|
1809
|
+
if (error.code === "ENOENT" || msg.includes("not found")) {
|
|
1810
|
+
return SieveErrorCode.FILE_NOT_FOUND;
|
|
1811
|
+
} else if (msg.includes("no video stream") || msg.includes("invalid format")) {
|
|
1812
|
+
return SieveErrorCode.INVALID_FORMAT;
|
|
1813
|
+
} else if (msg.includes("worker")) {
|
|
1814
|
+
return SieveErrorCode.WORKER_ERROR;
|
|
1815
|
+
} else {
|
|
1816
|
+
return SieveErrorCode.PIPELINE_ERROR;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/utils/command-registry.ts
|
|
1821
|
+
var SIEVE_COMMAND = {
|
|
1822
|
+
name: "scene-sieve",
|
|
1823
|
+
description: "Extract key frames from video and GIF files",
|
|
1824
|
+
usage: "scene-sieve <input> [options]",
|
|
1825
|
+
arguments: [
|
|
1826
|
+
{
|
|
1827
|
+
name: "input",
|
|
1828
|
+
description: "Input video or GIF file path",
|
|
1829
|
+
required: true
|
|
1830
|
+
}
|
|
1831
|
+
],
|
|
1832
|
+
options: [
|
|
1833
|
+
{
|
|
1834
|
+
flag: "-n, --count <number>",
|
|
1835
|
+
description: "Max number of frames to keep (default: 20)",
|
|
1836
|
+
type: "number"
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
flag: "-t, --threshold <number>",
|
|
1840
|
+
description: "Normalized threshold 0~1 (default: 0.5; keeps frames above ratio of max change)",
|
|
1841
|
+
type: "number"
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
flag: "-o, --output <path>",
|
|
1845
|
+
description: "Output directory path",
|
|
1846
|
+
type: "string"
|
|
1847
|
+
},
|
|
1848
|
+
{
|
|
1849
|
+
flag: "--fps <number>",
|
|
1850
|
+
description: "Max FPS for frame extraction",
|
|
1851
|
+
type: "number",
|
|
1852
|
+
default: "5"
|
|
1853
|
+
},
|
|
1854
|
+
{
|
|
1855
|
+
flag: "-mf, --max-frames <number>",
|
|
1856
|
+
description: "Max frames to extract (auto-reduces FPS for long videos)",
|
|
1857
|
+
type: "number",
|
|
1858
|
+
default: "300"
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
flag: "-s, --scale <number>",
|
|
1862
|
+
description: "Scale size for vision analysis",
|
|
1863
|
+
type: "number",
|
|
1864
|
+
default: "720"
|
|
1865
|
+
},
|
|
1866
|
+
{
|
|
1867
|
+
flag: "-q, --quality <number>",
|
|
1868
|
+
description: "JPEG output quality 1-100",
|
|
1869
|
+
type: "number",
|
|
1870
|
+
default: "80"
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
flag: "-it, --iou-threshold <number>",
|
|
1874
|
+
description: "IoU threshold for animation tracking (0-1) (default: 0.9)",
|
|
1875
|
+
type: "number"
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
flag: "-at, --anim-threshold <number>",
|
|
1879
|
+
description: "Min consecutive frames for animation (default: 5)",
|
|
1880
|
+
type: "number"
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
flag: "--max-segment-duration <number>",
|
|
1884
|
+
description: "Max segment duration in seconds for long video splitting (default: 300)",
|
|
1885
|
+
type: "number"
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
flag: "--concurrency <number>",
|
|
1889
|
+
description: "Number of segments to process in parallel (default: 2)",
|
|
1890
|
+
type: "number"
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
flag: "--debug",
|
|
1894
|
+
description: "Enable debug mode (preserve temp workspace)",
|
|
1895
|
+
type: "boolean"
|
|
1896
|
+
},
|
|
1897
|
+
{
|
|
1898
|
+
flag: "--json",
|
|
1899
|
+
description: "Output structured JSON to stdout",
|
|
1900
|
+
type: "boolean"
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
flag: "--describe",
|
|
1904
|
+
description: "Output JSON schema of available options",
|
|
1905
|
+
type: "boolean"
|
|
1906
|
+
}
|
|
1907
|
+
],
|
|
1908
|
+
examples: [
|
|
1909
|
+
"scene-sieve video.mp4",
|
|
1910
|
+
"scene-sieve video.mp4 -n 10",
|
|
1911
|
+
"scene-sieve video.mp4 -t 0.3 -o ./output",
|
|
1912
|
+
"scene-sieve video.mp4 --json",
|
|
1913
|
+
"scene-sieve --describe"
|
|
1914
|
+
]
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
// src/utils/parse-options.ts
|
|
1918
|
+
function parsePipelineOptions(opts) {
|
|
1919
|
+
return {
|
|
1920
|
+
...opts.threshold !== void 0 ? { threshold: parseFloat(opts.threshold) } : {},
|
|
1921
|
+
...opts.count !== void 0 ? { count: parseInt(opts.count, 10) } : {},
|
|
1922
|
+
outputPath: opts.output,
|
|
1923
|
+
fps: parseInt(opts.fps, 10),
|
|
1924
|
+
maxFrames: parseInt(opts.maxFrames, 10),
|
|
1925
|
+
scale: parseInt(opts.scale, 10),
|
|
1926
|
+
quality: parseInt(opts.quality, 10),
|
|
1927
|
+
iouThreshold: opts.iouThreshold !== void 0 ? parseFloat(opts.iouThreshold) : void 0,
|
|
1928
|
+
animationThreshold: opts.animThreshold !== void 0 ? parseInt(opts.animThreshold, 10) : void 0,
|
|
1929
|
+
maxSegmentDuration: opts.maxSegmentDuration !== void 0 ? parseInt(opts.maxSegmentDuration, 10) : void 0,
|
|
1930
|
+
concurrency: opts.concurrency !== void 0 ? parseInt(opts.concurrency, 10) : void 0,
|
|
1931
|
+
debug: opts.debug ?? false
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// src/commands/Sieve.tsx
|
|
1744
1936
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1745
1937
|
var PHASE_DEFS = [
|
|
1746
1938
|
{ key: "INIT", label: "Initializing workspace", hasProgress: false },
|
|
@@ -1760,6 +1952,78 @@ function createInitialPhases() {
|
|
|
1760
1952
|
function phaseKeyToIndex(phase) {
|
|
1761
1953
|
return PHASE_DEFS.findIndex((d) => d.key === phase);
|
|
1762
1954
|
}
|
|
1955
|
+
function registerSieveCommand(program2, version2) {
|
|
1956
|
+
const cmd = SIEVE_COMMAND;
|
|
1957
|
+
program2.argument("<input>", cmd.arguments[0].description).option("-n, --count <number>", cmd.options.find((o) => o.flag.includes("--count")).description).option(
|
|
1958
|
+
"-t, --threshold <number>",
|
|
1959
|
+
cmd.options.find((o) => o.flag.includes("--threshold")).description
|
|
1960
|
+
).option("-o, --output <path>", cmd.options.find((o) => o.flag.includes("--output")).description).option("--fps <number>", cmd.options.find((o) => o.flag.includes("--fps")).description, String(DEFAULT_FPS)).option(
|
|
1961
|
+
"-mf, --max-frames <number>",
|
|
1962
|
+
cmd.options.find((o) => o.flag.includes("--max-frames")).description,
|
|
1963
|
+
String(DEFAULT_MAX_FRAMES)
|
|
1964
|
+
).option(
|
|
1965
|
+
"-s, --scale <number>",
|
|
1966
|
+
cmd.options.find((o) => o.flag.includes("--scale")).description,
|
|
1967
|
+
String(DEFAULT_SCALE)
|
|
1968
|
+
).option(
|
|
1969
|
+
"-q, --quality <number>",
|
|
1970
|
+
cmd.options.find((o) => o.flag.includes("--quality")).description,
|
|
1971
|
+
String(DEFAULT_QUALITY)
|
|
1972
|
+
).option(
|
|
1973
|
+
"-it, --iou-threshold <number>",
|
|
1974
|
+
cmd.options.find((o) => o.flag.includes("--iou-threshold")).description
|
|
1975
|
+
).option(
|
|
1976
|
+
"-at, --anim-threshold <number>",
|
|
1977
|
+
cmd.options.find((o) => o.flag.includes("--anim-threshold")).description
|
|
1978
|
+
).option(
|
|
1979
|
+
"--max-segment-duration <number>",
|
|
1980
|
+
cmd.options.find((o) => o.flag.includes("--max-segment-duration")).description
|
|
1981
|
+
).option(
|
|
1982
|
+
"--concurrency <number>",
|
|
1983
|
+
cmd.options.find((o) => o.flag.includes("--concurrency")).description
|
|
1984
|
+
).option("--debug", cmd.options.find((o) => o.flag.includes("--debug")).description).option("--json", cmd.options.find((o) => o.flag.includes("--json")).description).option("--describe", cmd.options.find((o) => o.flag.includes("--describe")).description).action(async (input, opts) => {
|
|
1985
|
+
const parsed = parsePipelineOptions(opts);
|
|
1986
|
+
if (opts.json) {
|
|
1987
|
+
const startTime = Date.now();
|
|
1988
|
+
if (!existsSync(input)) {
|
|
1989
|
+
respondError("extract", SieveErrorCode.FILE_NOT_FOUND, `File not found: ${input}`, startTime, version2);
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
const result = await runPipeline({
|
|
1994
|
+
mode: "file",
|
|
1995
|
+
inputPath: input,
|
|
1996
|
+
...parsed,
|
|
1997
|
+
onProgress: (phase, percent) => {
|
|
1998
|
+
process.stderr.write(JSON.stringify({ phase, percent }) + "\n");
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
const data = {
|
|
2002
|
+
success: result.success,
|
|
2003
|
+
originalFrames: result.originalFramesCount,
|
|
2004
|
+
selectedFrames: result.prunedFramesCount,
|
|
2005
|
+
outputFiles: result.outputFiles,
|
|
2006
|
+
animations: result.animations ?? [],
|
|
2007
|
+
video: result.video ?? null
|
|
2008
|
+
};
|
|
2009
|
+
respond("extract", data, startTime, version2);
|
|
2010
|
+
} catch (error) {
|
|
2011
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2012
|
+
respondError("extract", classifyError(err), err.message, startTime, version2);
|
|
2013
|
+
}
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
const { outputPath, ...viewOpts } = parsed;
|
|
2017
|
+
const { waitUntilExit } = render(
|
|
2018
|
+
React.createElement(SieveView, {
|
|
2019
|
+
input,
|
|
2020
|
+
...viewOpts,
|
|
2021
|
+
output: outputPath
|
|
2022
|
+
})
|
|
2023
|
+
);
|
|
2024
|
+
await waitUntilExit();
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
1763
2027
|
var SieveView = (props) => {
|
|
1764
2028
|
const { exit } = useApp();
|
|
1765
2029
|
const [phases, setPhases] = useState(createInitialPhases);
|
|
@@ -1915,58 +2179,32 @@ var SieveView = (props) => {
|
|
|
1915
2179
|
};
|
|
1916
2180
|
|
|
1917
2181
|
// src/cli.ts
|
|
1918
|
-
init_constants();
|
|
1919
2182
|
var require3 = createRequire2(import.meta.url);
|
|
1920
2183
|
var { version } = require3("../package.json");
|
|
1921
2184
|
var program = new Command();
|
|
1922
|
-
program.name("scene-sieve").description("Extract key frames from video and GIF files").version(version)
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
).option(
|
|
1938
|
-
"-it, --iou-threshold <number>",
|
|
1939
|
-
`IoU threshold for animation tracking (0-1) (default: ${IOU_THRESHOLD})`
|
|
1940
|
-
).option(
|
|
1941
|
-
"-at, --anim-threshold <number>",
|
|
1942
|
-
`Min consecutive frames for animation (default: ${ANIMATION_FRAME_THRESHOLD})`
|
|
1943
|
-
).option(
|
|
1944
|
-
"--max-segment-duration <number>",
|
|
1945
|
-
`Max segment duration in seconds for long video splitting (default: ${DEFAULT_MAX_SEGMENT_DURATION})`
|
|
1946
|
-
).option(
|
|
1947
|
-
"--concurrency <number>",
|
|
1948
|
-
`Number of segments to process in parallel (default: ${DEFAULT_SEGMENT_CONCURRENCY})`
|
|
1949
|
-
).option("--debug", "Enable debug mode (preserve temp workspace)").action(async (input, opts) => {
|
|
1950
|
-
const { waitUntilExit } = render(
|
|
1951
|
-
React2.createElement(SieveView, {
|
|
1952
|
-
input,
|
|
1953
|
-
...opts.threshold !== void 0 ? { threshold: parseFloat(opts.threshold) } : {},
|
|
1954
|
-
...opts.count !== void 0 ? { count: parseInt(opts.count, 10) } : {},
|
|
1955
|
-
output: opts.output,
|
|
1956
|
-
fps: parseInt(opts.fps, 10),
|
|
1957
|
-
maxFrames: parseInt(opts.maxFrames, 10),
|
|
1958
|
-
scale: parseInt(opts.scale, 10),
|
|
1959
|
-
quality: parseInt(opts.quality, 10),
|
|
1960
|
-
iouThreshold: opts.iouThreshold !== void 0 ? parseFloat(opts.iouThreshold) : void 0,
|
|
1961
|
-
animationThreshold: opts.animThreshold !== void 0 ? parseInt(opts.animThreshold, 10) : void 0,
|
|
1962
|
-
maxSegmentDuration: opts.maxSegmentDuration !== void 0 ? parseInt(opts.maxSegmentDuration, 10) : void 0,
|
|
1963
|
-
concurrency: opts.concurrency !== void 0 ? parseInt(opts.concurrency, 10) : void 0,
|
|
1964
|
-
debug: opts.debug ?? false
|
|
1965
|
-
})
|
|
2185
|
+
program.name("scene-sieve").description("Extract key frames from video and GIF files").version(version);
|
|
2186
|
+
registerSieveCommand(program, version);
|
|
2187
|
+
if (process.argv.includes("--describe")) {
|
|
2188
|
+
const startTime = Date.now();
|
|
2189
|
+
respond(
|
|
2190
|
+
"describe",
|
|
2191
|
+
{
|
|
2192
|
+
name: SIEVE_COMMAND.name,
|
|
2193
|
+
version,
|
|
2194
|
+
description: SIEVE_COMMAND.description,
|
|
2195
|
+
arguments: SIEVE_COMMAND.arguments,
|
|
2196
|
+
options: SIEVE_COMMAND.options
|
|
2197
|
+
},
|
|
2198
|
+
startTime,
|
|
2199
|
+
version
|
|
1966
2200
|
);
|
|
1967
|
-
|
|
1968
|
-
}
|
|
2201
|
+
process.exit(0);
|
|
2202
|
+
}
|
|
1969
2203
|
program.parseAsync(process.argv).catch((error) => {
|
|
1970
|
-
|
|
2204
|
+
if (process.argv.includes("--json")) {
|
|
2205
|
+
respondError("extract", SieveErrorCode.UNKNOWN, error.message, Date.now(), version);
|
|
2206
|
+
} else {
|
|
2207
|
+
console.error("Fatal error:", error.message);
|
|
2208
|
+
}
|
|
1971
2209
|
process.exit(1);
|
|
1972
2210
|
});
|
package/dist/commands/Sieve.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
export interface SieveViewProps {
|
|
3
4
|
input: string;
|
|
@@ -14,4 +15,5 @@ export interface SieveViewProps {
|
|
|
14
15
|
concurrency?: number;
|
|
15
16
|
debug: boolean;
|
|
16
17
|
}
|
|
18
|
+
export declare function registerSieveCommand(program: Command, version: string): void;
|
|
17
19
|
export declare const SieveView: React.FC<SieveViewProps>;
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const SieveErrorCode: {
|
|
2
|
+
readonly INVALID_INPUT: "INVALID_INPUT";
|
|
3
|
+
readonly FILE_NOT_FOUND: "FILE_NOT_FOUND";
|
|
4
|
+
readonly INVALID_FORMAT: "INVALID_FORMAT";
|
|
5
|
+
readonly PIPELINE_ERROR: "PIPELINE_ERROR";
|
|
6
|
+
readonly WORKER_ERROR: "WORKER_ERROR";
|
|
7
|
+
readonly UNKNOWN: "UNKNOWN";
|
|
8
|
+
};
|
|
9
|
+
export type SieveErrorCode = (typeof SieveErrorCode)[keyof typeof SieveErrorCode];
|
|
10
|
+
export declare function classifyError(error: Error): SieveErrorCode;
|
package/dist/index.cjs
CHANGED
|
@@ -1192,10 +1192,7 @@ function computeSegmentPlan(totalDuration, maxSegmentDuration, maxFrames, fps) {
|
|
|
1192
1192
|
}
|
|
1193
1193
|
return segments;
|
|
1194
1194
|
}
|
|
1195
|
-
function
|
|
1196
|
-
if (segmentResults.length === 0) {
|
|
1197
|
-
return { frames: [], edges: [], animations: [] };
|
|
1198
|
-
}
|
|
1195
|
+
function collectAllFrames(segmentResults) {
|
|
1199
1196
|
const allFrames = [];
|
|
1200
1197
|
for (const result of segmentResults) {
|
|
1201
1198
|
for (const frame of result.frames) {
|
|
@@ -1210,19 +1207,24 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1210
1207
|
});
|
|
1211
1208
|
}
|
|
1212
1209
|
}
|
|
1213
|
-
allFrames
|
|
1214
|
-
|
|
1210
|
+
return allFrames;
|
|
1211
|
+
}
|
|
1212
|
+
function deduplicateFrames(frames, effectiveFps) {
|
|
1213
|
+
frames.sort((a, b) => a.frame.timestamp - b.frame.timestamp);
|
|
1215
1214
|
const dupThreshold = 1 / (effectiveFps * 2);
|
|
1216
|
-
const
|
|
1217
|
-
for (const entry of
|
|
1218
|
-
if (
|
|
1219
|
-
const last =
|
|
1215
|
+
const unique = [];
|
|
1216
|
+
for (const entry of frames) {
|
|
1217
|
+
if (unique.length > 0) {
|
|
1218
|
+
const last = unique[unique.length - 1];
|
|
1220
1219
|
if (Math.abs(entry.frame.timestamp - last.frame.timestamp) < dupThreshold) {
|
|
1221
1220
|
continue;
|
|
1222
1221
|
}
|
|
1223
1222
|
}
|
|
1224
|
-
|
|
1223
|
+
unique.push(entry);
|
|
1225
1224
|
}
|
|
1225
|
+
return unique;
|
|
1226
|
+
}
|
|
1227
|
+
function remapFrameIds(uniqueFrames) {
|
|
1226
1228
|
const globalIdMap = /* @__PURE__ */ new Map();
|
|
1227
1229
|
const frames = uniqueFrames.map((entry, globalId) => {
|
|
1228
1230
|
globalIdMap.set(`${entry.segmentIndex}:${entry.localId}`, globalId);
|
|
@@ -1232,6 +1234,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1232
1234
|
extractPath: entry.frame.extractPath
|
|
1233
1235
|
};
|
|
1234
1236
|
});
|
|
1237
|
+
return { frames, globalIdMap };
|
|
1238
|
+
}
|
|
1239
|
+
function remapEdges(segmentResults, globalIdMap) {
|
|
1235
1240
|
const edges = [];
|
|
1236
1241
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
1237
1242
|
for (const result of segmentResults) {
|
|
@@ -1263,6 +1268,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1263
1268
|
}
|
|
1264
1269
|
}
|
|
1265
1270
|
}
|
|
1271
|
+
return edges;
|
|
1272
|
+
}
|
|
1273
|
+
function remapAnimations(segmentResults, globalIdMap) {
|
|
1266
1274
|
const animations = [];
|
|
1267
1275
|
for (const result of segmentResults) {
|
|
1268
1276
|
for (const anim of result.animations) {
|
|
@@ -1280,6 +1288,18 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1280
1288
|
});
|
|
1281
1289
|
}
|
|
1282
1290
|
}
|
|
1291
|
+
return animations;
|
|
1292
|
+
}
|
|
1293
|
+
function mergeSegmentFrames(segmentResults) {
|
|
1294
|
+
if (segmentResults.length === 0) {
|
|
1295
|
+
return { frames: [], edges: [], animations: [] };
|
|
1296
|
+
}
|
|
1297
|
+
const effectiveFps = segmentResults[0].segment.effectiveFps;
|
|
1298
|
+
const allFrames = collectAllFrames(segmentResults);
|
|
1299
|
+
const uniqueFrames = deduplicateFrames(allFrames, effectiveFps);
|
|
1300
|
+
const { frames, globalIdMap } = remapFrameIds(uniqueFrames);
|
|
1301
|
+
const edges = remapEdges(segmentResults, globalIdMap);
|
|
1302
|
+
const animations = remapAnimations(segmentResults, globalIdMap);
|
|
1283
1303
|
return { frames, edges, animations };
|
|
1284
1304
|
}
|
|
1285
1305
|
function buildSegmentContext(segment, frames, segmentWorkspacePath, resolvedOptions, onProgress) {
|
package/dist/index.mjs
CHANGED
|
@@ -1152,10 +1152,7 @@ function computeSegmentPlan(totalDuration, maxSegmentDuration, maxFrames, fps) {
|
|
|
1152
1152
|
}
|
|
1153
1153
|
return segments;
|
|
1154
1154
|
}
|
|
1155
|
-
function
|
|
1156
|
-
if (segmentResults.length === 0) {
|
|
1157
|
-
return { frames: [], edges: [], animations: [] };
|
|
1158
|
-
}
|
|
1155
|
+
function collectAllFrames(segmentResults) {
|
|
1159
1156
|
const allFrames = [];
|
|
1160
1157
|
for (const result of segmentResults) {
|
|
1161
1158
|
for (const frame of result.frames) {
|
|
@@ -1170,19 +1167,24 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1170
1167
|
});
|
|
1171
1168
|
}
|
|
1172
1169
|
}
|
|
1173
|
-
allFrames
|
|
1174
|
-
|
|
1170
|
+
return allFrames;
|
|
1171
|
+
}
|
|
1172
|
+
function deduplicateFrames(frames, effectiveFps) {
|
|
1173
|
+
frames.sort((a, b) => a.frame.timestamp - b.frame.timestamp);
|
|
1175
1174
|
const dupThreshold = 1 / (effectiveFps * 2);
|
|
1176
|
-
const
|
|
1177
|
-
for (const entry of
|
|
1178
|
-
if (
|
|
1179
|
-
const last =
|
|
1175
|
+
const unique = [];
|
|
1176
|
+
for (const entry of frames) {
|
|
1177
|
+
if (unique.length > 0) {
|
|
1178
|
+
const last = unique[unique.length - 1];
|
|
1180
1179
|
if (Math.abs(entry.frame.timestamp - last.frame.timestamp) < dupThreshold) {
|
|
1181
1180
|
continue;
|
|
1182
1181
|
}
|
|
1183
1182
|
}
|
|
1184
|
-
|
|
1183
|
+
unique.push(entry);
|
|
1185
1184
|
}
|
|
1185
|
+
return unique;
|
|
1186
|
+
}
|
|
1187
|
+
function remapFrameIds(uniqueFrames) {
|
|
1186
1188
|
const globalIdMap = /* @__PURE__ */ new Map();
|
|
1187
1189
|
const frames = uniqueFrames.map((entry, globalId) => {
|
|
1188
1190
|
globalIdMap.set(`${entry.segmentIndex}:${entry.localId}`, globalId);
|
|
@@ -1192,6 +1194,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1192
1194
|
extractPath: entry.frame.extractPath
|
|
1193
1195
|
};
|
|
1194
1196
|
});
|
|
1197
|
+
return { frames, globalIdMap };
|
|
1198
|
+
}
|
|
1199
|
+
function remapEdges(segmentResults, globalIdMap) {
|
|
1195
1200
|
const edges = [];
|
|
1196
1201
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
1197
1202
|
for (const result of segmentResults) {
|
|
@@ -1223,6 +1228,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1223
1228
|
}
|
|
1224
1229
|
}
|
|
1225
1230
|
}
|
|
1231
|
+
return edges;
|
|
1232
|
+
}
|
|
1233
|
+
function remapAnimations(segmentResults, globalIdMap) {
|
|
1226
1234
|
const animations = [];
|
|
1227
1235
|
for (const result of segmentResults) {
|
|
1228
1236
|
for (const anim of result.animations) {
|
|
@@ -1240,6 +1248,18 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1240
1248
|
});
|
|
1241
1249
|
}
|
|
1242
1250
|
}
|
|
1251
|
+
return animations;
|
|
1252
|
+
}
|
|
1253
|
+
function mergeSegmentFrames(segmentResults) {
|
|
1254
|
+
if (segmentResults.length === 0) {
|
|
1255
|
+
return { frames: [], edges: [], animations: [] };
|
|
1256
|
+
}
|
|
1257
|
+
const effectiveFps = segmentResults[0].segment.effectiveFps;
|
|
1258
|
+
const allFrames = collectAllFrames(segmentResults);
|
|
1259
|
+
const uniqueFrames = deduplicateFrames(allFrames, effectiveFps);
|
|
1260
|
+
const { frames, globalIdMap } = remapFrameIds(uniqueFrames);
|
|
1261
|
+
const edges = remapEdges(segmentResults, globalIdMap);
|
|
1262
|
+
const animations = remapAnimations(segmentResults, globalIdMap);
|
|
1243
1263
|
return { frames, edges, animations };
|
|
1244
1264
|
}
|
|
1245
1265
|
function buildSegmentContext(segment, frames, segmentWorkspacePath, resolvedOptions, onProgress) {
|
package/dist/pipeline-worker.mjs
CHANGED
|
@@ -1155,10 +1155,7 @@ function computeSegmentPlan(totalDuration, maxSegmentDuration, maxFrames, fps) {
|
|
|
1155
1155
|
}
|
|
1156
1156
|
return segments;
|
|
1157
1157
|
}
|
|
1158
|
-
function
|
|
1159
|
-
if (segmentResults.length === 0) {
|
|
1160
|
-
return { frames: [], edges: [], animations: [] };
|
|
1161
|
-
}
|
|
1158
|
+
function collectAllFrames(segmentResults) {
|
|
1162
1159
|
const allFrames = [];
|
|
1163
1160
|
for (const result of segmentResults) {
|
|
1164
1161
|
for (const frame of result.frames) {
|
|
@@ -1173,19 +1170,24 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1173
1170
|
});
|
|
1174
1171
|
}
|
|
1175
1172
|
}
|
|
1176
|
-
allFrames
|
|
1177
|
-
|
|
1173
|
+
return allFrames;
|
|
1174
|
+
}
|
|
1175
|
+
function deduplicateFrames(frames, effectiveFps) {
|
|
1176
|
+
frames.sort((a, b) => a.frame.timestamp - b.frame.timestamp);
|
|
1178
1177
|
const dupThreshold = 1 / (effectiveFps * 2);
|
|
1179
|
-
const
|
|
1180
|
-
for (const entry of
|
|
1181
|
-
if (
|
|
1182
|
-
const last =
|
|
1178
|
+
const unique = [];
|
|
1179
|
+
for (const entry of frames) {
|
|
1180
|
+
if (unique.length > 0) {
|
|
1181
|
+
const last = unique[unique.length - 1];
|
|
1183
1182
|
if (Math.abs(entry.frame.timestamp - last.frame.timestamp) < dupThreshold) {
|
|
1184
1183
|
continue;
|
|
1185
1184
|
}
|
|
1186
1185
|
}
|
|
1187
|
-
|
|
1186
|
+
unique.push(entry);
|
|
1188
1187
|
}
|
|
1188
|
+
return unique;
|
|
1189
|
+
}
|
|
1190
|
+
function remapFrameIds(uniqueFrames) {
|
|
1189
1191
|
const globalIdMap = /* @__PURE__ */ new Map();
|
|
1190
1192
|
const frames = uniqueFrames.map((entry, globalId) => {
|
|
1191
1193
|
globalIdMap.set(`${entry.segmentIndex}:${entry.localId}`, globalId);
|
|
@@ -1195,6 +1197,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1195
1197
|
extractPath: entry.frame.extractPath
|
|
1196
1198
|
};
|
|
1197
1199
|
});
|
|
1200
|
+
return { frames, globalIdMap };
|
|
1201
|
+
}
|
|
1202
|
+
function remapEdges(segmentResults, globalIdMap) {
|
|
1198
1203
|
const edges = [];
|
|
1199
1204
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
1200
1205
|
for (const result of segmentResults) {
|
|
@@ -1226,6 +1231,9 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1226
1231
|
}
|
|
1227
1232
|
}
|
|
1228
1233
|
}
|
|
1234
|
+
return edges;
|
|
1235
|
+
}
|
|
1236
|
+
function remapAnimations(segmentResults, globalIdMap) {
|
|
1229
1237
|
const animations = [];
|
|
1230
1238
|
for (const result of segmentResults) {
|
|
1231
1239
|
for (const anim of result.animations) {
|
|
@@ -1243,6 +1251,18 @@ function mergeSegmentFrames(segmentResults) {
|
|
|
1243
1251
|
});
|
|
1244
1252
|
}
|
|
1245
1253
|
}
|
|
1254
|
+
return animations;
|
|
1255
|
+
}
|
|
1256
|
+
function mergeSegmentFrames(segmentResults) {
|
|
1257
|
+
if (segmentResults.length === 0) {
|
|
1258
|
+
return { frames: [], edges: [], animations: [] };
|
|
1259
|
+
}
|
|
1260
|
+
const effectiveFps = segmentResults[0].segment.effectiveFps;
|
|
1261
|
+
const allFrames = collectAllFrames(segmentResults);
|
|
1262
|
+
const uniqueFrames = deduplicateFrames(allFrames, effectiveFps);
|
|
1263
|
+
const { frames, globalIdMap } = remapFrameIds(uniqueFrames);
|
|
1264
|
+
const edges = remapEdges(segmentResults, globalIdMap);
|
|
1265
|
+
const animations = remapAnimations(segmentResults, globalIdMap);
|
|
1246
1266
|
return { frames, edges, animations };
|
|
1247
1267
|
}
|
|
1248
1268
|
function buildSegmentContext(segment, frames, segmentWorkspacePath, resolvedOptions, onProgress) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central command registry for scene-sieve CLI.
|
|
3
|
+
* Single source of truth for command metadata, used by --describe and --help.
|
|
4
|
+
*/
|
|
5
|
+
export interface CommandOption {
|
|
6
|
+
flag: string;
|
|
7
|
+
description: string;
|
|
8
|
+
type?: 'boolean' | 'string' | 'number';
|
|
9
|
+
default?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CommandArgument {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
required: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface CommandInfo {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
usage: string;
|
|
20
|
+
arguments?: CommandArgument[];
|
|
21
|
+
options?: CommandOption[];
|
|
22
|
+
examples?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare const SIEVE_COMMAND: CommandInfo;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SieveOptionsBase } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Common pipeline options parsed from CLI opts (Commander string values → typed values).
|
|
4
|
+
* Does NOT include mode-specific fields (mode, inputPath, onProgress).
|
|
5
|
+
*/
|
|
6
|
+
export type ParsedPipelineOptions = Pick<Required<SieveOptionsBase>, 'fps' | 'maxFrames' | 'scale' | 'quality' | 'debug'> & Pick<SieveOptionsBase, 'count' | 'threshold' | 'outputPath' | 'iouThreshold' | 'animationThreshold' | 'maxSegmentDuration' | 'concurrency'>;
|
|
7
|
+
export interface RawCliOptions {
|
|
8
|
+
count?: string;
|
|
9
|
+
threshold?: string;
|
|
10
|
+
output?: string;
|
|
11
|
+
fps: string;
|
|
12
|
+
maxFrames: string;
|
|
13
|
+
scale: string;
|
|
14
|
+
quality: string;
|
|
15
|
+
iouThreshold?: string;
|
|
16
|
+
animThreshold?: string;
|
|
17
|
+
maxSegmentDuration?: string;
|
|
18
|
+
concurrency?: string;
|
|
19
|
+
debug?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function parsePipelineOptions(opts: RawCliOptions): ParsedPipelineOptions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumy-pack/scene-sieve",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "CLI tool for extracting key frames from video and GIF files",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"main": "dist/index.cjs",
|
|
34
34
|
"module": "dist/index.mjs",
|
|
35
35
|
"types": "dist/index.d.ts",
|
|
36
|
-
"bin": "
|
|
36
|
+
"bin": "dist/cli.mjs",
|
|
37
37
|
"files": [
|
|
38
38
|
"dist",
|
|
39
39
|
"!dist/tsconfig.tsbuildinfo"
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"sharp": "^0.33.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
+
"@lumy-pack/shared": "0.0.1",
|
|
69
70
|
"@types/node": "^20.11.0",
|
|
70
71
|
"@types/react": "^18.0.0",
|
|
71
72
|
"@vitest/coverage-v8": "^3.2.4"
|