react-native-android-media-fetcher 1.0.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/android/build.gradle +111 -0
  4. package/android/gradle.properties +3 -0
  5. package/android/src/main/AndroidManifest.xml +3 -0
  6. package/android/src/main/AndroidManifestNew.xml +2 -0
  7. package/android/src/main/java/com/androidmediafetcher/AndroidMediaFetcherPackage.kt +21 -0
  8. package/android/src/main/java/com/androidmediafetcher/models/AudioFile.kt +57 -0
  9. package/android/src/main/java/com/androidmediafetcher/providers/AudioMediaProvider.kt +361 -0
  10. package/android/src/main/java/com/androidmediafetcher/providers/BaseMediaProvider.kt +43 -0
  11. package/android/src/newarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +173 -0
  12. package/android/src/newarch/java/com/androidmediafetcher/NativeAndroidMediaFetcherSpec.kt +21 -0
  13. package/android/src/oldarch/java/com/androidmediafetcher/AndroidMediaFetcherModule.kt +170 -0
  14. package/app.plugin.js +1 -0
  15. package/lib/commonjs/AudioFetcher.js +153 -0
  16. package/lib/commonjs/AudioFetcher.js.map +1 -0
  17. package/lib/commonjs/NativeModule.js +37 -0
  18. package/lib/commonjs/NativeModule.js.map +1 -0
  19. package/lib/commonjs/events.js +73 -0
  20. package/lib/commonjs/events.js.map +1 -0
  21. package/lib/commonjs/index.js +32 -0
  22. package/lib/commonjs/index.js.map +1 -0
  23. package/lib/commonjs/types.js +2 -0
  24. package/lib/commonjs/types.js.map +1 -0
  25. package/lib/module/AudioFetcher.js +147 -0
  26. package/lib/module/AudioFetcher.js.map +1 -0
  27. package/lib/module/NativeModule.js +31 -0
  28. package/lib/module/NativeModule.js.map +1 -0
  29. package/lib/module/events.js +65 -0
  30. package/lib/module/events.js.map +1 -0
  31. package/lib/module/index.js +17 -0
  32. package/lib/module/index.js.map +1 -0
  33. package/lib/module/types.js +2 -0
  34. package/lib/module/types.js.map +1 -0
  35. package/lib/typescript/src/AudioFetcher.d.ts +93 -0
  36. package/lib/typescript/src/AudioFetcher.d.ts.map +1 -0
  37. package/lib/typescript/src/NativeModule.d.ts +21 -0
  38. package/lib/typescript/src/NativeModule.d.ts.map +1 -0
  39. package/lib/typescript/src/events.d.ts +38 -0
  40. package/lib/typescript/src/events.d.ts.map +1 -0
  41. package/lib/typescript/src/index.d.ts +12 -0
  42. package/lib/typescript/src/index.d.ts.map +1 -0
  43. package/lib/typescript/src/types.d.ts +135 -0
  44. package/lib/typescript/src/types.d.ts.map +1 -0
  45. package/package.json +101 -0
  46. package/plugin/build/index.js +48 -0
  47. package/plugin/src/index.ts +63 -0
  48. package/plugin/tsconfig.json +34 -0
  49. package/react-native.config.js +12 -0
  50. package/src/AudioFetcher.ts +163 -0
  51. package/src/NativeModule.ts +44 -0
  52. package/src/events.ts +79 -0
  53. package/src/index.ts +29 -0
  54. package/src/types.ts +171 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rakesh Prasad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,398 @@
