diff options
author | Ivan Chen <ivan@tagg.id> | 2021-07-16 15:48:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-16 15:48:31 -0400 |
commit | 6161a73ec29dbcf4ffb9ee90d86a3c4e46eb58dc (patch) | |
tree | ed8b2fb574e598f25d1db0e0204504ebe472924b /src | |
parent | 0838a170c84c32d502377c17825d7bf8d21300d0 (diff) | |
parent | 580774b43f2919c4fb4230308afb06cc9891a5e9 (diff) |
Merge pull request #503 from sotech117/tma954-video-trimming-component
[TMA-954] Video Trimming Component
Diffstat (limited to 'src')
-rw-r--r-- | src/components/comments/ZoomInCropper.tsx | 58 | ||||
-rw-r--r-- | src/components/moments/trimmer.tsx | 98 | ||||
-rw-r--r-- | src/screens/profile/CaptionScreen.tsx | 2 | ||||
-rw-r--r-- | src/utils/camera.ts | 15 |
4 files changed, 151 insertions, 22 deletions
diff --git a/src/components/comments/ZoomInCropper.tsx b/src/components/comments/ZoomInCropper.tsx index 3581168d..b4333cbb 100644 --- a/src/components/comments/ZoomInCropper.tsx +++ b/src/components/comments/ZoomInCropper.tsx @@ -9,13 +9,14 @@ import CloseIcon from '../../assets/ionicons/close-outline.svg'; import {MainStackParams} from '../../routes'; import { cropVideo, + trimVideo, HeaderHeight, SCREEN_HEIGHT, SCREEN_WIDTH, } from '../../utils'; import {TaggSquareButton, TaggLoadingIndicator} from '../common'; import ReactNativeZoomableView from '@dudigital/react-native-zoomable-view/src/ReactNativeZoomableView'; -import Video from 'react-native-video'; +import {TrimmerPlayer} from '../moments/trimmer'; type ZoomInCropperRouteProps = RouteProp<MainStackParams, 'ZoomInCropper'>; type ZoomInCropperNavigationProps = StackNavigationProp< @@ -52,6 +53,15 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ cropOffsetY?: number; }>({}); + // Stores the current trim endpoints + const [trimEnds, setTrimEnds] = useState<{ + end: number; + start: number; + }>({ + end: 60, + start: 0, + }); + const checkIfUriImage = (uri: string) => { return ( uri.endsWith('jpg') || @@ -138,20 +148,25 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ })); } setCropLoading(true); - cropVideo( + trimVideo( media.uri, - (croppedURL: string) => { - setCropLoading(false); - navigation.navigate('CaptionScreen', { - screenType, - media: { - uri: croppedURL, - isVideo: true, + (trimmedURL: string) => + cropVideo( + trimmedURL, + (croppedURL: string) => { + setCropLoading(false); + navigation.navigate('CaptionScreen', { + screenType, + media: { + uri: croppedURL, + isVideo: true, + }, + selectedCategory, + }); }, - selectedCategory, - }); - }, - videoCrop, + videoCrop, + ), + trimEnds, ); } }; @@ -308,22 +323,23 @@ export const ZoomInCropper: React.FC<ZoomInCropperProps> = ({ }} style={styles.zoomView}> <View style={styles.videoParent} ref={vidRef}> - <Video - source={{uri: media.uri}} - volume={1} - style={[ + <TrimmerPlayer + hideTrimmer={false} + source={media.uri} + videoStyles={[ styles.media, { height: SCREEN_WIDTH / aspectRatio, }, ]} - repeat={true} - resizeMode={'contain'} - onLoad={(response) => { - const {width, height} = response.naturalSize; + handleLoad={(response: {width: number; height: number}) => { + const {width, height} = response; setOrigDimensions([width, height]); setAspectRatio(width / height); }} + onChangedEndpoints={(response: {start: number; end: number}) => + setTrimEnds(response) + } /> </View> </ReactNativeZoomableView> diff --git a/src/components/moments/trimmer.tsx b/src/components/moments/trimmer.tsx new file mode 100644 index 00000000..c99eaa6f --- /dev/null +++ b/src/components/moments/trimmer.tsx @@ -0,0 +1,98 @@ +import React, {useEffect, useState} from 'react'; +import Video from 'react-native-video'; +import {Trimmer} from 'react-native-video-processing'; +import {useRef} from 'react'; +import {SCREEN_WIDTH} from '../../utils'; + +export const TrimmerPlayer: React.FC<{ + source: string; + videoStyles: Object; + hideTrimmer: boolean; + handleLoad: Function; + onChangedEndpoints: Function; +}> = ({source, videoStyles, hideTrimmer, handleLoad, onChangedEndpoints}) => { + // Stores the reference to player for seeking + const playerRef = useRef<Video>(); + // Stores where the video is playing (seekTime) + const [seekTime, setSeekTime] = useState<number>(0); + const [paused, setPaused] = useState<boolean>(false); + // Stores where the tracker is + const [trackerTime, setTrackerTime] = useState<number>(0); + // Stores start/end of desired trimmed video + const [end, setEnd] = useState<number>(60); + const [start, setStart] = useState<number>(0); + + useEffect(() => { + playerRef.current?.seek(seekTime); + }, [seekTime]); + useEffect(() => { + if (!paused && (trackerTime >= end || trackerTime < start)) { + setTrackerTime(start); + playerRef.current?.seek(start); + } + }, [trackerTime]); + useEffect(() => { + setSeekTime(start); + setTrackerTime(start); + }, [start]); + useEffect(() => { + setSeekTime(end); + setTrackerTime(start); + }, [end]); + // Callback so parent knows where the trimming endpts are + useEffect(() => onChangedEndpoints({end, start}), [end, start]); + + return ( + <> + <Video + // link to descr and use of props of video player -> + // https://github.com/react-native-video/react-native-video + ref={(ref) => { + playerRef.current = ref || undefined; + }} + source={{uri: source}} + rate={1.0} + volume={1.0} + muted={false} + paused={paused} + resizeMode={'contain'} + repeat={true} + onLoad={(payload) => { + console.log(payload, source); + setEnd(payload.duration); + handleLoad(payload.naturalSize); + }} + onProgress={(e) => { + if (!paused) { + setTrackerTime(e.currentTime); + } + }} // Callback every ~250ms with currentTime + style={videoStyles} + onTouchEnd={() => { + setPaused((state) => !state); + }} + /> + <Trimmer + // link to descr and use of props for trimmer -> + // https://github.com/shahen94/react-native-video-processing + source={source} + height={hideTrimmer ? 0 : 75} + width={hideTrimmer ? 0 : SCREEN_WIDTH} + borderWidth={hideTrimmer ? 0 : 100} + onTrackerMove={(e: {currentTime: number}) => { + setPaused(true); + setSeekTime(e.currentTime); + }} + currentTime={trackerTime} + themeColor={'white'} + thumbWidth={10} + trackerColor={'white'} + onChange={(e: {endTime: number; startTime: number}) => { + setPaused(true); + setEnd(e.endTime); + setStart(e.startTime); + }} + /> + </> + ); +}; diff --git a/src/screens/profile/CaptionScreen.tsx b/src/screens/profile/CaptionScreen.tsx index 6bcf0e24..eba3e4bf 100644 --- a/src/screens/profile/CaptionScreen.tsx +++ b/src/screens/profile/CaptionScreen.tsx @@ -49,7 +49,6 @@ import {RootState} from '../../store/rootReducer'; import {MomentTagType} from '../../types'; import {isIPhoneX, normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {mentionPartTypes} from '../../utils/comments'; - /** * Upload Screen to allow users to upload posts to Tagg */ @@ -362,6 +361,7 @@ const CaptionScreen: React.FC<CaptionScreenProps> = ({route, navigation}) => { </SearchBackground> ); }; + const styles = StyleSheet.create({ contentContainer: { paddingTop: StatusBarHeight, diff --git a/src/utils/camera.ts b/src/utils/camera.ts index c0b0c6bf..5485b1ca 100644 --- a/src/utils/camera.ts +++ b/src/utils/camera.ts @@ -103,6 +103,21 @@ export const showGIFFailureAlert = (onSuccess: () => void) => }, ); +export const trimVideo = ( + sourceUri: string, + handleData: (data: any) => any, + ends: { + start: number; + end: number; + }, +) => { + ProcessingManager.trim(sourceUri, { + startTime: ends.start / 2, //needed divide by 2 for bug in module + endTime: ends.end, + quality: 'passthrough', + }).then((data: any) => handleData(data)); +}; + export const cropVideo = ( sourceUri: string, handleData: (data: any) => any, |