@remotion/timeline-utils 4.0.529 → 4.0.531
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-waveform/draw-peaks.d.ts +2 -1
- package/dist/audio-waveform/draw-peaks.js +10 -7
- package/dist/device-pixel-alignment.d.ts +14 -0
- package/dist/device-pixel-alignment.js +19 -0
- package/dist/esm/index.mjs +69 -24
- package/dist/image-thumbnail/draw-repeating-image-thumbnail.d.ts +10 -0
- package/dist/image-thumbnail/draw-repeating-image-thumbnail.js +48 -0
- package/dist/image-thumbnail/get-scaled-image-thumbnail-dimensions.d.ts +8 -0
- package/dist/image-thumbnail/get-scaled-image-thumbnail-dimensions.js +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/render-frame-strip-to-canvas.d.ts +2 -1
- package/dist/render-frame-strip-to-canvas.js +13 -8
- package/dist/render-frame-strip.d.ts +6 -3
- package/dist/render-frame-strip.js +19 -5
- package/package.json +3 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export type WaveformVolume = number | readonly number[];
|
|
2
|
-
export declare const drawBars: ({ canvas, color, peaks, volume, width, }: {
|
|
2
|
+
export declare const drawBars: ({ canvas, color, peaks, volume, width, horizontalOffset, }: {
|
|
3
3
|
readonly canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
4
4
|
readonly peaks: Float32Array<ArrayBufferLike>;
|
|
5
5
|
readonly color: string;
|
|
6
6
|
readonly volume: WaveformVolume;
|
|
7
7
|
readonly width: number;
|
|
8
|
+
readonly horizontalOffset: number;
|
|
8
9
|
}) => void;
|
|
@@ -4,6 +4,7 @@ exports.drawBars = void 0;
|
|
|
4
4
|
const parse_color_1 = require("./parse-color");
|
|
5
5
|
const CLIPPING_COLOR = '#FF7F50';
|
|
6
6
|
const WAVEFORM_HEIGHT_SCALE = 0.8;
|
|
7
|
+
const MIN_VISIBLE_DECIBELS = -37;
|
|
7
8
|
const getVolumeAtBar = ({ barIndex, numBars, volume, }) => {
|
|
8
9
|
var _a;
|
|
9
10
|
if (typeof volume === 'number') {
|
|
@@ -18,7 +19,7 @@ const getVolumeAtBar = ({ barIndex, numBars, volume, }) => {
|
|
|
18
19
|
const volumeIndex = Math.round((barIndex / (numBars - 1)) * (volume.length - 1));
|
|
19
20
|
return (_a = volume[volumeIndex]) !== null && _a !== void 0 ? _a : 1;
|
|
20
21
|
};
|
|
21
|
-
const drawBars = ({ canvas, color, peaks, volume, width, }) => {
|
|
22
|
+
const drawBars = ({ canvas, color, peaks, volume, width, horizontalOffset, }) => {
|
|
22
23
|
const ctx = canvas.getContext('2d');
|
|
23
24
|
if (!ctx) {
|
|
24
25
|
throw new Error('Failed to get canvas context');
|
|
@@ -29,7 +30,7 @@ const drawBars = ({ canvas, color, peaks, volume, width, }) => {
|
|
|
29
30
|
// `createImageData(0, h)` / `(w, 0)` throws a DOMException, which
|
|
30
31
|
// surfaces in Studio's console for compositions with many audio
|
|
31
32
|
// sequences — some segments are 0 px wide at certain zoom levels.
|
|
32
|
-
if (w === 0 || height === 0) {
|
|
33
|
+
if (w === 0 || height === 0 || width <= 0) {
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
ctx.clearRect(0, 0, w, height);
|
|
@@ -39,15 +40,17 @@ const drawBars = ({ canvas, color, peaks, volume, width, }) => {
|
|
|
39
40
|
const { data } = imageData;
|
|
40
41
|
const numBars = width;
|
|
41
42
|
const fullScaleHalfBar = (height * WAVEFORM_HEIGHT_SCALE) / 2;
|
|
42
|
-
for (let
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
for (let x = 0; x < w; x++) {
|
|
44
|
+
// The canvas may start before its fractionally positioned container so its
|
|
45
|
+
// bitmap stays device-pixel aligned. Sample from the exact container origin.
|
|
46
|
+
const barIndex = Math.max(0, Math.min(numBars - Number.EPSILON * Math.max(1, numBars), x - horizontalOffset));
|
|
46
47
|
const peakIndex = Math.floor((barIndex / numBars) * peaks.length);
|
|
47
48
|
const peak = peaks[peakIndex] || 0;
|
|
48
49
|
const barVolume = getVolumeAtBar({ barIndex, numBars, volume });
|
|
49
50
|
const scaledPeak = peak * barVolume;
|
|
50
|
-
const
|
|
51
|
+
const decibelPeak = scaledPeak <= 0 ? MIN_VISIBLE_DECIBELS : 20 * Math.log10(scaledPeak);
|
|
52
|
+
const visualPeak = Math.max(0, (decibelPeak - MIN_VISIBLE_DECIBELS) / -MIN_VISIBLE_DECIBELS);
|
|
53
|
+
const halfBar = Math.max(0, Math.min(height / 2, visualPeak * fullScaleHalfBar));
|
|
51
54
|
if (halfBar === 0)
|
|
52
55
|
continue;
|
|
53
56
|
const mid = height / 2;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const getDevicePixelAlignedCanvasLayout: ({ cssWidth, cssHeight, containerLeft, devicePixelRatio, }: {
|
|
2
|
+
readonly cssWidth: number;
|
|
3
|
+
readonly cssHeight: number;
|
|
4
|
+
readonly containerLeft: number;
|
|
5
|
+
readonly devicePixelRatio: number;
|
|
6
|
+
}) => {
|
|
7
|
+
height: number;
|
|
8
|
+
horizontalOffset: number;
|
|
9
|
+
width: number;
|
|
10
|
+
};
|
|
11
|
+
export declare const snapCanvasPositionToDevicePixel: ({ position, horizontalOffset, }: {
|
|
12
|
+
readonly position: number;
|
|
13
|
+
readonly horizontalOffset: number;
|
|
14
|
+
}) => number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snapCanvasPositionToDevicePixel = exports.getDevicePixelAlignedCanvasLayout = void 0;
|
|
4
|
+
const getDevicePixelAlignedCanvasLayout = ({ cssWidth, cssHeight, containerLeft, devicePixelRatio, }) => {
|
|
5
|
+
const containerLeftInPixels = containerLeft * devicePixelRatio;
|
|
6
|
+
const horizontalOffset = cssWidth === 0
|
|
7
|
+
? 0
|
|
8
|
+
: containerLeftInPixels - Math.floor(containerLeftInPixels);
|
|
9
|
+
return {
|
|
10
|
+
height: Math.ceil(cssHeight * devicePixelRatio),
|
|
11
|
+
horizontalOffset,
|
|
12
|
+
width: Math.ceil(cssWidth * devicePixelRatio + horizontalOffset),
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
exports.getDevicePixelAlignedCanvasLayout = getDevicePixelAlignedCanvasLayout;
|
|
16
|
+
const snapCanvasPositionToDevicePixel = ({ position, horizontalOffset, }) => {
|
|
17
|
+
return Math.floor(position + horizontalOffset) - horizontalOffset;
|
|
18
|
+
};
|
|
19
|
+
exports.snapCanvasPositionToDevicePixel = snapCanvasPositionToDevicePixel;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -18,6 +18,7 @@ var parseColor = (color) => {
|
|
|
18
18
|
// src/audio-waveform/draw-peaks.ts
|
|
19
19
|
var CLIPPING_COLOR = "#FF7F50";
|
|
20
20
|
var WAVEFORM_HEIGHT_SCALE = 0.8;
|
|
21
|
+
var MIN_VISIBLE_DECIBELS = -37;
|
|
21
22
|
var getVolumeAtBar = ({
|
|
22
23
|
barIndex,
|
|
23
24
|
numBars,
|
|
@@ -40,7 +41,8 @@ var drawBars = ({
|
|
|
40
41
|
color,
|
|
41
42
|
peaks,
|
|
42
43
|
volume,
|
|
43
|
-
width
|
|
44
|
+
width,
|
|
45
|
+
horizontalOffset
|
|
44
46
|
}) => {
|
|
45
47
|
const ctx = canvas.getContext("2d");
|
|
46
48
|
if (!ctx) {
|
|
@@ -48,7 +50,7 @@ var drawBars = ({
|
|
|
48
50
|
}
|
|
49
51
|
const { height } = canvas;
|
|
50
52
|
const w = canvas.width;
|
|
51
|
-
if (w === 0 || height === 0) {
|
|
53
|
+
if (w === 0 || height === 0 || width <= 0) {
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
ctx.clearRect(0, 0, w, height);
|
|
@@ -58,15 +60,15 @@ var drawBars = ({
|
|
|
58
60
|
const { data } = imageData;
|
|
59
61
|
const numBars = width;
|
|
60
62
|
const fullScaleHalfBar = height * WAVEFORM_HEIGHT_SCALE / 2;
|
|
61
|
-
for (let
|
|
62
|
-
const
|
|
63
|
-
if (x >= w)
|
|
64
|
-
break;
|
|
63
|
+
for (let x = 0;x < w; x++) {
|
|
64
|
+
const barIndex = Math.max(0, Math.min(numBars - Number.EPSILON * Math.max(1, numBars), x - horizontalOffset));
|
|
65
65
|
const peakIndex = Math.floor(barIndex / numBars * peaks.length);
|
|
66
66
|
const peak = peaks[peakIndex] || 0;
|
|
67
67
|
const barVolume = getVolumeAtBar({ barIndex, numBars, volume });
|
|
68
68
|
const scaledPeak = peak * barVolume;
|
|
69
|
-
const
|
|
69
|
+
const decibelPeak = scaledPeak <= 0 ? MIN_VISIBLE_DECIBELS : 20 * Math.log10(scaledPeak);
|
|
70
|
+
const visualPeak = Math.max(0, (decibelPeak - MIN_VISIBLE_DECIBELS) / -MIN_VISIBLE_DECIBELS);
|
|
71
|
+
const halfBar = Math.max(0, Math.min(height / 2, visualPeak * fullScaleHalfBar));
|
|
70
72
|
if (halfBar === 0)
|
|
71
73
|
continue;
|
|
72
74
|
const mid = height / 2;
|
|
@@ -618,6 +620,27 @@ var clampTimestampsToDuration = ({
|
|
|
618
620
|
const lastTimestamp = Math.max(0, durationInSeconds - LAST_FRAME_EPSILON_IN_SECONDS);
|
|
619
621
|
return timestamps.map((timestamp) => Math.min(timestamp, lastTimestamp));
|
|
620
622
|
};
|
|
623
|
+
// src/device-pixel-alignment.ts
|
|
624
|
+
var getDevicePixelAlignedCanvasLayout = ({
|
|
625
|
+
cssWidth,
|
|
626
|
+
cssHeight,
|
|
627
|
+
containerLeft,
|
|
628
|
+
devicePixelRatio
|
|
629
|
+
}) => {
|
|
630
|
+
const containerLeftInPixels = containerLeft * devicePixelRatio;
|
|
631
|
+
const horizontalOffset = cssWidth === 0 ? 0 : containerLeftInPixels - Math.floor(containerLeftInPixels);
|
|
632
|
+
return {
|
|
633
|
+
height: Math.ceil(cssHeight * devicePixelRatio),
|
|
634
|
+
horizontalOffset,
|
|
635
|
+
width: Math.ceil(cssWidth * devicePixelRatio + horizontalOffset)
|
|
636
|
+
};
|
|
637
|
+
};
|
|
638
|
+
var snapCanvasPositionToDevicePixel = ({
|
|
639
|
+
position,
|
|
640
|
+
horizontalOffset
|
|
641
|
+
}) => {
|
|
642
|
+
return Math.floor(position + horizontalOffset) - horizontalOffset;
|
|
643
|
+
};
|
|
621
644
|
// src/extract-frames.ts
|
|
622
645
|
import {
|
|
623
646
|
ALL_FORMATS as ALL_FORMATS2,
|
|
@@ -855,7 +878,8 @@ var drawSlot = ({
|
|
|
855
878
|
segmentDuration,
|
|
856
879
|
fromSeconds,
|
|
857
880
|
devicePixelRatio,
|
|
858
|
-
frameHeight
|
|
881
|
+
frameHeight,
|
|
882
|
+
horizontalOffset
|
|
859
883
|
}) => {
|
|
860
884
|
const durationOfOneFrame = getDurationOfOneFrame({
|
|
861
885
|
visualizationWidth,
|
|
@@ -866,8 +890,17 @@ var drawSlot = ({
|
|
|
866
890
|
const relativeTimestamp = timestamp - fromSeconds * WEBCODECS_TIMESCALE;
|
|
867
891
|
const frameIndex = relativeTimestamp / durationOfOneFrame;
|
|
868
892
|
const thumbnailWidth = frame.displayWidth / devicePixelRatio;
|
|
869
|
-
const
|
|
870
|
-
|
|
893
|
+
const idealLeft = frameIndex * thumbnailWidth;
|
|
894
|
+
const idealRight = idealLeft + thumbnailWidth;
|
|
895
|
+
const left = snapCanvasPositionToDevicePixel({
|
|
896
|
+
horizontalOffset,
|
|
897
|
+
position: idealLeft
|
|
898
|
+
});
|
|
899
|
+
const right = snapCanvasPositionToDevicePixel({
|
|
900
|
+
horizontalOffset,
|
|
901
|
+
position: idealRight
|
|
902
|
+
});
|
|
903
|
+
ctx.drawImage(frame, left, 0, right - left, frame.displayHeight / devicePixelRatio);
|
|
871
904
|
filledSlots.set(timestamp, frame.timestamp);
|
|
872
905
|
};
|
|
873
906
|
var fillWithCachedFrames = ({
|
|
@@ -878,7 +911,8 @@ var fillWithCachedFrames = ({
|
|
|
878
911
|
segmentDuration,
|
|
879
912
|
fromSeconds,
|
|
880
913
|
devicePixelRatio,
|
|
881
|
-
frameHeight
|
|
914
|
+
frameHeight,
|
|
915
|
+
horizontalOffset
|
|
882
916
|
}) => {
|
|
883
917
|
const prefix = getFrameDatabaseKeyPrefix(src);
|
|
884
918
|
const keys = Array.from(frameDatabase.keys()).filter((k) => k.startsWith(prefix));
|
|
@@ -914,7 +948,8 @@ var fillWithCachedFrames = ({
|
|
|
914
948
|
segmentDuration,
|
|
915
949
|
fromSeconds,
|
|
916
950
|
devicePixelRatio,
|
|
917
|
-
frameHeight
|
|
951
|
+
frameHeight,
|
|
952
|
+
horizontalOffset
|
|
918
953
|
});
|
|
919
954
|
}
|
|
920
955
|
};
|
|
@@ -926,7 +961,8 @@ var fillFrameWhereItFits = ({
|
|
|
926
961
|
segmentDuration,
|
|
927
962
|
fromSeconds,
|
|
928
963
|
devicePixelRatio,
|
|
929
|
-
frameHeight
|
|
964
|
+
frameHeight,
|
|
965
|
+
horizontalOffset
|
|
930
966
|
}) => {
|
|
931
967
|
const slots = Array.from(filledSlots.keys());
|
|
932
968
|
for (let i = 0;i < slots.length; i++) {
|
|
@@ -947,7 +983,8 @@ var fillFrameWhereItFits = ({
|
|
|
947
983
|
segmentDuration,
|
|
948
984
|
fromSeconds,
|
|
949
985
|
devicePixelRatio,
|
|
950
|
-
frameHeight
|
|
986
|
+
frameHeight,
|
|
987
|
+
horizontalOffset
|
|
951
988
|
});
|
|
952
989
|
}
|
|
953
990
|
};
|
|
@@ -1009,9 +1046,11 @@ var renderFrameStripToCanvas = async ({
|
|
|
1009
1046
|
width,
|
|
1010
1047
|
frameHeight,
|
|
1011
1048
|
devicePixelRatio,
|
|
1049
|
+
horizontalOffset,
|
|
1012
1050
|
signal
|
|
1013
1051
|
}) => {
|
|
1014
|
-
const
|
|
1052
|
+
const drawingWidth = Math.max(1, width);
|
|
1053
|
+
const naturalWidth = Math.max(1, Math.ceil(width + horizontalOffset));
|
|
1015
1054
|
const naturalHeight = Math.max(1, Math.ceil(frameHeight));
|
|
1016
1055
|
const segmentDuration = toSeconds - fromSeconds;
|
|
1017
1056
|
canvas.width = naturalWidth;
|
|
@@ -1021,12 +1060,13 @@ var renderFrameStripToCanvas = async ({
|
|
|
1021
1060
|
}
|
|
1022
1061
|
const ctx = getContext(canvas);
|
|
1023
1062
|
ctx.clearRect(0, 0, naturalWidth, naturalHeight);
|
|
1063
|
+
ctx.setTransform(1, 0, 0, 1, horizontalOffset, 0);
|
|
1024
1064
|
const filledSlots = new Map;
|
|
1025
1065
|
let aspectRatio = getAspectRatioFromCache(src);
|
|
1026
1066
|
if (aspectRatio !== null) {
|
|
1027
1067
|
ensureSlots({
|
|
1028
1068
|
filledSlots,
|
|
1029
|
-
naturalWidth,
|
|
1069
|
+
naturalWidth: drawingWidth,
|
|
1030
1070
|
fromSeconds,
|
|
1031
1071
|
toSeconds,
|
|
1032
1072
|
aspectRatio,
|
|
@@ -1034,13 +1074,14 @@ var renderFrameStripToCanvas = async ({
|
|
|
1034
1074
|
});
|
|
1035
1075
|
fillWithCachedFrames({
|
|
1036
1076
|
ctx,
|
|
1037
|
-
naturalWidth,
|
|
1077
|
+
naturalWidth: drawingWidth,
|
|
1038
1078
|
filledSlots,
|
|
1039
1079
|
src,
|
|
1040
1080
|
segmentDuration,
|
|
1041
1081
|
fromSeconds,
|
|
1042
1082
|
devicePixelRatio,
|
|
1043
|
-
frameHeight
|
|
1083
|
+
frameHeight,
|
|
1084
|
+
horizontalOffset
|
|
1044
1085
|
});
|
|
1045
1086
|
const hasUnfilledSlots = Array.from(filledSlots.values()).some((value) => value === undefined);
|
|
1046
1087
|
if (!hasUnfilledSlots) {
|
|
@@ -1055,7 +1096,7 @@ var renderFrameStripToCanvas = async ({
|
|
|
1055
1096
|
aspectRatioCache.set(src, aspectRatio);
|
|
1056
1097
|
ensureSlots({
|
|
1057
1098
|
filledSlots,
|
|
1058
|
-
naturalWidth,
|
|
1099
|
+
naturalWidth: drawingWidth,
|
|
1059
1100
|
fromSeconds,
|
|
1060
1101
|
toSeconds,
|
|
1061
1102
|
aspectRatio,
|
|
@@ -1079,7 +1120,7 @@ var renderFrameStripToCanvas = async ({
|
|
|
1079
1120
|
}
|
|
1080
1121
|
ensureSlots({
|
|
1081
1122
|
filledSlots,
|
|
1082
|
-
naturalWidth,
|
|
1123
|
+
naturalWidth: drawingWidth,
|
|
1083
1124
|
fromSeconds,
|
|
1084
1125
|
toSeconds,
|
|
1085
1126
|
aspectRatio,
|
|
@@ -1088,12 +1129,13 @@ var renderFrameStripToCanvas = async ({
|
|
|
1088
1129
|
fillFrameWhereItFits({
|
|
1089
1130
|
ctx,
|
|
1090
1131
|
filledSlots,
|
|
1091
|
-
visualizationWidth:
|
|
1132
|
+
visualizationWidth: drawingWidth,
|
|
1092
1133
|
frame: transformed,
|
|
1093
1134
|
segmentDuration,
|
|
1094
1135
|
fromSeconds,
|
|
1095
1136
|
devicePixelRatio,
|
|
1096
|
-
frameHeight
|
|
1137
|
+
frameHeight,
|
|
1138
|
+
horizontalOffset
|
|
1097
1139
|
});
|
|
1098
1140
|
} catch (e) {
|
|
1099
1141
|
if (frame) {
|
|
@@ -1110,13 +1152,14 @@ var renderFrameStripToCanvas = async ({
|
|
|
1110
1152
|
}
|
|
1111
1153
|
fillWithCachedFrames({
|
|
1112
1154
|
ctx,
|
|
1113
|
-
naturalWidth,
|
|
1155
|
+
naturalWidth: drawingWidth,
|
|
1114
1156
|
filledSlots,
|
|
1115
1157
|
src,
|
|
1116
1158
|
segmentDuration,
|
|
1117
1159
|
fromSeconds,
|
|
1118
1160
|
devicePixelRatio,
|
|
1119
|
-
frameHeight
|
|
1161
|
+
frameHeight,
|
|
1162
|
+
horizontalOffset
|
|
1120
1163
|
});
|
|
1121
1164
|
};
|
|
1122
1165
|
// src/format-average-audio-volume.ts
|
|
@@ -1128,6 +1171,7 @@ var formatAverageAudioVolume = (volume) => {
|
|
|
1128
1171
|
};
|
|
1129
1172
|
export {
|
|
1130
1173
|
subscribeToWaveformPeaks,
|
|
1174
|
+
snapCanvasPositionToDevicePixel,
|
|
1131
1175
|
sliceWaveformPeaks,
|
|
1132
1176
|
sliceVisibleWaveformPeaks,
|
|
1133
1177
|
shouldTileLoopDisplay,
|
|
@@ -1141,6 +1185,7 @@ export {
|
|
|
1141
1185
|
getLoopDisplaySegments,
|
|
1142
1186
|
getFrameDatabaseKeyPrefix,
|
|
1143
1187
|
getDurationOfOneFrame,
|
|
1188
|
+
getDevicePixelAlignedCanvasLayout,
|
|
1144
1189
|
getAspectRatioFromCache,
|
|
1145
1190
|
frameDatabase,
|
|
1146
1191
|
formatAverageAudioVolume,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type ImageWithNaturalDimensions = CanvasImageSource & {
|
|
2
|
+
readonly naturalWidth: number;
|
|
3
|
+
readonly naturalHeight: number;
|
|
4
|
+
};
|
|
5
|
+
export declare const drawRepeatingImageThumbnail: ({ canvas, image, offsetInPixels, }: {
|
|
6
|
+
readonly canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
7
|
+
readonly image: ImageWithNaturalDimensions;
|
|
8
|
+
readonly offsetInPixels?: number | undefined;
|
|
9
|
+
}) => void;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.drawRepeatingImageThumbnail = void 0;
|
|
4
|
+
const get_scaled_image_thumbnail_dimensions_1 = require("./get-scaled-image-thumbnail-dimensions");
|
|
5
|
+
const createOffscreenCanvas = (width, height) => {
|
|
6
|
+
if (typeof document !== 'undefined') {
|
|
7
|
+
const canvas = document.createElement('canvas');
|
|
8
|
+
canvas.width = width;
|
|
9
|
+
canvas.height = height;
|
|
10
|
+
return canvas;
|
|
11
|
+
}
|
|
12
|
+
if (typeof OffscreenCanvas !== 'undefined') {
|
|
13
|
+
return new OffscreenCanvas(width, height);
|
|
14
|
+
}
|
|
15
|
+
throw new Error('No canvas implementation available');
|
|
16
|
+
};
|
|
17
|
+
const drawRepeatingImageThumbnail = ({ canvas, image, offsetInPixels = 0, }) => {
|
|
18
|
+
const ctx = canvas.getContext('2d');
|
|
19
|
+
if (!ctx) {
|
|
20
|
+
throw new Error('Failed to get canvas context');
|
|
21
|
+
}
|
|
22
|
+
const { width, height } = canvas;
|
|
23
|
+
if (width === 0 || height === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { scaledWidth, scaledHeight } = (0, get_scaled_image_thumbnail_dimensions_1.getScaledImageThumbnailDimensions)({
|
|
27
|
+
naturalWidth: image.naturalWidth,
|
|
28
|
+
naturalHeight: image.naturalHeight,
|
|
29
|
+
canvasHeight: height,
|
|
30
|
+
});
|
|
31
|
+
if (scaledWidth === 0 || scaledHeight === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const offscreen = createOffscreenCanvas(scaledWidth, scaledHeight);
|
|
35
|
+
const offCtx = offscreen.getContext('2d');
|
|
36
|
+
if (!offCtx) {
|
|
37
|
+
throw new Error('Failed to get offscreen canvas context');
|
|
38
|
+
}
|
|
39
|
+
offCtx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
|
|
40
|
+
const pattern = ctx.createPattern(offscreen, 'repeat-x');
|
|
41
|
+
if (!pattern) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
pattern.setTransform(new DOMMatrix().translate(-offsetInPixels, 0));
|
|
45
|
+
ctx.fillStyle = pattern;
|
|
46
|
+
ctx.fillRect(0, 0, width, height);
|
|
47
|
+
};
|
|
48
|
+
exports.drawRepeatingImageThumbnail = drawRepeatingImageThumbnail;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const getScaledImageThumbnailDimensions: ({ naturalWidth, naturalHeight, canvasHeight, }: {
|
|
2
|
+
readonly naturalWidth: number;
|
|
3
|
+
readonly naturalHeight: number;
|
|
4
|
+
readonly canvasHeight: number;
|
|
5
|
+
}) => {
|
|
6
|
+
scaledWidth: number;
|
|
7
|
+
scaledHeight: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getScaledImageThumbnailDimensions = void 0;
|
|
4
|
+
const getScaledImageThumbnailDimensions = ({ naturalWidth, naturalHeight, canvasHeight, }) => {
|
|
5
|
+
if (naturalWidth === 0 || naturalHeight === 0 || canvasHeight === 0) {
|
|
6
|
+
return {
|
|
7
|
+
scaledWidth: 0,
|
|
8
|
+
scaledHeight: 0,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const scale = canvasHeight / naturalHeight;
|
|
12
|
+
return {
|
|
13
|
+
scaledWidth: naturalWidth * scale,
|
|
14
|
+
scaledHeight: canvasHeight,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
exports.getScaledImageThumbnailDimensions = getScaledImageThumbnailDimensions;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { subscribeToWaveformPeaks } from './audio-waveform/subscribe-to-waveform
|
|
|
8
8
|
export { sliceWaveformPeaks } from './audio-waveform/slice-waveform-peaks';
|
|
9
9
|
export { createWaveformPeakProcessor, emitWaveformProgress, } from './audio-waveform/waveform-peak-processor';
|
|
10
10
|
export { clampTimestampsToDuration } from './clamp-timestamps-to-duration';
|
|
11
|
+
export { getDevicePixelAlignedCanvasLayout, snapCanvasPositionToDevicePixel, } from './device-pixel-alignment';
|
|
11
12
|
export { extractFrames } from './extract-frames';
|
|
12
13
|
export type { ExtractFramesProps, ExtractFramesTimestampsInSecondsFn, } from './extract-frames';
|
|
13
14
|
export { addFrameToCache, aspectRatioCache, frameDatabase, getAspectRatioFromCache, getFrameDatabaseKeyPrefix, getTimestampFromFrameDatabaseKey, makeFrameDatabaseKey, } from './frame-database';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatAverageAudioVolume = exports.resizeVideoFrame = exports.renderFrameStripToCanvas = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.WEBCODECS_TIMESCALE = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.getLoopDisplaySegments = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.clampTimestampsToDuration = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.subscribeToWaveformPeaks = exports.sliceVisibleWaveformPeaks = exports.loadWaveformPeaks = exports.getVisibleWaveformVolume = exports.drawBars = exports.TARGET_SAMPLE_RATE = void 0;
|
|
3
|
+
exports.formatAverageAudioVolume = exports.resizeVideoFrame = exports.renderFrameStripToCanvas = exports.getDurationOfOneFrame = exports.fillWithCachedFrames = exports.fillFrameWhereItFits = exports.ensureSlots = exports.drawSlot = exports.calculateTimestampSlots = exports.WEBCODECS_TIMESCALE = exports.shouldTileLoopDisplay = exports.getLoopDisplayWidth = exports.getLoopDisplaySegments = exports.makeFrameDatabaseKey = exports.getTimestampFromFrameDatabaseKey = exports.getFrameDatabaseKeyPrefix = exports.getAspectRatioFromCache = exports.frameDatabase = exports.aspectRatioCache = exports.addFrameToCache = exports.extractFrames = exports.snapCanvasPositionToDevicePixel = exports.getDevicePixelAlignedCanvasLayout = exports.clampTimestampsToDuration = exports.emitWaveformProgress = exports.createWaveformPeakProcessor = exports.sliceWaveformPeaks = exports.subscribeToWaveformPeaks = exports.sliceVisibleWaveformPeaks = exports.loadWaveformPeaks = exports.getVisibleWaveformVolume = exports.drawBars = exports.TARGET_SAMPLE_RATE = void 0;
|
|
4
4
|
const constants_1 = require("./audio-waveform/constants");
|
|
5
5
|
Object.defineProperty(exports, "TARGET_SAMPLE_RATE", { enumerable: true, get: function () { return constants_1.TARGET_SAMPLE_RATE; } });
|
|
6
6
|
const draw_peaks_1 = require("./audio-waveform/draw-peaks");
|
|
@@ -20,6 +20,9 @@ Object.defineProperty(exports, "createWaveformPeakProcessor", { enumerable: true
|
|
|
20
20
|
Object.defineProperty(exports, "emitWaveformProgress", { enumerable: true, get: function () { return waveform_peak_processor_1.emitWaveformProgress; } });
|
|
21
21
|
const clamp_timestamps_to_duration_1 = require("./clamp-timestamps-to-duration");
|
|
22
22
|
Object.defineProperty(exports, "clampTimestampsToDuration", { enumerable: true, get: function () { return clamp_timestamps_to_duration_1.clampTimestampsToDuration; } });
|
|
23
|
+
const device_pixel_alignment_1 = require("./device-pixel-alignment");
|
|
24
|
+
Object.defineProperty(exports, "getDevicePixelAlignedCanvasLayout", { enumerable: true, get: function () { return device_pixel_alignment_1.getDevicePixelAlignedCanvasLayout; } });
|
|
25
|
+
Object.defineProperty(exports, "snapCanvasPositionToDevicePixel", { enumerable: true, get: function () { return device_pixel_alignment_1.snapCanvasPositionToDevicePixel; } });
|
|
23
26
|
const extract_frames_1 = require("./extract-frames");
|
|
24
27
|
Object.defineProperty(exports, "extractFrames", { enumerable: true, get: function () { return extract_frames_1.extractFrames; } });
|
|
25
28
|
const frame_database_1 = require("./frame-database");
|
|
@@ -7,7 +7,8 @@ export type RenderFrameStripToCanvasOptions = {
|
|
|
7
7
|
readonly width: number;
|
|
8
8
|
readonly frameHeight: number;
|
|
9
9
|
readonly devicePixelRatio: number;
|
|
10
|
+
readonly horizontalOffset: number;
|
|
10
11
|
readonly signal?: AbortSignal;
|
|
11
12
|
};
|
|
12
|
-
export declare const renderFrameStripToCanvas: ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, signal, }: RenderFrameStripToCanvasOptions) => Promise<void>;
|
|
13
|
+
export declare const renderFrameStripToCanvas: ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, horizontalOffset, signal, }: RenderFrameStripToCanvasOptions) => Promise<void>;
|
|
13
14
|
export {};
|
|
@@ -12,8 +12,9 @@ const getContext = (canvas) => {
|
|
|
12
12
|
}
|
|
13
13
|
return ctx;
|
|
14
14
|
};
|
|
15
|
-
const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, signal, }) => {
|
|
16
|
-
const
|
|
15
|
+
const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, width, frameHeight, devicePixelRatio, horizontalOffset, signal, }) => {
|
|
16
|
+
const drawingWidth = Math.max(1, width);
|
|
17
|
+
const naturalWidth = Math.max(1, Math.ceil(width + horizontalOffset));
|
|
17
18
|
const naturalHeight = Math.max(1, Math.ceil(frameHeight));
|
|
18
19
|
const segmentDuration = toSeconds - fromSeconds;
|
|
19
20
|
canvas.width = naturalWidth;
|
|
@@ -23,12 +24,13 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
23
24
|
}
|
|
24
25
|
const ctx = getContext(canvas);
|
|
25
26
|
ctx.clearRect(0, 0, naturalWidth, naturalHeight);
|
|
27
|
+
ctx.setTransform(1, 0, 0, 1, horizontalOffset, 0);
|
|
26
28
|
const filledSlots = new Map();
|
|
27
29
|
let aspectRatio = (0, frame_database_1.getAspectRatioFromCache)(src);
|
|
28
30
|
if (aspectRatio !== null) {
|
|
29
31
|
(0, render_frame_strip_1.ensureSlots)({
|
|
30
32
|
filledSlots,
|
|
31
|
-
naturalWidth,
|
|
33
|
+
naturalWidth: drawingWidth,
|
|
32
34
|
fromSeconds,
|
|
33
35
|
toSeconds,
|
|
34
36
|
aspectRatio,
|
|
@@ -36,13 +38,14 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
36
38
|
});
|
|
37
39
|
(0, render_frame_strip_1.fillWithCachedFrames)({
|
|
38
40
|
ctx,
|
|
39
|
-
naturalWidth,
|
|
41
|
+
naturalWidth: drawingWidth,
|
|
40
42
|
filledSlots,
|
|
41
43
|
src,
|
|
42
44
|
segmentDuration,
|
|
43
45
|
fromSeconds,
|
|
44
46
|
devicePixelRatio,
|
|
45
47
|
frameHeight,
|
|
48
|
+
horizontalOffset,
|
|
46
49
|
});
|
|
47
50
|
const hasUnfilledSlots = Array.from(filledSlots.values()).some((value) => value === undefined);
|
|
48
51
|
if (!hasUnfilledSlots) {
|
|
@@ -57,7 +60,7 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
57
60
|
frame_database_1.aspectRatioCache.set(src, aspectRatio);
|
|
58
61
|
(0, render_frame_strip_1.ensureSlots)({
|
|
59
62
|
filledSlots,
|
|
60
|
-
naturalWidth,
|
|
63
|
+
naturalWidth: drawingWidth,
|
|
61
64
|
fromSeconds,
|
|
62
65
|
toSeconds,
|
|
63
66
|
aspectRatio,
|
|
@@ -81,7 +84,7 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
81
84
|
}
|
|
82
85
|
(0, render_frame_strip_1.ensureSlots)({
|
|
83
86
|
filledSlots,
|
|
84
|
-
naturalWidth,
|
|
87
|
+
naturalWidth: drawingWidth,
|
|
85
88
|
fromSeconds,
|
|
86
89
|
toSeconds,
|
|
87
90
|
aspectRatio,
|
|
@@ -90,12 +93,13 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
90
93
|
(0, render_frame_strip_1.fillFrameWhereItFits)({
|
|
91
94
|
ctx,
|
|
92
95
|
filledSlots,
|
|
93
|
-
visualizationWidth:
|
|
96
|
+
visualizationWidth: drawingWidth,
|
|
94
97
|
frame: transformed,
|
|
95
98
|
segmentDuration,
|
|
96
99
|
fromSeconds,
|
|
97
100
|
devicePixelRatio,
|
|
98
101
|
frameHeight,
|
|
102
|
+
horizontalOffset,
|
|
99
103
|
});
|
|
100
104
|
}
|
|
101
105
|
catch (e) {
|
|
@@ -114,13 +118,14 @@ const renderFrameStripToCanvas = async ({ canvas, src, fromSeconds, toSeconds, w
|
|
|
114
118
|
}
|
|
115
119
|
(0, render_frame_strip_1.fillWithCachedFrames)({
|
|
116
120
|
ctx,
|
|
117
|
-
naturalWidth,
|
|
121
|
+
naturalWidth: drawingWidth,
|
|
118
122
|
filledSlots,
|
|
119
123
|
src,
|
|
120
124
|
segmentDuration,
|
|
121
125
|
fromSeconds,
|
|
122
126
|
devicePixelRatio,
|
|
123
127
|
frameHeight,
|
|
128
|
+
horizontalOffset,
|
|
124
129
|
});
|
|
125
130
|
};
|
|
126
131
|
exports.renderFrameStripToCanvas = renderFrameStripToCanvas;
|
|
@@ -20,7 +20,7 @@ export declare const ensureSlots: ({ filledSlots, naturalWidth, fromSeconds, toS
|
|
|
20
20
|
aspectRatio: number;
|
|
21
21
|
frameHeight: number;
|
|
22
22
|
}) => void;
|
|
23
|
-
export declare const drawSlot: ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }: {
|
|
23
|
+
export declare const drawSlot: ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }: {
|
|
24
24
|
frame: VideoFrame;
|
|
25
25
|
ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
|
|
26
26
|
filledSlots: Map<number, number | undefined>;
|
|
@@ -30,8 +30,9 @@ export declare const drawSlot: ({ frame, ctx, filledSlots, visualizationWidth, t
|
|
|
30
30
|
fromSeconds: number;
|
|
31
31
|
devicePixelRatio: number;
|
|
32
32
|
frameHeight: number;
|
|
33
|
+
horizontalOffset: number;
|
|
33
34
|
}) => void;
|
|
34
|
-
export declare const fillWithCachedFrames: ({ ctx, naturalWidth, filledSlots, src, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }: {
|
|
35
|
+
export declare const fillWithCachedFrames: ({ ctx, naturalWidth, filledSlots, src, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }: {
|
|
35
36
|
ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
|
|
36
37
|
naturalWidth: number;
|
|
37
38
|
filledSlots: Map<number, number | undefined>;
|
|
@@ -40,8 +41,9 @@ export declare const fillWithCachedFrames: ({ ctx, naturalWidth, filledSlots, sr
|
|
|
40
41
|
fromSeconds: number;
|
|
41
42
|
devicePixelRatio: number;
|
|
42
43
|
frameHeight: number;
|
|
44
|
+
horizontalOffset: number;
|
|
43
45
|
}) => void;
|
|
44
|
-
export declare const fillFrameWhereItFits: ({ frame, filledSlots, ctx, visualizationWidth, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }: {
|
|
46
|
+
export declare const fillFrameWhereItFits: ({ frame, filledSlots, ctx, visualizationWidth, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }: {
|
|
45
47
|
frame: VideoFrame;
|
|
46
48
|
filledSlots: Map<number, number | undefined>;
|
|
47
49
|
ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
|
|
@@ -50,4 +52,5 @@ export declare const fillFrameWhereItFits: ({ frame, filledSlots, ctx, visualiza
|
|
|
50
52
|
fromSeconds: number;
|
|
51
53
|
devicePixelRatio: number;
|
|
52
54
|
frameHeight: number;
|
|
55
|
+
horizontalOffset: number;
|
|
53
56
|
}) => void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fillFrameWhereItFits = exports.fillWithCachedFrames = exports.drawSlot = exports.ensureSlots = exports.calculateTimestampSlots = exports.getDurationOfOneFrame = exports.WEBCODECS_TIMESCALE = void 0;
|
|
4
|
+
const device_pixel_alignment_1 = require("./device-pixel-alignment");
|
|
4
5
|
const frame_database_1 = require("./frame-database");
|
|
5
6
|
exports.WEBCODECS_TIMESCALE = 1000000;
|
|
6
7
|
const MAX_TIME_DEVIATION = exports.WEBCODECS_TIMESCALE * 0.05;
|
|
@@ -50,7 +51,7 @@ const ensureSlots = ({ filledSlots, naturalWidth, fromSeconds, toSeconds, aspect
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
exports.ensureSlots = ensureSlots;
|
|
53
|
-
const drawSlot = ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }) => {
|
|
54
|
+
const drawSlot = ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }) => {
|
|
54
55
|
const durationOfOneFrame = (0, exports.getDurationOfOneFrame)({
|
|
55
56
|
visualizationWidth,
|
|
56
57
|
aspectRatio: frame.displayWidth / frame.displayHeight,
|
|
@@ -60,12 +61,23 @@ const drawSlot = ({ frame, ctx, filledSlots, visualizationWidth, timestamp, segm
|
|
|
60
61
|
const relativeTimestamp = timestamp - fromSeconds * exports.WEBCODECS_TIMESCALE;
|
|
61
62
|
const frameIndex = relativeTimestamp / durationOfOneFrame;
|
|
62
63
|
const thumbnailWidth = frame.displayWidth / devicePixelRatio;
|
|
63
|
-
const
|
|
64
|
-
|
|
64
|
+
const idealLeft = frameIndex * thumbnailWidth;
|
|
65
|
+
const idealRight = idealLeft + thumbnailWidth;
|
|
66
|
+
// When the canvas is aligned to the device-pixel grid, use the same absolute
|
|
67
|
+
// grid for both edges so adjacent thumbnails neither shimmer nor leave seams.
|
|
68
|
+
const left = (0, device_pixel_alignment_1.snapCanvasPositionToDevicePixel)({
|
|
69
|
+
horizontalOffset,
|
|
70
|
+
position: idealLeft,
|
|
71
|
+
});
|
|
72
|
+
const right = (0, device_pixel_alignment_1.snapCanvasPositionToDevicePixel)({
|
|
73
|
+
horizontalOffset,
|
|
74
|
+
position: idealRight,
|
|
75
|
+
});
|
|
76
|
+
ctx.drawImage(frame, left, 0, right - left, frame.displayHeight / devicePixelRatio);
|
|
65
77
|
filledSlots.set(timestamp, frame.timestamp);
|
|
66
78
|
};
|
|
67
79
|
exports.drawSlot = drawSlot;
|
|
68
|
-
const fillWithCachedFrames = ({ ctx, naturalWidth, filledSlots, src, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }) => {
|
|
80
|
+
const fillWithCachedFrames = ({ ctx, naturalWidth, filledSlots, src, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }) => {
|
|
69
81
|
const prefix = (0, frame_database_1.getFrameDatabaseKeyPrefix)(src);
|
|
70
82
|
const keys = Array.from(frame_database_1.frameDatabase.keys()).filter((k) => k.startsWith(prefix));
|
|
71
83
|
const targets = Array.from(filledSlots.keys());
|
|
@@ -103,11 +115,12 @@ const fillWithCachedFrames = ({ ctx, naturalWidth, filledSlots, src, segmentDura
|
|
|
103
115
|
fromSeconds,
|
|
104
116
|
devicePixelRatio,
|
|
105
117
|
frameHeight,
|
|
118
|
+
horizontalOffset,
|
|
106
119
|
});
|
|
107
120
|
}
|
|
108
121
|
};
|
|
109
122
|
exports.fillWithCachedFrames = fillWithCachedFrames;
|
|
110
|
-
const fillFrameWhereItFits = ({ frame, filledSlots, ctx, visualizationWidth, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, }) => {
|
|
123
|
+
const fillFrameWhereItFits = ({ frame, filledSlots, ctx, visualizationWidth, segmentDuration, fromSeconds, devicePixelRatio, frameHeight, horizontalOffset, }) => {
|
|
111
124
|
const slots = Array.from(filledSlots.keys());
|
|
112
125
|
for (let i = 0; i < slots.length; i++) {
|
|
113
126
|
const slot = slots[i];
|
|
@@ -129,6 +142,7 @@ const fillFrameWhereItFits = ({ frame, filledSlots, ctx, visualizationWidth, seg
|
|
|
129
142
|
fromSeconds,
|
|
130
143
|
devicePixelRatio,
|
|
131
144
|
frameHeight,
|
|
145
|
+
horizontalOffset,
|
|
132
146
|
});
|
|
133
147
|
}
|
|
134
148
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/timeline-utils"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/timeline-utils",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.531",
|
|
7
7
|
"description": "Internal utilities for rendering Remotion timelines",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"mediabunny": "1.56.1",
|
|
25
|
-
"remotion": "4.0.
|
|
25
|
+
"remotion": "4.0.531"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@mediabunny/server": "1.56.1",
|
|
29
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
29
|
+
"@remotion/eslint-config-internal": "4.0.531",
|
|
30
30
|
"eslint": "9.19.0",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
32
32
|
},
|