1 |
|
2 |
|
3 | import Asset from './Asset';
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export type PlaybackSource = number | { uri: string } | Asset;
|
15 |
|
16 | export type PlaybackStatus =
|
17 | | {
|
18 | isLoaded: false,
|
19 | androidImplementation?: string,
|
20 | error?: string,
|
21 | }
|
22 | | {
|
23 | isLoaded: true,
|
24 | androidImplementation?: string,
|
25 |
|
26 | uri: string,
|
27 |
|
28 | progressUpdateIntervalMillis: number,
|
29 | durationMillis?: number,
|
30 | positionMillis: number,
|
31 | playableDurationMillis?: number,
|
32 |
|
33 | shouldPlay: boolean,
|
34 | isPlaying: boolean,
|
35 | isBuffering: boolean,
|
36 |
|
37 | rate: number,
|
38 | shouldCorrectPitch: boolean,
|
39 | volume: number,
|
40 | isMuted: boolean,
|
41 | isLooping: boolean,
|
42 |
|
43 | didJustFinish: boolean,
|
44 | };
|
45 |
|
46 | export type PlaybackStatusToSet = {
|
47 | androidImplementation?: string,
|
48 | progressUpdateIntervalMillis?: number,
|
49 | positionMillis?: number,
|
50 | shouldPlay?: boolean,
|
51 | rate?: number,
|
52 | shouldCorrectPitch?: boolean,
|
53 | volume?: number,
|
54 | isMuted?: boolean,
|
55 | isLooping?: boolean,
|
56 | };
|
57 |
|
58 | export const _DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS: number = 500;
|
59 | export const _DEFAULT_INITIAL_PLAYBACK_STATUS: PlaybackStatusToSet = {
|
60 | positionMillis: 0,
|
61 | progressUpdateIntervalMillis: _DEFAULT_PROGRESS_UPDATE_INTERVAL_MILLIS,
|
62 | shouldPlay: false,
|
63 | rate: 1.0,
|
64 | shouldCorrectPitch: false,
|
65 | volume: 1.0,
|
66 | isMuted: false,
|
67 | isLooping: false,
|
68 | };
|
69 |
|
70 | const _getAssetFromPlaybackSource = (source: ?PlaybackSource): ?Asset => {
|
71 | if (source == null) {
|
72 | return null;
|
73 | }
|
74 |
|
75 | let asset: ?Asset = null;
|
76 | if (typeof source === 'number') {
|
77 | asset = Asset.fromModule(source);
|
78 | } else if ('constructor' in source && source.constructor.name === 'Asset') {
|
79 | asset = source;
|
80 | }
|
81 | return asset;
|
82 | };
|
83 |
|
84 | export const _getURIFromSource = (source: ?PlaybackSource): ?string => {
|
85 | let uri: ?string = null;
|
86 | let asset: ?Asset = _getAssetFromPlaybackSource(source);
|
87 | if (asset != null) {
|
88 | uri = asset.localUri || asset.uri;
|
89 | } else if (
|
90 | source != null &&
|
91 | typeof source !== 'number' &&
|
92 | 'uri' in source &&
|
93 | typeof source.uri === 'string'
|
94 | ) {
|
95 | uri = source.uri;
|
96 | }
|
97 | return uri;
|
98 | };
|
99 |
|
100 | export const _throwErrorIfValuesOutOfBoundsInStatus = async (
|
101 | status: PlaybackStatusToSet
|
102 | ) => {
|
103 | if (
|
104 | typeof status.rate === 'number' &&
|
105 | (status.rate < 0.0 || status.rate > 32.0)
|
106 | ) {
|
107 | throw new Error('Rate value must be between 0.0 and 32.0.');
|
108 | }
|
109 | if (
|
110 | typeof status.volume === 'number' &&
|
111 | (status.volume < 0.0 || status.volume > 1.0)
|
112 | ) {
|
113 | throw new Error('Volume value must be between 0.0 and 1.0.');
|
114 | }
|
115 | };
|
116 |
|
117 | export const _getURIAndFullInitialStatusForLoadAsync = async (
|
118 | source: ?PlaybackSource,
|
119 | initialStatus: ?PlaybackStatusToSet,
|
120 | downloadFirst: boolean
|
121 | ): Promise<{ uri: string, fullInitialStatus: PlaybackStatusToSet }> => {
|
122 |
|
123 | let asset: ?Asset = _getAssetFromPlaybackSource(source);
|
124 | if (downloadFirst && asset != null) {
|
125 |
|
126 | await asset.downloadAsync();
|
127 | }
|
128 |
|
129 |
|
130 | const uri: ?string = _getURIFromSource(source);
|
131 | if (uri == null) {
|
132 | throw new Error('Cannot load null source!');
|
133 | }
|
134 |
|
135 |
|
136 | const fullInitialStatus: PlaybackStatusToSet =
|
137 | initialStatus == null
|
138 | ? _DEFAULT_INITIAL_PLAYBACK_STATUS
|
139 | : {
|
140 | ..._DEFAULT_INITIAL_PLAYBACK_STATUS,
|
141 | ...initialStatus,
|
142 | };
|
143 | _throwErrorIfValuesOutOfBoundsInStatus(fullInitialStatus);
|
144 |
|
145 | return { uri, fullInitialStatus };
|
146 | };
|
147 |
|
148 | export const _getUnloadedStatus = (error: ?string = null): PlaybackStatus => {
|
149 | const status: Object = { isLoaded: false };
|
150 | if (error) {
|
151 | status.error = error;
|
152 | }
|
153 | return status;
|
154 | };
|
155 |
|
156 |
|
157 | export const _COMMON_AV_PLAYBACK_METHODS = {
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | async playAsync(): Promise<PlaybackStatus> {
|
166 | return this.setStatusAsync({ shouldPlay: true });
|
167 | },
|
168 | async playFromPositionAsync(positionMillis: number): Promise<PlaybackStatus> {
|
169 | return this.setStatusAsync({ positionMillis, shouldPlay: true });
|
170 | },
|
171 | async pauseAsync(): Promise<PlaybackStatus> {
|
172 | return this.setStatusAsync({ shouldPlay: false });
|
173 | },
|
174 | async stopAsync(): Promise<PlaybackStatus> {
|
175 | return this.setStatusAsync({ positionMillis: 0, shouldPlay: false });
|
176 | },
|
177 | async setPositionAsync(positionMillis: number): Promise<PlaybackStatus> {
|
178 | return this.setStatusAsync({ positionMillis });
|
179 | },
|
180 | async setRateAsync(
|
181 | rate: number,
|
182 | shouldCorrectPitch: boolean
|
183 | ): Promise<PlaybackStatus> {
|
184 | return this.setStatusAsync({ rate, shouldCorrectPitch });
|
185 | },
|
186 | async setVolumeAsync(volume: number): Promise<PlaybackStatus> {
|
187 | return this.setStatusAsync({ volume });
|
188 | },
|
189 | async setIsMutedAsync(isMuted: boolean): Promise<PlaybackStatus> {
|
190 | return this.setStatusAsync({ isMuted });
|
191 | },
|
192 | async setIsLoopingAsync(isLooping: boolean): Promise<PlaybackStatus> {
|
193 | return this.setStatusAsync({ isLooping });
|
194 | },
|
195 | async setProgressUpdateIntervalAsync(
|
196 | progressUpdateIntervalMillis: number
|
197 | ): Promise<PlaybackStatus> {
|
198 | return this.setStatusAsync({ progressUpdateIntervalMillis });
|
199 | },
|
200 | };
|