react-native-mp3 0.1.0
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/android/build.gradle +111 -0
- package/android/src/main/AndroidManifest.xml +44 -0
- package/android/src/main/java/com/reactnativemp3/Mp3Package.kt +23 -0
- package/android/src/main/java/com/reactnativemp3/Mp3TurboModule.kt +43 -0
- package/android/src/main/java/com/reactnativemp3/database/MusicDatabase.kt +48 -0
- package/android/src/main/java/com/reactnativemp3/database/dao/SongDao.kt +72 -0
- package/android/src/main/java/com/reactnativemp3/database/entities/PlaylistEntity.kt +58 -0
- package/android/src/main/java/com/reactnativemp3/database/entities/SongEntity.kt +104 -0
- package/android/src/main/java/com/reactnativemp3/database/entities/ThumbnailCacheEntity.kt +43 -0
- package/android/src/main/java/com/reactnativemp3/managers/CacheManager.kt +0 -0
- package/android/src/main/java/com/reactnativemp3/managers/EqualizerManager.kt +0 -0
- package/android/src/main/java/com/reactnativemp3/modules/MetadataModule.kt +330 -0
- package/android/src/main/java/com/reactnativemp3/modules/NotificationModule.kt +236 -0
- package/android/src/main/java/com/reactnativemp3/modules/PlayerModule.kt +710 -0
- package/android/src/main/java/com/reactnativemp3/modules/ScannerModule.kt +640 -0
- package/android/src/main/java/com/reactnativemp3/services/AudioFocusService.kt +0 -0
- package/android/src/main/java/com/reactnativemp3/services/FileObserverService.kt +0 -0
- package/android/src/main/java/com/reactnativemp3/services/MusicService.kt +309 -0
- package/android/src/main/java/com/reactnativemp3/utils/MediaStoreUtils.kt +0 -0
- package/android/src/main/java/com/reactnativemp3/utils/PermissionUtils.kt +0 -0
- package/android/src/main/jni/Mp3TurboModule.cpp +29 -0
- package/android/src/main/res/drawable/ic_music_note.xml +11 -0
- package/android/src/main/res/drawable/ic_pause.xml +11 -0
- package/android/src/main/res/drawable/ic_play.xml +11 -0
- package/android/src/main/res/drawable/ic_skip_next.xml +11 -0
- package/android/src/main/res/drawable/ic_skip_previous.xml +11 -0
- package/android/src/main/res/drawable/ic_stop.xml +11 -0
- package/lib/components/MusicList.d.ts +0 -0
- package/lib/components/MusicList.js +1 -0
- package/lib/components/MusicPlayerUI.d.ts +0 -0
- package/lib/components/MusicPlayerUI.js +1 -0
- package/lib/hooks/useMusicPlayer.d.ts +38 -0
- package/lib/hooks/useMusicPlayer.js +242 -0
- package/lib/hooks/useMusicScanner.d.ts +27 -0
- package/lib/hooks/useMusicScanner.js +217 -0
- package/lib/hooks/usePermissions.d.ts +9 -0
- package/lib/hooks/usePermissions.js +55 -0
- package/lib/index.d.ts +144 -0
- package/lib/index.js +148 -0
- package/lib/types/common.types.d.ts +79 -0
- package/lib/types/common.types.js +2 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +2 -0
- package/lib/types/player.types.d.ts +35 -0
- package/lib/types/player.types.js +2 -0
- package/lib/types/scanner.types.d.ts +29 -0
- package/lib/types/scanner.types.js +2 -0
- package/lib/utils/constants.d.ts +31 -0
- package/lib/utils/constants.js +55 -0
- package/lib/utils/events.d.ts +0 -0
- package/lib/utils/events.js +1 -0
- package/package.json +62 -0
- package/src/components/MusicList.tsx +0 -0
- package/src/components/MusicPlayerUI.tsx +0 -0
- package/src/hooks/useMusicPlayer.ts +358 -0
- package/src/hooks/useMusicScanner.ts +286 -0
- package/src/hooks/usePermissions.ts +64 -0
- package/src/index.ts +214 -0
- package/src/types/common.types.ts +86 -0
- package/src/types/index.ts +4 -0
- package/src/types/player.types.ts +37 -0
- package/src/types/scanner.types.ts +31 -0
- package/src/utils/constants.ts +56 -0
- package/src/utils/events.ts +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMusicPlayer = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
const defaultPlaybackState = {
|
|
7
|
+
isPlaying: false,
|
|
8
|
+
currentTime: 0,
|
|
9
|
+
duration: 0,
|
|
10
|
+
buffered: 0,
|
|
11
|
+
playbackRate: 1.0,
|
|
12
|
+
volume: 1.0,
|
|
13
|
+
repeatMode: 'none',
|
|
14
|
+
shuffleMode: false,
|
|
15
|
+
currentSong: undefined,
|
|
16
|
+
queue: [],
|
|
17
|
+
queuePosition: -1,
|
|
18
|
+
error: undefined,
|
|
19
|
+
};
|
|
20
|
+
const useMusicPlayer = ({ autoSetup = true, playerOptions, } = {}) => {
|
|
21
|
+
const [playbackState, setPlaybackState] = (0, react_1.useState)(defaultPlaybackState);
|
|
22
|
+
const [currentSong, setCurrentSong] = (0, react_1.useState)(null);
|
|
23
|
+
const [queue, setQueue] = (0, react_1.useState)([]);
|
|
24
|
+
const [isPlaying, setIsPlaying] = (0, react_1.useState)(false);
|
|
25
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
26
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
27
|
+
// Initialize player
|
|
28
|
+
const initializePlayer = (0, react_1.useCallback)(async () => {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
try {
|
|
31
|
+
setIsLoading(true);
|
|
32
|
+
setError(null);
|
|
33
|
+
// Setup background playback if options specify
|
|
34
|
+
if ((playerOptions === null || playerOptions === void 0 ? void 0 : playerOptions.audioFocus) !== false) {
|
|
35
|
+
index_1.Player.setupBackgroundPlayback({
|
|
36
|
+
keepAwake: (_a = playerOptions === null || playerOptions === void 0 ? void 0 : playerOptions.keepAwake) !== null && _a !== void 0 ? _a : true,
|
|
37
|
+
ducking: (_b = playerOptions === null || playerOptions === void 0 ? void 0 : playerOptions.ducking) !== null && _b !== void 0 ? _b : true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Get initial state
|
|
41
|
+
const state = await index_1.Player.getPlaybackState();
|
|
42
|
+
updateStateFromNative(state);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
setError(err.message || 'Failed to initialize player');
|
|
46
|
+
console.error('Error initializing player:', err);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
setIsLoading(false);
|
|
50
|
+
}
|
|
51
|
+
}, [playerOptions]);
|
|
52
|
+
// Update state from native response
|
|
53
|
+
const updateStateFromNative = (0, react_1.useCallback)((state) => {
|
|
54
|
+
setPlaybackState({
|
|
55
|
+
isPlaying: state.isPlaying,
|
|
56
|
+
currentTime: state.currentTime,
|
|
57
|
+
duration: state.duration,
|
|
58
|
+
playbackRate: state.playbackRate,
|
|
59
|
+
volume: state.volume,
|
|
60
|
+
repeatMode: state.repeatMode,
|
|
61
|
+
shuffleMode: state.shuffleMode,
|
|
62
|
+
currentSong: state.currentSong,
|
|
63
|
+
queue: state.queue || [],
|
|
64
|
+
queuePosition: state.queuePosition,
|
|
65
|
+
});
|
|
66
|
+
setIsPlaying(state.isPlaying);
|
|
67
|
+
setCurrentSong(state.currentSong || null);
|
|
68
|
+
setQueue(state.queue || []);
|
|
69
|
+
}, []);
|
|
70
|
+
// Play a song by URI
|
|
71
|
+
const play = (0, react_1.useCallback)(async (uri) => {
|
|
72
|
+
try {
|
|
73
|
+
setIsLoading(true);
|
|
74
|
+
setError(null);
|
|
75
|
+
await index_1.Player.play(uri);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
setError(err.message || 'Failed to play song');
|
|
79
|
+
console.error('Error playing song:', err);
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
setIsLoading(false);
|
|
84
|
+
}
|
|
85
|
+
}, []);
|
|
86
|
+
// Play a song object
|
|
87
|
+
const playSong = (0, react_1.useCallback)(async (song) => {
|
|
88
|
+
await play(song.uri);
|
|
89
|
+
}, [play]);
|
|
90
|
+
// Play from queue
|
|
91
|
+
const playFromQueue = (0, react_1.useCallback)((index) => {
|
|
92
|
+
index_1.Player.playFromQueue(index);
|
|
93
|
+
}, []);
|
|
94
|
+
// Pause playback
|
|
95
|
+
const pause = (0, react_1.useCallback)(() => {
|
|
96
|
+
index_1.Player.pause();
|
|
97
|
+
}, []);
|
|
98
|
+
// Stop playback
|
|
99
|
+
const stop = (0, react_1.useCallback)(() => {
|
|
100
|
+
index_1.Player.stop();
|
|
101
|
+
}, []);
|
|
102
|
+
// Seek to position
|
|
103
|
+
const seekTo = (0, react_1.useCallback)((position) => {
|
|
104
|
+
index_1.Player.seekTo(position);
|
|
105
|
+
}, []);
|
|
106
|
+
// Skip to next
|
|
107
|
+
const skipToNext = (0, react_1.useCallback)(() => {
|
|
108
|
+
index_1.Player.skipToNext();
|
|
109
|
+
}, []);
|
|
110
|
+
// Skip to previous
|
|
111
|
+
const skipToPrevious = (0, react_1.useCallback)(() => {
|
|
112
|
+
index_1.Player.skipToPrevious();
|
|
113
|
+
}, []);
|
|
114
|
+
// Set volume
|
|
115
|
+
const setVolume = (0, react_1.useCallback)((volume) => {
|
|
116
|
+
index_1.Player.setVolume(volume);
|
|
117
|
+
}, []);
|
|
118
|
+
// Set playback rate
|
|
119
|
+
const setPlaybackRate = (0, react_1.useCallback)((rate) => {
|
|
120
|
+
index_1.Player.setPlaybackRate(rate);
|
|
121
|
+
}, []);
|
|
122
|
+
// Set queue
|
|
123
|
+
const setQueueCallback = (0, react_1.useCallback)((songs) => {
|
|
124
|
+
index_1.Player.setQueue(songs);
|
|
125
|
+
}, []);
|
|
126
|
+
// Add to queue
|
|
127
|
+
const addToQueue = (0, react_1.useCallback)((songs) => {
|
|
128
|
+
index_1.Player.addToQueue(songs);
|
|
129
|
+
}, []);
|
|
130
|
+
// Clear queue
|
|
131
|
+
const clearQueue = (0, react_1.useCallback)(() => {
|
|
132
|
+
index_1.Player.clearQueue();
|
|
133
|
+
}, []);
|
|
134
|
+
// Get queue
|
|
135
|
+
const getQueue = (0, react_1.useCallback)(async () => {
|
|
136
|
+
return await index_1.Player.getQueue();
|
|
137
|
+
}, []);
|
|
138
|
+
// Set repeat mode
|
|
139
|
+
const setRepeatMode = (0, react_1.useCallback)((mode) => {
|
|
140
|
+
index_1.Player.setRepeatMode(mode);
|
|
141
|
+
}, []);
|
|
142
|
+
// Set shuffle mode
|
|
143
|
+
const setShuffleMode = (0, react_1.useCallback)((enabled) => {
|
|
144
|
+
index_1.Player.setShuffleMode(enabled);
|
|
145
|
+
}, []);
|
|
146
|
+
// Equalizer controls
|
|
147
|
+
const setEqualizerPreset = (0, react_1.useCallback)((preset) => {
|
|
148
|
+
index_1.Player.setEqualizerPreset(preset);
|
|
149
|
+
}, []);
|
|
150
|
+
const setEqualizerBands = (0, react_1.useCallback)((bands) => {
|
|
151
|
+
index_1.Player.setEqualizerBands(bands);
|
|
152
|
+
}, []);
|
|
153
|
+
const enableEqualizer = (0, react_1.useCallback)((enabled) => {
|
|
154
|
+
index_1.Player.enableEqualizer(enabled);
|
|
155
|
+
}, []);
|
|
156
|
+
// Background playback
|
|
157
|
+
const setupBackgroundPlayback = (0, react_1.useCallback)((config) => {
|
|
158
|
+
index_1.Player.setupBackgroundPlayback(config);
|
|
159
|
+
}, []);
|
|
160
|
+
const stopBackgroundPlayback = (0, react_1.useCallback)(() => {
|
|
161
|
+
index_1.Player.stopBackgroundPlayback();
|
|
162
|
+
}, []);
|
|
163
|
+
// Sleep timer
|
|
164
|
+
const setSleepTimer = (0, react_1.useCallback)((minutes) => {
|
|
165
|
+
index_1.Player.setSleepTimer(minutes);
|
|
166
|
+
}, []);
|
|
167
|
+
const cancelSleepTimer = (0, react_1.useCallback)(() => {
|
|
168
|
+
index_1.Player.cancelSleepTimer();
|
|
169
|
+
}, []);
|
|
170
|
+
// Set up event listeners
|
|
171
|
+
(0, react_1.useEffect)(() => {
|
|
172
|
+
if (!autoSetup)
|
|
173
|
+
return;
|
|
174
|
+
const playbackStateSub = index_1.Player.addEventListener(index_1.PlayerEvents.PLAYBACK_STATE_CHANGED, (data) => {
|
|
175
|
+
setPlaybackState(prev => (Object.assign(Object.assign({}, prev), { isPlaying: data.isPlaying, currentTime: data.currentTime, duration: data.duration, volume: data.volume, playbackRate: data.playbackRate })));
|
|
176
|
+
setIsPlaying(data.isPlaying);
|
|
177
|
+
});
|
|
178
|
+
const queueChangedSub = index_1.Player.addEventListener(index_1.PlayerEvents.PLAYBACK_QUEUE_CHANGED, (data) => {
|
|
179
|
+
setQueue(data.queue || []);
|
|
180
|
+
setPlaybackState(prev => (Object.assign(Object.assign({}, prev), { queue: data.queue || [], queuePosition: data.position })));
|
|
181
|
+
});
|
|
182
|
+
const playbackErrorSub = index_1.Player.addEventListener(index_1.PlayerEvents.PLAYBACK_ERROR, (data) => {
|
|
183
|
+
setError(data.error || 'Playback error');
|
|
184
|
+
setPlaybackState(prev => (Object.assign(Object.assign({}, prev), { error: data.error })));
|
|
185
|
+
});
|
|
186
|
+
const playbackCompleteSub = index_1.Player.addEventListener(index_1.PlayerEvents.PLAYBACK_COMPLETE, () => {
|
|
187
|
+
// Auto-play next is handled by native module
|
|
188
|
+
});
|
|
189
|
+
// Initialize player
|
|
190
|
+
initializePlayer();
|
|
191
|
+
return () => {
|
|
192
|
+
// Remove specific listeners
|
|
193
|
+
playbackStateSub.remove();
|
|
194
|
+
queueChangedSub.remove();
|
|
195
|
+
playbackErrorSub.remove();
|
|
196
|
+
playbackCompleteSub.remove();
|
|
197
|
+
// Clean up background playback if it was set up
|
|
198
|
+
if (autoSetup && (playerOptions === null || playerOptions === void 0 ? void 0 : playerOptions.audioFocus) !== false) {
|
|
199
|
+
index_1.Player.stopBackgroundPlayback();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}, [autoSetup, initializePlayer, playerOptions]);
|
|
203
|
+
return {
|
|
204
|
+
// State
|
|
205
|
+
playbackState,
|
|
206
|
+
currentSong,
|
|
207
|
+
queue,
|
|
208
|
+
isPlaying,
|
|
209
|
+
isLoading,
|
|
210
|
+
error,
|
|
211
|
+
// Playback Controls
|
|
212
|
+
play,
|
|
213
|
+
playSong,
|
|
214
|
+
playFromQueue,
|
|
215
|
+
pause,
|
|
216
|
+
stop,
|
|
217
|
+
seekTo,
|
|
218
|
+
skipToNext,
|
|
219
|
+
skipToPrevious,
|
|
220
|
+
setVolume,
|
|
221
|
+
setPlaybackRate,
|
|
222
|
+
// Queue Management
|
|
223
|
+
setQueue: setQueueCallback,
|
|
224
|
+
addToQueue,
|
|
225
|
+
clearQueue,
|
|
226
|
+
getQueue,
|
|
227
|
+
// Playback Modes
|
|
228
|
+
setRepeatMode,
|
|
229
|
+
setShuffleMode,
|
|
230
|
+
// Equalizer
|
|
231
|
+
setEqualizerPreset,
|
|
232
|
+
setEqualizerBands,
|
|
233
|
+
enableEqualizer,
|
|
234
|
+
// Background Playback
|
|
235
|
+
setupBackgroundPlayback,
|
|
236
|
+
stopBackgroundPlayback,
|
|
237
|
+
// Sleep Timer
|
|
238
|
+
setSleepTimer,
|
|
239
|
+
cancelSleepTimer,
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
exports.useMusicPlayer = useMusicPlayer;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Song, Album, Artist, ScannerOptions } from '../types/common.types';
|
|
2
|
+
interface UseMusicScannerProps {
|
|
3
|
+
autoScan?: boolean;
|
|
4
|
+
scanOptions?: ScannerOptions;
|
|
5
|
+
}
|
|
6
|
+
interface UseMusicScannerReturn {
|
|
7
|
+
songs: Song[];
|
|
8
|
+
albums: Album[];
|
|
9
|
+
artists: Artist[];
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
isScanning: boolean;
|
|
12
|
+
scanProgress: number;
|
|
13
|
+
permissionGranted: boolean;
|
|
14
|
+
error: string | null;
|
|
15
|
+
scanMusic: (options?: ScannerOptions) => Promise<void>;
|
|
16
|
+
refresh: () => Promise<void>;
|
|
17
|
+
search: (query: string) => Promise<Song[]>;
|
|
18
|
+
deleteSong: (songId: string) => Promise<boolean>;
|
|
19
|
+
addToFavorites: (songId: string) => Promise<void>;
|
|
20
|
+
removeFromFavorites: (songId: string) => Promise<void>;
|
|
21
|
+
requestPermission: () => Promise<boolean>;
|
|
22
|
+
checkPermission: () => Promise<boolean>;
|
|
23
|
+
onScanProgress?: (progress: number) => void;
|
|
24
|
+
onScanComplete?: (totalSongs: number) => void;
|
|
25
|
+
}
|
|
26
|
+
export declare const useMusicScanner: ({ autoScan, scanOptions, }?: UseMusicScannerProps) => UseMusicScannerReturn;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMusicScanner = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
const useMusicScanner = ({ autoScan = true, scanOptions, } = {}) => {
|
|
7
|
+
const [songs, setSongs] = (0, react_1.useState)([]);
|
|
8
|
+
const [albums, setAlbums] = (0, react_1.useState)([]);
|
|
9
|
+
const [artists, setArtists] = (0, react_1.useState)([]);
|
|
10
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
11
|
+
const [isScanning, setIsScanning] = (0, react_1.useState)(false);
|
|
12
|
+
const [scanProgress, setScanProgress] = (0, react_1.useState)(0);
|
|
13
|
+
const [permissionGranted, setPermissionGranted] = (0, react_1.useState)(false);
|
|
14
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
15
|
+
// Load initial data
|
|
16
|
+
const loadInitialData = (0, react_1.useCallback)(async () => {
|
|
17
|
+
try {
|
|
18
|
+
setIsLoading(true);
|
|
19
|
+
setError(null);
|
|
20
|
+
const hasPermission = await index_1.Scanner.checkPermissionStatus();
|
|
21
|
+
setPermissionGranted(hasPermission);
|
|
22
|
+
if (hasPermission) {
|
|
23
|
+
const cachedSongs = await index_1.Scanner.getCachedSongs();
|
|
24
|
+
setSongs(cachedSongs);
|
|
25
|
+
const [albumsList, artistsList] = await Promise.all([
|
|
26
|
+
index_1.Scanner.getAlbums(),
|
|
27
|
+
index_1.Scanner.getArtists(),
|
|
28
|
+
]);
|
|
29
|
+
setAlbums(albumsList);
|
|
30
|
+
setArtists(artistsList);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
setError(err.message || 'Failed to load music library');
|
|
35
|
+
console.error('Error loading music library:', err);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setIsLoading(false);
|
|
39
|
+
}
|
|
40
|
+
}, []);
|
|
41
|
+
// Scan music files
|
|
42
|
+
const scanMusic = (0, react_1.useCallback)(async (options) => {
|
|
43
|
+
try {
|
|
44
|
+
setIsScanning(true);
|
|
45
|
+
setScanProgress(0);
|
|
46
|
+
setError(null);
|
|
47
|
+
const hasPermission = await index_1.Scanner.checkPermissionStatus();
|
|
48
|
+
if (!hasPermission) {
|
|
49
|
+
const granted = await index_1.Scanner.requestStoragePermission();
|
|
50
|
+
if (!granted) {
|
|
51
|
+
setError('Storage permission is required to scan music files');
|
|
52
|
+
setIsScanning(false);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setPermissionGranted(true);
|
|
56
|
+
}
|
|
57
|
+
const scanOpts = options || scanOptions || {};
|
|
58
|
+
const result = await index_1.Scanner.scanMusicFiles(scanOpts);
|
|
59
|
+
// Reload data after scan
|
|
60
|
+
await loadInitialData();
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
setError(err.message || 'Failed to scan music files');
|
|
65
|
+
console.error('Error scanning music files:', err);
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
setIsScanning(false);
|
|
70
|
+
setScanProgress(0);
|
|
71
|
+
}
|
|
72
|
+
}, [scanOptions, loadInitialData]);
|
|
73
|
+
// Refresh data
|
|
74
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
75
|
+
await loadInitialData();
|
|
76
|
+
}, [loadInitialData]);
|
|
77
|
+
// Search songs
|
|
78
|
+
const search = (0, react_1.useCallback)(async (query) => {
|
|
79
|
+
if (!query.trim()) {
|
|
80
|
+
return songs;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const results = await index_1.Scanner.searchSongs(query);
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
setError(err.message || 'Search failed');
|
|
88
|
+
console.error('Error searching songs:', err);
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}, [songs]);
|
|
92
|
+
// Delete song
|
|
93
|
+
const deleteSong = (0, react_1.useCallback)(async (songId) => {
|
|
94
|
+
try {
|
|
95
|
+
const success = await index_1.Scanner.deleteSong(songId);
|
|
96
|
+
if (success) {
|
|
97
|
+
// Remove from local state
|
|
98
|
+
setSongs(prev => prev.filter(song => song.id !== songId));
|
|
99
|
+
// Update albums and artists
|
|
100
|
+
await refresh();
|
|
101
|
+
}
|
|
102
|
+
return success;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
setError(err.message || 'Failed to delete song');
|
|
106
|
+
console.error('Error deleting song:', err);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}, [refresh]);
|
|
110
|
+
// Add to favorites
|
|
111
|
+
const addToFavorites = (0, react_1.useCallback)(async (songId) => {
|
|
112
|
+
try {
|
|
113
|
+
await index_1.Scanner.addToFavorites(songId);
|
|
114
|
+
// Update local state
|
|
115
|
+
setSongs(prev => prev.map(song => song.id === songId ? Object.assign(Object.assign({}, song), { isFavorite: true }) : song));
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
setError(err.message || 'Failed to add to favorites');
|
|
119
|
+
console.error('Error adding to favorites:', err);
|
|
120
|
+
}
|
|
121
|
+
}, []);
|
|
122
|
+
// Remove from favorites
|
|
123
|
+
const removeFromFavorites = (0, react_1.useCallback)(async (songId) => {
|
|
124
|
+
try {
|
|
125
|
+
await index_1.Scanner.removeFromFavorites(songId);
|
|
126
|
+
// Update local state
|
|
127
|
+
setSongs(prev => prev.map(song => song.id === songId ? Object.assign(Object.assign({}, song), { isFavorite: false }) : song));
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
setError(err.message || 'Failed to remove from favorites');
|
|
131
|
+
console.error('Error removing from favorites:', err);
|
|
132
|
+
}
|
|
133
|
+
}, []);
|
|
134
|
+
// Request permission
|
|
135
|
+
const requestPermission = (0, react_1.useCallback)(async () => {
|
|
136
|
+
try {
|
|
137
|
+
const granted = await index_1.Scanner.requestStoragePermission();
|
|
138
|
+
setPermissionGranted(granted);
|
|
139
|
+
return granted;
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
setError(err.message || 'Failed to request permission');
|
|
143
|
+
console.error('Error requesting permission:', err);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}, []);
|
|
147
|
+
// Check permission
|
|
148
|
+
const checkPermission = (0, react_1.useCallback)(async () => {
|
|
149
|
+
try {
|
|
150
|
+
const granted = await index_1.Scanner.checkPermissionStatus();
|
|
151
|
+
setPermissionGranted(granted);
|
|
152
|
+
return granted;
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
setError(err.message || 'Failed to check permission');
|
|
156
|
+
console.error('Error checking permission:', err);
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}, []);
|
|
160
|
+
// Set up event listeners
|
|
161
|
+
(0, react_1.useEffect)(() => {
|
|
162
|
+
const scanProgressSub = index_1.Scanner.addEventListener(index_1.ScannerEvents.SCAN_PROGRESS, (data) => {
|
|
163
|
+
setScanProgress(data.percentage);
|
|
164
|
+
});
|
|
165
|
+
const scanCompleteSub = index_1.Scanner.addEventListener(index_1.ScannerEvents.SCAN_COMPLETE, () => {
|
|
166
|
+
loadInitialData();
|
|
167
|
+
});
|
|
168
|
+
const fileAddedSub = index_1.Scanner.addEventListener(index_1.ScannerEvents.FILE_ADDED, () => {
|
|
169
|
+
loadInitialData();
|
|
170
|
+
});
|
|
171
|
+
const fileDeletedSub = index_1.Scanner.addEventListener(index_1.ScannerEvents.FILE_DELETED, () => {
|
|
172
|
+
loadInitialData();
|
|
173
|
+
});
|
|
174
|
+
const permissionSub = index_1.Scanner.addEventListener(index_1.ScannerEvents.PERMISSION_STATUS, (data) => {
|
|
175
|
+
setPermissionGranted(data.granted);
|
|
176
|
+
});
|
|
177
|
+
// Auto scan on mount if enabled
|
|
178
|
+
if (autoScan && permissionGranted) {
|
|
179
|
+
loadInitialData();
|
|
180
|
+
}
|
|
181
|
+
return () => {
|
|
182
|
+
// Remove specific listeners instead of all
|
|
183
|
+
scanProgressSub.remove();
|
|
184
|
+
scanCompleteSub.remove();
|
|
185
|
+
fileAddedSub.remove();
|
|
186
|
+
fileDeletedSub.remove();
|
|
187
|
+
permissionSub.remove();
|
|
188
|
+
// If you want to remove all listeners:
|
|
189
|
+
// Scanner.removeAllListeners(ScannerEvents.SCAN_PROGRESS);
|
|
190
|
+
// Scanner.removeAllListeners(ScannerEvents.SCAN_COMPLETE);
|
|
191
|
+
// Scanner.removeAllListeners(ScannerEvents.FILE_ADDED);
|
|
192
|
+
// Scanner.removeAllListeners(ScannerEvents.FILE_DELETED);
|
|
193
|
+
// Scanner.removeAllListeners(ScannerEvents.PERMISSION_STATUS);
|
|
194
|
+
};
|
|
195
|
+
}, [autoScan, loadInitialData, permissionGranted]);
|
|
196
|
+
return {
|
|
197
|
+
// State
|
|
198
|
+
songs,
|
|
199
|
+
albums,
|
|
200
|
+
artists,
|
|
201
|
+
isLoading,
|
|
202
|
+
isScanning,
|
|
203
|
+
scanProgress,
|
|
204
|
+
permissionGranted,
|
|
205
|
+
error,
|
|
206
|
+
// Methods
|
|
207
|
+
scanMusic,
|
|
208
|
+
refresh,
|
|
209
|
+
search,
|
|
210
|
+
deleteSong,
|
|
211
|
+
addToFavorites,
|
|
212
|
+
removeFromFavorites,
|
|
213
|
+
requestPermission,
|
|
214
|
+
checkPermission,
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
exports.useMusicScanner = useMusicScanner;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface UsePermissionsReturn {
|
|
2
|
+
permissionGranted: boolean;
|
|
3
|
+
isChecking: boolean;
|
|
4
|
+
error: string | null;
|
|
5
|
+
requestPermission: () => Promise<boolean>;
|
|
6
|
+
checkPermission: () => Promise<boolean>;
|
|
7
|
+
}
|
|
8
|
+
export declare const usePermissions: () => UsePermissionsReturn;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.usePermissions = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
const usePermissions = () => {
|
|
7
|
+
const [permissionGranted, setPermissionGranted] = (0, react_1.useState)(false);
|
|
8
|
+
const [isChecking, setIsChecking] = (0, react_1.useState)(false);
|
|
9
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
10
|
+
const checkPermission = async () => {
|
|
11
|
+
try {
|
|
12
|
+
setIsChecking(true);
|
|
13
|
+
setError(null);
|
|
14
|
+
const granted = await index_1.Scanner.checkPermissionStatus();
|
|
15
|
+
setPermissionGranted(granted);
|
|
16
|
+
return granted;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
setError(err.message || 'Failed to check permission');
|
|
20
|
+
console.error('Error checking permission:', err);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
setIsChecking(false);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const requestPermission = async () => {
|
|
28
|
+
try {
|
|
29
|
+
setIsChecking(true);
|
|
30
|
+
setError(null);
|
|
31
|
+
const granted = await index_1.Scanner.requestStoragePermission();
|
|
32
|
+
setPermissionGranted(granted);
|
|
33
|
+
return granted;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
setError(err.message || 'Failed to request permission');
|
|
37
|
+
console.error('Error requesting permission:', err);
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
setIsChecking(false);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
(0, react_1.useEffect)(() => {
|
|
45
|
+
checkPermission();
|
|
46
|
+
}, []);
|
|
47
|
+
return {
|
|
48
|
+
permissionGranted,
|
|
49
|
+
isChecking,
|
|
50
|
+
error,
|
|
51
|
+
requestPermission,
|
|
52
|
+
checkPermission,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
exports.usePermissions = usePermissions;
|