asciify-engine 1.0.48 → 1.0.50

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/index.cjs CHANGED
@@ -694,15 +694,15 @@ function imageToAsciiFrame(source, options, targetWidth, targetHeight) {
694
694
  }
695
695
  return { frame, cols, rows };
696
696
  }
697
- async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress) {
698
- const duration = Math.min(video.duration, maxDuration);
697
+ async function videoToAsciiFrames(video, options, targetWidth, targetHeight, targetFps = 12, maxDuration = 10, onProgress, startTime = 0) {
698
+ const duration = Math.min(video.duration - startTime, maxDuration);
699
699
  const totalFrames = Math.ceil(duration * targetFps);
700
700
  const frames = [];
701
701
  let cols = 0;
702
702
  let rows = 0;
703
703
  for (let i = 0; i < totalFrames; i++) {
704
- const time = i / targetFps;
705
- if (time > duration) break;
704
+ const time = startTime + i / targetFps;
705
+ if (time > startTime + duration) break;
706
706
  video.currentTime = time;
707
707
  await new Promise((resolve) => {
708
708
  const handler = () => {
@@ -1089,7 +1089,9 @@ async function asciifyGif(source, canvas, { fontSize = 10, artStyle = "classic",
1089
1089
  cancelAnimationFrame(animId);
1090
1090
  };
1091
1091
  }
1092
- async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic", options = {}, fitTo, preExtract = false, onReady, onFrame } = {}) {
1092
+ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic", options = {}, fitTo, preExtract = false, trim, onReady, onFrame } = {}) {
1093
+ const trimStart = trim?.start ?? 0;
1094
+ const trimEnd = trim?.end;
1093
1095
  const merged = { ...DEFAULT_OPTIONS, ...ART_STYLE_PRESETS[artStyle], ...options, fontSize };
1094
1096
  const ctx = canvas.getContext("2d");
1095
1097
  if (!ctx) throw new Error("asciifyVideo: could not get 2d context from canvas.");
@@ -1110,9 +1112,10 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1110
1112
  video2 = source;
1111
1113
  }
1112
1114
  if (container) sizeCanvasToContainer(canvas, container, video2.videoWidth / video2.videoHeight);
1113
- onReady?.(video2);
1114
- const { frames, fps } = await videoToAsciiFrames(video2, merged, canvas.width, canvas.height);
1115
+ const maxDur = trimEnd !== void 0 ? trimEnd - trimStart : 10;
1116
+ const { frames, fps } = await videoToAsciiFrames(video2, merged, canvas.width, canvas.height, void 0, maxDur, void 0, trimStart);
1115
1117
  let cancelled2 = false, animId2, i = 0, last = performance.now();
1118
+ let firstFrame2 = true;
1116
1119
  const interval = 1e3 / fps;
1117
1120
  const tick2 = (now) => {
1118
1121
  if (cancelled2) return;
@@ -1120,6 +1123,10 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1120
1123
  renderFrameToCanvas(ctx, frames[i], merged, canvas.width, canvas.height);
1121
1124
  i = (i + 1) % frames.length;
1122
1125
  last = now;
1126
+ if (firstFrame2) {
1127
+ firstFrame2 = false;
1128
+ onReady?.(video2);
1129
+ }
1123
1130
  onFrame?.();
1124
1131
  }
1125
1132
  animId2 = requestAnimationFrame(tick2);
@@ -1162,6 +1169,23 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1162
1169
  if (video.paused) await video.play().catch(() => {
1163
1170
  });
1164
1171
  }
1172
+ if (trimStart > 0) {
1173
+ video.currentTime = trimStart;
1174
+ await new Promise((resolve) => {
1175
+ const h = () => {
1176
+ video.removeEventListener("seeked", h);
1177
+ resolve();
1178
+ };
1179
+ video.addEventListener("seeked", h);
1180
+ });
1181
+ }
1182
+ let timeupdateHandler = null;
1183
+ if (trimEnd !== void 0) {
1184
+ timeupdateHandler = () => {
1185
+ if (video.currentTime >= trimEnd) video.currentTime = trimStart;
1186
+ };
1187
+ video.addEventListener("timeupdate", timeupdateHandler);
1188
+ }
1165
1189
  let ro = null;
1166
1190
  if (container) {
1167
1191
  const aspect = video.videoWidth / video.videoHeight;
@@ -1169,9 +1193,9 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1169
1193
  ro = new ResizeObserver(() => sizeCanvasToContainer(canvas, container, aspect));
1170
1194
  ro.observe(container);
1171
1195
  }
1172
- onReady?.(video);
1173
1196
  let cancelled = false;
1174
1197
  let animId;
1198
+ let firstFrame = true;
1175
1199
  const tick = () => {
1176
1200
  if (cancelled) return;
1177
1201
  animId = requestAnimationFrame(tick);
@@ -1179,6 +1203,10 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1179
1203
  const { frame } = imageToAsciiFrame(video, merged, canvas.width, canvas.height);
1180
1204
  if (frame.length > 0) {
1181
1205
  renderFrameToCanvas(ctx, frame, merged, canvas.width, canvas.height, 0, null);
1206
+ if (firstFrame) {
1207
+ firstFrame = false;
1208
+ onReady?.(video);
1209
+ }
1182
1210
  onFrame?.();
1183
1211
  }
1184
1212
  };
@@ -1187,6 +1215,7 @@ async function asciifyVideo(source, canvas, { fontSize = 10, artStyle = "classic
1187
1215
  cancelled = true;
1188
1216
  cancelAnimationFrame(animId);
1189
1217
  ro?.disconnect();
1218
+ if (timeupdateHandler) video.removeEventListener("timeupdate", timeupdateHandler);
1190
1219
  if (ownedVideo) {
1191
1220
  video.pause();
1192
1221
  video.src = "";