1
+ # react-native-android-media-fetcher
2
+
3
+ [![npm version](https://img.shields.io/npm/v/react-native-android-media-fetcher.svg)](https://www.npmjs.com/package/react-native-android-media-fetcher)
4
+ [![license](https://img.shields.io/npm/l/react-native-android-media-fetcher.svg)](https://github.com/rakeshprasad28/react-native-android-media-fetcher/blob/main/LICENSE)
5
+ [![Android](https://img.shields.io/badge/Platform-Android-green.svg)](https://developer.android.com/)
6
+
7
+ A React Native package for fetching audio files with complete metadata from Android devices. Built with TypeScript, supports pagination, and provides progress events for large music libraries.
8
+
9
+ ## Features
10
+
11
+ - 🎵 **Fetch all audio files** from device storage
12
+ - 📊 **Rich metadata**: title, artist, album, duration, size, bitrate, genre, and more
13
+ - 🎨 **Album art extraction** as base64 for easy display
14
+ - 📑 **Efficient pagination** for large libraries
15
+ - 📡 **Progress events** for real-time loading feedback
16
+ - âš¡ **New Architecture ready** (TurboModules support)
17
+ - 📱 **Expo compatible** (with config plugin)
18
+
19
+ ## Requirements
20
+
21
+ - React Native >= 0.60.0
22
+ - Android API 29+ (Android 10+)
23
+ - Expo SDK 47+ (for Expo projects, requires development builds)
24
+
25
+ ## Installation
26
+
27
+ ### React Native CLI
28
+
29
+ ```bash
30
+ npm install react-native-android-media-fetcher
31
+ # or
32
+ yarn add react-native-android-media-fetcher
33
+ ```
34
+
35
+ The package uses auto-linking, so no manual linking is required for React Native 0.60+.
36
+
37
+ ### Expo
38
+
39
+ ```bash
40
+ npx expo install react-native-android-media-fetcher
41
+ ```
42
+
43
+ Add the plugin to your `app.json` or `app.config.js`:
44
+
45
+ ```json
46
+ {
47
+ "expo": {
48
+ "plugins": ["react-native-android-media-fetcher"]
49
+ }
50
+ }
51
+ ```
52
+
53
+ > **Note:** This package requires a development build. It will not work with Expo Go.
54
+
55
+ ```bash
56
+ npx expo prebuild
57
+ npx expo run:android
58
+ ```
59
+
60
+ ## Setup
61
+
62
+ ### Add Permissions (React Native CLI)
63
+
64
+ Add the following permissions to your `android/app/src/main/AndroidManifest.xml`:
65
+
66
+ ```xml
67
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
68
+
69
+ <!-- For Android 13+ (API 33+) -->
70
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
71
+
72
+ <!-- For Android 10-12 (API 29-32) -->
73
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
74
+ android:maxSdkVersion="32" />
75
+
76
+ <application>
77
+ ...
78
+ </application>
79
+ </manifest>
80
+ ```
81
+
82
+ ### Request Runtime Permissions
83
+
84
+ You must request runtime permissions before using the library. Here's an example using React Native's built-in `PermissionsAndroid`:
85
+
86
+ ```typescript
87
+ import { PermissionsAndroid, Platform } from 'react-native';
88
+
89
+ async function requestAudioPermission(): Promise<boolean> {
90
+ if (Platform.OS !== 'android') return false;
91
+
92
+ try {
93
+ if (Platform.Version >= 33) {
94
+ const granted = await PermissionsAndroid.request(
95
+ PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO,
96
+ {
97
+ title: 'Audio Library Permission',
98
+ message: 'This app needs access to your audio library.',
99
+ buttonNeutral: 'Ask Me Later',
100
+ buttonNegative: 'Cancel',
101
+ buttonPositive: 'OK',
102
+ }
103
+ );
104
+ return granted === PermissionsAndroid.RESULTS.GRANTED;
105
+ } else {
106
+ const granted = await PermissionsAndroid.request(
107
+ PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
108
+ {
109
+ title: 'Storage Permission',
110
+ message: 'This app needs access to your storage to read audio files.',
111
+ buttonNeutral: 'Ask Me Later',
112
+ buttonNegative: 'Cancel',
113
+ buttonPositive: 'OK',
114
+ }
115
+ );
116
+ return granted === PermissionsAndroid.RESULTS.GRANTED;
117
+ }
118
+ } catch (err) {
119
+ console.warn(err);
120
+ return false;
121
+ }
122
+ }
123
+ ```
124
+
125
+ ## Usage
126
+
127
+ ### Basic Usage
128
+
129
+ ```typescript
130
+ import { AudioFetcher } from 'react-native-android-media-fetcher';
131
+
132
+ // Fetch first 20 audio files
133
+ const result = await AudioFetcher.getAudioFiles({ pageSize: 20, offset: 0 });
134
+
135
+ console.log(`Total files: ${result.totalCount}`);
136
+ console.log(`Has more: ${result.hasMore}`);
137
+
138
+ result.files.forEach((file) => {
139
+ console.log(`${file.title} - ${file.artist}`);
140
+ });
141
+ ```
142
+
143
+ ### Pagination
144
+
145
+ ```typescript
146
+ import { AudioFetcher } from 'react-native-android-media-fetcher';
147
+
148
+ async function loadAudioFiles() {
149
+ const allFiles = [];
150
+ let offset = 0;
151
+ const pageSize = 50;
152
+ let hasMore = true;
153
+
154
+ while (hasMore) {
155
+ const result = await AudioFetcher.getAudioFiles({ pageSize, offset });
156
+ allFiles.push(...result.files);
157
+ offset = result.nextOffset;
158
+ hasMore = result.hasMore;
159
+ }
160
+
161
+ return allFiles;
162
+ }
163
+ ```
164
+
165
+ ### Fetch All with Progress
166
+
167
+ ```typescript
168
+ import {
169
+ AudioFetcher,
170
+ addProgressListener
171
+ } from 'react-native-android-media-fetcher';
172
+
173
+ // Subscribe to progress updates
174
+ const unsubscribe = addProgressListener((progress) => {
175
+ console.log(`Loading: ${progress.percentage}% (${progress.current}/${progress.total})`);
176
+ });
177
+
178
+ // Fetch all files
179
+ const allFiles = await AudioFetcher.getAllAudioFiles();
180
+
181
+ // Clean up listener
182
+ unsubscribe();
183
+
184
+ console.log(`Loaded ${allFiles.length} audio files`);
185
+ ```
186
+
187
+ ### Get Single File by ID
188
+
189
+ ```typescript
190
+ import { AudioFetcher } from 'react-native-android-media-fetcher';
191
+
192
+ const file = await AudioFetcher.getAudioFileById('12345');
193
+
194
+ if (file) {
195
+ console.log(`Title: ${file.title}`);
196
+ console.log(`Artist: ${file.artist}`);
197
+ console.log(`Duration: ${file.duration}ms`);
198
+ }
199
+ ```
200
+
201
+ ### Get Total Count
202
+
203
+ ```typescript
204
+ import { AudioFetcher } from 'react-native-android-media-fetcher';
205
+
206
+ const count = await AudioFetcher.getTotalAudioCount();
207
+ console.log(`Device has ${count} audio files`);
208
+ ```
209
+
210
+ ### Display Album Art
211
+
212
+ ```typescript
213
+ import { Image } from 'react-native';
214
+
215
+ // file.albumArt is a base64 string
216
+ {file.albumArt && (
217
+ <Image
218
+ source={{ uri: `data:image/jpeg;base64,${file.albumArt}` }}
219
+ style={{ width: 100, height: 100 }}
220
+ />
221
+ )}
222
+ ```
223
+
224
+ ### React Hook Example
225
+
226
+ ```typescript
227
+ import { useState, useEffect, useCallback } from 'react';
228
+ import {
229
+ AudioFetcher,
230
+ addProgressListener,
231
+ type AudioFile,
232
+ type FetchProgress
233
+ } from 'react-native-android-media-fetcher';
234
+
235
+ function useAudioLibrary() {
236
+ const [files, setFiles] = useState<AudioFile[]>([]);
237
+ const [loading, setLoading] = useState(false);
238
+ const [progress, setProgress] = useState<FetchProgress | null>(null);
239
+ const [error, setError] = useState<string | null>(null);
240
+
241
+ const loadFiles = useCallback(async () => {
242
+ setLoading(true);
243
+ setError(null);
244
+
245
+ const unsubscribe = addProgressListener(setProgress);
246
+
247
+ try {
248
+ const allFiles = await AudioFetcher.getAllAudioFiles();
249
+ setFiles(allFiles);
250
+ } catch (err) {
251
+ setError(err instanceof Error ? err.message : 'Failed to load files');
252
+ } finally {
253
+ setLoading(false);
254
+ setProgress(null);
255
+ unsubscribe();
256
+ }
257
+ }, []);
258
+
259
+ return { files, loading, progress, error, loadFiles };
260
+ }
261
+ ```
262
+
263
+ ## API Reference
264
+
265
+ ### AudioFetcher
266
+
267
+ #### `getAudioFiles(options?: FetchOptions): Promise<FetchResult>`
268
+
269
+ Fetches audio files with pagination.
270
+
271
+ | Option | Type | Default | Description |
272
+ |--------|------|---------|-------------|
273
+ | `pageSize` | `number` | 50 | Number of files per page (max: 500) |
274
+ | `offset` | `number` | 0 | Starting position |
275
+
276
+ Returns `FetchResult`:
277
+
278
+ ```typescript
279
+ interface FetchResult {
280
+ files: AudioFile[]; // Array of audio files
281
+ totalCount: number; // Total files on device
282
+ hasMore: boolean; // Whether more files exist
283
+ nextOffset: number; // Offset for next page
284
+ }
285
+ ```
286
+
287
+ #### `getAllAudioFiles(): Promise<AudioFile[]>`
288
+
289
+ Fetches all audio files. Use with `addProgressListener` for progress updates.
290
+
291
+ #### `getAudioFileById(id: string): Promise<AudioFile | null>`
292
+
293
+ Fetches a single audio file by its ID.
294
+
295
+ #### `getTotalAudioCount(): Promise<number>`
296
+
297
+ Returns the total count of audio files on the device.
298
+
299
+ ### Event Functions
300
+
301
+ #### `addProgressListener(listener: ProgressListener): Unsubscribe`
302
+
303
+ Subscribes to progress updates during `getAllAudioFiles()`.
304
+
305
+ ```typescript
306
+ type ProgressListener = (progress: FetchProgress) => void;
307
+
308
+ interface FetchProgress {
309
+ current: number; // Files processed
310
+ total: number; // Total files
311
+ percentage: number; // Progress 0-100
312
+ }
313
+ ```
314
+
315
+ #### `addErrorListener(listener: ErrorListener): Unsubscribe`
316
+
317
+ Subscribes to error events.
318
+
319
+ #### `removeAllListeners(): void`
320
+
321
+ Removes all event listeners.
322
+
323
+ ### AudioFile Interface
324
+
325
+ ```typescript
326
+ interface AudioFile {
327
+ id: string; // Unique identifier
328
+ uri: string; // Content URI
329
+ displayName: string; // Filename with extension
330
+ title: string; // Track title
331
+ artist: string; // Artist name
332
+ album: string; // Album name
333
+ duration: number; // Duration in milliseconds
334
+ size: number; // File size in bytes
335
+ mimeType: string; // MIME type (e.g., "audio/mpeg")
336
+ dateAdded: number; // Unix timestamp (seconds)
337
+ dateModified: number; // Unix timestamp (seconds)
338
+ albumArt: string | null; // Base64 encoded album art
339
+ path: string; // Absolute file path
340
+ bitrate: number; // Bitrate in bps
341
+ sampleRate: number; // Sample rate in Hz
342
+ albumArtist: string; // Album artist
343
+ composer: string; // Composer
344
+ genre: string; // Genre
345
+ year: number; // Release year
346
+ trackNumber: number; // Track number
347
+ }
348
+ ```
349
+
350
+ ### Supported Audio Formats
351
+
352
+ All audio formats recognized by Android MediaStore:
353
+
354
+ - MP3, M4A, AAC, FLAC, WAV
355
+ - OGG, OPUS, WMA, AMR
356
+ - MIDI, and more
357
+
358
+ ## Troubleshooting
359
+
360
+ ### "The package doesn't seem to be linked"
361
+
362
+ 1. Make sure you've rebuilt the app after installing:
363
+ ```bash
364
+ cd android && ./gradlew clean && cd ..
365
+ npx react-native run-android
366
+ ```
367
+
368
+ 2. For Expo, ensure you're using a development build, not Expo Go.
369
+
370
+ ### No audio files returned
371
+
372
+ 1. Verify permissions are granted:
373
+ ```typescript
374
+ const granted = await PermissionsAndroid.check(
375
+ Platform.Version >= 33
376
+ ? PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO
377
+ : PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE
378
+ );
379
+ console.log('Permission granted:', granted);
380
+ ```
381
+
382
+ 2. Check if audio files exist on the device/emulator.
383
+
384
+ ### Album art not showing
385
+
386
+ Album art extraction depends on:
387
+ - Having embedded artwork in the audio file
388
+ - Sufficient permissions
389
+
390
+ Some files may not have embedded album art. In those cases, `albumArt` will be `null`.
391
+
392
+ ## License
393
+
394
+ MIT © [Rakesh Prasad](https://github.com/rakeshprasad28)
395
+
396
+ ## Contributing
397
+
398
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
@@ -0,0 +1,111 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:8.1.0"
9
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0"
10
+ }
11
+ }
12
+
13
+ def isNewArchitectureEnabled() {
14
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
15
+ }
16
+
17
+ apply plugin: "com.android.library"
18
+ apply plugin: "kotlin-android"
19
+
20
+ if (isNewArchitectureEnabled()) {
21
+ apply plugin: "com.facebook.react"
22
+ }
23
+
24
+ def getExtOrDefault(name) {
25
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["AndroidMediaFetcher_" + name]
26
+ }
27
+
28
+ def getExtOrIntegerDefault(name) {
29
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["AndroidMediaFetcher_" + name]).toInteger()
30
+ }
31
+
32
+ def supportsNamespace() {
33
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
34
+ def major = parsed[0].toInteger()
35
+ def minor = parsed[1].toInteger()
36
+ return (major == 7 && minor >= 3) || major >= 8
37
+ }
38
+
39
+ android {
40
+ if (supportsNamespace()) {
41
+ namespace "com.androidmediafetcher"
42
+
43
+ sourceSets {
44
+ main {
45
+ manifest.srcFile "src/main/AndroidManifestNew.xml"
46
+ }
47
+ }
48
+ }
49
+
50
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
51
+
52
+ defaultConfig {
53
+ minSdkVersion 29
54
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
55
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
56
+ }
57
+
58
+ buildFeatures {
59
+ buildConfig true
60
+ }
61
+
62
+ buildTypes {
63
+ release {
64
+ minifyEnabled false
65
+ }
66
+ }
67
+
68
+ lintOptions {
69
+ disable "GradleCompatible"
70
+ }
71
+
72
+ compileOptions {
73
+ sourceCompatibility JavaVersion.VERSION_1_8
74
+ targetCompatibility JavaVersion.VERSION_1_8
75
+ }
76
+
77
+ kotlinOptions {
78
+ jvmTarget = "1.8"
79
+ }
80
+
81
+ sourceSets {
82
+ main {
83
+ if (isNewArchitectureEnabled()) {
84
+ java.srcDirs += ["src/newarch"]
85
+ } else {
86
+ java.srcDirs += ["src/oldarch"]
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ repositories {
93
+ mavenCentral()
94
+ google()
95
+ }
96
+
97
+ def kotlin_version = getExtOrDefault("kotlinVersion")
98
+
99
+ dependencies {
100
+ implementation "com.facebook.react:react-native:+"
101
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
102
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
103
+ }
104
+
105
+ if (isNewArchitectureEnabled()) {
106
+ react {
107
+ jsRootDir = file("../src/")
108
+ libraryName = "AndroidMediaFetcher"
109
+ codegenJavaPackageName = "com.androidmediafetcher"
110
+ }
111
+ }
@@ -0,0 +1,3 @@
1
+ AndroidMediaFetcher_kotlinVersion=1.9.0
2
+ AndroidMediaFetcher_compileSdkVersion=34
3
+ AndroidMediaFetcher_targetSdkVersion=34
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.androidmediafetcher">
3
+ </manifest>
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,21 @@
1
+ package com.androidmediafetcher
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ /**
9
+ * React Native Package registration for AndroidMediaFetcher
10
+ * This registers the native module with React Native
11
+ */
12
+ class AndroidMediaFetcherPackage : ReactPackage {
13
+
14
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
15
+ return listOf(AndroidMediaFetcherModule(reactContext))
16
+ }
17
+
18
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
19
+ return emptyList()
20
+ }
21
+ }
@@ -0,0 +1,57 @@
1
+ package com.androidmediafetcher.models
2
+
3
+ import org.json.JSONObject
4
+
5
+ /**
6
+ * Data class representing an audio file with all its metadata
7
+ */
8
+ data class AudioFile(
9
+ val id: String,
10
+ val uri: String,
11
+ val displayName: String,
12
+ val title: String,
13
+ val artist: String,
14
+ val album: String,
15
+ val duration: Long,
16
+ val size: Long,
17
+ val mimeType: String,
18
+ val dateAdded: Long,
19
+ val dateModified: Long,
20
+ val albumArt: String?,
21
+ val path: String,
22
+ val bitrate: Int,
23
+ val sampleRate: Int,
24
+ val albumArtist: String,
25
+ val composer: String,
26
+ val genre: String,
27
+ val year: Int,
28
+ val trackNumber: Int
29
+ ) {
30
+ /**
31
+ * Convert AudioFile to JSON object for passing to JavaScript
32
+ */
33
+ fun toJson(): JSONObject {
34
+ return JSONObject().apply {
35
+ put("id", id)
36
+ put("uri", uri)
37
+ put("displayName", displayName)
38
+ put("title", title)
39
+ put("artist", artist)
40
+ put("album", album)
41
+ put("duration", duration)
42
+ put("size", size)
43
+ put("mimeType", mimeType)
44
+ put("dateAdded", dateAdded)
45
+ put("dateModified", dateModified)
46
+ put("albumArt", albumArt ?: JSONObject.NULL)
47
+ put("path", path)
48
+ put("bitrate", bitrate)
49
+ put("sampleRate", sampleRate)
50
+ put("albumArtist", albumArtist)
51
+ put("composer", composer)
52
+ put("genre", genre)
53
+ put("year", year)
54
+ put("trackNumber", trackNumber)
55
+ }
56
+ }
57
+ }