@technotoil/image-video-editor 0.1.1 → 0.1.2
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/README.md +17 -3
- package/android/src/main/java/com/technotoil/image_videoeditor/FrameGrabberModule.kt +2 -6
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaEditorModule.kt +75 -35
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaLibraryModule.kt +51 -35
- package/android/src/main/java/com/technotoil/image_videoeditor/RNVideoPreviewManager.kt +98 -117
- package/ios/RNMediaEditor.m +38 -7
- package/ios/RNMediaLibrary.m +19 -15
- package/ios/RNVideoPreviewManager.m +2 -0
- package/lib/commonjs/components/VideoEditor.js +100 -32
- package/lib/commonjs/components/VideoEditor.js.map +1 -1
- package/lib/commonjs/screens/CropScreen.js +5 -3
- package/lib/commonjs/screens/CropScreen.js.map +1 -1
- package/lib/commonjs/screens/EditorScreen.js +229 -44
- package/lib/commonjs/screens/EditorScreen.js.map +1 -1
- package/lib/commonjs/screens/PickScreen.js +214 -122
- package/lib/commonjs/screens/PickScreen.js.map +1 -1
- package/lib/module/components/VideoEditor.js +100 -33
- package/lib/module/components/VideoEditor.js.map +1 -1
- package/lib/module/screens/CropScreen.js +5 -3
- package/lib/module/screens/CropScreen.js.map +1 -1
- package/lib/module/screens/EditorScreen.js +229 -44
- package/lib/module/screens/EditorScreen.js.map +1 -1
- package/lib/module/screens/PickScreen.js +215 -123
- package/lib/module/screens/PickScreen.js.map +1 -1
- package/lib/typescript/src/components/VideoEditor.d.ts +10 -2
- package/lib/typescript/src/screens/CropScreen.d.ts +2 -1
- package/lib/typescript/src/screens/EditorScreen.d.ts +2 -1
- package/lib/typescript/src/screens/PickScreen.d.ts +4 -1
- package/lib/typescript/src/types.d.ts +1 -0
- package/package.json +4 -1
- package/src/components/VideoEditor.tsx +68 -11
- package/src/screens/CropScreen.tsx +8 -3
- package/src/screens/EditorScreen.tsx +227 -61
- package/src/screens/PickScreen.tsx +197 -119
- package/src/types.ts +1 -0
|
@@ -174,12 +174,16 @@ function EditorScreen({
|
|
|
174
174
|
onBack,
|
|
175
175
|
onSaved,
|
|
176
176
|
onOpenCrop,
|
|
177
|
-
musicList
|
|
177
|
+
musicList,
|
|
178
|
+
maxVideoDurationMs
|
|
178
179
|
}) {
|
|
179
180
|
const [activeIndex, setActiveIndex] = (0, _react.useState)(initialIndex);
|
|
180
181
|
const currentItem = items[activeIndex] || items[0];
|
|
181
182
|
const item = currentItem; // Aliasing to 'item' for ease of compatibility
|
|
182
183
|
|
|
184
|
+
(0, _react.useEffect)(() => {
|
|
185
|
+
setActiveIndex(initialIndex);
|
|
186
|
+
}, [initialIndex]);
|
|
183
187
|
const [activeFilter, setActiveFilter] = (0, _react.useState)('none');
|
|
184
188
|
const [imageOptions, setImageOptions] = (0, _react.useState)({
|
|
185
189
|
rotateDegrees: 0,
|
|
@@ -190,13 +194,21 @@ function EditorScreen({
|
|
|
190
194
|
saturation: 1,
|
|
191
195
|
grayscale: false
|
|
192
196
|
});
|
|
197
|
+
const [panel, setPanel] = (0, _react.useState)(item.type === 'video' ? 'trim' : 'filter');
|
|
193
198
|
const [trimStart, setTrimStart] = (0, _react.useState)(0);
|
|
194
|
-
const [trimEnd, setTrimEnd] = (0, _react.useState)(
|
|
199
|
+
const [trimEnd, setTrimEnd] = (0, _react.useState)(() => {
|
|
200
|
+
const end = item.durationMs || 10000;
|
|
201
|
+
return maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end;
|
|
202
|
+
});
|
|
195
203
|
(0, _react.useEffect)(() => {
|
|
196
204
|
setTrimStart(0);
|
|
197
|
-
|
|
205
|
+
const end = item.durationMs || maxVideoDurationMs || 10000;
|
|
206
|
+
setTrimEnd(maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end);
|
|
198
207
|
setThumbnails([]);
|
|
199
|
-
|
|
208
|
+
if (item.type === 'video' && maxVideoDurationMs && end > maxVideoDurationMs) {
|
|
209
|
+
setPanel('trim');
|
|
210
|
+
}
|
|
211
|
+
}, [item.id, item.durationMs, maxVideoDurationMs]);
|
|
200
212
|
const [editsHistory, setEditsHistory] = (0, _react.useState)({});
|
|
201
213
|
const editsHistoryRef = (0, _react.useRef)({});
|
|
202
214
|
const [dimensionsMap, setDimensionsMap] = (0, _react.useState)({});
|
|
@@ -253,7 +265,7 @@ function EditorScreen({
|
|
|
253
265
|
setCropOffset(saved.cropOffset);
|
|
254
266
|
setZoomScale(saved.zoomScale);
|
|
255
267
|
setStraightenAngle(saved.straightenAngle);
|
|
256
|
-
setIsMuted(saved.isMuted);
|
|
268
|
+
setIsMuted(selectedMusic ? true : saved.isMuted);
|
|
257
269
|
} else {
|
|
258
270
|
setActiveFilter('none');
|
|
259
271
|
setImageOptions({
|
|
@@ -266,7 +278,8 @@ function EditorScreen({
|
|
|
266
278
|
grayscale: false
|
|
267
279
|
});
|
|
268
280
|
setTrimStart(0);
|
|
269
|
-
|
|
281
|
+
const end = targetItem.durationMs || 10000;
|
|
282
|
+
setTrimEnd(maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end);
|
|
270
283
|
setOverlays([]);
|
|
271
284
|
setCropRatio(null);
|
|
272
285
|
setCropOffset({
|
|
@@ -275,7 +288,14 @@ function EditorScreen({
|
|
|
275
288
|
});
|
|
276
289
|
setZoomScale(1);
|
|
277
290
|
setStraightenAngle(0);
|
|
278
|
-
setIsMuted(false);
|
|
291
|
+
setIsMuted(selectedMusic ? true : false);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Force trim panel if video is too long
|
|
295
|
+
if (targetItem.type === 'video' && maxVideoDurationMs && targetItem.durationMs && targetItem.durationMs > maxVideoDurationMs) {
|
|
296
|
+
setPanel('trim');
|
|
297
|
+
} else if (!saved) {
|
|
298
|
+
setPanel(targetItem.type === 'video' ? 'trim' : 'filter');
|
|
279
299
|
}
|
|
280
300
|
};
|
|
281
301
|
const handleScrollEnd = e => {
|
|
@@ -334,13 +354,13 @@ function EditorScreen({
|
|
|
334
354
|
y: (o.y + 8) * renderScale,
|
|
335
355
|
color: o.color,
|
|
336
356
|
fontSize: o.fontSize * renderScale
|
|
337
|
-
}))
|
|
357
|
+
})),
|
|
358
|
+
frameUri: edits.imageOptions.frame && FRAME_IMAGES[edits.imageOptions.frame] ? _reactNative.Image.resolveAssetSource(FRAME_IMAGES[edits.imageOptions.frame]).uri : undefined
|
|
338
359
|
};
|
|
339
360
|
};
|
|
340
361
|
const [saving, setSaving] = (0, _react.useState)(false);
|
|
341
362
|
const [videoPaused, setVideoPaused] = (0, _react.useState)(false);
|
|
342
|
-
const
|
|
343
|
-
const resolvedMusicList = musicList || DUMMY_MUSIC_LIST;
|
|
363
|
+
const resolvedMusicList = musicList || [];
|
|
344
364
|
const [selectedMusic, setSelectedMusic] = (0, _react.useState)(null);
|
|
345
365
|
const [musicPaused, setMusicPaused] = (0, _react.useState)(false);
|
|
346
366
|
const [showMusicModal, setShowMusicModal] = (0, _react.useState)(false);
|
|
@@ -1119,13 +1139,18 @@ function EditorScreen({
|
|
|
1119
1139
|
y: (o.y + 8) * renderScale,
|
|
1120
1140
|
color: o.color,
|
|
1121
1141
|
fontSize: o.fontSize * renderScale
|
|
1122
|
-
}))
|
|
1142
|
+
})),
|
|
1143
|
+
frameUri: imageOptions.frame && FRAME_IMAGES[imageOptions.frame] ? _reactNative.Image.resolveAssetSource(FRAME_IMAGES[imageOptions.frame]).uri : undefined
|
|
1123
1144
|
};
|
|
1124
1145
|
}, [imageOptions, cropOffset, maxPan, dimensions, cropRatio, straightenAngle, overlays]);
|
|
1125
1146
|
|
|
1126
1147
|
// For visual trim
|
|
1127
1148
|
|
|
1128
1149
|
const duration = item.durationMs ?? 10_000;
|
|
1150
|
+
const durationRef = (0, _react.useRef)(duration);
|
|
1151
|
+
(0, _react.useEffect)(() => {
|
|
1152
|
+
durationRef.current = duration;
|
|
1153
|
+
}, [duration]);
|
|
1129
1154
|
const formatTime = ms => {
|
|
1130
1155
|
const totalSec = Math.floor(ms / 1000);
|
|
1131
1156
|
const mins = Math.floor(totalSec / 60);
|
|
@@ -1187,6 +1212,39 @@ function EditorScreen({
|
|
|
1187
1212
|
generateThumbs();
|
|
1188
1213
|
}
|
|
1189
1214
|
}, [item.type, item.uri, duration, thumbnails.length]);
|
|
1215
|
+
const leftOverlayRef = (0, _react.useRef)(null);
|
|
1216
|
+
const rightOverlayRef = (0, _react.useRef)(null);
|
|
1217
|
+
const selectionRangeRef = (0, _react.useRef)(null);
|
|
1218
|
+
const leftHandleRef = (0, _react.useRef)(null);
|
|
1219
|
+
const rightHandleRef = (0, _react.useRef)(null);
|
|
1220
|
+
const updateNativeRefs = (newStartX, newEndX) => {
|
|
1221
|
+
leftOverlayRef.current?.setNativeProps({
|
|
1222
|
+
style: {
|
|
1223
|
+
width: newStartX
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
rightOverlayRef.current?.setNativeProps({
|
|
1227
|
+
style: {
|
|
1228
|
+
left: newEndX
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
selectionRangeRef.current?.setNativeProps({
|
|
1232
|
+
style: {
|
|
1233
|
+
left: newStartX,
|
|
1234
|
+
width: newEndX - newStartX
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
leftHandleRef.current?.setNativeProps({
|
|
1238
|
+
style: {
|
|
1239
|
+
left: newStartX - 16
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
rightHandleRef.current?.setNativeProps({
|
|
1243
|
+
style: {
|
|
1244
|
+
left: newEndX - 16
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
};
|
|
1190
1248
|
const startPanOffset = (0, _react.useRef)(0);
|
|
1191
1249
|
const startPan = (0, _react.useRef)(_reactNative.PanResponder.create({
|
|
1192
1250
|
onStartShouldSetPanResponder: () => true,
|
|
@@ -1200,20 +1258,76 @@ function EditorScreen({
|
|
|
1200
1258
|
setScrollEnabled(false);
|
|
1201
1259
|
},
|
|
1202
1260
|
onPanResponderMove: (_, gesture) => {
|
|
1203
|
-
|
|
1261
|
+
let newX = Math.max(0, Math.min(endX.current - 32, startPanOffset.current + gesture.dx));
|
|
1262
|
+
let newTime = newX / TIMELINE_WIDTH * durationRef.current;
|
|
1263
|
+
const currentTrimEnd = endX.current / TIMELINE_WIDTH * durationRef.current;
|
|
1264
|
+
if (maxVideoDurationMs && currentTrimEnd - newTime > maxVideoDurationMs) {
|
|
1265
|
+
newTime = currentTrimEnd - maxVideoDurationMs;
|
|
1266
|
+
newX = newTime / durationRef.current * TIMELINE_WIDTH;
|
|
1267
|
+
}
|
|
1204
1268
|
startX.current = newX;
|
|
1205
|
-
|
|
1206
|
-
setTrimStart(newTime);
|
|
1269
|
+
updateNativeRefs(newX, endX.current);
|
|
1207
1270
|
throttledSeek(newTime);
|
|
1208
1271
|
},
|
|
1209
1272
|
onPanResponderRelease: () => {
|
|
1210
1273
|
isDraggingHandle.current = false;
|
|
1211
1274
|
setScrollEnabled(true);
|
|
1275
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1276
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1212
1277
|
setSeekToMs(-1);
|
|
1213
1278
|
},
|
|
1214
1279
|
onPanResponderTerminate: () => {
|
|
1215
1280
|
isDraggingHandle.current = false;
|
|
1216
1281
|
setScrollEnabled(true);
|
|
1282
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1283
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1284
|
+
setSeekToMs(-1);
|
|
1285
|
+
}
|
|
1286
|
+
})).current;
|
|
1287
|
+
const middlePanOffsetStart = (0, _react.useRef)(0);
|
|
1288
|
+
const middlePanOffsetEnd = (0, _react.useRef)(0);
|
|
1289
|
+
const middlePan = (0, _react.useRef)(_reactNative.PanResponder.create({
|
|
1290
|
+
onStartShouldSetPanResponder: () => true,
|
|
1291
|
+
onStartShouldSetPanResponderCapture: () => true,
|
|
1292
|
+
onMoveShouldSetPanResponder: () => true,
|
|
1293
|
+
onMoveShouldSetPanResponderCapture: () => true,
|
|
1294
|
+
onPanResponderGrant: () => {
|
|
1295
|
+
pushToHistory();
|
|
1296
|
+
middlePanOffsetStart.current = startX.current;
|
|
1297
|
+
middlePanOffsetEnd.current = endX.current;
|
|
1298
|
+
isDraggingHandle.current = true;
|
|
1299
|
+
setScrollEnabled(false);
|
|
1300
|
+
},
|
|
1301
|
+
onPanResponderMove: (_, gesture) => {
|
|
1302
|
+
const windowWidth = middlePanOffsetEnd.current - middlePanOffsetStart.current;
|
|
1303
|
+
let newStartX = middlePanOffsetStart.current + gesture.dx;
|
|
1304
|
+
let newEndX = middlePanOffsetEnd.current + gesture.dx;
|
|
1305
|
+
if (newStartX < 0) {
|
|
1306
|
+
newStartX = 0;
|
|
1307
|
+
newEndX = windowWidth;
|
|
1308
|
+
}
|
|
1309
|
+
if (newEndX > TIMELINE_WIDTH) {
|
|
1310
|
+
newEndX = TIMELINE_WIDTH;
|
|
1311
|
+
newStartX = TIMELINE_WIDTH - windowWidth;
|
|
1312
|
+
}
|
|
1313
|
+
startX.current = newStartX;
|
|
1314
|
+
endX.current = newEndX;
|
|
1315
|
+
const newStartTime = newStartX / TIMELINE_WIDTH * durationRef.current;
|
|
1316
|
+
updateNativeRefs(newStartX, newEndX);
|
|
1317
|
+
throttledSeek(newStartTime);
|
|
1318
|
+
},
|
|
1319
|
+
onPanResponderRelease: () => {
|
|
1320
|
+
isDraggingHandle.current = false;
|
|
1321
|
+
setScrollEnabled(true);
|
|
1322
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1323
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1324
|
+
setSeekToMs(-1);
|
|
1325
|
+
},
|
|
1326
|
+
onPanResponderTerminate: () => {
|
|
1327
|
+
isDraggingHandle.current = false;
|
|
1328
|
+
setScrollEnabled(true);
|
|
1329
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1330
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1217
1331
|
setSeekToMs(-1);
|
|
1218
1332
|
}
|
|
1219
1333
|
})).current;
|
|
@@ -1230,20 +1344,29 @@ function EditorScreen({
|
|
|
1230
1344
|
setScrollEnabled(false);
|
|
1231
1345
|
},
|
|
1232
1346
|
onPanResponderMove: (_, gesture) => {
|
|
1233
|
-
|
|
1347
|
+
let newX = Math.min(TIMELINE_WIDTH, Math.max(startX.current + 32, endPanOffset.current + gesture.dx));
|
|
1348
|
+
let newTime = newX / TIMELINE_WIDTH * durationRef.current;
|
|
1349
|
+
const currentTrimStart = startX.current / TIMELINE_WIDTH * durationRef.current;
|
|
1350
|
+
if (maxVideoDurationMs && newTime - currentTrimStart > maxVideoDurationMs) {
|
|
1351
|
+
newTime = currentTrimStart + maxVideoDurationMs;
|
|
1352
|
+
newX = newTime / durationRef.current * TIMELINE_WIDTH;
|
|
1353
|
+
}
|
|
1234
1354
|
endX.current = newX;
|
|
1235
|
-
|
|
1236
|
-
setTrimEnd(newTime);
|
|
1355
|
+
updateNativeRefs(startX.current, newX);
|
|
1237
1356
|
throttledSeek(newTime);
|
|
1238
1357
|
},
|
|
1239
1358
|
onPanResponderRelease: () => {
|
|
1240
1359
|
isDraggingHandle.current = false;
|
|
1241
1360
|
setScrollEnabled(true);
|
|
1361
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1362
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1242
1363
|
setSeekToMs(-1);
|
|
1243
1364
|
},
|
|
1244
1365
|
onPanResponderTerminate: () => {
|
|
1245
1366
|
isDraggingHandle.current = false;
|
|
1246
1367
|
setScrollEnabled(true);
|
|
1368
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1369
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1247
1370
|
setSeekToMs(-1);
|
|
1248
1371
|
}
|
|
1249
1372
|
})).current;
|
|
@@ -2018,11 +2141,16 @@ function EditorScreen({
|
|
|
2018
2141
|
});
|
|
2019
2142
|
}
|
|
2020
2143
|
} else {
|
|
2144
|
+
const safeEndMs = Math.min(trimEnd, item.durationMs || 10000);
|
|
2145
|
+
const safeStartMs = Math.min(trimStart, Math.max(0, safeEndMs - 100));
|
|
2146
|
+
const isFullTrim = trimStart === 0 && trimEnd >= (item.durationMs || 10000);
|
|
2021
2147
|
exportUri = await (0, _MediaEditor.trimVideo)(item.uri, {
|
|
2022
|
-
startMs:
|
|
2023
|
-
endMs:
|
|
2148
|
+
startMs: safeStartMs,
|
|
2149
|
+
endMs: safeEndMs,
|
|
2024
2150
|
mute: isMuted,
|
|
2025
|
-
|
|
2151
|
+
...(selectedMusic?.url ? {
|
|
2152
|
+
musicUri: selectedMusic.url
|
|
2153
|
+
} : {}),
|
|
2026
2154
|
...activeOptions
|
|
2027
2155
|
});
|
|
2028
2156
|
}
|
|
@@ -2039,6 +2167,7 @@ function EditorScreen({
|
|
|
2039
2167
|
setSaving(true);
|
|
2040
2168
|
saveEditsForIndex(activeIndex);
|
|
2041
2169
|
const updatedItems = [...items];
|
|
2170
|
+
let cumulativeMusicOffsetMs = 0;
|
|
2042
2171
|
for (let i = 0; i < items.length; i++) {
|
|
2043
2172
|
const targetItem = items[i];
|
|
2044
2173
|
let edits = editsHistoryRef.current[targetItem.id];
|
|
@@ -2064,6 +2193,7 @@ function EditorScreen({
|
|
|
2064
2193
|
outUri = await (0, _MediaEditor.trimVideo)(outUri, {
|
|
2065
2194
|
isImage: true,
|
|
2066
2195
|
musicUri: selectedMusic.url,
|
|
2196
|
+
musicOffsetMs: cumulativeMusicOffsetMs,
|
|
2067
2197
|
rotateDegrees: 0,
|
|
2068
2198
|
flipX: false,
|
|
2069
2199
|
flipY: false,
|
|
@@ -2078,12 +2208,20 @@ function EditorScreen({
|
|
|
2078
2208
|
uri: outUri,
|
|
2079
2209
|
thumbnailUri: outUri
|
|
2080
2210
|
};
|
|
2211
|
+
cumulativeMusicOffsetMs += 10000; // Images are 10s by default
|
|
2081
2212
|
} else {
|
|
2213
|
+
const originalDuration = targetItem.durationMs || maxVideoDurationMs || 10000;
|
|
2214
|
+
const safeEndMs = Math.min(edits.trimEnd, originalDuration);
|
|
2215
|
+
const safeStartMs = Math.min(edits.trimStart, Math.max(0, safeEndMs - 100));
|
|
2216
|
+
const isFullTrim = edits.trimStart === 0 && edits.trimEnd >= originalDuration;
|
|
2082
2217
|
const outUri = await (0, _MediaEditor.trimVideo)(targetItem.uri, {
|
|
2083
|
-
startMs:
|
|
2084
|
-
endMs:
|
|
2218
|
+
startMs: safeStartMs,
|
|
2219
|
+
endMs: safeEndMs,
|
|
2085
2220
|
mute: edits.isMuted,
|
|
2086
|
-
|
|
2221
|
+
...(selectedMusic?.url ? {
|
|
2222
|
+
musicUri: selectedMusic.url,
|
|
2223
|
+
musicOffsetMs: cumulativeMusicOffsetMs
|
|
2224
|
+
} : {}),
|
|
2087
2225
|
...opts
|
|
2088
2226
|
});
|
|
2089
2227
|
let newThumb = undefined;
|
|
@@ -2101,6 +2239,7 @@ function EditorScreen({
|
|
|
2101
2239
|
thumbnailUri: newThumb ? newThumb : targetItem.thumbnailUri,
|
|
2102
2240
|
durationMs: newDuration
|
|
2103
2241
|
};
|
|
2242
|
+
cumulativeMusicOffsetMs += newDuration;
|
|
2104
2243
|
}
|
|
2105
2244
|
} else {
|
|
2106
2245
|
if (selectedMusic) {
|
|
@@ -2109,6 +2248,7 @@ function EditorScreen({
|
|
|
2109
2248
|
outUri = await (0, _MediaEditor.trimVideo)(targetItem.uri, {
|
|
2110
2249
|
isImage: true,
|
|
2111
2250
|
musicUri: selectedMusic.url,
|
|
2251
|
+
musicOffsetMs: cumulativeMusicOffsetMs,
|
|
2112
2252
|
rotateDegrees: 0,
|
|
2113
2253
|
flipX: false,
|
|
2114
2254
|
flipY: false,
|
|
@@ -2117,12 +2257,17 @@ function EditorScreen({
|
|
|
2117
2257
|
saturation: 1,
|
|
2118
2258
|
grayscale: false
|
|
2119
2259
|
});
|
|
2260
|
+
cumulativeMusicOffsetMs += 10000;
|
|
2120
2261
|
} else {
|
|
2262
|
+
const safeEndMs = targetItem.durationMs || 10000;
|
|
2121
2263
|
outUri = await (0, _MediaEditor.trimVideo)(targetItem.uri, {
|
|
2122
2264
|
startMs: 0,
|
|
2123
|
-
endMs:
|
|
2265
|
+
endMs: safeEndMs,
|
|
2124
2266
|
mute: isMuted,
|
|
2125
|
-
|
|
2267
|
+
...(selectedMusic?.url ? {
|
|
2268
|
+
musicUri: selectedMusic.url,
|
|
2269
|
+
musicOffsetMs: cumulativeMusicOffsetMs
|
|
2270
|
+
} : {}),
|
|
2126
2271
|
rotateDegrees: 0,
|
|
2127
2272
|
flipX: false,
|
|
2128
2273
|
flipY: false,
|
|
@@ -2131,6 +2276,7 @@ function EditorScreen({
|
|
|
2131
2276
|
saturation: 1,
|
|
2132
2277
|
grayscale: false
|
|
2133
2278
|
});
|
|
2279
|
+
cumulativeMusicOffsetMs += safeEndMs;
|
|
2134
2280
|
}
|
|
2135
2281
|
updatedItems[i] = {
|
|
2136
2282
|
...targetItem,
|
|
@@ -2672,7 +2818,15 @@ function EditorScreen({
|
|
|
2672
2818
|
},
|
|
2673
2819
|
style: styles.filmstripImage
|
|
2674
2820
|
}, idx)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2675
|
-
style: styles.timelineOverlay
|
|
2821
|
+
style: [styles.timelineOverlay, {
|
|
2822
|
+
left: 0,
|
|
2823
|
+
width: trimStart / duration * TIMELINE_WIDTH
|
|
2824
|
+
}]
|
|
2825
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2826
|
+
style: [styles.timelineOverlay, {
|
|
2827
|
+
left: trimEnd / duration * TIMELINE_WIDTH,
|
|
2828
|
+
right: 0
|
|
2829
|
+
}]
|
|
2676
2830
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2677
2831
|
style: [styles.selectionRange, {
|
|
2678
2832
|
left: trimStart / duration * TIMELINE_WIDTH,
|
|
@@ -2683,20 +2837,18 @@ function EditorScreen({
|
|
|
2683
2837
|
style: [styles.customHandle, styles.customHandleLeft, {
|
|
2684
2838
|
left: trimStart / duration * TIMELINE_WIDTH - 16
|
|
2685
2839
|
}],
|
|
2686
|
-
...startPan.panHandlers,
|
|
2687
2840
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2688
2841
|
style: styles.handleBarLine
|
|
2689
2842
|
})
|
|
2690
2843
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2691
2844
|
style: [styles.customHandle, styles.customHandleRight, {
|
|
2692
|
-
left: trimEnd / duration * TIMELINE_WIDTH
|
|
2845
|
+
left: trimEnd / duration * TIMELINE_WIDTH - 16
|
|
2693
2846
|
}],
|
|
2694
|
-
...endPan.panHandlers,
|
|
2695
2847
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
2696
2848
|
style: styles.handleBarLine
|
|
2697
2849
|
})
|
|
2698
2850
|
})]
|
|
2699
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
2851
|
+
}), resolvedMusicList.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
2700
2852
|
style: styles.subTrackRow,
|
|
2701
2853
|
onPress: () => setShowMusicModal(true),
|
|
2702
2854
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -2977,7 +3129,7 @@ function EditorScreen({
|
|
|
2977
3129
|
style: styles.toolLabel,
|
|
2978
3130
|
children: "Text"
|
|
2979
3131
|
})]
|
|
2980
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
3132
|
+
}), resolvedMusicList.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
2981
3133
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
2982
3134
|
onPress: () => setShowMusicModal(true),
|
|
2983
3135
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -3139,7 +3291,7 @@ function EditorScreen({
|
|
|
3139
3291
|
contentContainerStyle: [styles.toolButtonsRow, {
|
|
3140
3292
|
flexGrow: 1
|
|
3141
3293
|
}],
|
|
3142
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
3294
|
+
children: [resolvedMusicList.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
3143
3295
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
3144
3296
|
onPress: () => setShowMusicModal(true),
|
|
3145
3297
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -3308,6 +3460,12 @@ function EditorScreen({
|
|
|
3308
3460
|
data: items,
|
|
3309
3461
|
extraData: [overlays, editingTextId, editsHistory, activeFilter, imageOptions, trimStart, trimEnd, isMuted, activeIndex],
|
|
3310
3462
|
keyExtractor: it => it.id,
|
|
3463
|
+
initialScrollIndex: activeIndex,
|
|
3464
|
+
getItemLayout: (_, index) => ({
|
|
3465
|
+
length: SCREEN_WIDTH,
|
|
3466
|
+
offset: SCREEN_WIDTH * index,
|
|
3467
|
+
index
|
|
3468
|
+
}),
|
|
3311
3469
|
horizontal: true,
|
|
3312
3470
|
pagingEnabled: false,
|
|
3313
3471
|
showsHorizontalScrollIndicator: false,
|
|
@@ -3418,7 +3576,7 @@ function EditorScreen({
|
|
|
3418
3576
|
fontWeight: '700'
|
|
3419
3577
|
},
|
|
3420
3578
|
children: (() => {
|
|
3421
|
-
const selMs =
|
|
3579
|
+
const selMs = (endX.current - startX.current) / TIMELINE_WIDTH * duration;
|
|
3422
3580
|
const totalSec = Math.floor(selMs / 1000);
|
|
3423
3581
|
return totalSec >= 60 ? `${Math.floor(totalSec / 60)}:${(totalSec % 60).toString().padStart(2, '0')} selected` : `${(selMs / 1000).toFixed(1)}s selected`;
|
|
3424
3582
|
})()
|
|
@@ -3429,30 +3587,54 @@ function EditorScreen({
|
|
|
3429
3587
|
}],
|
|
3430
3588
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
3431
3589
|
style: styles.filmstrip,
|
|
3432
|
-
children: [thumbnails.
|
|
3590
|
+
children: [thumbnails.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3591
|
+
style: {
|
|
3592
|
+
flex: 1,
|
|
3593
|
+
justifyContent: 'center',
|
|
3594
|
+
alignItems: 'center'
|
|
3595
|
+
},
|
|
3596
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
3597
|
+
color: "#ffffff",
|
|
3598
|
+
size: "small"
|
|
3599
|
+
})
|
|
3600
|
+
}) : thumbnails.map((uri, idx) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Image, {
|
|
3433
3601
|
source: {
|
|
3434
3602
|
uri
|
|
3435
3603
|
},
|
|
3436
3604
|
style: styles.filmstripImage
|
|
3437
3605
|
}, idx)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3438
|
-
|
|
3606
|
+
ref: leftOverlayRef,
|
|
3607
|
+
style: [styles.timelineOverlay, {
|
|
3608
|
+
left: 0,
|
|
3609
|
+
width: startX.current
|
|
3610
|
+
}]
|
|
3439
3611
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3612
|
+
ref: rightOverlayRef,
|
|
3613
|
+
style: [styles.timelineOverlay, {
|
|
3614
|
+
left: endX.current,
|
|
3615
|
+
right: 0
|
|
3443
3616
|
}]
|
|
3617
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3618
|
+
ref: selectionRangeRef,
|
|
3619
|
+
style: [styles.selectionRange, {
|
|
3620
|
+
left: startX.current,
|
|
3621
|
+
width: endX.current - startX.current
|
|
3622
|
+
}],
|
|
3623
|
+
...middlePan.panHandlers
|
|
3444
3624
|
})]
|
|
3445
3625
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3626
|
+
ref: leftHandleRef,
|
|
3446
3627
|
style: [styles.customHandle, styles.customHandleLeft, {
|
|
3447
|
-
left:
|
|
3628
|
+
left: startX.current - 16
|
|
3448
3629
|
}],
|
|
3449
3630
|
...startPan.panHandlers,
|
|
3450
3631
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3451
3632
|
style: styles.handleBarLine
|
|
3452
3633
|
})
|
|
3453
3634
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
3635
|
+
ref: rightHandleRef,
|
|
3454
3636
|
style: [styles.customHandle, styles.customHandleRight, {
|
|
3455
|
-
left:
|
|
3637
|
+
left: endX.current - 16
|
|
3456
3638
|
}],
|
|
3457
3639
|
...endPan.panHandlers,
|
|
3458
3640
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -3730,7 +3912,7 @@ function EditorScreen({
|
|
|
3730
3912
|
contentContainerStyle: [styles.toolButtonsRow, {
|
|
3731
3913
|
flexGrow: 1
|
|
3732
3914
|
}],
|
|
3733
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
3915
|
+
children: [resolvedMusicList.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
3734
3916
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
3735
3917
|
onPress: () => setShowMusicModal(true),
|
|
3736
3918
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -4038,6 +4220,7 @@ function EditorScreen({
|
|
|
4038
4220
|
onPress: () => {
|
|
4039
4221
|
setSelectedMusic(null);
|
|
4040
4222
|
setMusicPaused(true);
|
|
4223
|
+
setIsMuted(false);
|
|
4041
4224
|
},
|
|
4042
4225
|
style: styles.musicFooterRemoveBtn,
|
|
4043
4226
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -4805,7 +4988,9 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
4805
4988
|
height: 60
|
|
4806
4989
|
},
|
|
4807
4990
|
timelineOverlay: {
|
|
4808
|
-
|
|
4991
|
+
position: 'absolute',
|
|
4992
|
+
top: 0,
|
|
4993
|
+
bottom: 0,
|
|
4809
4994
|
backgroundColor: 'rgba(0,0,0,0.6)'
|
|
4810
4995
|
},
|
|
4811
4996
|
selectionRange: {
|
|
@@ -4815,7 +5000,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
4815
5000
|
backgroundColor: 'transparent',
|
|
4816
5001
|
borderTopWidth: 2,
|
|
4817
5002
|
borderBottomWidth: 2,
|
|
4818
|
-
borderColor: '#
|
|
5003
|
+
borderColor: '#FFD60A'
|
|
4819
5004
|
},
|
|
4820
5005
|
handle: {
|
|
4821
5006
|
position: 'absolute',
|
|
@@ -4841,7 +5026,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
4841
5026
|
top: 0,
|
|
4842
5027
|
width: 16,
|
|
4843
5028
|
height: 60,
|
|
4844
|
-
backgroundColor: '#
|
|
5029
|
+
backgroundColor: '#FFD60A',
|
|
4845
5030
|
justifyContent: 'center',
|
|
4846
5031
|
alignItems: 'center',
|
|
4847
5032
|
zIndex: 20
|