Skip to content

Commit a0147be

Browse files
feat(UNT-T27088): external url support
1 parent 7b44723 commit a0147be

File tree

11 files changed

+342
-94
lines changed

11 files changed

+342
-94
lines changed

README.md

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ Here's how to get started with react-native-audio-waveform in your React Native
3333
##### 1. Install the package
3434

3535
```sh
36-
npm install @simform_solutions/react-native-audio-waveform react-native-gesture-handler
36+
npm install @simform_solutions/react-native-audio-waveform react-native-blob-util react-native-gesture-handler
3737
```
3838

3939
###### --- or ---
4040

4141
```sh
42-
yarn add @simform_solutions/react-native-audio-waveform react-native-gesture-handler
42+
yarn add @simform_solutions/react-native-audio-waveform react-native-blob-util react-native-gesture-handler
4343
```
4444

4545
##### 2. Install CocoaPods in the iOS project
@@ -48,7 +48,7 @@ yarn add @simform_solutions/react-native-audio-waveform react-native-gesture-han
4848
npx pod-install
4949
```
5050

51-
##### Know more about [react-native-gesture-handler](https://www.npmjs.com/package/react-native-gesture-handler)
51+
##### Know more about [react-native-blob-util](https://www.npmjs.com/package/react-native-blob-util) and [react-native-gesture-handler](https://www.npmjs.com/package/react-native-gesture-handler)
5252

5353
##### 3. Add audio recording permissions
5454

@@ -90,7 +90,35 @@ const ref = useRef<IWaveformRef>(null);
9090
<Waveform
9191
mode="static"
9292
ref={ref}
93-
path={item}
93+
path={path}
94+
candleSpace={2}
95+
candleWidth={4}
96+
scrubColor="white"
97+
onPlayerStateChange={playerState => console.log(playerState)}
98+
onPanStateChange={isMoving => console.log(isMoving)}
99+
/>;
100+
```
101+
102+
When you want to show a waveform for a external audio URL, you need to use `static` mode for the waveform and set isExternalUrl to true.
103+
104+
Check the example below for more information.
105+
106+
```tsx
107+
import {
108+
Waveform,
109+
type IWaveformRef,
110+
} from '@simform_solutions/react-native-audio-waveform';
111+
112+
const url = 'https://www2.cs.uic.edu/~i101/SoundFiles/taunt.wav'; // URL to the audio file for which you want to show waveform
113+
const ref = useRef<IWaveformRef>(null);
114+
<Waveform
115+
mode="static"
116+
ref={ref}
117+
path={url}
118+
isExternalUrl={true}
119+
autoDownloadExternalAudio={true}
120+
onDownloadingStateChange={state => console.log(state)}
121+
onDownloadProgressChange={progress => console.log(progress)}
94122
candleSpace={2}
95123
candleWidth={4}
96124
scrubColor="white"
@@ -133,6 +161,9 @@ You can check out the full example at [Example](./example/src/App.tsx).
133161
| ref\* | - ||| IWaveformRef | Type of ref provided to waveform component. If waveform mode is `static`, some methods from ref will throw error and same for `live`.<br> Check [IWaveformRef](#iwaveformref-methods) for more details about which methods these refs provides. |
134162
| path\* | - ||| string | Used for `static` type. It is the resource path of an audio source file. |
135163
| playbackSpeed | 1.0 ||| 1.0 / 1.5 / 2.0 | The playback speed of the audio player. Note: Currently playback speed only supports, Normal (1x) Faster(1.5x) and Fastest(2.0x), any value passed to playback speed greater than 2.0 will be automatically adjusted to normal playback speed |
164+
| volume | 3 ||| number | Used for `static` type. It is a volume level for the media player, ranging from 1 to 10. |
165+
| isExternalUrl | false ||| boolean | Used for `static` type. If the resource path of an audio file is a URL, then pass true; otherwise, pass false. |
166+
| autoDownloadExternalAudio | true ||| boolean | Used for `static` type. Indicates whether the external media should be auto downloaded or not. |
136167
| candleSpace | 2 ||| number | Space between two candlesticks of waveform |
137168
| candleWidth | 5 ||| number | Width of single candlestick of waveform |
138169
| candleHeightScale | 3 ||| number | Scaling height of candlestick of waveform |
@@ -145,6 +176,8 @@ You can check out the full example at [Example](./example/src/App.tsx).
145176
| onRecorderStateChange | - ||| ( recorderState : RecorderState ) => void | callback function which returns the recorder state whenever the recorder state changes. Check RecorderState for more details |
146177
| onCurrentProgressChange | - ||| ( currentProgress : number, songDuration: number ) => void | callback function, which returns current progress of audio and total song duration. |
147178
| onChangeWaveformLoadState | - ||| ( state : boolean ) => void | callback function which returns the loading state of waveform candlestick. |
179+
| onDownloadingStateChange | - ||| ( state : boolean ) => void | A callback function that returns the loading state of a file download from an external URL. |
180+
| onDownloadProgressChange | - ||| ( currentProgress : number ) => void | Used when isExternalUrl is true; a callback function that returns the current progress of a file download from an external URL |
148181
| onError | - ||| ( error : Error ) => void | callback function which returns the error for static audio waveform |
149182

150183
##### Know more about [ViewStyle](https://reactnative.dev/docs/view-style-props), [PlayerState](#playerstate), and [RecorderState](#recorderstate)
@@ -191,6 +224,14 @@ resumePlayer(): Promise<boolean>
191224

192225
It returns a boolean indicating whether playback is resumed again.
193226

227+
#### downloadExternalAudio()
228+
229+
```ts
230+
downloadExternalAudio(): Promise<boolean>
231+
```
232+
233+
It returns a boolean indicating whether download and cache operation was successful
234+
194235
#### For Live mode
195236

196237
#### startRecord()

example/ios/Podfile.lock

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,12 @@ PODS:
387387
- React-jsinspector (0.72.7)
388388
- React-logger (0.72.7):
389389
- glog
390-
- react-native-audio-waveform (1.0.0):
390+
- react-native-audio-waveform (2.1.2):
391391
- RCT-Folly (= 2021.07.22.00)
392392
- React-Core
393-
- react-native-safe-area-context (4.11.0):
393+
- react-native-blob-util (0.19.11):
394+
- React-Core
395+
- react-native-safe-area-context (4.14.0):
394396
- React-Core
395397
- React-NativeModulesApple (0.72.7):
396398
- hermes-engine
@@ -502,15 +504,13 @@ PODS:
502504
- React-jsi (= 0.72.7)
503505
- React-logger (= 0.72.7)
504506
- React-perflogger (= 0.72.7)
505-
- rn-fetch-blob (0.12.0):
506-
- React-Core
507507
- RNFastImage (8.6.3):
508508
- React-Core
509509
- SDWebImage (~> 5.11.1)
510510
- SDWebImageWebPCoder (~> 0.8.4)
511511
- RNFS (2.20.0):
512512
- React-Core
513-
- RNGestureHandler (2.19.0):
513+
- RNGestureHandler (2.21.2):
514514
- RCT-Folly (= 2021.07.22.00)
515515
- React-Core
516516
- SDWebImage (5.11.1):
@@ -571,6 +571,7 @@ DEPENDENCIES:
571571
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
572572
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
573573
- react-native-audio-waveform (from `../..`)
574+
- react-native-blob-util (from `../node_modules/react-native-blob-util`)
574575
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
575576
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
576577
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -589,7 +590,6 @@ DEPENDENCIES:
589590
- React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`)
590591
- React-utils (from `../node_modules/react-native/ReactCommon/react/utils`)
591592
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
592-
- rn-fetch-blob (from `../node_modules/rn-fetch-blob`)
593593
- RNFastImage (from `../node_modules/react-native-fast-image`)
594594
- RNFS (from `../node_modules/react-native-fs`)
595595
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
@@ -661,6 +661,8 @@ EXTERNAL SOURCES:
661661
:path: "../node_modules/react-native/ReactCommon/logger"
662662
react-native-audio-waveform:
663663
:path: "../.."
664+
react-native-blob-util:
665+
:path: "../node_modules/react-native-blob-util"
664666
react-native-safe-area-context:
665667
:path: "../node_modules/react-native-safe-area-context"
666668
React-NativeModulesApple:
@@ -697,8 +699,6 @@ EXTERNAL SOURCES:
697699
:path: "../node_modules/react-native/ReactCommon/react/utils"
698700
ReactCommon:
699701
:path: "../node_modules/react-native/ReactCommon"
700-
rn-fetch-blob:
701-
:path: "../node_modules/rn-fetch-blob"
702702
RNFastImage:
703703
:path: "../node_modules/react-native-fast-image"
704704
RNFS:
@@ -743,8 +743,9 @@ SPEC CHECKSUMS:
743743
React-jsiexecutor: c49502e5d02112247ee4526bc3ccfc891ae3eb9b
744744
React-jsinspector: 8baadae51f01d867c3921213a25ab78ab4fbcd91
745745
React-logger: 8edc785c47c8686c7962199a307015e2ce9a0e4f
746-
react-native-audio-waveform: 7cdb6e4963eeae907240396975b9c79713591758
747-
react-native-safe-area-context: 851c62c48dce80ccaa5637b6aa5991a1bc36eca9
746+
react-native-audio-waveform: 99f401dee91ac357ce40cba147a31a18b539d312
747+
react-native-blob-util: 39a20f2ef11556d958dc4beb0aa07d1ef2690745
748+
react-native-safe-area-context: 4532f1a0c5d34a46b9324ccaaedcb5582a302b7d
748749
React-NativeModulesApple: b6868ee904013a7923128892ee4a032498a1024a
749750
React-perflogger: 31ea61077185eb1428baf60c0db6e2886f141a5a
750751
React-RCTActionSheet: 392090a3abc8992eb269ef0eaa561750588fc39d
@@ -762,10 +763,9 @@ SPEC CHECKSUMS:
762763
React-runtimescheduler: 7649c3b46c8dee1853691ecf60146a16ae59253c
763764
React-utils: 56838edeaaf651220d1e53cd0b8934fb8ce68415
764765
ReactCommon: 5f704096ccf7733b390f59043b6fa9cc180ee4f6
765-
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
766766
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
767767
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
768-
RNGestureHandler: 7ad14a6c7b491add489246611d324f10009083ac
768+
RNGestureHandler: 0972b77a2cab0f1a61ca1a3ce348a424614f65c2
769769
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
770770
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
771771
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17

