@lumy-pack/scene-sieve 0.0.10 → 0.0.12

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.
@@ -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
@@ -40,6 +40,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
40
 
41
41
  // src/core/orchestrator.ts
42
42
  var import_node_crypto2 = require("crypto");
43
+ var import_common_utils7 = require("@winglet/common-utils");
43
44
 
44
45
  // src/utils/logger.ts
45
46
  var import_picocolors = __toESM(require("picocolors"), 1);
@@ -73,6 +74,7 @@ ${import_picocolors.default.green("done")} ${message}`);
73
74
 
74
75
  // src/core/analyzer.ts
75
76
  var import_node_module = require("module");
77
+ var import_common_utils = require("@winglet/common-utils");
76
78
  var import_sharp = __toESM(require("sharp"), 1);
77
79
 
78
80
  // src/constants.ts
@@ -289,7 +291,8 @@ var IoUTracker = class {
289
291
  this.collectAnimation(region);
290
292
  }
291
293
  }
292
- this.regions = this.regions.filter(
294
+ this.regions = (0, import_common_utils.filter)(
295
+ this.regions,
293
296
  (r, i) => r.weight > 0.01 || matched.has(i)
294
297
  );
295
298
  return animationIndices;
@@ -470,7 +473,7 @@ function computeInformationGain(clusters, clusterPoints, imageArea, animationInd
470
473
  async function analyzeBatch(cvLib, frames, scale, tracker, pairOffset) {
471
474
  const edges = [];
472
475
  const preprocessed = await Promise.all(
473
- frames.map((f) => preprocessFrame(f.extractPath, scale))
476
+ (0, import_common_utils.map)(frames, (f) => preprocessFrame(f.extractPath, scale))
474
477
  );
475
478
  const imageWidth = preprocessed[0]?.width ?? scale;
476
479
  const imageHeight = preprocessed[0]?.height ?? Math.round(scale * 9 / 16);
@@ -512,7 +515,8 @@ async function analyzeBatch(cvLib, frames, scale, tracker, pairOffset) {
512
515
  }
513
516
  }
514
517
  const animationIndices = tracker.update(clusters, pairIndex);
515
- const animationWeights = clusters.map(
518
+ const animationWeights = (0, import_common_utils.map)(
519
+ clusters,
516
520
  (_, ci) => animationIndices.has(ci) ? tracker.getAnimationWeight(ci, clusters) : 0
517
521
  );
518
522
  const score = computeInformationGain(
@@ -577,6 +581,7 @@ async function analyzeFrames(ctx) {
577
581
  var import_promises2 = require("fs/promises");
578
582
  var import_node_path3 = require("path");
579
583
  var import_ffprobe = require("@ffprobe-installer/ffprobe");
584
+ var import_common_utils2 = require("@winglet/common-utils");
580
585
  var import_execa = require("execa");
581
586
  var import_ffmpeg_static = __toESM(require("ffmpeg-static"), 1);
582
587
 
@@ -688,11 +693,11 @@ async function getVideoMetadata(inputPath) {
688
693
  }
689
694
  async function buildFrameList(framesDir, duration) {
690
695
  const files = await (0, import_promises2.readdir)(framesDir);
691
- const jpgFiles = files.filter((f) => f.endsWith(".jpg")).sort();
696
+ const jpgFiles = (0, import_common_utils2.filter)(files, (f) => f.endsWith(".jpg")).sort();
692
697
  if (jpgFiles.length === 0) {
693
698
  return [];
694
699
  }
695
- return jpgFiles.map((file, index) => ({
700
+ return (0, import_common_utils2.map)(jpgFiles, (file, index) => ({
696
701
  id: index,
697
702
  timestamp: duration > 0 && jpgFiles.length > 1 ? duration * index / (jpgFiles.length - 1) : index,
698
703
  extractPath: (0, import_node_path3.join)(framesDir, file)
@@ -722,6 +727,7 @@ var import_node_path5 = require("path");
722
727
  // src/core/workspace.ts
723
728
  var import_promises3 = require("fs/promises");
724
729
  var import_node_path4 = require("path");
730
+ var import_common_utils3 = require("@winglet/common-utils");
725
731
  var import_sharp2 = __toESM(require("sharp"), 1);
726
732
  async function createWorkspace(sessionId) {
727
733
  const workspacePath = getTempWorkspaceDir(sessionId);
@@ -762,7 +768,7 @@ async function finalizeOutput(ctx, selectedFrames) {
762
768
  }
763
769
  },
764
770
  frames: framesMetadata,
765
- animations: (ctx.animations || []).map((anim) => ({
771
+ animations: (0, import_common_utils3.map)(ctx.animations || [], (anim) => ({
766
772
  ...anim,
767
773
  startFrameId: anim.startFrameId + 1,
768
774
  endFrameId: anim.endFrameId + 1,
@@ -815,7 +821,8 @@ async function writeInputFrames(frames, workspacePath) {
815
821
  }
816
822
  async function readFramesAsBuffers(frameNodes, quality) {
817
823
  return Promise.all(
818
- frameNodes.map(
824
+ (0, import_common_utils3.map)(
825
+ frameNodes,
819
826
  (f) => (0, import_sharp2.default)(f.extractPath).jpeg({ quality, mozjpeg: true }).toBuffer()
820
827
  )
821
828
  );
@@ -872,39 +879,46 @@ async function resolveInput(options, workspacePath) {
872
879
  throw new Error(`Unsupported input mode: ${options.mode}`);
873
880
  }
874
881
 
882
+ // src/core/pruner.ts
883
+ var import_common_utils5 = require("@winglet/common-utils");
884
+
875
885
  // src/utils/math.ts
886
+ var import_common_utils4 = require("@winglet/common-utils");
876
887
  function normalizeScores(items) {
877
888
  if (items.length === 0) return [];
878
- const safeScores = items.map(
889
+ const safeScores = (0, import_common_utils4.map)(
890
+ items,
879
891
  (e) => Number.isFinite(e.score) && e.score > 0 ? e.score : 0
880
892
  );
881
- const positiveScores = safeScores.filter((s) => s > 0);
893
+ const positiveScores = (0, import_common_utils4.filter)(safeScores, (s) => s > 0);
882
894
  if (positiveScores.length === 0) return safeScores;
883
895
  const sorted = [...positiveScores].sort((a, b) => a - b);
884
896
  if (positiveScores.length <= NORMALIZATION_MIN_SAMPLE_SIZE) {
885
897
  const min = sorted[0];
886
898
  const max = sorted[sorted.length - 1];
887
- if (max === min) return safeScores.map((s) => s > 0 ? 1 : 0);
888
- return safeScores.map(
899
+ if (max === min) return (0, import_common_utils4.map)(safeScores, (s) => s > 0 ? 1 : 0);
900
+ return (0, import_common_utils4.map)(
901
+ safeScores,
889
902
  (s) => s <= 0 ? 0 : Math.max(0, Math.min((s - min) / (max - min), 1))
890
903
  );
891
904
  }
892
905
  const median = sorted[Math.floor(sorted.length / 2)];
893
- const absoluteDiffs = positiveScores.map((v) => Math.abs(v - median));
906
+ const absoluteDiffs = (0, import_common_utils4.map)(positiveScores, (v) => Math.abs(v - median));
894
907
  const mad = [...absoluteDiffs].sort((a, b) => a - b)[Math.floor(absoluteDiffs.length / 2)];
895
908
  const scale = mad === 0 ? median : mad * NORMALIZATION_MAD_COEFFICIENT;
896
- const logisticZ = safeScores.map((s) => {
909
+ const logisticZ = (0, import_common_utils4.map)(safeScores, (s) => {
897
910
  if (s <= 0) return 0;
898
911
  if (scale === 0) return 1;
899
912
  const z = (s - median) / scale;
900
913
  return 1 / (1 + Math.exp(-NORMALIZATION_LOGISTIC_K * z));
901
914
  });
902
- const cdf = safeScores.map((s) => {
915
+ const cdf = (0, import_common_utils4.map)(safeScores, (s) => {
903
916
  if (s <= 0) return 0;
904
917
  const rank = sorted.findIndex((v) => v >= s);
905
918
  return rank / sorted.length;
906
919
  });
907
- return logisticZ.map(
920
+ return (0, import_common_utils4.map)(
921
+ logisticZ,
908
922
  (z, i) => z * (1 - NORMALIZATION_ALPHA) + cdf[i] * NORMALIZATION_ALPHA
909
923
  );
910
924
  }
@@ -956,7 +970,7 @@ var MinHeap = class {
956
970
  // src/core/pruner.ts
957
971
  function pruneTo(graph, frames, targetCount) {
958
972
  if (frames.length <= targetCount) {
959
- return new Set(frames.map((f) => f.id));
973
+ return new Set((0, import_common_utils5.map)(frames, (f) => f.id));
960
974
  }
961
975
  const prev = /* @__PURE__ */ new Map();
962
976
  const next = /* @__PURE__ */ new Map();
@@ -974,7 +988,7 @@ function pruneTo(graph, frames, targetCount) {
974
988
  tgtId: edge.targetId
975
989
  });
976
990
  }
977
- const surviving = new Set(frames.map((f) => f.id));
991
+ const surviving = new Set((0, import_common_utils5.map)(frames, (f) => f.id));
978
992
  const firstId = frames[0].id;
979
993
  const lastId = frames[frames.length - 1].id;
980
994
  while (surviving.size > targetCount && heap.size > 0) {
@@ -1067,7 +1081,7 @@ function pruneByThresholdWithCap(graph, frames, threshold, maxCount) {
1067
1081
  if (thresholdSurvivors.size <= maxCount) {
1068
1082
  return thresholdSurvivors;
1069
1083
  }
1070
- const survivingFrames = frames.filter((f) => thresholdSurvivors.has(f.id));
1084
+ const survivingFrames = (0, import_common_utils5.filter)(frames, (f) => thresholdSurvivors.has(f.id));
1071
1085
  const idToOrigIdx = /* @__PURE__ */ new Map();
1072
1086
  for (let i = 0; i < frames.length; i++) {
1073
1087
  idToOrigIdx.set(frames[i].id, i);
@@ -1103,6 +1117,7 @@ function pruneByThresholdWithCap(graph, frames, threshold, maxCount) {
1103
1117
  // src/core/segmenter.ts
1104
1118
  var import_node_crypto = require("crypto");
1105
1119
  var import_node_path6 = require("path");
1120
+ var import_common_utils6 = require("@winglet/common-utils");
1106
1121
 
1107
1122
  // src/utils/concurrency.ts
1108
1123
  function concurrencyLimit(limit) {
@@ -1241,18 +1256,30 @@ function remapEdges(segmentResults, globalIdMap) {
1241
1256
  const edgeMap = /* @__PURE__ */ new Map();
1242
1257
  for (const result of segmentResults) {
1243
1258
  for (const edge of result.edges) {
1244
- const newSourceId = globalIdMap.get(`${result.segment.index}:${edge.sourceId}`);
1245
- const newTargetId = globalIdMap.get(`${result.segment.index}:${edge.targetId}`);
1259
+ const newSourceId = globalIdMap.get(
1260
+ `${result.segment.index}:${edge.sourceId}`
1261
+ );
1262
+ const newTargetId = globalIdMap.get(
1263
+ `${result.segment.index}:${edge.targetId}`
1264
+ );
1246
1265
  if (newSourceId === void 0 || newTargetId === void 0) continue;
1247
1266
  const edgeKey = `${newSourceId}-${newTargetId}`;
1248
1267
  const existingIdx = edgeMap.get(edgeKey);
1249
1268
  if (existingIdx !== void 0) {
1250
1269
  if (edges[existingIdx].score < edge.score) {
1251
- edges[existingIdx] = { sourceId: newSourceId, targetId: newTargetId, score: edge.score };
1270
+ edges[existingIdx] = {
1271
+ sourceId: newSourceId,
1272
+ targetId: newTargetId,
1273
+ score: edge.score
1274
+ };
1252
1275
  }
1253
1276
  } else {
1254
1277
  edgeMap.set(edgeKey, edges.length);
1255
- edges.push({ sourceId: newSourceId, targetId: newTargetId, score: edge.score });
1278
+ edges.push({
1279
+ sourceId: newSourceId,
1280
+ targetId: newTargetId,
1281
+ score: edge.score
1282
+ });
1256
1283
  }
1257
1284
  }
1258
1285
  }
@@ -1262,10 +1289,18 @@ function remapAnimations(segmentResults, globalIdMap) {
1262
1289
  const animations = [];
1263
1290
  for (const result of segmentResults) {
1264
1291
  for (const anim of result.animations) {
1265
- const newStartId = globalIdMap.get(`${result.segment.index}:${anim.startFrameId}`);
1266
- const newEndId = globalIdMap.get(`${result.segment.index}:${anim.endFrameId}`);
1292
+ const newStartId = globalIdMap.get(
1293
+ `${result.segment.index}:${anim.startFrameId}`
1294
+ );
1295
+ const newEndId = globalIdMap.get(
1296
+ `${result.segment.index}:${anim.endFrameId}`
1297
+ );
1267
1298
  if (newStartId === void 0 || newEndId === void 0) continue;
1268
- animations.push({ ...anim, startFrameId: newStartId, endFrameId: newEndId });
1299
+ animations.push({
1300
+ ...anim,
1301
+ startFrameId: newStartId,
1302
+ endFrameId: newEndId
1303
+ });
1269
1304
  }
1270
1305
  }
1271
1306
  return animations;
@@ -1346,7 +1381,7 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1346
1381
  logger.debug(`Segment plan: ${segments.length} segments`);
1347
1382
  const limit = concurrencyLimit(resolvedOptions.concurrency);
1348
1383
  const segmentProgresses = new Array(segments.length).fill(0);
1349
- const weights = segments.map((s) => s.duration / totalDuration);
1384
+ const weights = (0, import_common_utils6.map)(segments, (s) => s.duration / totalDuration);
1350
1385
  const emitOverallProgress = (phase) => {
1351
1386
  if (!options.onProgress) return;
1352
1387
  const overall = weights.reduce(
@@ -1357,7 +1392,8 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1357
1392
  };
1358
1393
  options.onProgress?.("EXTRACTING", 0);
1359
1394
  const results = await Promise.all(
1360
- segments.map(
1395
+ (0, import_common_utils6.map)(
1396
+ segments,
1361
1397
  (segment) => limit(async () => {
1362
1398
  const segWorkspace = await createSegmentWorkspace(
1363
1399
  mainWorkspace,
@@ -1389,7 +1425,7 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1389
1425
  resolvedOptions.threshold,
1390
1426
  resolvedOptions.count
1391
1427
  );
1392
- const prunedFrames = frames.filter((f) => survivingIds.has(f.id));
1428
+ const prunedFrames = (0, import_common_utils6.filter)(frames, (f) => survivingIds.has(f.id));
1393
1429
  options.onProgress?.("PRUNING", 100);
1394
1430
  options.onProgress?.("FINALIZING", 0);
1395
1431
  const ctx = {
@@ -1499,7 +1535,7 @@ async function runPipeline(options) {
1499
1535
  resolvedOptions.threshold,
1500
1536
  resolvedOptions.count
1501
1537
  );
1502
- const prunedFrames = ctx.frames.filter((f) => survivingIds.has(f.id));
1538
+ const prunedFrames = (0, import_common_utils7.filter)(ctx.frames, (f) => survivingIds.has(f.id));
1503
1539
  ctx.emitProgress(100);
1504
1540
  ctx.status = "FINALIZING";
1505
1541
  let outputFiles = [];
@@ -0,0 +1,2 @@
1
+ export type { BoundingBox, DBSCANResult, FrameNode, ProcessContext, ProgressPhase, ResolvedOptions, ScoreEdge, SieveInput, SieveOptions, SieveOptionsBase, SieveResult, } from './types/index.js';
2
+ export { runPipeline as extractScenes } from './core/orchestrator.js';
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/core/orchestrator.ts
2
2
  import { randomUUID as randomUUID2 } from "crypto";
3
+ import { filter as filter6 } from "@winglet/common-utils";
3
4
 
4
5
  // src/utils/logger.ts
5
6
  import pc from "picocolors";
@@ -33,6 +34,7 @@ ${pc.green("done")} ${message}`);
33
34
 
