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,111 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext {
|
|
3
|
+
kotlinVersion = "1.8.0"
|
|
4
|
+
minSdkVersion = 21
|
|
5
|
+
compileSdkVersion = 33
|
|
6
|
+
targetSdkVersion = 33
|
|
7
|
+
roomVersion = "2.5.0"
|
|
8
|
+
}
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
apply plugin: 'com.android.library'
|
|
19
|
+
apply plugin: 'kotlin-android'
|
|
20
|
+
apply plugin: 'kotlin-kapt'
|
|
21
|
+
|
|
22
|
+
android {
|
|
23
|
+
compileSdkVersion rootProject.ext.compileSdkVersion
|
|
24
|
+
|
|
25
|
+
defaultConfig {
|
|
26
|
+
minSdkVersion rootProject.ext.minSdkVersion
|
|
27
|
+
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
28
|
+
versionCode 1
|
|
29
|
+
versionName "1.0"
|
|
30
|
+
|
|
31
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
32
|
+
|
|
33
|
+
if (isNewArchitectureEnabled()) {
|
|
34
|
+
externalNativeBuild {
|
|
35
|
+
ndkBuild {
|
|
36
|
+
arguments "APP_PLATFORM=android-21",
|
|
37
|
+
"APP_STL=c++_shared",
|
|
38
|
+
"NDK_TOOLCHAIN_VERSION=clang",
|
|
39
|
+
"GENERATED_SRC_DIR=$buildDir/generated/source",
|
|
40
|
+
"PROJECT_BUILD_DIR=$buildDir",
|
|
41
|
+
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
|
42
|
+
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
|
|
43
|
+
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
|
|
44
|
+
cppFlags "-std=c++17"
|
|
45
|
+
targets "reactnativemp3"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
externalNativeBuild {
|
|
52
|
+
ndkBuild {
|
|
53
|
+
path "src/main/jni/Android.mk"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
buildFeatures {
|
|
58
|
+
buildConfig true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
sourceSets {
|
|
62
|
+
main {
|
|
63
|
+
if (isNewArchitectureEnabled()) {
|
|
64
|
+
java.srcDirs += ["$buildDir/generated/source/codegen/java"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
compileOptions {
|
|
70
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
71
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
kotlinOptions {
|
|
75
|
+
jvmTarget = "1.8"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
dependencies {
|
|
80
|
+
implementation "com.facebook.react:react-native:+"
|
|
81
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
|
82
|
+
|
|
83
|
+
// Room Database
|
|
84
|
+
implementation "androidx.room:room-runtime:$roomVersion"
|
|
85
|
+
kapt "androidx.room:room-compiler:$roomVersion"
|
|
86
|
+
implementation "androidx.room:room-ktx:$roomVersion"
|
|
87
|
+
|
|
88
|
+
// Lifecycle components
|
|
89
|
+
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
|
|
90
|
+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
|
|
91
|
+
implementation "androidx.lifecycle:lifecycle-common-java8:2.6.1"
|
|
92
|
+
|
|
93
|
+
// WorkManager for background tasks
|
|
94
|
+
implementation "androidx.work:work-runtime-ktx:2.8.1"
|
|
95
|
+
|
|
96
|
+
// Coroutines
|
|
97
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
98
|
+
|
|
99
|
+
// Media and ExoPlayer for advanced features
|
|
100
|
+
implementation "androidx.media:media:1.6.0"
|
|
101
|
+
implementation "com.google.android.exoplayer:exoplayer-core:2.18.7"
|
|
102
|
+
implementation "com.google.android.exoplayer:exoplayer-ui:2.18.7"
|
|
103
|
+
|
|
104
|
+
// Glide for image loading/caching
|
|
105
|
+
implementation "com.github.bumptech.glide:glide:4.15.1"
|
|
106
|
+
kapt "com.github.bumptech.glide:compiler:4.15.1"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
def isNewArchitectureEnabled() {
|
|
110
|
+
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
111
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
package="com.reactnativemp3">
|
|
4
|
+
|
|
5
|
+
<!-- Storage Permissions -->
|
|
6
|
+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
7
|
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
8
|
+
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
|
9
|
+
android:minSdkVersion="30" />
|
|
10
|
+
|
|
11
|
+
<!-- Audio Permissions -->
|
|
12
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
13
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
14
|
+
|
|
15
|
+
<!-- Network Permissions (for online streaming) -->
|
|
16
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
17
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
18
|
+
|
|
19
|
+
<application>
|
|
20
|
+
<!-- Music Service for background playback -->
|
|
21
|
+
<service
|
|
22
|
+
android:name=".services.MusicService"
|
|
23
|
+
android:enabled="true"
|
|
24
|
+
android:exported="false"
|
|
25
|
+
android:foregroundServiceType="mediaPlayback">
|
|
26
|
+
<intent-filter>
|
|
27
|
+
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
|
28
|
+
</intent-filter>
|
|
29
|
+
</service>
|
|
30
|
+
|
|
31
|
+
<!-- File Monitoring Service -->
|
|
32
|
+
<service
|
|
33
|
+
android:name=".services.FileObserverService"
|
|
34
|
+
android:enabled="true"
|
|
35
|
+
android:exported="false" />
|
|
36
|
+
|
|
37
|
+
<!-- Broadcast Receiver for Audio Focus -->
|
|
38
|
+
<receiver android:name=".services.AudioFocusReceiver">
|
|
39
|
+
<intent-filter>
|
|
40
|
+
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
|
|
41
|
+
</intent-filter>
|
|
42
|
+
</receiver>
|
|
43
|
+
</application>
|
|
44
|
+
</manifest>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package com.reactnativemp3
|
|
2
|
+
|
|
3
|
+
import android.view.View
|
|
4
|
+
import com.facebook.react.ReactPackage
|
|
5
|
+
import com.facebook.react.bridge.NativeModule
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.uimanager.ReactShadowNode
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager
|
|
9
|
+
|
|
10
|
+
class Mp3Package : ReactPackage {
|
|
11
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
12
|
+
return listOf(
|
|
13
|
+
ScannerModule(reactContext),
|
|
14
|
+
PlayerModule(reactContext),
|
|
15
|
+
MetadataModule(reactContext),
|
|
16
|
+
NotificationModule(reactContext)
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<View, ReactShadowNode<*>>> {
|
|
21
|
+
return emptyList()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.reactnativemp3
|
|
2
|
+
|
|
3
|
+
import com.facebook.jni.HybridData
|
|
4
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
8
|
+
import com.facebook.react.turbomodule.core.interfaces.TurboModule
|
|
9
|
+
import java.lang.ref.WeakReference
|
|
10
|
+
|
|
11
|
+
@DoNotStrip
|
|
12
|
+
abstract class Mp3TurboModule(
|
|
13
|
+
reactContext: ReactApplicationContext,
|
|
14
|
+
moduleName: String
|
|
15
|
+
) : TurboModule {
|
|
16
|
+
|
|
17
|
+
protected val reactContextRef: WeakReference<ReactApplicationContext> = WeakReference(reactContext)
|
|
18
|
+
|
|
19
|
+
@DoNotStrip
|
|
20
|
+
@Suppress("UNUSED")
|
|
21
|
+
private val mHybridData: HybridData
|
|
22
|
+
|
|
23
|
+
init {
|
|
24
|
+
mHybridData = initHybrid(reactContext.jsCallInvokerHolder as CallInvokerHolderImpl)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@DoNotStrip
|
|
28
|
+
private external fun initHybrid(callInvokerHolder: CallInvokerHolderImpl): HybridData
|
|
29
|
+
|
|
30
|
+
override fun getName(): String = moduleName
|
|
31
|
+
|
|
32
|
+
override fun invalidate() {
|
|
33
|
+
mHybridData.resetNative()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected fun getReactApplicationContext(): ReactApplicationContext? {
|
|
37
|
+
return reactContextRef.get()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Helper methods for emitting events to JS
|
|
41
|
+
@DoNotStrip
|
|
42
|
+
protected external fun emitEvent(eventName: String, params: WritableMap?)
|
|
43
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.reactnativemp3.database
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.room.Database
|
|
5
|
+
import androidx.room.Room
|
|
6
|
+
import androidx.room.RoomDatabase
|
|
7
|
+
import androidx.room.TypeConverters
|
|
8
|
+
import com.reactnativemp3.database.dao.*
|
|
9
|
+
import com.reactnativemp3.database.entities.*
|
|
10
|
+
|
|
11
|
+
@Database(
|
|
12
|
+
entities = [
|
|
13
|
+
SongEntity::class,
|
|
14
|
+
PlaylistEntity::class,
|
|
15
|
+
PlaylistSongEntity::class,
|
|
16
|
+
ThumbnailCacheEntity::class
|
|
17
|
+
],
|
|
18
|
+
version = 1,
|
|
19
|
+
exportSchema = true
|
|
20
|
+
)
|
|
21
|
+
@TypeConverters(Converters::class)
|
|
22
|
+
abstract class MusicDatabase : RoomDatabase() {
|
|
23
|
+
|
|
24
|
+
abstract fun songDao(): SongDao
|
|
25
|
+
abstract fun playlistDao(): PlaylistDao
|
|
26
|
+
abstract fun thumbnailCacheDao(): ThumbnailCacheDao
|
|
27
|
+
|
|
28
|
+
companion object {
|
|
29
|
+
@Volatile
|
|
30
|
+
private var INSTANCE: MusicDatabase? = null
|
|
31
|
+
|
|
32
|
+
fun getInstance(context: Context): MusicDatabase {
|
|
33
|
+
return INSTANCE ?: synchronized(this) {
|
|
34
|
+
val instance = Room.databaseBuilder(
|
|
35
|
+
context.applicationContext,
|
|
36
|
+
MusicDatabase::class.java,
|
|
37
|
+
"music_database"
|
|
38
|
+
)
|
|
39
|
+
.addMigrations() // Add migrations here as needed
|
|
40
|
+
.setJournalMode(JournalMode.TRUNCATE)
|
|
41
|
+
.build()
|
|
42
|
+
|
|
43
|
+
INSTANCE = instance
|
|
44
|
+
instance
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.reactnativemp3.database.dao
|
|
2
|
+
|
|
3
|
+
import androidx.room.*
|
|
4
|
+
import com.reactnativemp3.database.entities.SongEntity
|
|
5
|
+
import kotlinx.coroutines.flow.Flow
|
|
6
|
+
|
|
7
|
+
@Dao
|
|
8
|
+
interface SongDao {
|
|
9
|
+
|
|
10
|
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
11
|
+
suspend fun insert(song: SongEntity)
|
|
12
|
+
|
|
13
|
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
|
14
|
+
suspend fun insertAll(songs: List<SongEntity>)
|
|
15
|
+
|
|
16
|
+
@Update
|
|
17
|
+
suspend fun update(song: SongEntity)
|
|
18
|
+
|
|
19
|
+
@Delete
|
|
20
|
+
suspend fun delete(song: SongEntity)
|
|
21
|
+
|
|
22
|
+
@Query("DELETE FROM songs WHERE id = :songId")
|
|
23
|
+
suspend fun deleteById(songId: String)
|
|
24
|
+
|
|
25
|
+
@Query("SELECT * FROM songs WHERE id = :songId")
|
|
26
|
+
suspend fun getById(songId: String): SongEntity?
|
|
27
|
+
|
|
28
|
+
@Query("SELECT * FROM songs ORDER BY title COLLATE NOCASE")
|
|
29
|
+
fun getAllSongs(): Flow<List<SongEntity>>
|
|
30
|
+
|
|
31
|
+
@Query("SELECT * FROM songs ORDER BY date_added DESC")
|
|
32
|
+
fun getRecentlyAdded(): Flow<List<SongEntity>>
|
|
33
|
+
|
|
34
|
+
@Query("SELECT * FROM songs WHERE last_played IS NOT NULL ORDER BY last_played DESC")
|
|
35
|
+
fun getRecentlyPlayed(): Flow<List<SongEntity>>
|
|
36
|
+
|
|
37
|
+
@Query("SELECT * FROM songs WHERE is_favorite = 1 ORDER BY title COLLATE NOCASE")
|
|
38
|
+
fun getFavorites(): Flow<List<SongEntity>>
|
|
39
|
+
|
|
40
|
+
@Query("SELECT * FROM songs WHERE album = :album ORDER BY track_number, title")
|
|
41
|
+
fun getSongsByAlbum(album: String): Flow<List<SongEntity>>
|
|
42
|
+
|
|
43
|
+
@Query("SELECT DISTINCT album FROM songs WHERE album IS NOT NULL AND album != '' ORDER BY album COLLATE NOCASE")
|
|
44
|
+
fun getAllAlbums(): Flow<List<String>>
|
|
45
|
+
|
|
46
|
+
@Query("SELECT DISTINCT artist FROM songs WHERE artist IS NOT NULL AND artist != '' ORDER BY artist COLLATE NOCASE")
|
|
47
|
+
fun getAllArtists(): Flow<List<String>>
|
|
48
|
+
|
|
49
|
+
@Query("SELECT DISTINCT genre FROM songs WHERE genre IS NOT NULL AND genre != '' ORDER BY genre COLLATE NOCASE")
|
|
50
|
+
fun getAllGenres(): Flow<List<String>>
|
|
51
|
+
|
|
52
|
+
@Query("SELECT COUNT(*) FROM songs")
|
|
53
|
+
suspend fun getCount(): Int
|
|
54
|
+
|
|
55
|
+
@Query("SELECT * FROM songs WHERE title LIKE :query OR artist LIKE :query OR album LIKE :query")
|
|
56
|
+
fun search(query: String): Flow<List<SongEntity>>
|
|
57
|
+
|
|
58
|
+
@Query("UPDATE songs SET play_count = play_count + 1, last_played = :timestamp WHERE id = :songId")
|
|
59
|
+
suspend fun incrementPlayCount(songId: String, timestamp: Long)
|
|
60
|
+
|
|
61
|
+
@Query("UPDATE songs SET is_favorite = :isFavorite WHERE id = :songId")
|
|
62
|
+
suspend fun setFavorite(songId: String, isFavorite: Boolean)
|
|
63
|
+
|
|
64
|
+
@Query("UPDATE songs SET bookmark = :position WHERE id = :songId")
|
|
65
|
+
suspend fun setBookmark(songId: String, position: Long)
|
|
66
|
+
|
|
67
|
+
@Query("SELECT * FROM songs WHERE path = :filePath")
|
|
68
|
+
suspend fun getByPath(filePath: String): SongEntity?
|
|
69
|
+
|
|
70
|
+
@Query("DELETE FROM songs WHERE path = :filePath")
|
|
71
|
+
suspend fun deleteByPath(filePath: String)
|
|
72
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package com.reactnativemp3.database.entities
|
|
2
|
+
|
|
3
|
+
import androidx.room.*
|
|
4
|
+
|
|
5
|
+
@Entity(
|
|
6
|
+
tableName = "playlists",
|
|
7
|
+
indices = [Index(value = ["name"], unique = true)]
|
|
8
|
+
)
|
|
9
|
+
data class PlaylistEntity(
|
|
10
|
+
@PrimaryKey(autoGenerate = true)
|
|
11
|
+
@ColumnInfo(name = "id")
|
|
12
|
+
val id: Long = 0,
|
|
13
|
+
|
|
14
|
+
@ColumnInfo(name = "name")
|
|
15
|
+
val name: String,
|
|
16
|
+
|
|
17
|
+
@ColumnInfo(name = "created_at")
|
|
18
|
+
val createdAt: Long = System.currentTimeMillis(),
|
|
19
|
+
|
|
20
|
+
@ColumnInfo(name = "song_count")
|
|
21
|
+
val songCount: Int = 0,
|
|
22
|
+
|
|
23
|
+
@ColumnInfo(name = "thumbnail_uri")
|
|
24
|
+
val thumbnailUri: String? = null
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
@Entity(
|
|
28
|
+
tableName = "playlist_songs",
|
|
29
|
+
primaryKeys = ["playlist_id", "song_id"],
|
|
30
|
+
foreignKeys = [
|
|
31
|
+
ForeignKey(
|
|
32
|
+
entity = PlaylistEntity::class,
|
|
33
|
+
parentColumns = ["id"],
|
|
34
|
+
childColumns = ["playlist_id"],
|
|
35
|
+
onDelete = ForeignKey.CASCADE
|
|
36
|
+
),
|
|
37
|
+
ForeignKey(
|
|
38
|
+
entity = SongEntity::class,
|
|
39
|
+
parentColumns = ["id"],
|
|
40
|
+
childColumns = ["song_id"],
|
|
41
|
+
onDelete = ForeignKey.CASCADE
|
|
42
|
+
)
|
|
43
|
+
],
|
|
44
|
+
indices = [
|
|
45
|
+
Index(value = ["playlist_id"]),
|
|
46
|
+
Index(value = ["song_id"])
|
|
47
|
+
]
|
|
48
|
+
)
|
|
49
|
+
data class PlaylistSongEntity(
|
|
50
|
+
@ColumnInfo(name = "playlist_id")
|
|
51
|
+
val playlistId: Long,
|
|
52
|
+
|
|
53
|
+
@ColumnInfo(name = "song_id")
|
|
54
|
+
val songId: String,
|
|
55
|
+
|
|
56
|
+
@ColumnInfo(name = "position")
|
|
57
|
+
val position: Int
|
|
58
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
package com.reactnativemp3.database.entities
|
|
2
|
+
|
|
3
|
+
import androidx.room.ColumnInfo
|
|
4
|
+
import androidx.room.Entity
|
|
5
|
+
import androidx.room.Index
|
|
6
|
+
import androidx.room.PrimaryKey
|
|
7
|
+
import java.util.*
|
|
8
|
+
|
|
9
|
+
@Entity(
|
|
10
|
+
tableName = "songs",
|
|
11
|
+
indices = [
|
|
12
|
+
Index(value = ["title"]),
|
|
13
|
+
Index(value = ["artist"]),
|
|
14
|
+
Index(value = ["album"]),
|
|
15
|
+
Index(value = ["date_added"], orders = [Index.Order.DESC]),
|
|
16
|
+
Index(value = ["last_played"], orders = [Index.Order.DESC])
|
|
17
|
+
]
|
|
18
|
+
)
|
|
19
|
+
data class SongEntity(
|
|
20
|
+
@PrimaryKey
|
|
21
|
+
@ColumnInfo(name = "id")
|
|
22
|
+
val id: String,
|
|
23
|
+
|
|
24
|
+
@ColumnInfo(name = "title")
|
|
25
|
+
val title: String,
|
|
26
|
+
|
|
27
|
+
@ColumnInfo(name = "artist")
|
|
28
|
+
val artist: String = "Unknown Artist",
|
|
29
|
+
|
|
30
|
+
@ColumnInfo(name = "album")
|
|
31
|
+
val album: String = "Unknown Album",
|
|
32
|
+
|
|
33
|
+
@ColumnInfo(name = "duration")
|
|
34
|
+
val duration: Long,
|
|
35
|
+
|
|
36
|
+
@ColumnInfo(name = "path")
|
|
37
|
+
val path: String,
|
|
38
|
+
|
|
39
|
+
@ColumnInfo(name = "uri")
|
|
40
|
+
val uri: String,
|
|
41
|
+
|
|
42
|
+
@ColumnInfo(name = "album_art_uri")
|
|
43
|
+
val albumArtUri: String?,
|
|
44
|
+
|
|
45
|
+
@ColumnInfo(name = "size")
|
|
46
|
+
val size: Long,
|
|
47
|
+
|
|
48
|
+
@ColumnInfo(name = "mime_type")
|
|
49
|
+
val mimeType: String,
|
|
50
|
+
|
|
51
|
+
@ColumnInfo(name = "track_number")
|
|
52
|
+
val trackNumber: Int = 0,
|
|
53
|
+
|
|
54
|
+
@ColumnInfo(name = "year")
|
|
55
|
+
val year: Int? = null,
|
|
56
|
+
|
|
57
|
+
@ColumnInfo(name = "genre")
|
|
58
|
+
val genre: String? = null,
|
|
59
|
+
|
|
60
|
+
@ColumnInfo(name = "date_added")
|
|
61
|
+
val dateAdded: Long,
|
|
62
|
+
|
|
63
|
+
@ColumnInfo(name = "last_modified")
|
|
64
|
+
val lastModified: Long,
|
|
65
|
+
|
|
66
|
+
@ColumnInfo(name = "is_favorite")
|
|
67
|
+
val isFavorite: Boolean = false,
|
|
68
|
+
|
|
69
|
+
@ColumnInfo(name = "play_count")
|
|
70
|
+
val playCount: Int = 0,
|
|
71
|
+
|
|
72
|
+
@ColumnInfo(name = "last_played")
|
|
73
|
+
val lastPlayed: Long? = null,
|
|
74
|
+
|
|
75
|
+
@ColumnInfo(name = "bitrate")
|
|
76
|
+
val bitrate: Int? = null,
|
|
77
|
+
|
|
78
|
+
@ColumnInfo(name = "sample_rate")
|
|
79
|
+
val sampleRate: Int? = null,
|
|
80
|
+
|
|
81
|
+
@ColumnInfo(name = "channels")
|
|
82
|
+
val channels: Int? = null,
|
|
83
|
+
|
|
84
|
+
@ColumnInfo(name = "composer")
|
|
85
|
+
val composer: String? = null,
|
|
86
|
+
|
|
87
|
+
@ColumnInfo(name = "lyrics")
|
|
88
|
+
val lyrics: String? = null,
|
|
89
|
+
|
|
90
|
+
@ColumnInfo(name = "rating")
|
|
91
|
+
val rating: Int? = null,
|
|
92
|
+
|
|
93
|
+
@ColumnInfo(name = "is_podcast")
|
|
94
|
+
val isPodcast: Boolean = false,
|
|
95
|
+
|
|
96
|
+
@ColumnInfo(name = "bookmark")
|
|
97
|
+
val bookmark: Long = 0
|
|
98
|
+
) {
|
|
99
|
+
companion object {
|
|
100
|
+
fun generateId(path: String): String {
|
|
101
|
+
return UUID.nameUUIDFromBytes(path.toByteArray()).toString()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.reactnativemp3.database.entities
|
|
2
|
+
|
|
3
|
+
import androidx.room.ColumnInfo
|
|
4
|
+
import androidx.room.Entity
|
|
5
|
+
import androidx.room.PrimaryKey
|
|
6
|
+
|
|
7
|
+
@Entity(tableName = "thumbnail_cache")
|
|
8
|
+
data class ThumbnailCacheEntity(
|
|
9
|
+
@PrimaryKey
|
|
10
|
+
@ColumnInfo(name = "song_id")
|
|
11
|
+
val songId: String,
|
|
12
|
+
|
|
13
|
+
@ColumnInfo(name = "thumbnail_data")
|
|
14
|
+
val thumbnailData: ByteArray,
|
|
15
|
+
|
|
16
|
+
@ColumnInfo(name = "last_accessed")
|
|
17
|
+
val lastAccessed: Long = System.currentTimeMillis(),
|
|
18
|
+
|
|
19
|
+
@ColumnInfo(name = "size")
|
|
20
|
+
val size: Int
|
|
21
|
+
) {
|
|
22
|
+
override fun equals(other: Any?): Boolean {
|
|
23
|
+
if (this === other) return true
|
|
24
|
+
if (javaClass != other?.javaClass) return false
|
|
25
|
+
|
|
26
|
+
other as ThumbnailCacheEntity
|
|
27
|
+
|
|
28
|
+
if (songId != other.songId) return false
|
|
29
|
+
if (!thumbnailData.contentEquals(other.thumbnailData)) return false
|
|
30
|
+
if (lastAccessed != other.lastAccessed) return false
|
|
31
|
+
if (size != other.size) return false
|
|
32
|
+
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override fun hashCode(): Int {
|
|
37
|
+
var result = songId.hashCode()
|
|
38
|
+
result = 31 * result + thumbnailData.contentHashCode()
|
|
39
|
+
result = 31 * result + lastAccessed.hashCode()
|
|
40
|
+
result = 31 * result + size
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
}
|
|
File without changes
|
|
File without changes
|