@technotoil/image-video-editor 0.1.1 → 0.1.3
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 +103 -37
- 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/Icon.js +31 -0
- package/lib/commonjs/components/Icon.js.map +1 -0
- package/lib/commonjs/components/VideoEditor.js +97 -32
- package/lib/commonjs/components/VideoEditor.js.map +1 -1
- package/lib/commonjs/icons.js +35 -0
- package/lib/commonjs/icons.js.map +1 -0
- package/lib/commonjs/screens/CropScreen.js +5 -3
- package/lib/commonjs/screens/CropScreen.js.map +1 -1
- package/lib/commonjs/screens/EditorScreen.js +269 -84
- package/lib/commonjs/screens/EditorScreen.js.map +1 -1
- package/lib/commonjs/screens/PickScreen.js +221 -129
- package/lib/commonjs/screens/PickScreen.js.map +1 -1
- package/lib/module/components/Icon.js +25 -0
- package/lib/module/components/Icon.js.map +1 -0
- package/lib/module/components/VideoEditor.js +98 -33
- package/lib/module/components/VideoEditor.js.map +1 -1
- package/lib/module/icons.js +31 -0
- package/lib/module/icons.js.map +1 -0
- package/lib/module/screens/CropScreen.js +5 -3
- package/lib/module/screens/CropScreen.js.map +1 -1
- package/lib/module/screens/EditorScreen.js +230 -45
- package/lib/module/screens/EditorScreen.js.map +1 -1
- package/lib/module/screens/PickScreen.js +216 -124
- package/lib/module/screens/PickScreen.js.map +1 -1
- package/lib/typescript/src/components/Icon.d.ts +11 -0
- package/lib/typescript/src/components/VideoEditor.d.ts +10 -2
- package/lib/typescript/src/icons.d.ts +28 -0
- 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 -4
- package/src/components/Icon.tsx +19 -0
- package/src/components/VideoEditor.tsx +67 -11
- package/src/icons.ts +28 -0
- package/src/screens/CropScreen.tsx +8 -3
- package/src/screens/EditorScreen.tsx +228 -62
- package/src/screens/PickScreen.tsx +198 -120
- package/src/types.ts +1 -0
|
@@ -7,7 +7,7 @@ import { captureFrame } from "../native/FrameGrabber.js";
|
|
|
7
7
|
import { saveToGallery } from "../native/MediaLibrary.js";
|
|
8
8
|
import { VideoPreview } from "../native/VideoPreview.js";
|
|
9
9
|
import Video from 'react-native-video';
|
|
10
|
-
import Ionicons from
|
|
10
|
+
import { Icon as Ionicons } from "../components/Icon.js";
|
|
11
11
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
12
12
|
const SCREEN_WIDTH = Dimensions.get('window').width;
|
|
13
13
|
const SCREEN_HEIGHT = Dimensions.get('window').height;
|
|
@@ -168,12 +168,16 @@ export function EditorScreen({
|
|
|
168
168
|
onBack,
|
|
169
169
|
onSaved,
|
|
170
170
|
onOpenCrop,
|
|
171
|
-
musicList
|
|
171
|
+
musicList,
|
|
172
|
+
maxVideoDurationMs
|
|
172
173
|
}) {
|
|
173
174
|
const [activeIndex, setActiveIndex] = useState(initialIndex);
|
|
174
175
|
const currentItem = items[activeIndex] || items[0];
|
|
175
176
|
const item = currentItem; // Aliasing to 'item' for ease of compatibility
|
|
176
177
|
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
setActiveIndex(initialIndex);
|
|
180
|
+
}, [initialIndex]);
|
|
177
181
|
const [activeFilter, setActiveFilter] = useState('none');
|
|
178
182
|
const [imageOptions, setImageOptions] = useState({
|
|
179
183
|
rotateDegrees: 0,
|
|
@@ -184,13 +188,21 @@ export function EditorScreen({
|
|
|
184
188
|
saturation: 1,
|
|
185
189
|
grayscale: false
|
|
186
190
|
});
|
|
191
|
+
const [panel, setPanel] = useState(item.type === 'video' ? 'trim' : 'filter');
|
|
187
192
|
const [trimStart, setTrimStart] = useState(0);
|
|
188
|
-
const [trimEnd, setTrimEnd] = useState(
|
|
193
|
+
const [trimEnd, setTrimEnd] = useState(() => {
|
|
194
|
+
const end = item.durationMs || 10000;
|
|
195
|
+
return maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end;
|
|
196
|
+
});
|
|
189
197
|
useEffect(() => {
|
|
190
198
|
setTrimStart(0);
|
|
191
|
-
|
|
199
|
+
const end = item.durationMs || maxVideoDurationMs || 10000;
|
|
200
|
+
setTrimEnd(maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end);
|
|
192
201
|
setThumbnails([]);
|
|
193
|
-
|
|
202
|
+
if (item.type === 'video' && maxVideoDurationMs && end > maxVideoDurationMs) {
|
|
203
|
+
setPanel('trim');
|
|
204
|
+
}
|
|
205
|
+
}, [item.id, item.durationMs, maxVideoDurationMs]);
|
|
194
206
|
const [editsHistory, setEditsHistory] = useState({});
|
|
195
207
|
const editsHistoryRef = useRef({});
|
|
196
208
|
const [dimensionsMap, setDimensionsMap] = useState({});
|
|
@@ -247,7 +259,7 @@ export function EditorScreen({
|
|
|
247
259
|
setCropOffset(saved.cropOffset);
|
|
248
260
|
setZoomScale(saved.zoomScale);
|
|
249
261
|
setStraightenAngle(saved.straightenAngle);
|
|
250
|
-
setIsMuted(saved.isMuted);
|
|
262
|
+
setIsMuted(selectedMusic ? true : saved.isMuted);
|
|
251
263
|
} else {
|
|
252
264
|
setActiveFilter('none');
|
|
253
265
|
setImageOptions({
|
|
@@ -260,7 +272,8 @@ export function EditorScreen({
|
|
|
260
272
|
grayscale: false
|
|
261
273
|
});
|
|
262
274
|
setTrimStart(0);
|
|
263
|
-
|
|
275
|
+
const end = targetItem.durationMs || 10000;
|
|
276
|
+
setTrimEnd(maxVideoDurationMs ? Math.min(end, maxVideoDurationMs) : end);
|
|
264
277
|
setOverlays([]);
|
|
265
278
|
setCropRatio(null);
|
|
266
279
|
setCropOffset({
|
|
@@ -269,7 +282,14 @@ export function EditorScreen({
|
|
|
269
282
|
});
|
|
270
283
|
setZoomScale(1);
|
|
271
284
|
setStraightenAngle(0);
|
|
272
|
-
setIsMuted(false);
|
|
285
|
+
setIsMuted(selectedMusic ? true : false);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Force trim panel if video is too long
|
|
289
|
+
if (targetItem.type === 'video' && maxVideoDurationMs && targetItem.durationMs && targetItem.durationMs > maxVideoDurationMs) {
|
|
290
|
+
setPanel('trim');
|
|
291
|
+
} else if (!saved) {
|
|
292
|
+
setPanel(targetItem.type === 'video' ? 'trim' : 'filter');
|
|
273
293
|
}
|
|
274
294
|
};
|
|
275
295
|
const handleScrollEnd = e => {
|
|
@@ -328,13 +348,13 @@ export function EditorScreen({
|
|
|
328
348
|
y: (o.y + 8) * renderScale,
|
|
329
349
|
color: o.color,
|
|
330
350
|
fontSize: o.fontSize * renderScale
|
|
331
|
-
}))
|
|
351
|
+
})),
|
|
352
|
+
frameUri: edits.imageOptions.frame && FRAME_IMAGES[edits.imageOptions.frame] ? Image.resolveAssetSource(FRAME_IMAGES[edits.imageOptions.frame]).uri : undefined
|
|
332
353
|
};
|
|
333
354
|
};
|
|
334
355
|
const [saving, setSaving] = useState(false);
|
|
335
356
|
const [videoPaused, setVideoPaused] = useState(false);
|
|
336
|
-
const
|
|
337
|
-
const resolvedMusicList = musicList || DUMMY_MUSIC_LIST;
|
|
357
|
+
const resolvedMusicList = musicList || [];
|
|
338
358
|
const [selectedMusic, setSelectedMusic] = useState(null);
|
|
339
359
|
const [musicPaused, setMusicPaused] = useState(false);
|
|
340
360
|
const [showMusicModal, setShowMusicModal] = useState(false);
|
|
@@ -1113,13 +1133,18 @@ export function EditorScreen({
|
|
|
1113
1133
|
y: (o.y + 8) * renderScale,
|
|
1114
1134
|
color: o.color,
|
|
1115
1135
|
fontSize: o.fontSize * renderScale
|
|
1116
|
-
}))
|
|
1136
|
+
})),
|
|
1137
|
+
frameUri: imageOptions.frame && FRAME_IMAGES[imageOptions.frame] ? Image.resolveAssetSource(FRAME_IMAGES[imageOptions.frame]).uri : undefined
|
|
1117
1138
|
};
|
|
1118
1139
|
}, [imageOptions, cropOffset, maxPan, dimensions, cropRatio, straightenAngle, overlays]);
|
|
1119
1140
|
|
|
1120
1141
|
// For visual trim
|
|
1121
1142
|
|
|
1122
1143
|
const duration = item.durationMs ?? 10_000;
|
|
1144
|
+
const durationRef = useRef(duration);
|
|
1145
|
+
useEffect(() => {
|
|
1146
|
+
durationRef.current = duration;
|
|
1147
|
+
}, [duration]);
|
|
1123
1148
|
const formatTime = ms => {
|
|
1124
1149
|
const totalSec = Math.floor(ms / 1000);
|
|
1125
1150
|
const mins = Math.floor(totalSec / 60);
|
|
@@ -1181,6 +1206,39 @@ export function EditorScreen({
|
|
|
1181
1206
|
generateThumbs();
|
|
1182
1207
|
}
|
|
1183
1208
|
}, [item.type, item.uri, duration, thumbnails.length]);
|
|
1209
|
+
const leftOverlayRef = useRef(null);
|
|
1210
|
+
const rightOverlayRef = useRef(null);
|
|
1211
|
+
const selectionRangeRef = useRef(null);
|
|
1212
|
+
const leftHandleRef = useRef(null);
|
|
1213
|
+
const rightHandleRef = useRef(null);
|
|
1214
|
+
const updateNativeRefs = (newStartX, newEndX) => {
|
|
1215
|
+
leftOverlayRef.current?.setNativeProps({
|
|
1216
|
+
style: {
|
|
1217
|
+
width: newStartX
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
rightOverlayRef.current?.setNativeProps({
|
|
1221
|
+
style: {
|
|
1222
|
+
left: newEndX
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
selectionRangeRef.current?.setNativeProps({
|
|
1226
|
+
style: {
|
|
1227
|
+
left: newStartX,
|
|
1228
|
+
width: newEndX - newStartX
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
leftHandleRef.current?.setNativeProps({
|
|
1232
|
+
style: {
|
|
1233
|
+
left: newStartX - 16
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
rightHandleRef.current?.setNativeProps({
|
|
1237
|
+
style: {
|
|
1238
|
+
left: newEndX - 16
|
|
1239
|
+
}
|
|
1240
|
+
});
|
|
1241
|
+
};
|
|
1184
1242
|
const startPanOffset = useRef(0);
|
|
1185
1243
|
const startPan = useRef(PanResponder.create({
|
|
1186
1244
|
onStartShouldSetPanResponder: () => true,
|
|
@@ -1194,20 +1252,76 @@ export function EditorScreen({
|
|
|
1194
1252
|
setScrollEnabled(false);
|
|
1195
1253
|
},
|
|
1196
1254
|
onPanResponderMove: (_, gesture) => {
|
|
1197
|
-
|
|
1255
|
+
let newX = Math.max(0, Math.min(endX.current - 32, startPanOffset.current + gesture.dx));
|
|
1256
|
+
let newTime = newX / TIMELINE_WIDTH * durationRef.current;
|
|
1257
|
+
const currentTrimEnd = endX.current / TIMELINE_WIDTH * durationRef.current;
|
|
1258
|
+
if (maxVideoDurationMs && currentTrimEnd - newTime > maxVideoDurationMs) {
|
|
1259
|
+
newTime = currentTrimEnd - maxVideoDurationMs;
|
|
1260
|
+
newX = newTime / durationRef.current * TIMELINE_WIDTH;
|
|
1261
|
+
}
|
|
1198
1262
|
startX.current = newX;
|
|
1199
|
-
|
|
1200
|
-
setTrimStart(newTime);
|
|
1263
|
+
updateNativeRefs(newX, endX.current);
|
|
1201
1264
|
throttledSeek(newTime);
|
|
1202
1265
|
},
|
|
1203
1266
|
onPanResponderRelease: () => {
|
|
1204
1267
|
isDraggingHandle.current = false;
|
|
1205
1268
|
setScrollEnabled(true);
|
|
1269
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1270
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1206
1271
|
setSeekToMs(-1);
|
|
1207
1272
|
},
|
|
1208
1273
|
onPanResponderTerminate: () => {
|
|
1209
1274
|
isDraggingHandle.current = false;
|
|
1210
1275
|
setScrollEnabled(true);
|
|
1276
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1277
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1278
|
+
setSeekToMs(-1);
|
|
1279
|
+
}
|
|
1280
|
+
})).current;
|
|
1281
|
+
const middlePanOffsetStart = useRef(0);
|
|
1282
|
+
const middlePanOffsetEnd = useRef(0);
|
|
1283
|
+
const middlePan = useRef(PanResponder.create({
|
|
1284
|
+
onStartShouldSetPanResponder: () => true,
|
|
1285
|
+
onStartShouldSetPanResponderCapture: () => true,
|
|
1286
|
+
onMoveShouldSetPanResponder: () => true,
|
|
1287
|
+
onMoveShouldSetPanResponderCapture: () => true,
|
|
1288
|
+
onPanResponderGrant: () => {
|
|
1289
|
+
pushToHistory();
|
|
1290
|
+
middlePanOffsetStart.current = startX.current;
|
|
1291
|
+
middlePanOffsetEnd.current = endX.current;
|
|
1292
|
+
isDraggingHandle.current = true;
|
|
1293
|
+
setScrollEnabled(false);
|
|
1294
|
+
},
|
|
1295
|
+
onPanResponderMove: (_, gesture) => {
|
|
1296
|
+
const windowWidth = middlePanOffsetEnd.current - middlePanOffsetStart.current;
|
|
1297
|
+
let newStartX = middlePanOffsetStart.current + gesture.dx;
|
|
1298
|
+
let newEndX = middlePanOffsetEnd.current + gesture.dx;
|
|
1299
|
+
if (newStartX < 0) {
|
|
1300
|
+
newStartX = 0;
|
|
1301
|
+
newEndX = windowWidth;
|
|
1302
|
+
}
|
|
1303
|
+
if (newEndX > TIMELINE_WIDTH) {
|
|
1304
|
+
newEndX = TIMELINE_WIDTH;
|
|
1305
|
+
newStartX = TIMELINE_WIDTH - windowWidth;
|
|
1306
|
+
}
|
|
1307
|
+
startX.current = newStartX;
|
|
1308
|
+
endX.current = newEndX;
|
|
1309
|
+
const newStartTime = newStartX / TIMELINE_WIDTH * durationRef.current;
|
|
1310
|
+
updateNativeRefs(newStartX, newEndX);
|
|
1311
|
+
throttledSeek(newStartTime);
|
|
1312
|
+
},
|
|
1313
|
+
onPanResponderRelease: () => {
|
|
1314
|
+
isDraggingHandle.current = false;
|
|
1315
|
+
setScrollEnabled(true);
|
|
1316
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1317
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1318
|
+
setSeekToMs(-1);
|
|
1319
|
+
},
|
|
1320
|
+
onPanResponderTerminate: () => {
|
|
1321
|
+
isDraggingHandle.current = false;
|
|
1322
|
+
setScrollEnabled(true);
|
|
1323
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1324
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1211
1325
|
setSeekToMs(-1);
|
|
1212
1326
|
}
|
|
1213
1327
|
})).current;
|
|
@@ -1224,20 +1338,29 @@ export function EditorScreen({
|
|
|
1224
1338
|
setScrollEnabled(false);
|
|
1225
1339
|
},
|
|
1226
1340
|
onPanResponderMove: (_, gesture) => {
|
|
1227
|
-
|
|
1341
|
+
let newX = Math.min(TIMELINE_WIDTH, Math.max(startX.current + 32, endPanOffset.current + gesture.dx));
|
|
1342
|
+
let newTime = newX / TIMELINE_WIDTH * durationRef.current;
|
|
1343
|
+
const currentTrimStart = startX.current / TIMELINE_WIDTH * durationRef.current;
|
|
1344
|
+
if (maxVideoDurationMs && newTime - currentTrimStart > maxVideoDurationMs) {
|
|
1345
|
+
newTime = currentTrimStart + maxVideoDurationMs;
|
|
1346
|
+
newX = newTime / durationRef.current * TIMELINE_WIDTH;
|
|
1347
|
+
}
|
|
1228
1348
|
endX.current = newX;
|
|
1229
|
-
|
|
1230
|
-
setTrimEnd(newTime);
|
|
1349
|
+
updateNativeRefs(startX.current, newX);
|
|
1231
1350
|
throttledSeek(newTime);
|
|
1232
1351
|
},
|
|
1233
1352
|
onPanResponderRelease: () => {
|
|
1234
1353
|
isDraggingHandle.current = false;
|
|
1235
1354
|
setScrollEnabled(true);
|
|
1355
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1356
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1236
1357
|
setSeekToMs(-1);
|
|
1237
1358
|
},
|
|
1238
1359
|
onPanResponderTerminate: () => {
|
|
1239
1360
|
isDraggingHandle.current = false;
|
|
1240
1361
|
setScrollEnabled(true);
|
|
1362
|
+
setTrimStart(startX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1363
|
+
setTrimEnd(endX.current / TIMELINE_WIDTH * durationRef.current);
|
|
1241
1364
|
setSeekToMs(-1);
|
|
1242
1365
|
}
|
|
1243
1366
|
})).current;
|
|
@@ -2012,11 +2135,16 @@ export function EditorScreen({
|
|
|
2012
2135
|
});
|
|
2013
2136
|
}
|
|
2014
2137
|
} else {
|
|
2138
|
+
const safeEndMs = Math.min(trimEnd, item.durationMs || 10000);
|
|
2139
|
+
const safeStartMs = Math.min(trimStart, Math.max(0, safeEndMs - 100));
|
|
2140
|
+
const isFullTrim = trimStart === 0 && trimEnd >= (item.durationMs || 10000);
|
|
2015
2141
|
exportUri = await trimVideo(item.uri, {
|
|
2016
|
-
startMs:
|
|
2017
|
-
endMs:
|
|
2142
|
+
startMs: safeStartMs,
|
|
2143
|
+
endMs: safeEndMs,
|
|
2018
2144
|
mute: isMuted,
|
|
2019
|
-
|
|
2145
|
+
...(selectedMusic?.url ? {
|
|
2146
|
+
musicUri: selectedMusic.url
|
|
2147
|
+
} : {}),
|
|
2020
2148
|
...activeOptions
|
|
2021
2149
|
});
|
|
2022
2150
|
}
|
|
@@ -2033,6 +2161,7 @@ export function EditorScreen({
|
|
|
2033
2161
|
setSaving(true);
|
|
2034
2162
|
saveEditsForIndex(activeIndex);
|
|
2035
2163
|
const updatedItems = [...items];
|
|
2164
|
+
let cumulativeMusicOffsetMs = 0;
|
|
2036
2165
|
for (let i = 0; i < items.length; i++) {
|
|
2037
2166
|
const targetItem = items[i];
|
|
2038
2167
|
let edits = editsHistoryRef.current[targetItem.id];
|
|
@@ -2058,6 +2187,7 @@ export function EditorScreen({
|
|
|
2058
2187
|
outUri = await trimVideo(outUri, {
|
|
2059
2188
|
isImage: true,
|
|
2060
2189
|
musicUri: selectedMusic.url,
|
|
2190
|
+
musicOffsetMs: cumulativeMusicOffsetMs,
|
|
2061
2191
|
rotateDegrees: 0,
|
|
2062
2192
|
flipX: false,
|
|
2063
2193
|
flipY: false,
|
|
@@ -2072,12 +2202,20 @@ export function EditorScreen({
|
|
|
2072
2202
|
uri: outUri,
|
|
2073
2203
|
thumbnailUri: outUri
|
|
2074
2204
|
};
|
|
2205
|
+
cumulativeMusicOffsetMs += 10000; // Images are 10s by default
|
|
2075
2206
|
} else {
|
|
2207
|
+
const originalDuration = targetItem.durationMs || maxVideoDurationMs || 10000;
|
|
2208
|
+
const safeEndMs = Math.min(edits.trimEnd, originalDuration);
|
|
2209
|
+
const safeStartMs = Math.min(edits.trimStart, Math.max(0, safeEndMs - 100));
|
|
2210
|
+
const isFullTrim = edits.trimStart === 0 && edits.trimEnd >= originalDuration;
|
|
2076
2211
|
const outUri = await trimVideo(targetItem.uri, {
|
|
2077
|
-
startMs:
|
|
2078
|
-
endMs:
|
|
2212
|
+
startMs: safeStartMs,
|
|
2213
|
+
endMs: safeEndMs,
|
|
2079
2214
|
mute: edits.isMuted,
|
|
2080
|
-
|
|
2215
|
+
...(selectedMusic?.url ? {
|
|
2216
|
+
musicUri: selectedMusic.url,
|
|
2217
|
+
musicOffsetMs: cumulativeMusicOffsetMs
|
|
2218
|
+
} : {}),
|
|
2081
2219
|
...opts
|
|
2082
2220
|
});
|
|
2083
2221
|
let newThumb = undefined;
|
|
@@ -2095,6 +2233,7 @@ export function EditorScreen({
|
|
|
2095
2233
|
thumbnailUri: newThumb ? newThumb : targetItem.thumbnailUri,
|
|
2096
2234
|
durationMs: newDuration
|
|
2097
2235
|
};
|
|
2236
|
+
cumulativeMusicOffsetMs += newDuration;
|
|
2098
2237
|
}
|
|
2099
2238
|
} else {
|
|
2100
2239
|
if (selectedMusic) {
|
|
@@ -2103,6 +2242,7 @@ export function EditorScreen({
|
|
|
2103
2242
|
outUri = await trimVideo(targetItem.uri, {
|
|
2104
2243
|
isImage: true,
|
|
2105
2244
|
musicUri: selectedMusic.url,
|
|
2245
|
+
musicOffsetMs: cumulativeMusicOffsetMs,
|
|
2106
2246
|
rotateDegrees: 0,
|
|
2107
2247
|
flipX: false,
|
|
2108
2248
|
flipY: false,
|
|
@@ -2111,12 +2251,17 @@ export function EditorScreen({
|
|
|
2111
2251
|
saturation: 1,
|
|
2112
2252
|
grayscale: false
|
|
2113
2253
|
});
|
|
2254
|
+
cumulativeMusicOffsetMs += 10000;
|
|
2114
2255
|
} else {
|
|
2256
|
+
const safeEndMs = targetItem.durationMs || 10000;
|
|
2115
2257
|
outUri = await trimVideo(targetItem.uri, {
|
|
2116
2258
|
startMs: 0,
|
|
2117
|
-
endMs:
|
|
2259
|
+
endMs: safeEndMs,
|
|
2118
2260
|
mute: isMuted,
|
|
2119
|
-
|
|
2261
|
+
...(selectedMusic?.url ? {
|
|
2262
|
+
musicUri: selectedMusic.url,
|
|
2263
|
+
musicOffsetMs: cumulativeMusicOffsetMs
|
|
2264
|
+
} : {}),
|
|
2120
2265
|
rotateDegrees: 0,
|
|
2121
2266
|
flipX: false,
|
|
2122
2267
|
flipY: false,
|
|
@@ -2125,6 +2270,7 @@ export function EditorScreen({
|
|
|
2125
2270
|
saturation: 1,
|
|
2126
2271
|
grayscale: false
|
|
2127
2272
|
});
|
|
2273
|
+
cumulativeMusicOffsetMs += safeEndMs;
|
|
2128
2274
|
}
|
|
2129
2275
|
updatedItems[i] = {
|
|
2130
2276
|
...targetItem,
|
|
@@ -2666,7 +2812,15 @@ export function EditorScreen({
|
|
|
2666
2812
|
},
|
|
2667
2813
|
style: styles.filmstripImage
|
|
2668
2814
|
}, idx)), /*#__PURE__*/_jsx(View, {
|
|
2669
|
-
style: styles.timelineOverlay
|
|
2815
|
+
style: [styles.timelineOverlay, {
|
|
2816
|
+
left: 0,
|
|
2817
|
+
width: trimStart / duration * TIMELINE_WIDTH
|
|
2818
|
+
}]
|
|
2819
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
2820
|
+
style: [styles.timelineOverlay, {
|
|
2821
|
+
left: trimEnd / duration * TIMELINE_WIDTH,
|
|
2822
|
+
right: 0
|
|
2823
|
+
}]
|
|
2670
2824
|
}), /*#__PURE__*/_jsx(View, {
|
|
2671
2825
|
style: [styles.selectionRange, {
|
|
2672
2826
|
left: trimStart / duration * TIMELINE_WIDTH,
|
|
@@ -2677,20 +2831,18 @@ export function EditorScreen({
|
|
|
2677
2831
|
style: [styles.customHandle, styles.customHandleLeft, {
|
|
2678
2832
|
left: trimStart / duration * TIMELINE_WIDTH - 16
|
|
2679
2833
|
}],
|
|
2680
|
-
...startPan.panHandlers,
|
|
2681
2834
|
children: /*#__PURE__*/_jsx(View, {
|
|
2682
2835
|
style: styles.handleBarLine
|
|
2683
2836
|
})
|
|
2684
2837
|
}), /*#__PURE__*/_jsx(View, {
|
|
2685
2838
|
style: [styles.customHandle, styles.customHandleRight, {
|
|
2686
|
-
left: trimEnd / duration * TIMELINE_WIDTH
|
|
2839
|
+
left: trimEnd / duration * TIMELINE_WIDTH - 16
|
|
2687
2840
|
}],
|
|
2688
|
-
...endPan.panHandlers,
|
|
2689
2841
|
children: /*#__PURE__*/_jsx(View, {
|
|
2690
2842
|
style: styles.handleBarLine
|
|
2691
2843
|
})
|
|
2692
2844
|
})]
|
|
2693
|
-
}), /*#__PURE__*/_jsxs(Pressable, {
|
|
2845
|
+
}), resolvedMusicList.length > 0 && /*#__PURE__*/_jsxs(Pressable, {
|
|
2694
2846
|
style: styles.subTrackRow,
|
|
2695
2847
|
onPress: () => setShowMusicModal(true),
|
|
2696
2848
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
@@ -2971,7 +3123,7 @@ export function EditorScreen({
|
|
|
2971
3123
|
style: styles.toolLabel,
|
|
2972
3124
|
children: "Text"
|
|
2973
3125
|
})]
|
|
2974
|
-
}), /*#__PURE__*/_jsxs(Pressable, {
|
|
3126
|
+
}), resolvedMusicList.length > 0 && /*#__PURE__*/_jsxs(Pressable, {
|
|
2975
3127
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
2976
3128
|
onPress: () => setShowMusicModal(true),
|
|
2977
3129
|
children: [/*#__PURE__*/_jsx(View, {
|
|
@@ -3133,7 +3285,7 @@ export function EditorScreen({
|
|
|
3133
3285
|
contentContainerStyle: [styles.toolButtonsRow, {
|
|
3134
3286
|
flexGrow: 1
|
|
3135
3287
|
}],
|
|
3136
|
-
children: [/*#__PURE__*/_jsxs(Pressable, {
|
|
3288
|
+
children: [resolvedMusicList.length > 0 && /*#__PURE__*/_jsxs(Pressable, {
|
|
3137
3289
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
3138
3290
|
onPress: () => setShowMusicModal(true),
|
|
3139
3291
|
children: [/*#__PURE__*/_jsx(View, {
|
|
@@ -3302,6 +3454,12 @@ export function EditorScreen({
|
|
|
3302
3454
|
data: items,
|
|
3303
3455
|
extraData: [overlays, editingTextId, editsHistory, activeFilter, imageOptions, trimStart, trimEnd, isMuted, activeIndex],
|
|
3304
3456
|
keyExtractor: it => it.id,
|
|
3457
|
+
initialScrollIndex: activeIndex,
|
|
3458
|
+
getItemLayout: (_, index) => ({
|
|
3459
|
+
length: SCREEN_WIDTH,
|
|
3460
|
+
offset: SCREEN_WIDTH * index,
|
|
3461
|
+
index
|
|
3462
|
+
}),
|
|
3305
3463
|
horizontal: true,
|
|
3306
3464
|
pagingEnabled: false,
|
|
3307
3465
|
showsHorizontalScrollIndicator: false,
|
|
@@ -3412,7 +3570,7 @@ export function EditorScreen({
|
|
|
3412
3570
|
fontWeight: '700'
|
|
3413
3571
|
},
|
|
3414
3572
|
children: (() => {
|
|
3415
|
-
const selMs =
|
|
3573
|
+
const selMs = (endX.current - startX.current) / TIMELINE_WIDTH * duration;
|
|
3416
3574
|
const totalSec = Math.floor(selMs / 1000);
|
|
3417
3575
|
return totalSec >= 60 ? `${Math.floor(totalSec / 60)}:${(totalSec % 60).toString().padStart(2, '0')} selected` : `${(selMs / 1000).toFixed(1)}s selected`;
|
|
3418
3576
|
})()
|
|
@@ -3423,30 +3581,54 @@ export function EditorScreen({
|
|
|
3423
3581
|
}],
|
|
3424
3582
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
3425
3583
|
style: styles.filmstrip,
|
|
3426
|
-
children: [thumbnails.
|
|
3584
|
+
children: [thumbnails.length === 0 ? /*#__PURE__*/_jsx(View, {
|
|
3585
|
+
style: {
|
|
3586
|
+
flex: 1,
|
|
3587
|
+
justifyContent: 'center',
|
|
3588
|
+
alignItems: 'center'
|
|
3589
|
+
},
|
|
3590
|
+
children: /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
3591
|
+
color: "#ffffff",
|
|
3592
|
+
size: "small"
|
|
3593
|
+
})
|
|
3594
|
+
}) : thumbnails.map((uri, idx) => /*#__PURE__*/_jsx(Image, {
|
|
3427
3595
|
source: {
|
|
3428
3596
|
uri
|
|
3429
3597
|
},
|
|
3430
3598
|
style: styles.filmstripImage
|
|
3431
3599
|
}, idx)), /*#__PURE__*/_jsx(View, {
|
|
3432
|
-
|
|
3600
|
+
ref: leftOverlayRef,
|
|
3601
|
+
style: [styles.timelineOverlay, {
|
|
3602
|
+
left: 0,
|
|
3603
|
+
width: startX.current
|
|
3604
|
+
}]
|
|
3433
3605
|
}), /*#__PURE__*/_jsx(View, {
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3606
|
+
ref: rightOverlayRef,
|
|
3607
|
+
style: [styles.timelineOverlay, {
|
|
3608
|
+
left: endX.current,
|
|
3609
|
+
right: 0
|
|
3437
3610
|
}]
|
|
3611
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
3612
|
+
ref: selectionRangeRef,
|
|
3613
|
+
style: [styles.selectionRange, {
|
|
3614
|
+
left: startX.current,
|
|
3615
|
+
width: endX.current - startX.current
|
|
3616
|
+
}],
|
|
3617
|
+
...middlePan.panHandlers
|
|
3438
3618
|
})]
|
|
3439
3619
|
}), /*#__PURE__*/_jsx(View, {
|
|
3620
|
+
ref: leftHandleRef,
|
|
3440
3621
|
style: [styles.customHandle, styles.customHandleLeft, {
|
|
3441
|
-
left:
|
|
3622
|
+
left: startX.current - 16
|
|
3442
3623
|
}],
|
|
3443
3624
|
...startPan.panHandlers,
|
|
3444
3625
|
children: /*#__PURE__*/_jsx(View, {
|
|
3445
3626
|
style: styles.handleBarLine
|
|
3446
3627
|
})
|
|
3447
3628
|
}), /*#__PURE__*/_jsx(View, {
|
|
3629
|
+
ref: rightHandleRef,
|
|
3448
3630
|
style: [styles.customHandle, styles.customHandleRight, {
|
|
3449
|
-
left:
|
|
3631
|
+
left: endX.current - 16
|
|
3450
3632
|
}],
|
|
3451
3633
|
...endPan.panHandlers,
|
|
3452
3634
|
children: /*#__PURE__*/_jsx(View, {
|
|
@@ -3724,7 +3906,7 @@ export function EditorScreen({
|
|
|
3724
3906
|
contentContainerStyle: [styles.toolButtonsRow, {
|
|
3725
3907
|
flexGrow: 1
|
|
3726
3908
|
}],
|
|
3727
|
-
children: [/*#__PURE__*/_jsxs(Pressable, {
|
|
3909
|
+
children: [resolvedMusicList.length > 0 && /*#__PURE__*/_jsxs(Pressable, {
|
|
3728
3910
|
style: [styles.toolButton, showMusicModal && styles.toolButtonActive],
|
|
3729
3911
|
onPress: () => setShowMusicModal(true),
|
|
3730
3912
|
children: [/*#__PURE__*/_jsx(View, {
|
|
@@ -4032,6 +4214,7 @@ export function EditorScreen({
|
|
|
4032
4214
|
onPress: () => {
|
|
4033
4215
|
setSelectedMusic(null);
|
|
4034
4216
|
setMusicPaused(true);
|
|
4217
|
+
setIsMuted(false);
|
|
4035
4218
|
},
|
|
4036
4219
|
style: styles.musicFooterRemoveBtn,
|
|
4037
4220
|
children: /*#__PURE__*/_jsx(Text, {
|
|
@@ -4799,7 +4982,9 @@ const styles = StyleSheet.create({
|
|
|
4799
4982
|
height: 60
|
|
4800
4983
|
},
|
|
4801
4984
|
timelineOverlay: {
|
|
4802
|
-
|
|
4985
|
+
position: 'absolute',
|
|
4986
|
+
top: 0,
|
|
4987
|
+
bottom: 0,
|
|
4803
4988
|
backgroundColor: 'rgba(0,0,0,0.6)'
|
|
4804
4989
|
},
|
|
4805
4990
|
selectionRange: {
|
|
@@ -4809,7 +4994,7 @@ const styles = StyleSheet.create({
|
|
|
4809
4994
|
backgroundColor: 'transparent',
|
|
4810
4995
|
borderTopWidth: 2,
|
|
4811
4996
|
borderBottomWidth: 2,
|
|
4812
|
-
borderColor: '#
|
|
4997
|
+
borderColor: '#FFD60A'
|
|
4813
4998
|
},
|
|
4814
4999
|
handle: {
|
|
4815
5000
|
position: 'absolute',
|
|
@@ -4835,7 +5020,7 @@ const styles = StyleSheet.create({
|
|
|
4835
5020
|
top: 0,
|
|
4836
5021
|
width: 16,
|
|
4837
5022
|
height: 60,
|
|
4838
|
-
backgroundColor: '#
|
|
5023
|
+
backgroundColor: '#FFD60A',
|
|
4839
5024
|
justifyContent: 'center',
|
|
4840
5025
|
alignItems: 'center',
|
|
4841
5026
|
zIndex: 20
|