example/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
"dependencies": {
1313
"react": "18.2.0",
1414
"react-native": "0.72.7",
15+
"react-native-blob-util": "^0.19.11",
1516
"react-native-fast-image": "^8.6.3",
1617
"react-native-fs": "^2.20.0",
1718
"react-native-gesture-handler": "^2.13.4",
18-
"react-native-safe-area-context": "^4.9.0",
19-
"rn-fetch-blob": "^0.12.0"
19+
"react-native-safe-area-context": "^4.11.0"
2020
},
2121
"devDependencies": {
2222
"@babel/core": "^7.20.0",

example/src/App.tsx

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ScrollView,
2626
StatusBar,
2727
Text,
28+
TouchableOpacity,
2829
View,
2930
} from 'react-native';
3031
import { GestureHandlerRootView } from 'react-native-gesture-handler';
@@ -51,16 +52,19 @@ const RenderListItem = React.memo(
5152
onPanStateChange,
5253
currentPlaybackSpeed,
5354
changeSpeed,
55+
isExternalUrl = false,
5456
}: {
5557
item: ListItem;
5658
onPanStateChange: (value: boolean) => void;
5759
currentPlaybackSpeed: PlaybackSpeedType;
5860
changeSpeed: () => void;
61+
isExternalUrl?: boolean;
5962
}) => {
6063
const ref = useRef<IWaveformRef>(null);
6164
const [playerState, setPlayerState] = useState(PlayerState.stopped);
6265
const styles = stylesheet({ currentUser: item.fromCurrentUser });
63-
const [isLoading, setIsLoading] = useState(true);
66+
const [isLoading, setIsLoading] = useState(isExternalUrl ? false : true);
67+
const [isAudioDownloaded, setIsAudioDownloaded] = useState(false);
6468

6569
const handlePlayPauseAction = async () => {
6670
// If we are recording do nothing
@@ -75,9 +79,13 @@ const RenderListItem = React.memo(
7579
if (ref.current?.currentState === PlayerState.paused) {
7680
await ref.current?.resumePlayer();
7781
} else {
78-
await ref.current?.startPlayer({
79-
finishMode: FinishMode.stop,
80-
});
82+
try {
83+
await ref.current?.startPlayer({
84+
finishMode: FinishMode.stop,
85+
});
86+
} catch (error) {
87+
console.log('Error starting player', error);
88+
}
8189
}
8290
};
8391

@@ -105,9 +113,21 @@ const RenderListItem = React.memo(
105113
const handleStopAction = async () => {
106114
ref.current?.stopPlayer();
107115
};
116+
const handleDownloadPress = (): void => {
117+
setIsLoading(true);
118+
ref.current?.downloadExternalAudio();
119+
};
108120

109121
return (
110-
<View key={item.path} style={[styles.listItemContainer]}>
122+
<View
123+
key={item.path}
124+
style={[
125+
styles.listItemContainer,
126+
item.fromCurrentUser &&
127+
isExternalUrl &&
128+
!isAudioDownloaded &&
129+
styles.listItemReverseContainer,
130+
]}>
111131
<View style={styles.listItemWidth}>
112132
<View style={[styles.buttonContainer]}>
113133
<Pressable
@@ -160,10 +180,21 @@ const RenderListItem = React.memo(
160180
waveColor={Colors.lightWhite}
161181
candleHeightScale={4}
162182
onPlayerStateChange={setPlayerState}
183+
autoDownloadExternalAudio={false}
184+
isExternalUrl={isExternalUrl}
163185
onPanStateChange={onPanStateChange}
164186
onError={error => {
165187
console.log(error, 'we are in example');
166188
}}
189+
onDownloadingStateChange={state => {
190+
console.log('Download State', state);
191+
}}
192+
onDownloadProgressChange={progress => {
193+
console.log('Download Progress', `${progress}%`);
194+
if (progress === 100) {
195+
setIsAudioDownloaded(true);
196+
}
197+
}}
167198
onCurrentProgressChange={(currentProgress, songDuration) => {
168199
console.log(
169200
`currentProgress ${currentProgress}, songDuration ${songDuration}`
@@ -184,6 +215,15 @@ const RenderListItem = React.memo(
184215
)}
185216
</View>
186217
</View>
218+
{isExternalUrl && !isAudioDownloaded && !isLoading ? (
219+
<TouchableOpacity onPress={handleDownloadPress}>
220+
<Image
221+
source={Icons.download}
222+
style={styles.downloadIcon}
223+
resizeMode="contain"
224+
/>
225+
</TouchableOpacity>
226+
) : null}
187227
</View>
188228
);
189229
}
@@ -366,6 +406,7 @@ const AppContainer = () => {
366406
<RenderListItem
367407
key={item.path}
368408
item={item}
409+
isExternalUrl={item.isExternalUrl}
369410
onPanStateChange={value => setShouldScroll(!value)}
370411
{...{ currentPlaybackSpeed, changeSpeed }}
371412
/>
8.4 KB
Loading

example/src/assets/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const Icons = {
66
logo: require('./logo.png'),
77
delete: require('./delete.png'),
88
pause: require('./pause.png'),
9+
download: require('./download.png'),
910
};

0 commit comments

Comments
 (0)