34
35
  // src/core/analyzer.ts
35
36
  import { createRequire } from "module";
37
+ import { filter, map } from "@winglet/common-utils";
36
38
  import sharp from "sharp";
37
39
 
38
40
  // src/constants.ts
@@ -249,7 +251,8 @@ var IoUTracker = class {
249
251
  this.collectAnimation(region);
250
252
  }
251
253
  }
252
- this.regions = this.regions.filter(
254
+ this.regions = filter(
255
+ this.regions,
253
256
  (r, i) => r.weight > 0.01 || matched.has(i)
254
257
  );
255
258
  return animationIndices;
@@ -430,7 +433,7 @@ function computeInformationGain(clusters, clusterPoints, imageArea, animationInd
430
433
  async function analyzeBatch(cvLib, frames, scale, tracker, pairOffset) {
431
434
  const edges = [];
432
435
  const preprocessed = await Promise.all(
433
- frames.map((f) => preprocessFrame(f.extractPath, scale))
436
+ map(frames, (f) => preprocessFrame(f.extractPath, scale))
434
437
  );
435
438
  const imageWidth = preprocessed[0]?.width ?? scale;
436
439
  const imageHeight = preprocessed[0]?.height ?? Math.round(scale * 9 / 16);
@@ -472,7 +475,8 @@ async function analyzeBatch(cvLib, frames, scale, tracker, pairOffset) {
472
475
  }
473
476
  }
474
477
  const animationIndices = tracker.update(clusters, pairIndex);
475
- const animationWeights = clusters.map(
478
+ const animationWeights = map(
479
+ clusters,
476
480
  (_, ci) => animationIndices.has(ci) ? tracker.getAnimationWeight(ci, clusters) : 0
477
481
  );
478
482
  const score = computeInformationGain(
@@ -537,6 +541,7 @@ async function analyzeFrames(ctx) {
537
541
  import { readdir } from "fs/promises";
538
542
  import { join as join2 } from "path";
539
543
  import { path as ffprobePath } from "@ffprobe-installer/ffprobe";
544
+ import { filter as filter2, map as map2 } from "@winglet/common-utils";
540
545
  import { execa } from "execa";
541
546
  import ffmpegPath from "ffmpeg-static";
542
547
 
@@ -648,11 +653,11 @@ async function getVideoMetadata(inputPath) {
648
653
  }
649
654
  async function buildFrameList(framesDir, duration) {
650
655
  const files = await readdir(framesDir);
651
- const jpgFiles = files.filter((f) => f.endsWith(".jpg")).sort();
656
+ const jpgFiles = filter2(files, (f) => f.endsWith(".jpg")).sort();
652
657
  if (jpgFiles.length === 0) {
653
658
  return [];
654
659
  }
655
- return jpgFiles.map((file, index) => ({
660
+ return map2(jpgFiles, (file, index) => ({
656
661
  id: index,
657
662
  timestamp: duration > 0 && jpgFiles.length > 1 ? duration * index / (jpgFiles.length - 1) : index,
658
663
  extractPath: join2(framesDir, file)
@@ -682,6 +687,7 @@ import { join as join4 } from "path";
682
687
  // src/core/workspace.ts
683
688
  import { readdir as readdir2, rename, rm, stat as stat2, writeFile } from "fs/promises";
684
689
  import { join as join3 } from "path";
690
+ import { map as map3 } from "@winglet/common-utils";
685
691
  import sharp2 from "sharp";
686
692
  async function createWorkspace(sessionId) {
687
693
  const workspacePath = getTempWorkspaceDir(sessionId);
@@ -722,7 +728,7 @@ async function finalizeOutput(ctx, selectedFrames) {
722
728
  }
723
729
  },
724
730
  frames: framesMetadata,
725
- animations: (ctx.animations || []).map((anim) => ({
731
+ animations: map3(ctx.animations || [], (anim) => ({
726
732
  ...anim,
727
733
  startFrameId: anim.startFrameId + 1,
728
734
  endFrameId: anim.endFrameId + 1,
@@ -775,7 +781,8 @@ async function writeInputFrames(frames, workspacePath) {
775
781
  }
776
782
  async function readFramesAsBuffers(frameNodes, quality) {
777
783
  return Promise.all(
778
- frameNodes.map(
784
+ map3(
785
+ frameNodes,
779
786
  (f) => sharp2(f.extractPath).jpeg({ quality, mozjpeg: true }).toBuffer()
780
787
  )
781
788
  );
@@ -832,39 +839,46 @@ async function resolveInput(options, workspacePath) {
832
839
  throw new Error(`Unsupported input mode: ${options.mode}`);
833
840
  }
834
841
 
842
+ // src/core/pruner.ts
843
+ import { filter as filter4, map as map5 } from "@winglet/common-utils";
844
+
835
845
  // src/utils/math.ts
846
+ import { filter as filter3, map as map4 } from "@winglet/common-utils";
836
847
  function normalizeScores(items) {
837
848
  if (items.length === 0) return [];
838
- const safeScores = items.map(
849
+ const safeScores = map4(
850
+ items,
839
851
  (e) => Number.isFinite(e.score) && e.score > 0 ? e.score : 0
840
852
  );
841
- const positiveScores = safeScores.filter((s) => s > 0);
853
+ const positiveScores = filter3(safeScores, (s) => s > 0);
842
854
  if (positiveScores.length === 0) return safeScores;
843
855
  const sorted = [...positiveScores].sort((a, b) => a - b);
844
856
  if (positiveScores.length <= NORMALIZATION_MIN_SAMPLE_SIZE) {
845
857
  const min = sorted[0];
846
858
  const max = sorted[sorted.length - 1];
847
- if (max === min) return safeScores.map((s) => s > 0 ? 1 : 0);
848
- return safeScores.map(
859
+ if (max === min) return map4(safeScores, (s) => s > 0 ? 1 : 0);
860
+ return map4(
861
+ safeScores,
849
862
  (s) => s <= 0 ? 0 : Math.max(0, Math.min((s - min) / (max - min), 1))
850
863
  );
851
864
  }
852
865
  const median = sorted[Math.floor(sorted.length / 2)];
853
- const absoluteDiffs = positiveScores.map((v) => Math.abs(v - median));
866
+ const absoluteDiffs = map4(positiveScores, (v) => Math.abs(v - median));
854
867
  const mad = [...absoluteDiffs].sort((a, b) => a - b)[Math.floor(absoluteDiffs.length / 2)];
855
868
  const scale = mad === 0 ? median : mad * NORMALIZATION_MAD_COEFFICIENT;
856
- const logisticZ = safeScores.map((s) => {
869
+ const logisticZ = map4(safeScores, (s) => {
857
870
  if (s <= 0) return 0;
858
871
  if (scale === 0) return 1;
859
872
  const z = (s - median) / scale;
860
873
  return 1 / (1 + Math.exp(-NORMALIZATION_LOGISTIC_K * z));
861
874
  });
862
- const cdf = safeScores.map((s) => {
875
+ const cdf = map4(safeScores, (s) => {
863
876
  if (s <= 0) return 0;
864
877
  const rank = sorted.findIndex((v) => v >= s);
865
878
  return rank / sorted.length;
866
879
  });
867
- return logisticZ.map(
880
+ return map4(
881
+ logisticZ,
868
882
  (z, i) => z * (1 - NORMALIZATION_ALPHA) + cdf[i] * NORMALIZATION_ALPHA
869
883
  );
870
884
  }
@@ -916,7 +930,7 @@ var MinHeap = class {
916
930
  // src/core/pruner.ts
917
931
  function pruneTo(graph, frames, targetCount) {
918
932
  if (frames.length <= targetCount) {
919
- return new Set(frames.map((f) => f.id));
933
+ return new Set(map5(frames, (f) => f.id));
920
934
  }
921
935
  const prev = /* @__PURE__ */ new Map();
922
936
  const next = /* @__PURE__ */ new Map();
@@ -934,7 +948,7 @@ function pruneTo(graph, frames, targetCount) {
934
948
  tgtId: edge.targetId
935
949
  });
936
950
  }
937
- const surviving = new Set(frames.map((f) => f.id));
951
+ const surviving = new Set(map5(frames, (f) => f.id));
938
952
  const firstId = frames[0].id;
939
953
  const lastId = frames[frames.length - 1].id;
940
954
  while (surviving.size > targetCount && heap.size > 0) {
@@ -1027,7 +1041,7 @@ function pruneByThresholdWithCap(graph, frames, threshold, maxCount) {
1027
1041
  if (thresholdSurvivors.size <= maxCount) {
1028
1042
  return thresholdSurvivors;
1029
1043
  }
1030
- const survivingFrames = frames.filter((f) => thresholdSurvivors.has(f.id));
1044
+ const survivingFrames = filter4(frames, (f) => thresholdSurvivors.has(f.id));
1031
1045
  const idToOrigIdx = /* @__PURE__ */ new Map();
1032
1046
  for (let i = 0; i < frames.length; i++) {
1033
1047
  idToOrigIdx.set(frames[i].id, i);
@@ -1063,6 +1077,7 @@ function pruneByThresholdWithCap(graph, frames, threshold, maxCount) {
1063
1077
  // src/core/segmenter.ts
1064
1078
  import { randomUUID } from "crypto";
1065
1079
  import { join as join5 } from "path";
1080
+ import { filter as filter5, map as map6 } from "@winglet/common-utils";
1066
1081
 
1067
1082
  // src/utils/concurrency.ts
1068
1083
  function concurrencyLimit(limit) {
@@ -1201,18 +1216,30 @@ function remapEdges(segmentResults, globalIdMap) {
1201
1216
  const edgeMap = /* @__PURE__ */ new Map();
1202
1217
  for (const result of segmentResults) {
1203
1218
  for (const edge of result.edges) {
1204
- const newSourceId = globalIdMap.get(`${result.segment.index}:${edge.sourceId}`);
1205
- const newTargetId = globalIdMap.get(`${result.segment.index}:${edge.targetId}`);
1219
+ const newSourceId = globalIdMap.get(
1220
+ `${result.segment.index}:${edge.sourceId}`
1221
+ );
1222
+ const newTargetId = globalIdMap.get(
1223
+ `${result.segment.index}:${edge.targetId}`
1224
+ );
1206
1225
  if (newSourceId === void 0 || newTargetId === void 0) continue;
1207
1226
  const edgeKey = `${newSourceId}-${newTargetId}`;
1208
1227
  const existingIdx = edgeMap.get(edgeKey);
1209
1228
  if (existingIdx !== void 0) {
1210
1229
  if (edges[existingIdx].score < edge.score) {
1211
- edges[existingIdx] = { sourceId: newSourceId, targetId: newTargetId, score: edge.score };
1230
+ edges[existingIdx] = {
1231
+ sourceId: newSourceId,
1232
+ targetId: newTargetId,
1233
+ score: edge.score
1234
+ };
1212
1235
  }
1213
1236
  } else {
1214
1237
  edgeMap.set(edgeKey, edges.length);
1215
- edges.push({ sourceId: newSourceId, targetId: newTargetId, score: edge.score });
1238
+ edges.push({
1239
+ sourceId: newSourceId,
1240
+ targetId: newTargetId,
1241
+ score: edge.score
1242
+ });
1216
1243
  }
1217
1244
  }
1218
1245
  }
@@ -1222,10 +1249,18 @@ function remapAnimations(segmentResults, globalIdMap) {
1222
1249
  const animations = [];
1223
1250
  for (const result of segmentResults) {
1224
1251
  for (const anim of result.animations) {
1225
- const newStartId = globalIdMap.get(`${result.segment.index}:${anim.startFrameId}`);
1226
- const newEndId = globalIdMap.get(`${result.segment.index}:${anim.endFrameId}`);
1252
+ const newStartId = globalIdMap.get(
1253
+ `${result.segment.index}:${anim.startFrameId}`
1254
+ );
1255
+ const newEndId = globalIdMap.get(
1256
+ `${result.segment.index}:${anim.endFrameId}`
1257
+ );
1227
1258
  if (newStartId === void 0 || newEndId === void 0) continue;
1228
- animations.push({ ...anim, startFrameId: newStartId, endFrameId: newEndId });
1259
+ animations.push({
1260
+ ...anim,
1261
+ startFrameId: newStartId,
1262
+ endFrameId: newEndId
1263
+ });
1229
1264
  }
1230
1265
  }
1231
1266
  return animations;
@@ -1306,7 +1341,7 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1306
1341
  logger.debug(`Segment plan: ${segments.length} segments`);
1307
1342
  const limit = concurrencyLimit(resolvedOptions.concurrency);
1308
1343
  const segmentProgresses = new Array(segments.length).fill(0);
1309
- const weights = segments.map((s) => s.duration / totalDuration);
1344
+ const weights = map6(segments, (s) => s.duration / totalDuration);
1310
1345
  const emitOverallProgress = (phase) => {
1311
1346
  if (!options.onProgress) return;
1312
1347
  const overall = weights.reduce(
@@ -1317,7 +1352,8 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1317
1352
  };
1318
1353
  options.onProgress?.("EXTRACTING", 0);
1319
1354
  const results = await Promise.all(
1320
- segments.map(
1355
+ map6(
1356
+ segments,
1321
1357
  (segment) => limit(async () => {
1322
1358
  const segWorkspace = await createSegmentWorkspace(
1323
1359
  mainWorkspace,
@@ -1349,7 +1385,7 @@ async function runSegmentedPipeline(options, resolvedOptions) {
1349
1385
  resolvedOptions.threshold,
1350
1386
  resolvedOptions.count
1351
1387
  );
1352
- const prunedFrames = frames.filter((f) => survivingIds.has(f.id));
1388
+ const prunedFrames = filter5(frames, (f) => survivingIds.has(f.id));
1353
1389
  options.onProgress?.("PRUNING", 100);
1354
1390
  options.onProgress?.("FINALIZING", 0);
1355
1391
  const ctx = {
@@ -1459,7 +1495,7 @@ async function runPipeline(options) {
1459
1495
  resolvedOptions.threshold,
1460
1496
  resolvedOptions.count
1461
1497
  );
1462
- const prunedFrames = ctx.frames.filter((f) => survivingIds.has(f.id));
1498
+ const prunedFrames = filter6(ctx.frames, (f) => survivingIds.has(f.id));
1463
1499
  ctx.emitProgress(100);
1464
1500
  ctx.status = "FINALIZING";
1465
1501
  let outputFiles = [];