@movementinfra/expo-twostep-video 0.1.15 → 0.1.16
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 +80 -7
- package/build/ExpoTwoStepVideo.types.d.ts +49 -0
- package/build/ExpoTwoStepVideo.types.d.ts.map +1 -1
- package/build/ExpoTwoStepVideo.types.js.map +1 -1
- package/build/ExpoTwoStepVideoModule.d.ts +17 -0
- package/build/ExpoTwoStepVideoModule.d.ts.map +1 -1
- package/build/ExpoTwoStepVideoModule.js.map +1 -1
- package/build/ExpoTwoStepVideoModule.web.d.ts +4 -0
- package/build/ExpoTwoStepVideoModule.web.d.ts.map +1 -1
- package/build/ExpoTwoStepVideoModule.web.js +15 -0
- package/build/ExpoTwoStepVideoModule.web.js.map +1 -1
- package/build/ExpoTwoStepVideoView.d.ts.map +1 -1
- package/build/ExpoTwoStepVideoView.js +74 -13
- package/build/ExpoTwoStepVideoView.js.map +1 -1
- package/build/components/DoubleTapSkip.d.ts.map +1 -1
- package/build/components/DoubleTapSkip.js +35 -20
- package/build/components/DoubleTapSkip.js.map +1 -1
- package/build/components/PlayheadBar.d.ts.map +1 -1
- package/build/components/PlayheadBar.js +30 -18
- package/build/components/PlayheadBar.js.map +1 -1
- package/build/index.d.ts +83 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +91 -0
- package/build/index.js.map +1 -1
- package/ios/ExpoTwoStepVideoModule.swift +77 -1
- package/ios/ExpoTwoStepVideoView.swift +155 -36
- package/ios/TwoStepVideo/Core/MediaPicker.swift +355 -0
- package/ios/TwoStepVideo/TwoStepVideo.swift +4 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DoubleTapSkip.js","sourceRoot":"","sources":["../../src/components/DoubleTapSkip.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { useRef, useCallback } from 'react';\nimport {\n View,\n StyleSheet,\n TouchableWithoutFeedback,\n Animated,\n Text,\n} from 'react-native';\n\nexport type DoubleTapSkipProps = {\n currentTime: number;\n duration: number;\n skipInterval: number;\n onSeek: (time: number) => void;\n};\n\nconst DOUBLE_TAP_DELAY = 300;\n\nexport default function DoubleTapSkip({\n currentTime,\n duration,\n skipInterval,\n onSeek,\n}: DoubleTapSkipProps) {\n const currentTimeRef = useRef(currentTime);\n currentTimeRef.current = currentTime;\n\n const durationRef = useRef(duration);\n durationRef.current = duration;\n\n const lastTapLeft = useRef(0);\n const lastTapRight = useRef(0);\n const leftTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const rightTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const leftOpacity = useRef(new Animated.Value(0)).current;\n const rightOpacity = useRef(new Animated.Value(0)).current;\n\n const showFeedback = useCallback((opacity: Animated.Value) => {\n opacity.setValue(1);\n Animated.timing(opacity, {\n toValue: 0,\n duration: 600,\n delay: 200,\n useNativeDriver: true,\n }).start();\n }, []);\n\n const handleLeftTap = useCallback(() => {\n const now = Date.now();\n if (now - lastTapLeft.current < DOUBLE_TAP_DELAY) {\n // Double tap detected\n if (leftTimeoutRef.current) {\n clearTimeout(leftTimeoutRef.current);\n leftTimeoutRef.current = null;\n }\n const newTime = Math.max(0, currentTimeRef.current - skipInterval);\n onSeek(newTime);\n showFeedback(leftOpacity);\n lastTapLeft.current = 0;\n } else {\n lastTapLeft.current = now;\n leftTimeoutRef.current = setTimeout(() => {\n lastTapLeft.current = 0;\n leftTimeoutRef.current = null;\n }, DOUBLE_TAP_DELAY);\n }\n }, [skipInterval, onSeek, showFeedback, leftOpacity]);\n\n const handleRightTap = useCallback(() => {\n const now = Date.now();\n if (now - lastTapRight.current < DOUBLE_TAP_DELAY) {\n // Double tap detected\n if (rightTimeoutRef.current) {\n clearTimeout(rightTimeoutRef.current);\n rightTimeoutRef.current = null;\n }\n const newTime = Math.min(durationRef.current, currentTimeRef.current + skipInterval);\n onSeek(newTime);\n showFeedback(rightOpacity);\n lastTapRight.current = 0;\n } else {\n lastTapRight.current = now;\n rightTimeoutRef.current = setTimeout(() => {\n lastTapRight.current = 0;\n rightTimeoutRef.current = null;\n }, DOUBLE_TAP_DELAY);\n }\n }, [skipInterval, onSeek, showFeedback, rightOpacity]);\n\n return (\n <View style={styles.container} pointerEvents=\"box-none\">\n <TouchableWithoutFeedback onPress={handleLeftTap}>\n <View style={styles.half}>\n <Animated.View style={[styles.feedback, { opacity: leftOpacity }]}>\n <View style={styles.feedbackCircle}>\n <Text style={styles.arrowText}>{'<<'}</Text>\n <Text style={styles.skipText}>{skipInterval}s</Text>\n </View>\n </Animated.View>\n </View>\n </TouchableWithoutFeedback>\n <TouchableWithoutFeedback onPress={handleRightTap}>\n <View style={styles.half}>\n <Animated.View style={[styles.feedback, { opacity: rightOpacity }]}>\n <View style={styles.feedbackCircle}>\n <Text style={styles.arrowText}>{'>>'}</Text>\n <Text style={styles.skipText}>{skipInterval}s</Text>\n </View>\n </Animated.View>\n </View>\n </TouchableWithoutFeedback>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n ...StyleSheet.absoluteFillObject,\n flexDirection: 'row',\n },\n half: {\n flex: 1,\n justifyContent: 'center',\n alignItems: 'center',\n },\n feedback: {\n justifyContent: 'center',\n alignItems: 'center',\n },\n feedbackCircle: {\n width: 64,\n height: 64,\n borderRadius: 32,\n backgroundColor: 'rgba(0,0,0,0.4)',\n justifyContent: 'center',\n alignItems: 'center',\n },\n arrowText: {\n color: '#FFFFFF',\n fontSize: 20,\n fontWeight: '700',\n },\n skipText: {\n color: '#FFFFFF',\n fontSize: 12,\n fontWeight: '600',\n marginTop: 2,\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"DoubleTapSkip.js","sourceRoot":"","sources":["../../src/components/DoubleTapSkip.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { useRef, useCallback } from 'react';\nimport {\n View,\n StyleSheet,\n TouchableWithoutFeedback,\n Animated,\n Text,\n} from 'react-native';\n\nexport type DoubleTapSkipProps = {\n currentTime: number;\n duration: number;\n skipInterval: number;\n onSeek: (time: number) => void;\n};\n\nconst DOUBLE_TAP_DELAY = 300;\n\nexport default function DoubleTapSkip({\n currentTime,\n duration,\n skipInterval,\n onSeek,\n}: DoubleTapSkipProps) {\n const currentTimeRef = useRef(currentTime);\n currentTimeRef.current = currentTime;\n\n const durationRef = useRef(duration);\n durationRef.current = duration;\n\n const lastTapLeft = useRef(0);\n const lastTapRight = useRef(0);\n const leftTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const rightTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const leftOpacity = useRef(new Animated.Value(0)).current;\n const rightOpacity = useRef(new Animated.Value(0)).current;\n\n const showFeedback = useCallback((opacity: Animated.Value) => {\n opacity.setValue(1);\n Animated.timing(opacity, {\n toValue: 0,\n duration: 600,\n delay: 200,\n useNativeDriver: true,\n }).start();\n }, []);\n\n const handleLeftTap = useCallback(() => {\n const now = Date.now();\n if (now - lastTapLeft.current < DOUBLE_TAP_DELAY) {\n // Double tap detected\n if (leftTimeoutRef.current) {\n clearTimeout(leftTimeoutRef.current);\n leftTimeoutRef.current = null;\n }\n const newTime = Math.max(0, currentTimeRef.current - skipInterval);\n onSeek(newTime);\n showFeedback(leftOpacity);\n lastTapLeft.current = 0;\n } else {\n lastTapLeft.current = now;\n leftTimeoutRef.current = setTimeout(() => {\n lastTapLeft.current = 0;\n leftTimeoutRef.current = null;\n }, DOUBLE_TAP_DELAY);\n }\n }, [skipInterval, onSeek, showFeedback, leftOpacity]);\n\n const handleRightTap = useCallback(() => {\n const now = Date.now();\n if (now - lastTapRight.current < DOUBLE_TAP_DELAY) {\n // Double tap detected\n if (rightTimeoutRef.current) {\n clearTimeout(rightTimeoutRef.current);\n rightTimeoutRef.current = null;\n }\n const newTime = Math.min(durationRef.current, currentTimeRef.current + skipInterval);\n onSeek(newTime);\n showFeedback(rightOpacity);\n lastTapRight.current = 0;\n } else {\n lastTapRight.current = now;\n rightTimeoutRef.current = setTimeout(() => {\n lastTapRight.current = 0;\n rightTimeoutRef.current = null;\n }, DOUBLE_TAP_DELAY);\n }\n }, [skipInterval, onSeek, showFeedback, rightOpacity]);\n\n return (\n <View style={styles.container} pointerEvents=\"box-none\">\n {/* Left side - positioned as a smaller hit zone to allow pinch gestures elsewhere */}\n <View style={styles.half} pointerEvents=\"box-none\">\n <TouchableWithoutFeedback onPress={handleLeftTap}>\n <View style={styles.hitZone}>\n <Animated.View style={[styles.feedback, { opacity: leftOpacity }]}>\n <View style={styles.feedbackCircle}>\n <Text style={styles.arrowText}>{'<<'}</Text>\n <Text style={styles.skipText}>{skipInterval}s</Text>\n </View>\n </Animated.View>\n </View>\n </TouchableWithoutFeedback>\n </View>\n {/* Right side - positioned as a smaller hit zone to allow pinch gestures elsewhere */}\n <View style={styles.half} pointerEvents=\"box-none\">\n <TouchableWithoutFeedback onPress={handleRightTap}>\n <View style={styles.hitZone}>\n <Animated.View style={[styles.feedback, { opacity: rightOpacity }]}>\n <View style={styles.feedbackCircle}>\n <Text style={styles.arrowText}>{'>>'}</Text>\n <Text style={styles.skipText}>{skipInterval}s</Text>\n </View>\n </Animated.View>\n </View>\n </TouchableWithoutFeedback>\n </View>\n </View>\n );\n}\n\nconst HIT_ZONE_SIZE = 120;\n\nconst styles = StyleSheet.create({\n container: {\n ...StyleSheet.absoluteFillObject,\n flexDirection: 'row',\n },\n half: {\n flex: 1,\n justifyContent: 'center',\n alignItems: 'center',\n },\n hitZone: {\n // Smaller hit zone allows pinch gestures in other areas\n width: HIT_ZONE_SIZE,\n height: HIT_ZONE_SIZE,\n borderRadius: HIT_ZONE_SIZE / 2,\n justifyContent: 'center',\n alignItems: 'center',\n },\n feedback: {\n justifyContent: 'center',\n alignItems: 'center',\n },\n feedbackCircle: {\n width: 64,\n height: 64,\n borderRadius: 32,\n backgroundColor: 'rgba(0,0,0,0.4)',\n justifyContent: 'center',\n alignItems: 'center',\n },\n arrowText: {\n color: '#FFFFFF',\n fontSize: 20,\n fontWeight: '700',\n },\n skipText: {\n color: '#FFFFFF',\n fontSize: 12,\n fontWeight: '600',\n marginTop: 2,\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayheadBar.d.ts","sourceRoot":"","sources":["../../src/components/PlayheadBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAOF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAW,EACX,QAAQ,EACR,WAAW,EACX,MAAM,EACN,SAAS,GACV,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"PlayheadBar.d.ts","sourceRoot":"","sources":["../../src/components/PlayheadBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAOF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAW,EACX,QAAQ,EACR,WAAW,EACX,MAAM,EACN,SAAS,GACV,EAAE,gBAAgB,qBAuIlB"}
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useRef, useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import { View, StyleSheet, PanResponder, Animated, } from 'react-native';
|
|
4
4
|
const COLLAPSED_HEIGHT = 2;
|
|
5
5
|
const EXPANDED_HEIGHT = 4;
|
|
6
6
|
const THUMB_SIZE = 14;
|
|
7
7
|
const HIT_SLOP_HEIGHT = 30;
|
|
8
8
|
export default function PlayheadBar({ currentTime, duration, onSeekStart, onSeek, onSeekEnd, }) {
|
|
9
|
+
const [progressPercent, setProgressPercent] = useState(0);
|
|
9
10
|
const containerWidthRef = useRef(0);
|
|
10
11
|
const currentTimeRef = useRef(currentTime);
|
|
11
12
|
const durationRef = useRef(duration);
|
|
12
13
|
const isScrubbing = useRef(false);
|
|
14
|
+
// These animations use non-native driver (height) or native driver (scale/translate)
|
|
13
15
|
const barHeight = useRef(new Animated.Value(COLLAPSED_HEIGHT)).current;
|
|
14
16
|
const thumbScale = useRef(new Animated.Value(0)).current;
|
|
15
|
-
const
|
|
17
|
+
const thumbTranslateX = useRef(new Animated.Value(0)).current;
|
|
16
18
|
useEffect(() => {
|
|
17
19
|
currentTimeRef.current = currentTime;
|
|
18
20
|
if (!isScrubbing.current && duration > 0) {
|
|
19
|
-
|
|
21
|
+
const p = currentTime / duration;
|
|
22
|
+
setProgressPercent(p * 100);
|
|
23
|
+
thumbTranslateX.setValue(p * containerWidthRef.current);
|
|
20
24
|
}
|
|
21
|
-
}, [currentTime, duration,
|
|
25
|
+
}, [currentTime, duration, thumbTranslateX]);
|
|
22
26
|
useEffect(() => {
|
|
23
27
|
durationRef.current = duration;
|
|
24
28
|
}, [duration]);
|
|
@@ -50,6 +54,10 @@ export default function PlayheadBar({ currentTime, duration, onSeekStart, onSeek
|
|
|
50
54
|
}),
|
|
51
55
|
]).start();
|
|
52
56
|
}, [barHeight, thumbScale]);
|
|
57
|
+
const updateProgress = useCallback((p) => {
|
|
58
|
+
setProgressPercent(p * 100);
|
|
59
|
+
thumbTranslateX.setValue(p * containerWidthRef.current);
|
|
60
|
+
}, [thumbTranslateX]);
|
|
53
61
|
const clampProgress = useCallback((pageX) => {
|
|
54
62
|
const width = containerWidthRef.current;
|
|
55
63
|
if (width <= 0 || durationRef.current <= 0)
|
|
@@ -65,7 +73,7 @@ export default function PlayheadBar({ currentTime, duration, onSeekStart, onSeek
|
|
|
65
73
|
expand();
|
|
66
74
|
onSeekStart();
|
|
67
75
|
const p = clampProgress(evt.nativeEvent.locationX);
|
|
68
|
-
|
|
76
|
+
updateProgress(p);
|
|
69
77
|
onSeek(p * durationRef.current);
|
|
70
78
|
},
|
|
71
79
|
onPanResponderMove: (evt) => {
|
|
@@ -73,7 +81,7 @@ export default function PlayheadBar({ currentTime, duration, onSeekStart, onSeek
|
|
|
73
81
|
if (width <= 0)
|
|
74
82
|
return;
|
|
75
83
|
const p = Math.max(0, Math.min(1, evt.nativeEvent.locationX / width));
|
|
76
|
-
|
|
84
|
+
updateProgress(p);
|
|
77
85
|
onSeek(p * durationRef.current);
|
|
78
86
|
},
|
|
79
87
|
onPanResponderRelease: () => {
|
|
@@ -88,25 +96,27 @@ export default function PlayheadBar({ currentTime, duration, onSeekStart, onSeek
|
|
|
88
96
|
},
|
|
89
97
|
})).current;
|
|
90
98
|
const onLayout = useCallback((e) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
const width = e.nativeEvent.layout.width;
|
|
100
|
+
containerWidthRef.current = width;
|
|
101
|
+
// Update thumb position when container width changes
|
|
102
|
+
if (durationRef.current > 0) {
|
|
103
|
+
const p = currentTimeRef.current / durationRef.current;
|
|
104
|
+
thumbTranslateX.setValue(p * width);
|
|
105
|
+
}
|
|
106
|
+
}, [thumbTranslateX]);
|
|
97
107
|
return (<View style={styles.container} pointerEvents="box-none">
|
|
98
108
|
<View style={styles.hitArea} onLayout={onLayout} {...panResponder.panHandlers}>
|
|
99
109
|
<Animated.View style={[styles.track, { height: barHeight }]}>
|
|
100
|
-
|
|
110
|
+
{/* Use percentage-based width without Animated to avoid native driver issues */}
|
|
111
|
+
<View style={[styles.filled, { width: `${progressPercent}%` }]}/>
|
|
101
112
|
</Animated.View>
|
|
102
113
|
<Animated.View style={[
|
|
103
114
|
styles.thumb,
|
|
104
115
|
{
|
|
105
|
-
transform: [
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}),
|
|
116
|
+
transform: [
|
|
117
|
+
{ translateX: thumbTranslateX },
|
|
118
|
+
{ scale: thumbScale },
|
|
119
|
+
],
|
|
110
120
|
},
|
|
111
121
|
]}/>
|
|
112
122
|
</View>
|
|
@@ -135,6 +145,8 @@ const styles = StyleSheet.create({
|
|
|
135
145
|
thumb: {
|
|
136
146
|
position: 'absolute',
|
|
137
147
|
bottom: -(THUMB_SIZE / 2) + COLLAPSED_HEIGHT / 2,
|
|
148
|
+
// Position at left edge minus half thumb width, translateX moves it
|
|
149
|
+
left: -(THUMB_SIZE / 2),
|
|
138
150
|
width: THUMB_SIZE,
|
|
139
151
|
height: THUMB_SIZE,
|
|
140
152
|
borderRadius: THUMB_SIZE / 2,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlayheadBar.js","sourceRoot":"","sources":["../../src/components/PlayheadBar.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport { useRef, useCallback, useEffect } from 'react';\nimport {\n View,\n StyleSheet,\n PanResponder,\n Animated,\n LayoutChangeEvent,\n} from 'react-native';\n\nexport type PlayheadBarProps = {\n currentTime: number;\n duration: number;\n onSeekStart: () => void;\n onSeek: (time: number) => void;\n onSeekEnd: () => void;\n};\n\nconst COLLAPSED_HEIGHT = 2;\nconst EXPANDED_HEIGHT = 4;\nconst THUMB_SIZE = 14;\nconst HIT_SLOP_HEIGHT = 30;\n\nexport default function PlayheadBar({\n currentTime,\n duration,\n onSeekStart,\n onSeek,\n onSeekEnd,\n}: PlayheadBarProps) {\n const containerWidthRef = useRef(0);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const isScrubbing = useRef(false);\n\n const barHeight = useRef(new Animated.Value(COLLAPSED_HEIGHT)).current;\n const thumbScale = useRef(new Animated.Value(0)).current;\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n currentTimeRef.current = currentTime;\n if (!isScrubbing.current && duration > 0) {\n progress.setValue(currentTime / duration);\n }\n }, [currentTime, duration, progress]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n const expand = useCallback(() => {\n Animated.parallel([\n Animated.timing(barHeight, {\n toValue: EXPANDED_HEIGHT,\n duration: 150,\n useNativeDriver: false,\n }),\n Animated.timing(thumbScale, {\n toValue: 1,\n duration: 150,\n useNativeDriver: true,\n }),\n ]).start();\n }, [barHeight, thumbScale]);\n\n const collapse = useCallback(() => {\n Animated.parallel([\n Animated.timing(barHeight, {\n toValue: COLLAPSED_HEIGHT,\n duration: 150,\n useNativeDriver: false,\n }),\n Animated.timing(thumbScale, {\n toValue: 0,\n duration: 150,\n useNativeDriver: true,\n }),\n ]).start();\n }, [barHeight, thumbScale]);\n\n const clampProgress = useCallback((pageX: number): number => {\n const width = containerWidthRef.current;\n if (width <= 0 || durationRef.current <= 0) return 0;\n const clamped = Math.max(0, Math.min(1, pageX / width));\n return clamped;\n }, []);\n\n const panResponder = useRef(\n PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onMoveShouldSetPanResponder: () => true,\n onPanResponderGrant: (evt) => {\n isScrubbing.current = true;\n expand();\n onSeekStart();\n\n const p = clampProgress(evt.nativeEvent.locationX);\n progress.setValue(p);\n onSeek(p * durationRef.current);\n },\n onPanResponderMove: (evt) => {\n const width = containerWidthRef.current;\n if (width <= 0) return;\n const p = Math.max(0, Math.min(1, evt.nativeEvent.locationX / width));\n progress.setValue(p);\n onSeek(p * durationRef.current);\n },\n onPanResponderRelease: () => {\n isScrubbing.current = false;\n collapse();\n onSeekEnd();\n },\n onPanResponderTerminate: () => {\n isScrubbing.current = false;\n collapse();\n onSeekEnd();\n },\n })\n ).current;\n\n const onLayout = useCallback((e: LayoutChangeEvent) => {\n containerWidthRef.current = e.nativeEvent.layout.width;\n }, []);\n\n const filledWidth = progress.interpolate({\n inputRange: [0, 1],\n outputRange: ['0%', '100%'],\n });\n\n return (\n <View style={styles.container} pointerEvents=\"box-none\">\n <View\n style={styles.hitArea}\n onLayout={onLayout}\n {...panResponder.panHandlers}\n >\n <Animated.View style={[styles.track, { height: barHeight }]}>\n <Animated.View style={[styles.filled, { width: filledWidth }]} />\n </Animated.View>\n <Animated.View\n style={[\n styles.thumb,\n {\n transform: [{ scale: thumbScale }],\n left: progress.interpolate({\n inputRange: [0, 1],\n outputRange: [-(THUMB_SIZE / 2), containerWidthRef.current > 0 ? containerWidthRef.current - THUMB_SIZE / 2 : 0],\n }),\n },\n ]}\n />\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n },\n hitArea: {\n height: HIT_SLOP_HEIGHT,\n justifyContent: 'flex-end',\n },\n track: {\n width: '100%',\n backgroundColor: 'rgba(255,255,255,0.25)',\n overflow: 'hidden',\n },\n filled: {\n height: '100%',\n backgroundColor: 'rgba(255,255,255,0.7)',\n },\n thumb: {\n position: 'absolute',\n bottom: -(THUMB_SIZE / 2) + COLLAPSED_HEIGHT / 2,\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: THUMB_SIZE / 2,\n backgroundColor: '#FFFFFF',\n },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"PlayheadBar.js","sourceRoot":"","sources":["../../src/components/PlayheadBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EACL,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,QAAQ,GAET,MAAM,cAAc,CAAC;AAUtB,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAW,EACX,QAAQ,EACR,WAAW,EACX,MAAM,EACN,SAAS,GACQ;IACjB,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,iBAAiB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAElC,qFAAqF;IACrF,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;IACvE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACzD,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAE9D,SAAS,CAAC,GAAG,EAAE;QACb,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;QACrC,IAAI,CAAC,WAAW,CAAC,OAAO,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC;YACjC,kBAAkB,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;YAC5B,eAAe,CAAC,QAAQ,CAAC,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,QAAQ,CAAC,QAAQ,CAAC;YAChB,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE;gBACzB,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,GAAG;gBACb,eAAe,EAAE,KAAK;aACvB,CAAC;YACF,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE;gBAC1B,OAAO,EAAE,CAAC;gBACV,QAAQ,EAAE,GAAG;gBACb,eAAe,EAAE,IAAI;aACtB,CAAC;SACH,CAAC,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAE5B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,QAAQ,CAAC,QAAQ,CAAC;YAChB,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE;gBACzB,OAAO,EAAE,gBAAgB;gBACzB,QAAQ,EAAE,GAAG;gBACb,eAAe,EAAE,KAAK;aACvB,CAAC;YACF,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE;gBAC1B,OAAO,EAAE,CAAC;gBACV,QAAQ,EAAE,GAAG;gBACb,eAAe,EAAE,IAAI;aACtB,CAAC;SACH,CAAC,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAE5B,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAS,EAAE,EAAE;QAC/C,kBAAkB,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAC5B,eAAe,CAAC,QAAQ,CAAC,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,KAAa,EAAU,EAAE;QAC1D,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC;QACxC,IAAI,KAAK,IAAI,CAAC,IAAI,WAAW,CAAC,OAAO,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;QACxD,OAAO,OAAO,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,YAAY,GAAG,MAAM,CACzB,YAAY,CAAC,MAAM,CAAC;QAClB,4BAA4B,EAAE,GAAG,EAAE,CAAC,IAAI;QACxC,2BAA2B,EAAE,GAAG,EAAE,CAAC,IAAI;QACvC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAC3B,MAAM,EAAE,CAAC;YACT,WAAW,EAAE,CAAC;YAEd,MAAM,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACnD,cAAc,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC;YACxC,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAO;YACvB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;YACtE,cAAc,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,qBAAqB,EAAE,GAAG,EAAE;YAC1B,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;QACd,CAAC;QACD,uBAAuB,EAAE,GAAG,EAAE;YAC5B,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;YAC5B,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;QACd,CAAC;KACF,CAAC,CACH,CAAC,OAAO,CAAC;IAEV,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAoB,EAAE,EAAE;QACpD,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;QACzC,iBAAiB,CAAC,OAAO,GAAG,KAAK,CAAC;QAClC,qDAAqD;QACrD,IAAI,WAAW,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;YACvD,eAAe,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,UAAU,CACrD;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CACtB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CACnB,IAAI,YAAY,CAAC,WAAW,CAAC,CAE7B;QAAA,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAC1D;UAAA,CAAC,+EAA+E,CAChF;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,GAAG,eAAe,GAAG,EAAE,CAAC,CAAC,EACjE;QAAA,EAAE,QAAQ,CAAC,IAAI,CACf;QAAA,CAAC,QAAQ,CAAC,IAAI,CACZ,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,KAAK;YACZ;gBACE,SAAS,EAAE;oBACT,EAAE,UAAU,EAAE,eAAe,EAAE;oBAC/B,EAAE,KAAK,EAAE,UAAU,EAAE;iBACtB;aACF;SACF,CAAC,EAEN;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC;QACT,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,CAAC;KACT;IACD,OAAO,EAAE;QACP,MAAM,EAAE,eAAe;QACvB,cAAc,EAAE,UAAU;KAC3B;IACD,KAAK,EAAE;QACL,KAAK,EAAE,MAAM;QACb,eAAe,EAAE,wBAAwB;QACzC,QAAQ,EAAE,QAAQ;KACnB;IACD,MAAM,EAAE;QACN,MAAM,EAAE,MAAM;QACd,eAAe,EAAE,uBAAuB;KACzC;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,gBAAgB,GAAG,CAAC;QAChD,oEAAoE;QACpE,IAAI,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC;QACvB,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,UAAU;QAClB,YAAY,EAAE,UAAU,GAAG,CAAC;QAC5B,eAAe,EAAE,SAAS;KAC3B;CACF,CAAC,CAAC","sourcesContent":["import * as React from 'react';\nimport { useRef, useCallback, useEffect, useState } from 'react';\nimport {\n View,\n StyleSheet,\n PanResponder,\n Animated,\n LayoutChangeEvent,\n} from 'react-native';\n\nexport type PlayheadBarProps = {\n currentTime: number;\n duration: number;\n onSeekStart: () => void;\n onSeek: (time: number) => void;\n onSeekEnd: () => void;\n};\n\nconst COLLAPSED_HEIGHT = 2;\nconst EXPANDED_HEIGHT = 4;\nconst THUMB_SIZE = 14;\nconst HIT_SLOP_HEIGHT = 30;\n\nexport default function PlayheadBar({\n currentTime,\n duration,\n onSeekStart,\n onSeek,\n onSeekEnd,\n}: PlayheadBarProps) {\n const [progressPercent, setProgressPercent] = useState(0);\n const containerWidthRef = useRef(0);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const isScrubbing = useRef(false);\n\n // These animations use non-native driver (height) or native driver (scale/translate)\n const barHeight = useRef(new Animated.Value(COLLAPSED_HEIGHT)).current;\n const thumbScale = useRef(new Animated.Value(0)).current;\n const thumbTranslateX = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n currentTimeRef.current = currentTime;\n if (!isScrubbing.current && duration > 0) {\n const p = currentTime / duration;\n setProgressPercent(p * 100);\n thumbTranslateX.setValue(p * containerWidthRef.current);\n }\n }, [currentTime, duration, thumbTranslateX]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n const expand = useCallback(() => {\n Animated.parallel([\n Animated.timing(barHeight, {\n toValue: EXPANDED_HEIGHT,\n duration: 150,\n useNativeDriver: false,\n }),\n Animated.timing(thumbScale, {\n toValue: 1,\n duration: 150,\n useNativeDriver: true,\n }),\n ]).start();\n }, [barHeight, thumbScale]);\n\n const collapse = useCallback(() => {\n Animated.parallel([\n Animated.timing(barHeight, {\n toValue: COLLAPSED_HEIGHT,\n duration: 150,\n useNativeDriver: false,\n }),\n Animated.timing(thumbScale, {\n toValue: 0,\n duration: 150,\n useNativeDriver: true,\n }),\n ]).start();\n }, [barHeight, thumbScale]);\n\n const updateProgress = useCallback((p: number) => {\n setProgressPercent(p * 100);\n thumbTranslateX.setValue(p * containerWidthRef.current);\n }, [thumbTranslateX]);\n\n const clampProgress = useCallback((pageX: number): number => {\n const width = containerWidthRef.current;\n if (width <= 0 || durationRef.current <= 0) return 0;\n const clamped = Math.max(0, Math.min(1, pageX / width));\n return clamped;\n }, []);\n\n const panResponder = useRef(\n PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onMoveShouldSetPanResponder: () => true,\n onPanResponderGrant: (evt) => {\n isScrubbing.current = true;\n expand();\n onSeekStart();\n\n const p = clampProgress(evt.nativeEvent.locationX);\n updateProgress(p);\n onSeek(p * durationRef.current);\n },\n onPanResponderMove: (evt) => {\n const width = containerWidthRef.current;\n if (width <= 0) return;\n const p = Math.max(0, Math.min(1, evt.nativeEvent.locationX / width));\n updateProgress(p);\n onSeek(p * durationRef.current);\n },\n onPanResponderRelease: () => {\n isScrubbing.current = false;\n collapse();\n onSeekEnd();\n },\n onPanResponderTerminate: () => {\n isScrubbing.current = false;\n collapse();\n onSeekEnd();\n },\n })\n ).current;\n\n const onLayout = useCallback((e: LayoutChangeEvent) => {\n const width = e.nativeEvent.layout.width;\n containerWidthRef.current = width;\n // Update thumb position when container width changes\n if (durationRef.current > 0) {\n const p = currentTimeRef.current / durationRef.current;\n thumbTranslateX.setValue(p * width);\n }\n }, [thumbTranslateX]);\n\n return (\n <View style={styles.container} pointerEvents=\"box-none\">\n <View\n style={styles.hitArea}\n onLayout={onLayout}\n {...panResponder.panHandlers}\n >\n <Animated.View style={[styles.track, { height: barHeight }]}>\n {/* Use percentage-based width without Animated to avoid native driver issues */}\n <View style={[styles.filled, { width: `${progressPercent}%` }]} />\n </Animated.View>\n <Animated.View\n style={[\n styles.thumb,\n {\n transform: [\n { translateX: thumbTranslateX },\n { scale: thumbScale },\n ],\n },\n ]}\n />\n </View>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n position: 'absolute',\n bottom: 0,\n left: 0,\n right: 0,\n },\n hitArea: {\n height: HIT_SLOP_HEIGHT,\n justifyContent: 'flex-end',\n },\n track: {\n width: '100%',\n backgroundColor: 'rgba(255,255,255,0.25)',\n overflow: 'hidden',\n },\n filled: {\n height: '100%',\n backgroundColor: 'rgba(255,255,255,0.7)',\n },\n thumb: {\n position: 'absolute',\n bottom: -(THUMB_SIZE / 2) + COLLAPSED_HEIGHT / 2,\n // Position at left edge minus half thumb width, translateX moves it\n left: -(THUMB_SIZE / 2),\n width: THUMB_SIZE,\n height: THUMB_SIZE,\n borderRadius: THUMB_SIZE / 2,\n backgroundColor: '#FFFFFF',\n },\n});\n"]}
|
package/build/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Built on native AVFoundation with Expo Modules
|
|
4
4
|
*/
|
|
5
5
|
import { type EventSubscription } from 'expo-modules-core';
|
|
6
|
-
import type { PanZoomVideoOptions } from './ExpoTwoStepVideo.types';
|
|
6
|
+
import type { PanZoomVideoOptions, PickVideoOptions, PickedVideo } from './ExpoTwoStepVideo.types';
|
|
7
7
|
/**
|
|
8
8
|
* Quality presets for video export
|
|
9
9
|
*/
|
|
@@ -412,7 +412,84 @@ export declare function transformVideo(options: TransformVideoOptions): Promise<
|
|
|
412
412
|
* ```
|
|
413
413
|
*/
|
|
414
414
|
export declare function loopSegment(options: LoopSegmentOptions): Promise<LoopResult>;
|
|
415
|
-
export type { PanZoomVideoOptions } from './ExpoTwoStepVideo.types';
|
|
415
|
+
export type { PanZoomVideoOptions, PickVideoOptions, PickedVideo, DoubleTapSkipEvent } from './ExpoTwoStepVideo.types';
|
|
416
|
+
/**
|
|
417
|
+
* Pick video(s) from the photo library using native PHPickerViewController
|
|
418
|
+
*
|
|
419
|
+
* This provides a native photo library picker that:
|
|
420
|
+
* - Supports albums and favorites
|
|
421
|
+
* - Handles iCloud video downloading automatically (with native progress UI)
|
|
422
|
+
* - Returns comprehensive metadata including creation date
|
|
423
|
+
* - No editing UI - just clean selection
|
|
424
|
+
*
|
|
425
|
+
* @param options - Picker options (optional)
|
|
426
|
+
* @returns Promise resolving to array of picked videos (empty if cancelled)
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```typescript
|
|
430
|
+
* // Pick a single video
|
|
431
|
+
* const videos = await TwoStepVideo.pickVideo();
|
|
432
|
+
* if (videos.length > 0) {
|
|
433
|
+
* const video = videos[0];
|
|
434
|
+
* console.log(`Selected: ${video.fileName}`);
|
|
435
|
+
* console.log(`Duration: ${video.duration}s`);
|
|
436
|
+
* console.log(`Size: ${video.width}x${video.height}`);
|
|
437
|
+
* console.log(`File size: ${video.fileSize} bytes`);
|
|
438
|
+
* console.log(`Created: ${video.creationDate}`);
|
|
439
|
+
*
|
|
440
|
+
* // Load the video for editing
|
|
441
|
+
* const asset = await TwoStepVideo.loadAsset({ uri: video.uri });
|
|
442
|
+
*
|
|
443
|
+
* // Clean up the temp file when done
|
|
444
|
+
* TwoStepVideo.cleanupPickedVideo(video.path);
|
|
445
|
+
* }
|
|
446
|
+
*
|
|
447
|
+
* // Pick multiple videos
|
|
448
|
+
* const videos = await TwoStepVideo.pickVideo({ selectionLimit: 5 });
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
export declare function pickVideo(options?: PickVideoOptions): Promise<PickedVideo[]>;
|
|
452
|
+
/**
|
|
453
|
+
* Clean up a specific picked video file from temp storage
|
|
454
|
+
* Call this when you're done with a video that was picked from the library
|
|
455
|
+
*
|
|
456
|
+
* @param path - File path to clean up (use the `path` property from PickedVideo)
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* const videos = await TwoStepVideo.pickVideo();
|
|
461
|
+
* const video = videos[0];
|
|
462
|
+
*
|
|
463
|
+
* // ... use the video ...
|
|
464
|
+
*
|
|
465
|
+
* // Clean up when done
|
|
466
|
+
* TwoStepVideo.cleanupPickedVideo(video.path);
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
export declare function cleanupPickedVideo(path: string): void;
|
|
470
|
+
/**
|
|
471
|
+
* Clean up all picked video files from temp storage
|
|
472
|
+
* Useful for cleanup when unmounting screens or releasing all resources
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```typescript
|
|
476
|
+
* // In a cleanup effect
|
|
477
|
+
* useEffect(() => {
|
|
478
|
+
* return () => {
|
|
479
|
+
* TwoStepVideo.cleanupAllPickedVideos();
|
|
480
|
+
* TwoStepVideo.releaseAll();
|
|
481
|
+
* };
|
|
482
|
+
* }, []);
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
export declare function cleanupAllPickedVideos(): void;
|
|
486
|
+
/**
|
|
487
|
+
* Get list of currently tracked picked video paths
|
|
488
|
+
* Useful for debugging or manual cleanup
|
|
489
|
+
*
|
|
490
|
+
* @returns Array of file paths for picked videos in temp storage
|
|
491
|
+
*/
|
|
492
|
+
export declare function getPickedVideoPaths(): string[];
|
|
416
493
|
/**
|
|
417
494
|
* Apply pan and zoom transformation to a video
|
|
418
495
|
*
|
|
@@ -580,6 +657,10 @@ declare const _default: {
|
|
|
580
657
|
readonly VERTICAL: string;
|
|
581
658
|
readonly BOTH: string;
|
|
582
659
|
};
|
|
660
|
+
pickVideo: typeof pickVideo;
|
|
661
|
+
cleanupPickedVideo: typeof cleanupPickedVideo;
|
|
662
|
+
cleanupAllPickedVideos: typeof cleanupAllPickedVideos;
|
|
663
|
+
getPickedVideoPaths: typeof getPickedVideoPaths;
|
|
583
664
|
loadAsset: typeof loadAsset;
|
|
584
665
|
loadAssetFromPhotos: typeof loadAssetFromPhotos;
|
|
585
666
|
validateVideoUri: typeof validateVideoUri;
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -239,6 +239,92 @@ export async function transformVideo(options) {
|
|
|
239
239
|
export async function loopSegment(options) {
|
|
240
240
|
return await ExpoTwoStepVideoModule.loopSegment(options.assetId, options.startTime, options.endTime, options.loopCount);
|
|
241
241
|
}
|
|
242
|
+
// MARK: - Media Picker Functions
|
|
243
|
+
/**
|
|
244
|
+
* Pick video(s) from the photo library using native PHPickerViewController
|
|
245
|
+
*
|
|
246
|
+
* This provides a native photo library picker that:
|
|
247
|
+
* - Supports albums and favorites
|
|
248
|
+
* - Handles iCloud video downloading automatically (with native progress UI)
|
|
249
|
+
* - Returns comprehensive metadata including creation date
|
|
250
|
+
* - No editing UI - just clean selection
|
|
251
|
+
*
|
|
252
|
+
* @param options - Picker options (optional)
|
|
253
|
+
* @returns Promise resolving to array of picked videos (empty if cancelled)
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* // Pick a single video
|
|
258
|
+
* const videos = await TwoStepVideo.pickVideo();
|
|
259
|
+
* if (videos.length > 0) {
|
|
260
|
+
* const video = videos[0];
|
|
261
|
+
* console.log(`Selected: ${video.fileName}`);
|
|
262
|
+
* console.log(`Duration: ${video.duration}s`);
|
|
263
|
+
* console.log(`Size: ${video.width}x${video.height}`);
|
|
264
|
+
* console.log(`File size: ${video.fileSize} bytes`);
|
|
265
|
+
* console.log(`Created: ${video.creationDate}`);
|
|
266
|
+
*
|
|
267
|
+
* // Load the video for editing
|
|
268
|
+
* const asset = await TwoStepVideo.loadAsset({ uri: video.uri });
|
|
269
|
+
*
|
|
270
|
+
* // Clean up the temp file when done
|
|
271
|
+
* TwoStepVideo.cleanupPickedVideo(video.path);
|
|
272
|
+
* }
|
|
273
|
+
*
|
|
274
|
+
* // Pick multiple videos
|
|
275
|
+
* const videos = await TwoStepVideo.pickVideo({ selectionLimit: 5 });
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
export async function pickVideo(options) {
|
|
279
|
+
return await ExpoTwoStepVideoModule.pickVideo(options?.selectionLimit);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Clean up a specific picked video file from temp storage
|
|
283
|
+
* Call this when you're done with a video that was picked from the library
|
|
284
|
+
*
|
|
285
|
+
* @param path - File path to clean up (use the `path` property from PickedVideo)
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* const videos = await TwoStepVideo.pickVideo();
|
|
290
|
+
* const video = videos[0];
|
|
291
|
+
*
|
|
292
|
+
* // ... use the video ...
|
|
293
|
+
*
|
|
294
|
+
* // Clean up when done
|
|
295
|
+
* TwoStepVideo.cleanupPickedVideo(video.path);
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
export function cleanupPickedVideo(path) {
|
|
299
|
+
ExpoTwoStepVideoModule.cleanupPickedVideo(path);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Clean up all picked video files from temp storage
|
|
303
|
+
* Useful for cleanup when unmounting screens or releasing all resources
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```typescript
|
|
307
|
+
* // In a cleanup effect
|
|
308
|
+
* useEffect(() => {
|
|
309
|
+
* return () => {
|
|
310
|
+
* TwoStepVideo.cleanupAllPickedVideos();
|
|
311
|
+
* TwoStepVideo.releaseAll();
|
|
312
|
+
* };
|
|
313
|
+
* }, []);
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
export function cleanupAllPickedVideos() {
|
|
317
|
+
ExpoTwoStepVideoModule.cleanupAllPickedVideos();
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Get list of currently tracked picked video paths
|
|
321
|
+
* Useful for debugging or manual cleanup
|
|
322
|
+
*
|
|
323
|
+
* @returns Array of file paths for picked videos in temp storage
|
|
324
|
+
*/
|
|
325
|
+
export function getPickedVideoPaths() {
|
|
326
|
+
return ExpoTwoStepVideoModule.getPickedVideoPaths();
|
|
327
|
+
}
|
|
242
328
|
/**
|
|
243
329
|
* Apply pan and zoom transformation to a video
|
|
244
330
|
*
|
|
@@ -428,6 +514,11 @@ export default {
|
|
|
428
514
|
// Constants
|
|
429
515
|
Quality,
|
|
430
516
|
Mirror,
|
|
517
|
+
// Media picker
|
|
518
|
+
pickVideo,
|
|
519
|
+
cleanupPickedVideo,
|
|
520
|
+
cleanupAllPickedVideos,
|
|
521
|
+
getPickedVideoPaths,
|
|
431
522
|
// Core functions
|
|
432
523
|
loadAsset,
|
|
433
524
|
loadAssetFromPhotos,
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TwoStepVideo - Professional video editing for React Native\n * Built on native AVFoundation with Expo Modules\n */\n\nimport { type EventSubscription } from 'expo-modules-core';\nimport { useState, useEffect } from 'react';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\nimport type { PanZoomVideoOptions } from './ExpoTwoStepVideo.types';\n\n// MARK: - Types\n\n/**\n * Quality presets for video export\n */\nexport type VideoQuality = 'low' | 'medium' | 'high' | 'highest';\n\n/**\n * Mirror axis options for video mirroring\n */\nexport type MirrorAxis = 'horizontal' | 'vertical' | 'both';\n\n/**\n * Video asset metadata returned after loading\n */\nexport interface VideoAsset {\n /** Unique identifier for this asset */\n id: string;\n /** Duration in seconds */\n duration: number;\n /** Video width in pixels */\n width: number;\n /** Video height in pixels */\n height: number;\n /** Frame rate (fps) */\n frameRate: number;\n /** Whether the video has audio */\n hasAudio: boolean;\n}\n\n/**\n * Video composition metadata\n */\nexport interface VideoComposition {\n /** Unique identifier for this composition */\n id: string;\n /** Total duration in seconds */\n duration: number;\n}\n\n/**\n * Time segment for multi-segment trimming\n */\nexport interface TimeSegment {\n /** Start time in seconds */\n start: number;\n /** End time in seconds */\n end: number;\n}\n\n/**\n * Export result containing output file information\n */\nexport interface ExportResult {\n /** File URI (e.g., \"file:///path/to/video.mp4\") */\n uri: string;\n /** Absolute file path */\n path: string;\n}\n\n/**\n * Export progress event\n */\nexport interface ExportProgressEvent {\n /** Progress from 0.0 to 1.0 */\n progress: number;\n /** Asset ID if exporting asset directly */\n assetId?: string;\n /** Composition ID if exporting composition */\n compositionId?: string;\n}\n\n/**\n * Options for loading a video asset\n */\nexport interface LoadAssetOptions {\n /** File URI (e.g., \"file:///path/to/video.mp4\") */\n uri: string;\n}\n\n/**\n * Options for trimming a video\n */\nexport interface TrimVideoOptions {\n /** Asset ID to trim */\n assetId: string;\n /** Start time in seconds */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n}\n\n/**\n * Options for multi-segment trimming\n */\nexport interface TrimMultipleOptions {\n /** Asset ID to trim */\n assetId: string;\n /** Array of time segments to extract and concatenate */\n segments: TimeSegment[];\n}\n\n/**\n * Options for generating thumbnails\n */\nexport interface GenerateThumbnailsOptions {\n /** Asset ID to generate thumbnails from */\n assetId: string;\n /** Array of times (in seconds) to extract thumbnails */\n times: number[];\n /** Optional size for thumbnails */\n size?: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Options for exporting a composition\n */\nexport interface ExportCompositionOptions {\n /** Composition ID to export */\n compositionId: string;\n /** Optional output URI (uses temp directory if not provided) */\n outputUri?: string;\n /** Quality preset (default: 'high') */\n quality?: VideoQuality;\n}\n\n/**\n * Options for exporting an asset directly\n */\nexport interface ExportAssetOptions {\n /** Asset ID to export */\n assetId: string;\n /** Optional output URI (uses temp directory if not provided) */\n outputUri?: string;\n /** Quality preset (default: 'high') */\n quality?: VideoQuality;\n}\n\n/**\n * Options for mirroring a video\n */\nexport interface MirrorVideoOptions {\n /** Asset ID to mirror */\n assetId: string;\n /** Axis to mirror on: 'horizontal', 'vertical', or 'both' */\n axis: MirrorAxis;\n /** Optional start time in seconds (for segment mirroring) */\n startTime?: number;\n /** Optional end time in seconds (for segment mirroring) */\n endTime?: number;\n}\n\n/**\n * Options for adjusting video speed\n */\nexport interface AdjustSpeedOptions {\n /** Asset ID to adjust */\n assetId: string;\n /** Speed multiplier (0.25 = 4x slower, 2.0 = 2x faster) */\n speed: number;\n /** Optional start time in seconds */\n startTime?: number;\n /** Optional end time in seconds */\n endTime?: number;\n}\n\n/**\n * Options for combined video transformation\n */\nexport interface TransformVideoOptions {\n /** Asset ID to transform */\n assetId: string;\n /** Speed multiplier (default: 1.0) */\n speed?: number;\n /** Optional mirror axis */\n mirrorAxis?: MirrorAxis;\n /** Optional start time in seconds (for trimming) */\n startTime?: number;\n /** Optional end time in seconds (for trimming) */\n endTime?: number;\n}\n\n/**\n * Options for looping a video segment\n */\nexport interface LoopSegmentOptions {\n /** Asset ID to loop */\n assetId: string;\n /** Start time of the segment to loop (in seconds) */\n startTime: number;\n /** End time of the segment to loop (in seconds) */\n endTime: number;\n /** Number of times to repeat (total plays = loopCount + 1) */\n loopCount: number;\n}\n\n/**\n * Result from looping a video segment\n */\nexport interface LoopResult {\n /** Unique identifier for this composition */\n id: string;\n /** Total duration in seconds */\n duration: number;\n /** Number of times the segment repeats */\n loopCount: number;\n /** Total number of times the segment plays */\n totalPlays: number;\n}\n\n// MARK: - Constants\n\n/**\n * Quality constants\n */\nexport const Quality = {\n LOW: ExpoTwoStepVideoModule.QUALITY_LOW,\n MEDIUM: ExpoTwoStepVideoModule.QUALITY_MEDIUM,\n HIGH: ExpoTwoStepVideoModule.QUALITY_HIGH,\n HIGHEST: ExpoTwoStepVideoModule.QUALITY_HIGHEST,\n} as const;\n\n/**\n * Mirror axis constants\n */\nexport const Mirror = {\n HORIZONTAL: ExpoTwoStepVideoModule.MIRROR_HORIZONTAL,\n VERTICAL: ExpoTwoStepVideoModule.MIRROR_VERTICAL,\n BOTH: ExpoTwoStepVideoModule.MIRROR_BOTH,\n} as const;\n\n// MARK: - API Functions\n\n/**\n * Load a video asset from a file URI\n *\n * @param options - Load options containing the file URI\n * @returns Promise resolving to video asset metadata\n *\n * @example\n * ```typescript\n * const asset = await TwoStepVideo.loadAsset({\n * uri: 'file:///path/to/video.mp4'\n * });\n * console.log(`Duration: ${asset.duration}s, Size: ${asset.width}x${asset.height}`);\n * ```\n */\nexport async function loadAsset(options: LoadAssetOptions): Promise<VideoAsset> {\n return await ExpoTwoStepVideoModule.loadAsset(options.uri);\n}\n\n/**\n * Load a video asset from the Photos library\n *\n * @param localIdentifier - PHAsset local identifier from the Photos library\n * @returns Promise resolving to video asset metadata\n *\n * @example\n * ```typescript\n * // After using expo-media-library to get a photo asset\n * const asset = await TwoStepVideo.loadAssetFromPhotos(photoAsset.id);\n * ```\n */\nexport async function loadAssetFromPhotos(localIdentifier: string): Promise<VideoAsset> {\n return await ExpoTwoStepVideoModule.loadAssetFromPhotos(localIdentifier);\n}\n\n/**\n * Validate a video file URI without loading the full asset\n * Useful for quick validation before processing\n *\n * @param uri - File URI to validate\n * @returns Promise resolving to true if valid video file\n *\n * @example\n * ```typescript\n * const isValid = await TwoStepVideo.validateVideoUri('file:///path/to/video.mp4');\n * if (isValid) {\n * const asset = await TwoStepVideo.loadAsset({ uri });\n * }\n * ```\n */\nexport async function validateVideoUri(uri: string): Promise<boolean> {\n return await ExpoTwoStepVideoModule.validateVideoUri(uri);\n}\n\n/**\n * Trim a video to a single time range\n *\n * @param options - Trim options including asset ID and time range\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * const asset = await TwoStepVideo.loadAsset({ uri });\n * const composition = await TwoStepVideo.trimVideo({\n * assetId: asset.id,\n * startTime: 5.0,\n * endTime: 15.0\n * });\n * console.log(`Trimmed to ${composition.duration}s`);\n * ```\n */\nexport async function trimVideo(options: TrimVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.trimVideo(\n options.assetId,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Trim a video to multiple segments and concatenate them\n *\n * @param options - Trim options including asset ID and segments\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * const composition = await TwoStepVideo.trimVideoMultiple({\n * assetId: asset.id,\n * segments: [\n * { start: 0, end: 3 }, // First 3 seconds\n * { start: 10, end: 13 }, // 3 seconds from middle\n * { start: 20, end: 23 } // 3 seconds from end\n * ]\n * });\n * ```\n */\nexport async function trimVideoMultiple(options: TrimMultipleOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.trimVideoMultiple(options.assetId, options.segments);\n}\n\n/**\n * Mirror a video horizontally, vertically, or both\n *\n * @param options - Mirror options including asset ID and axis\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Mirror horizontally (flip left-right, common for selfie videos)\n * const composition = await TwoStepVideo.mirrorVideo({\n * assetId: asset.id,\n * axis: 'horizontal'\n * });\n *\n * // Mirror a specific segment\n * const composition = await TwoStepVideo.mirrorVideo({\n * assetId: asset.id,\n * axis: 'vertical',\n * startTime: 5,\n * endTime: 10\n * });\n * ```\n */\nexport async function mirrorVideo(options: MirrorVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.mirrorVideo(\n options.assetId,\n options.axis,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Adjust the playback speed of a video\n *\n * @param options - Speed options including asset ID and speed multiplier\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Slow motion (0.5x speed = 2x slower)\n * const slowMo = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 0.5\n * });\n *\n * // Fast forward (2x speed)\n * const fastForward = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 2.0\n * });\n *\n * // Speed up a specific segment\n * const timelapse = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 4.0,\n * startTime: 10,\n * endTime: 30\n * });\n * ```\n */\nexport async function adjustSpeed(options: AdjustSpeedOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.adjustSpeed(\n options.assetId,\n options.speed,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Transform a video with combined trimming, mirroring, and speed adjustment\n * Use the player's `loop` prop for continuous playback looping\n *\n * @param options - Transform options including asset ID, speed, and mirror axis\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Mirror and slow down\n * const transformed = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * speed: 0.5,\n * mirrorAxis: 'horizontal'\n * });\n *\n * // Just mirror (speed defaults to 1.0)\n * const mirrored = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * mirrorAxis: 'both'\n * });\n *\n * // Transform a specific segment (trim + mirror + speed)\n * const segment = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * speed: 2.0,\n * mirrorAxis: 'horizontal',\n * startTime: 0,\n * endTime: 5\n * });\n *\n * // Play in loop mode\n * <TwoStepVideoView compositionId={segment.id} loop />\n * ```\n */\nexport async function transformVideo(options: TransformVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.transformVideo(\n options.assetId,\n options.speed,\n options.mirrorAxis,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Loop a segment of a video multiple times\n *\n * @param options - Loop options including segment time range and repeat count\n * @returns Promise resolving to loop result with duration info\n *\n * @example\n * ```typescript\n * // Loop a 2-second segment 3 times (plays 4 times total)\n * const looped = await TwoStepVideo.loopSegment({\n * assetId: asset.id,\n * startTime: 5,\n * endTime: 7,\n * loopCount: 3\n * });\n * console.log(`Duration: ${looped.duration}s (plays ${looped.totalPlays} times)`);\n *\n * // Create a perfect loop for social media\n * const perfectLoop = await TwoStepVideo.loopSegment({\n * assetId: asset.id,\n * startTime: 0,\n * endTime: 3,\n * loopCount: 4 // 15 seconds total (3s * 5 plays)\n * });\n * ```\n */\nexport async function loopSegment(options: LoopSegmentOptions): Promise<LoopResult> {\n return await ExpoTwoStepVideoModule.loopSegment(\n options.assetId,\n options.startTime,\n options.endTime,\n options.loopCount\n );\n}\n\n// Re-export PanZoomVideoOptions from types\nexport type { PanZoomVideoOptions } from './ExpoTwoStepVideo.types';\n\n/**\n * Apply pan and zoom transformation to a video\n *\n * This creates a composition with the pan/zoom baked in for export.\n * For real-time preview, use the gesture controls on TwoStepVideoView.\n *\n * @param options - Pan/zoom options including asset ID and transform values\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Get current pan/zoom from player and apply to export\n * const panZoomState = await playerRef.current.getPanZoomState();\n * const composition = await TwoStepVideo.panZoomVideo({\n * assetId: asset.id,\n * panX: panZoomState.panX,\n * panY: panZoomState.panY,\n * zoomLevel: panZoomState.zoomLevel\n * });\n *\n * // Export the pan/zoomed video\n * const result = await TwoStepVideo.exportVideo({\n * compositionId: composition.id,\n * quality: 'high'\n * });\n *\n * // Apply zoom only (no pan, centered)\n * const zoomed = await TwoStepVideo.panZoomVideo({\n * assetId: asset.id,\n * zoomLevel: 2.0 // 2x zoom, centered\n * });\n * ```\n */\nexport async function panZoomVideo(options: PanZoomVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.panZoomVideo(\n options.assetId,\n options.panX ?? 0,\n options.panY ?? 0,\n options.zoomLevel ?? 1.0,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Generate thumbnail images from a video at specific times\n *\n * @param options - Thumbnail generation options\n * @returns Promise resolving to array of base64 encoded PNG images\n *\n * @example\n * ```typescript\n * const thumbnails = await TwoStepVideo.generateThumbnails({\n * assetId: asset.id,\n * times: [1, 5, 10, 15],\n * size: { width: 300, height: 300 }\n * });\n *\n * // Use in Image component\n * <Image source={{ uri: `data:image/png;base64,${thumbnails[0]}` }} />\n * ```\n */\nexport async function generateThumbnails(\n options: GenerateThumbnailsOptions\n): Promise<string[]> {\n return await ExpoTwoStepVideoModule.generateThumbnails(\n options.assetId,\n options.times,\n options.size\n );\n}\n\n/**\n * Export a composition to a video file\n *\n * @param options - Export options including composition ID and quality\n * @returns Promise resolving to export result with file URI\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportVideo({\n * compositionId: composition.id,\n * quality: 'high'\n * });\n * console.log(`Exported to: ${result.uri}`);\n * ```\n */\nexport async function exportVideo(options: ExportCompositionOptions): Promise<ExportResult> {\n return await ExpoTwoStepVideoModule.exportVideo(\n options.compositionId,\n options.outputUri,\n options.quality\n );\n}\n\n/**\n * Export an asset directly without creating a composition\n * Useful for re-encoding or changing quality without trimming\n *\n * @param options - Export options including asset ID and quality\n * @returns Promise resolving to export result with file URI\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportAsset({\n * assetId: asset.id,\n * quality: 'medium'\n * });\n * ```\n */\nexport async function exportAsset(options: ExportAssetOptions): Promise<ExportResult> {\n return await ExpoTwoStepVideoModule.exportAsset(\n options.assetId,\n options.outputUri,\n options.quality\n );\n}\n\n/**\n * Clean up a temporary file\n * Call this after you're done with exported files in the temp directory\n *\n * @param uri - File URI to clean up\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportVideo({ ... });\n * // ... do something with result.uri\n * TwoStepVideo.cleanupFile(result.uri);\n * ```\n */\nexport function cleanupFile(uri: string): void {\n ExpoTwoStepVideoModule.cleanupFile(uri);\n}\n\n/**\n * Release an asset from memory\n * Call this when you're done with an asset to free up memory\n *\n * @param assetId - ID of the asset to release\n */\nexport function releaseAsset(assetId: string): void {\n ExpoTwoStepVideoModule.releaseAsset(assetId);\n}\n\n/**\n * Release a composition from memory\n *\n * @param compositionId - ID of the composition to release\n */\nexport function releaseComposition(compositionId: string): void {\n ExpoTwoStepVideoModule.releaseComposition(compositionId);\n}\n\n/**\n * Release all cached assets and compositions\n * Useful for cleanup when unmounting screens\n */\nexport function releaseAll(): void {\n ExpoTwoStepVideoModule.releaseAll();\n}\n\n// MARK: - Event Listeners\n\n/**\n * Add a listener for export progress events\n *\n * @param listener - Callback function receiving progress events\n * @returns Subscription object with remove() method\n *\n * @example\n * ```typescript\n * const subscription = TwoStepVideo.addExportProgressListener((event) => {\n * console.log(`Export progress: ${Math.round(event.progress * 100)}%`);\n * setProgress(event.progress);\n * });\n *\n * // Later, remove the listener\n * subscription.remove();\n * ```\n */\nexport function addExportProgressListener(\n listener: (event: ExportProgressEvent) => void\n): EventSubscription {\n return ExpoTwoStepVideoModule.addListener('onExportProgress', listener);\n}\n\n// MARK: - Helper Hook (for React)\n\n/**\n * React hook for managing export progress\n * Only works in React components\n *\n * @returns Current export progress (0.0 to 1.0)\n *\n * @example\n * ```typescript\n * function VideoEditor() {\n * const progress = useExportProgress();\n *\n * return (\n * <View>\n * <Text>Export Progress: {Math.round(progress * 100)}%</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useExportProgress(): number {\n const [progress, setProgress] = useState(0);\n\n useEffect(() => {\n const subscription = addExportProgressListener((event) => {\n setProgress(event.progress);\n });\n\n return () => subscription.remove();\n }, []);\n\n return progress;\n}\n\n// MARK: - Exports\n\nexport default {\n // Constants\n Quality,\n Mirror,\n\n // Core functions\n loadAsset,\n loadAssetFromPhotos,\n validateVideoUri,\n trimVideo,\n trimVideoMultiple,\n generateThumbnails,\n exportVideo,\n exportAsset,\n\n // Transformation functions\n mirrorVideo,\n adjustSpeed,\n transformVideo,\n loopSegment,\n panZoomVideo,\n\n // Memory management\n cleanupFile,\n releaseAsset,\n releaseComposition,\n releaseAll,\n\n // Events\n addExportProgressListener,\n\n // React hook\n useExportProgress,\n};\n\n// MARK: - View Component Export\n\nexport { default as TwoStepVideoView } from './ExpoTwoStepVideoView';\nexport { default as TwoStepPlayerControllerView } from './TwoStepPlayerControllerView';\nexport { default as VideoScrubber } from './VideoScrubber';\nexport { useVideoScrubber } from './hooks/useVideoScrubber';\nexport type {\n BaseVideoViewRef,\n TwoStepVideoViewProps,\n TwoStepVideoViewRef,\n TwoStepPlayerControllerViewProps,\n TwoStepPlayerControllerViewRef,\n PlaybackStatusEvent,\n ProgressEvent,\n ErrorEvent,\n PanZoomState,\n PanZoomChangeEvent,\n} from './ExpoTwoStepVideo.types';\nexport type {\n VideoScrubberProps,\n VideoScrubberRef,\n VideoScrubberTheme,\n} from './VideoScrubber.types';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TwoStepVideo - Professional video editing for React Native\n * Built on native AVFoundation with Expo Modules\n */\n\nimport { type EventSubscription } from 'expo-modules-core';\nimport { useState, useEffect } from 'react';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\nimport type { PanZoomVideoOptions, PickVideoOptions, PickedVideo } from './ExpoTwoStepVideo.types';\n\n// MARK: - Types\n\n/**\n * Quality presets for video export\n */\nexport type VideoQuality = 'low' | 'medium' | 'high' | 'highest';\n\n/**\n * Mirror axis options for video mirroring\n */\nexport type MirrorAxis = 'horizontal' | 'vertical' | 'both';\n\n/**\n * Video asset metadata returned after loading\n */\nexport interface VideoAsset {\n /** Unique identifier for this asset */\n id: string;\n /** Duration in seconds */\n duration: number;\n /** Video width in pixels */\n width: number;\n /** Video height in pixels */\n height: number;\n /** Frame rate (fps) */\n frameRate: number;\n /** Whether the video has audio */\n hasAudio: boolean;\n}\n\n/**\n * Video composition metadata\n */\nexport interface VideoComposition {\n /** Unique identifier for this composition */\n id: string;\n /** Total duration in seconds */\n duration: number;\n}\n\n/**\n * Time segment for multi-segment trimming\n */\nexport interface TimeSegment {\n /** Start time in seconds */\n start: number;\n /** End time in seconds */\n end: number;\n}\n\n/**\n * Export result containing output file information\n */\nexport interface ExportResult {\n /** File URI (e.g., \"file:///path/to/video.mp4\") */\n uri: string;\n /** Absolute file path */\n path: string;\n}\n\n/**\n * Export progress event\n */\nexport interface ExportProgressEvent {\n /** Progress from 0.0 to 1.0 */\n progress: number;\n /** Asset ID if exporting asset directly */\n assetId?: string;\n /** Composition ID if exporting composition */\n compositionId?: string;\n}\n\n/**\n * Options for loading a video asset\n */\nexport interface LoadAssetOptions {\n /** File URI (e.g., \"file:///path/to/video.mp4\") */\n uri: string;\n}\n\n/**\n * Options for trimming a video\n */\nexport interface TrimVideoOptions {\n /** Asset ID to trim */\n assetId: string;\n /** Start time in seconds */\n startTime: number;\n /** End time in seconds */\n endTime: number;\n}\n\n/**\n * Options for multi-segment trimming\n */\nexport interface TrimMultipleOptions {\n /** Asset ID to trim */\n assetId: string;\n /** Array of time segments to extract and concatenate */\n segments: TimeSegment[];\n}\n\n/**\n * Options for generating thumbnails\n */\nexport interface GenerateThumbnailsOptions {\n /** Asset ID to generate thumbnails from */\n assetId: string;\n /** Array of times (in seconds) to extract thumbnails */\n times: number[];\n /** Optional size for thumbnails */\n size?: {\n width: number;\n height: number;\n };\n}\n\n/**\n * Options for exporting a composition\n */\nexport interface ExportCompositionOptions {\n /** Composition ID to export */\n compositionId: string;\n /** Optional output URI (uses temp directory if not provided) */\n outputUri?: string;\n /** Quality preset (default: 'high') */\n quality?: VideoQuality;\n}\n\n/**\n * Options for exporting an asset directly\n */\nexport interface ExportAssetOptions {\n /** Asset ID to export */\n assetId: string;\n /** Optional output URI (uses temp directory if not provided) */\n outputUri?: string;\n /** Quality preset (default: 'high') */\n quality?: VideoQuality;\n}\n\n/**\n * Options for mirroring a video\n */\nexport interface MirrorVideoOptions {\n /** Asset ID to mirror */\n assetId: string;\n /** Axis to mirror on: 'horizontal', 'vertical', or 'both' */\n axis: MirrorAxis;\n /** Optional start time in seconds (for segment mirroring) */\n startTime?: number;\n /** Optional end time in seconds (for segment mirroring) */\n endTime?: number;\n}\n\n/**\n * Options for adjusting video speed\n */\nexport interface AdjustSpeedOptions {\n /** Asset ID to adjust */\n assetId: string;\n /** Speed multiplier (0.25 = 4x slower, 2.0 = 2x faster) */\n speed: number;\n /** Optional start time in seconds */\n startTime?: number;\n /** Optional end time in seconds */\n endTime?: number;\n}\n\n/**\n * Options for combined video transformation\n */\nexport interface TransformVideoOptions {\n /** Asset ID to transform */\n assetId: string;\n /** Speed multiplier (default: 1.0) */\n speed?: number;\n /** Optional mirror axis */\n mirrorAxis?: MirrorAxis;\n /** Optional start time in seconds (for trimming) */\n startTime?: number;\n /** Optional end time in seconds (for trimming) */\n endTime?: number;\n}\n\n/**\n * Options for looping a video segment\n */\nexport interface LoopSegmentOptions {\n /** Asset ID to loop */\n assetId: string;\n /** Start time of the segment to loop (in seconds) */\n startTime: number;\n /** End time of the segment to loop (in seconds) */\n endTime: number;\n /** Number of times to repeat (total plays = loopCount + 1) */\n loopCount: number;\n}\n\n/**\n * Result from looping a video segment\n */\nexport interface LoopResult {\n /** Unique identifier for this composition */\n id: string;\n /** Total duration in seconds */\n duration: number;\n /** Number of times the segment repeats */\n loopCount: number;\n /** Total number of times the segment plays */\n totalPlays: number;\n}\n\n// MARK: - Constants\n\n/**\n * Quality constants\n */\nexport const Quality = {\n LOW: ExpoTwoStepVideoModule.QUALITY_LOW,\n MEDIUM: ExpoTwoStepVideoModule.QUALITY_MEDIUM,\n HIGH: ExpoTwoStepVideoModule.QUALITY_HIGH,\n HIGHEST: ExpoTwoStepVideoModule.QUALITY_HIGHEST,\n} as const;\n\n/**\n * Mirror axis constants\n */\nexport const Mirror = {\n HORIZONTAL: ExpoTwoStepVideoModule.MIRROR_HORIZONTAL,\n VERTICAL: ExpoTwoStepVideoModule.MIRROR_VERTICAL,\n BOTH: ExpoTwoStepVideoModule.MIRROR_BOTH,\n} as const;\n\n// MARK: - API Functions\n\n/**\n * Load a video asset from a file URI\n *\n * @param options - Load options containing the file URI\n * @returns Promise resolving to video asset metadata\n *\n * @example\n * ```typescript\n * const asset = await TwoStepVideo.loadAsset({\n * uri: 'file:///path/to/video.mp4'\n * });\n * console.log(`Duration: ${asset.duration}s, Size: ${asset.width}x${asset.height}`);\n * ```\n */\nexport async function loadAsset(options: LoadAssetOptions): Promise<VideoAsset> {\n return await ExpoTwoStepVideoModule.loadAsset(options.uri);\n}\n\n/**\n * Load a video asset from the Photos library\n *\n * @param localIdentifier - PHAsset local identifier from the Photos library\n * @returns Promise resolving to video asset metadata\n *\n * @example\n * ```typescript\n * // After using expo-media-library to get a photo asset\n * const asset = await TwoStepVideo.loadAssetFromPhotos(photoAsset.id);\n * ```\n */\nexport async function loadAssetFromPhotos(localIdentifier: string): Promise<VideoAsset> {\n return await ExpoTwoStepVideoModule.loadAssetFromPhotos(localIdentifier);\n}\n\n/**\n * Validate a video file URI without loading the full asset\n * Useful for quick validation before processing\n *\n * @param uri - File URI to validate\n * @returns Promise resolving to true if valid video file\n *\n * @example\n * ```typescript\n * const isValid = await TwoStepVideo.validateVideoUri('file:///path/to/video.mp4');\n * if (isValid) {\n * const asset = await TwoStepVideo.loadAsset({ uri });\n * }\n * ```\n */\nexport async function validateVideoUri(uri: string): Promise<boolean> {\n return await ExpoTwoStepVideoModule.validateVideoUri(uri);\n}\n\n/**\n * Trim a video to a single time range\n *\n * @param options - Trim options including asset ID and time range\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * const asset = await TwoStepVideo.loadAsset({ uri });\n * const composition = await TwoStepVideo.trimVideo({\n * assetId: asset.id,\n * startTime: 5.0,\n * endTime: 15.0\n * });\n * console.log(`Trimmed to ${composition.duration}s`);\n * ```\n */\nexport async function trimVideo(options: TrimVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.trimVideo(\n options.assetId,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Trim a video to multiple segments and concatenate them\n *\n * @param options - Trim options including asset ID and segments\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * const composition = await TwoStepVideo.trimVideoMultiple({\n * assetId: asset.id,\n * segments: [\n * { start: 0, end: 3 }, // First 3 seconds\n * { start: 10, end: 13 }, // 3 seconds from middle\n * { start: 20, end: 23 } // 3 seconds from end\n * ]\n * });\n * ```\n */\nexport async function trimVideoMultiple(options: TrimMultipleOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.trimVideoMultiple(options.assetId, options.segments);\n}\n\n/**\n * Mirror a video horizontally, vertically, or both\n *\n * @param options - Mirror options including asset ID and axis\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Mirror horizontally (flip left-right, common for selfie videos)\n * const composition = await TwoStepVideo.mirrorVideo({\n * assetId: asset.id,\n * axis: 'horizontal'\n * });\n *\n * // Mirror a specific segment\n * const composition = await TwoStepVideo.mirrorVideo({\n * assetId: asset.id,\n * axis: 'vertical',\n * startTime: 5,\n * endTime: 10\n * });\n * ```\n */\nexport async function mirrorVideo(options: MirrorVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.mirrorVideo(\n options.assetId,\n options.axis,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Adjust the playback speed of a video\n *\n * @param options - Speed options including asset ID and speed multiplier\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Slow motion (0.5x speed = 2x slower)\n * const slowMo = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 0.5\n * });\n *\n * // Fast forward (2x speed)\n * const fastForward = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 2.0\n * });\n *\n * // Speed up a specific segment\n * const timelapse = await TwoStepVideo.adjustSpeed({\n * assetId: asset.id,\n * speed: 4.0,\n * startTime: 10,\n * endTime: 30\n * });\n * ```\n */\nexport async function adjustSpeed(options: AdjustSpeedOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.adjustSpeed(\n options.assetId,\n options.speed,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Transform a video with combined trimming, mirroring, and speed adjustment\n * Use the player's `loop` prop for continuous playback looping\n *\n * @param options - Transform options including asset ID, speed, and mirror axis\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Mirror and slow down\n * const transformed = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * speed: 0.5,\n * mirrorAxis: 'horizontal'\n * });\n *\n * // Just mirror (speed defaults to 1.0)\n * const mirrored = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * mirrorAxis: 'both'\n * });\n *\n * // Transform a specific segment (trim + mirror + speed)\n * const segment = await TwoStepVideo.transformVideo({\n * assetId: asset.id,\n * speed: 2.0,\n * mirrorAxis: 'horizontal',\n * startTime: 0,\n * endTime: 5\n * });\n *\n * // Play in loop mode\n * <TwoStepVideoView compositionId={segment.id} loop />\n * ```\n */\nexport async function transformVideo(options: TransformVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.transformVideo(\n options.assetId,\n options.speed,\n options.mirrorAxis,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Loop a segment of a video multiple times\n *\n * @param options - Loop options including segment time range and repeat count\n * @returns Promise resolving to loop result with duration info\n *\n * @example\n * ```typescript\n * // Loop a 2-second segment 3 times (plays 4 times total)\n * const looped = await TwoStepVideo.loopSegment({\n * assetId: asset.id,\n * startTime: 5,\n * endTime: 7,\n * loopCount: 3\n * });\n * console.log(`Duration: ${looped.duration}s (plays ${looped.totalPlays} times)`);\n *\n * // Create a perfect loop for social media\n * const perfectLoop = await TwoStepVideo.loopSegment({\n * assetId: asset.id,\n * startTime: 0,\n * endTime: 3,\n * loopCount: 4 // 15 seconds total (3s * 5 plays)\n * });\n * ```\n */\nexport async function loopSegment(options: LoopSegmentOptions): Promise<LoopResult> {\n return await ExpoTwoStepVideoModule.loopSegment(\n options.assetId,\n options.startTime,\n options.endTime,\n options.loopCount\n );\n}\n\n// Re-export types from types file\nexport type { PanZoomVideoOptions, PickVideoOptions, PickedVideo, DoubleTapSkipEvent } from './ExpoTwoStepVideo.types';\n\n// MARK: - Media Picker Functions\n\n/**\n * Pick video(s) from the photo library using native PHPickerViewController\n *\n * This provides a native photo library picker that:\n * - Supports albums and favorites\n * - Handles iCloud video downloading automatically (with native progress UI)\n * - Returns comprehensive metadata including creation date\n * - No editing UI - just clean selection\n *\n * @param options - Picker options (optional)\n * @returns Promise resolving to array of picked videos (empty if cancelled)\n *\n * @example\n * ```typescript\n * // Pick a single video\n * const videos = await TwoStepVideo.pickVideo();\n * if (videos.length > 0) {\n * const video = videos[0];\n * console.log(`Selected: ${video.fileName}`);\n * console.log(`Duration: ${video.duration}s`);\n * console.log(`Size: ${video.width}x${video.height}`);\n * console.log(`File size: ${video.fileSize} bytes`);\n * console.log(`Created: ${video.creationDate}`);\n *\n * // Load the video for editing\n * const asset = await TwoStepVideo.loadAsset({ uri: video.uri });\n *\n * // Clean up the temp file when done\n * TwoStepVideo.cleanupPickedVideo(video.path);\n * }\n *\n * // Pick multiple videos\n * const videos = await TwoStepVideo.pickVideo({ selectionLimit: 5 });\n * ```\n */\nexport async function pickVideo(options?: PickVideoOptions): Promise<PickedVideo[]> {\n return await ExpoTwoStepVideoModule.pickVideo(options?.selectionLimit);\n}\n\n/**\n * Clean up a specific picked video file from temp storage\n * Call this when you're done with a video that was picked from the library\n *\n * @param path - File path to clean up (use the `path` property from PickedVideo)\n *\n * @example\n * ```typescript\n * const videos = await TwoStepVideo.pickVideo();\n * const video = videos[0];\n *\n * // ... use the video ...\n *\n * // Clean up when done\n * TwoStepVideo.cleanupPickedVideo(video.path);\n * ```\n */\nexport function cleanupPickedVideo(path: string): void {\n ExpoTwoStepVideoModule.cleanupPickedVideo(path);\n}\n\n/**\n * Clean up all picked video files from temp storage\n * Useful for cleanup when unmounting screens or releasing all resources\n *\n * @example\n * ```typescript\n * // In a cleanup effect\n * useEffect(() => {\n * return () => {\n * TwoStepVideo.cleanupAllPickedVideos();\n * TwoStepVideo.releaseAll();\n * };\n * }, []);\n * ```\n */\nexport function cleanupAllPickedVideos(): void {\n ExpoTwoStepVideoModule.cleanupAllPickedVideos();\n}\n\n/**\n * Get list of currently tracked picked video paths\n * Useful for debugging or manual cleanup\n *\n * @returns Array of file paths for picked videos in temp storage\n */\nexport function getPickedVideoPaths(): string[] {\n return ExpoTwoStepVideoModule.getPickedVideoPaths();\n}\n\n/**\n * Apply pan and zoom transformation to a video\n *\n * This creates a composition with the pan/zoom baked in for export.\n * For real-time preview, use the gesture controls on TwoStepVideoView.\n *\n * @param options - Pan/zoom options including asset ID and transform values\n * @returns Promise resolving to composition metadata\n *\n * @example\n * ```typescript\n * // Get current pan/zoom from player and apply to export\n * const panZoomState = await playerRef.current.getPanZoomState();\n * const composition = await TwoStepVideo.panZoomVideo({\n * assetId: asset.id,\n * panX: panZoomState.panX,\n * panY: panZoomState.panY,\n * zoomLevel: panZoomState.zoomLevel\n * });\n *\n * // Export the pan/zoomed video\n * const result = await TwoStepVideo.exportVideo({\n * compositionId: composition.id,\n * quality: 'high'\n * });\n *\n * // Apply zoom only (no pan, centered)\n * const zoomed = await TwoStepVideo.panZoomVideo({\n * assetId: asset.id,\n * zoomLevel: 2.0 // 2x zoom, centered\n * });\n * ```\n */\nexport async function panZoomVideo(options: PanZoomVideoOptions): Promise<VideoComposition> {\n return await ExpoTwoStepVideoModule.panZoomVideo(\n options.assetId,\n options.panX ?? 0,\n options.panY ?? 0,\n options.zoomLevel ?? 1.0,\n options.startTime,\n options.endTime\n );\n}\n\n/**\n * Generate thumbnail images from a video at specific times\n *\n * @param options - Thumbnail generation options\n * @returns Promise resolving to array of base64 encoded PNG images\n *\n * @example\n * ```typescript\n * const thumbnails = await TwoStepVideo.generateThumbnails({\n * assetId: asset.id,\n * times: [1, 5, 10, 15],\n * size: { width: 300, height: 300 }\n * });\n *\n * // Use in Image component\n * <Image source={{ uri: `data:image/png;base64,${thumbnails[0]}` }} />\n * ```\n */\nexport async function generateThumbnails(\n options: GenerateThumbnailsOptions\n): Promise<string[]> {\n return await ExpoTwoStepVideoModule.generateThumbnails(\n options.assetId,\n options.times,\n options.size\n );\n}\n\n/**\n * Export a composition to a video file\n *\n * @param options - Export options including composition ID and quality\n * @returns Promise resolving to export result with file URI\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportVideo({\n * compositionId: composition.id,\n * quality: 'high'\n * });\n * console.log(`Exported to: ${result.uri}`);\n * ```\n */\nexport async function exportVideo(options: ExportCompositionOptions): Promise<ExportResult> {\n return await ExpoTwoStepVideoModule.exportVideo(\n options.compositionId,\n options.outputUri,\n options.quality\n );\n}\n\n/**\n * Export an asset directly without creating a composition\n * Useful for re-encoding or changing quality without trimming\n *\n * @param options - Export options including asset ID and quality\n * @returns Promise resolving to export result with file URI\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportAsset({\n * assetId: asset.id,\n * quality: 'medium'\n * });\n * ```\n */\nexport async function exportAsset(options: ExportAssetOptions): Promise<ExportResult> {\n return await ExpoTwoStepVideoModule.exportAsset(\n options.assetId,\n options.outputUri,\n options.quality\n );\n}\n\n/**\n * Clean up a temporary file\n * Call this after you're done with exported files in the temp directory\n *\n * @param uri - File URI to clean up\n *\n * @example\n * ```typescript\n * const result = await TwoStepVideo.exportVideo({ ... });\n * // ... do something with result.uri\n * TwoStepVideo.cleanupFile(result.uri);\n * ```\n */\nexport function cleanupFile(uri: string): void {\n ExpoTwoStepVideoModule.cleanupFile(uri);\n}\n\n/**\n * Release an asset from memory\n * Call this when you're done with an asset to free up memory\n *\n * @param assetId - ID of the asset to release\n */\nexport function releaseAsset(assetId: string): void {\n ExpoTwoStepVideoModule.releaseAsset(assetId);\n}\n\n/**\n * Release a composition from memory\n *\n * @param compositionId - ID of the composition to release\n */\nexport function releaseComposition(compositionId: string): void {\n ExpoTwoStepVideoModule.releaseComposition(compositionId);\n}\n\n/**\n * Release all cached assets and compositions\n * Useful for cleanup when unmounting screens\n */\nexport function releaseAll(): void {\n ExpoTwoStepVideoModule.releaseAll();\n}\n\n// MARK: - Event Listeners\n\n/**\n * Add a listener for export progress events\n *\n * @param listener - Callback function receiving progress events\n * @returns Subscription object with remove() method\n *\n * @example\n * ```typescript\n * const subscription = TwoStepVideo.addExportProgressListener((event) => {\n * console.log(`Export progress: ${Math.round(event.progress * 100)}%`);\n * setProgress(event.progress);\n * });\n *\n * // Later, remove the listener\n * subscription.remove();\n * ```\n */\nexport function addExportProgressListener(\n listener: (event: ExportProgressEvent) => void\n): EventSubscription {\n return ExpoTwoStepVideoModule.addListener('onExportProgress', listener);\n}\n\n// MARK: - Helper Hook (for React)\n\n/**\n * React hook for managing export progress\n * Only works in React components\n *\n * @returns Current export progress (0.0 to 1.0)\n *\n * @example\n * ```typescript\n * function VideoEditor() {\n * const progress = useExportProgress();\n *\n * return (\n * <View>\n * <Text>Export Progress: {Math.round(progress * 100)}%</Text>\n * </View>\n * );\n * }\n * ```\n */\nexport function useExportProgress(): number {\n const [progress, setProgress] = useState(0);\n\n useEffect(() => {\n const subscription = addExportProgressListener((event) => {\n setProgress(event.progress);\n });\n\n return () => subscription.remove();\n }, []);\n\n return progress;\n}\n\n// MARK: - Exports\n\nexport default {\n // Constants\n Quality,\n Mirror,\n\n // Media picker\n pickVideo,\n cleanupPickedVideo,\n cleanupAllPickedVideos,\n getPickedVideoPaths,\n\n // Core functions\n loadAsset,\n loadAssetFromPhotos,\n validateVideoUri,\n trimVideo,\n trimVideoMultiple,\n generateThumbnails,\n exportVideo,\n exportAsset,\n\n // Transformation functions\n mirrorVideo,\n adjustSpeed,\n transformVideo,\n loopSegment,\n panZoomVideo,\n\n // Memory management\n cleanupFile,\n releaseAsset,\n releaseComposition,\n releaseAll,\n\n // Events\n addExportProgressListener,\n\n // React hook\n useExportProgress,\n};\n\n// MARK: - View Component Export\n\nexport { default as TwoStepVideoView } from './ExpoTwoStepVideoView';\nexport { default as TwoStepPlayerControllerView } from './TwoStepPlayerControllerView';\nexport { default as VideoScrubber } from './VideoScrubber';\nexport { useVideoScrubber } from './hooks/useVideoScrubber';\nexport type {\n BaseVideoViewRef,\n TwoStepVideoViewProps,\n TwoStepVideoViewRef,\n TwoStepPlayerControllerViewProps,\n TwoStepPlayerControllerViewRef,\n PlaybackStatusEvent,\n ProgressEvent,\n ErrorEvent,\n PanZoomState,\n PanZoomChangeEvent,\n} from './ExpoTwoStepVideo.types';\nexport type {\n VideoScrubberProps,\n VideoScrubberRef,\n VideoScrubberTheme,\n} from './VideoScrubber.types';\n"]}
|