@roitium/expo-orpheus 0.6.0 → 0.7.1
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/README.md +1 -1
- package/android/src/main/java/expo/modules/orpheus/DownloadCache.kt +13 -0
- package/android/src/main/java/expo/modules/orpheus/ExpoOrpheusModule.kt +19 -11
- package/android/src/main/java/expo/modules/orpheus/OrpheusMusicService.kt +20 -31
- package/android/src/main/java/expo/modules/orpheus/utils/DownloadUtil.kt +10 -18
- package/android/src/main/java/expo/modules/orpheus/utils/{Storage.kt → GeneralStorage.kt} +15 -5
- package/android/src/main/java/expo/modules/orpheus/utils/LoudnessStorage.kt +40 -0
- package/build/ExpoOrpheusModule.d.ts +2 -0
- package/build/ExpoOrpheusModule.d.ts.map +1 -1
- package/build/ExpoOrpheusModule.js.map +1 -1
- package/package.json +1 -1
- package/src/ExpoOrpheusModule.ts +2 -0
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@ package expo.modules.orpheus
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import androidx.media3.common.util.UnstableApi
|
|
5
5
|
import androidx.media3.database.StandaloneDatabaseProvider
|
|
6
|
+
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
|
|
6
7
|
import androidx.media3.datasource.cache.NoOpCacheEvictor
|
|
7
8
|
import androidx.media3.datasource.cache.SimpleCache
|
|
8
9
|
import java.io.File
|
|
@@ -10,6 +11,7 @@ import java.io.File
|
|
|
10
11
|
@UnstableApi
|
|
11
12
|
object DownloadCache {
|
|
12
13
|
private var stableCache: SimpleCache? = null
|
|
14
|
+
private var lruCache: SimpleCache? = null
|
|
13
15
|
|
|
14
16
|
@Synchronized
|
|
15
17
|
fun getStableCache(context: Context): SimpleCache {
|
|
@@ -21,4 +23,15 @@ object DownloadCache {
|
|
|
21
23
|
}
|
|
22
24
|
return stableCache!!
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
@Synchronized
|
|
28
|
+
fun getLruCache(context: Context): SimpleCache {
|
|
29
|
+
if (lruCache == null) {
|
|
30
|
+
val cacheDir = File(context.cacheDir, "media_cache_lru")
|
|
31
|
+
val evictor = LeastRecentlyUsedCacheEvictor(256 * 1024 * 1024)
|
|
32
|
+
val databaseProvider = StandaloneDatabaseProvider(context)
|
|
33
|
+
lruCache = SimpleCache(cacheDir, evictor, databaseProvider)
|
|
34
|
+
}
|
|
35
|
+
return lruCache!!
|
|
36
|
+
}
|
|
24
37
|
}
|
|
@@ -25,7 +25,8 @@ import expo.modules.kotlin.modules.Module
|
|
|
25
25
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
26
26
|
import expo.modules.orpheus.models.TrackRecord
|
|
27
27
|
import expo.modules.orpheus.utils.DownloadUtil
|
|
28
|
-
import expo.modules.orpheus.utils.
|
|
28
|
+
import expo.modules.orpheus.utils.GeneralStorage
|
|
29
|
+
import expo.modules.orpheus.utils.LoudnessStorage
|
|
29
30
|
import expo.modules.orpheus.utils.toMediaItem
|
|
30
31
|
|
|
31
32
|
@UnstableApi
|
|
@@ -156,12 +157,12 @@ class ExpoOrpheusModule : Module() {
|
|
|
156
157
|
|
|
157
158
|
override fun onRepeatModeChanged(repeatMode: Int) {
|
|
158
159
|
super.onRepeatModeChanged(repeatMode)
|
|
159
|
-
|
|
160
|
+
GeneralStorage.saveRepeatMode(repeatMode)
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
|
|
163
164
|
super.onShuffleModeEnabledChanged(shuffleModeEnabled)
|
|
164
|
-
|
|
165
|
+
GeneralStorage.saveShuffleMode(shuffleModeEnabled)
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|
|
@@ -181,7 +182,8 @@ class ExpoOrpheusModule : Module() {
|
|
|
181
182
|
|
|
182
183
|
OnCreate {
|
|
183
184
|
val context = appContext.reactContext ?: return@OnCreate
|
|
184
|
-
|
|
185
|
+
GeneralStorage.initialize(context)
|
|
186
|
+
LoudnessStorage.initialize(context)
|
|
185
187
|
val sessionToken = SessionToken(
|
|
186
188
|
context,
|
|
187
189
|
ComponentName(context, OrpheusMusicService::class.java)
|
|
@@ -217,11 +219,19 @@ class ExpoOrpheusModule : Module() {
|
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
Constant("restorePlaybackPositionEnabled") {
|
|
220
|
-
|
|
222
|
+
GeneralStorage.isRestoreEnabled()
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
Constant("loudnessNormalizationEnabled") {
|
|
224
|
-
|
|
226
|
+
GeneralStorage.isLoudnessNormalizationEnabled()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
Constant("autoplayOnStartEnabled") {
|
|
230
|
+
GeneralStorage.isAutoplayOnStartEnabled()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
Function("setAutoplayOnStartEnabled") { enabled: Boolean ->
|
|
234
|
+
GeneralStorage.setAutoplayOnStartEnabled(enabled)
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
Function("setBilibiliCookie") { cookie: String ->
|
|
@@ -229,11 +239,11 @@ class ExpoOrpheusModule : Module() {
|
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
Function("setLoudnessNormalizationEnabled") { enabled: Boolean ->
|
|
232
|
-
|
|
242
|
+
GeneralStorage.setLoudnessNormalizationEnabled(enabled)
|
|
233
243
|
}
|
|
234
244
|
|
|
235
245
|
Function("setRestorePlaybackPositionEnabled") { enabled: Boolean ->
|
|
236
|
-
|
|
246
|
+
GeneralStorage.setRestoreEnabled(enabled)
|
|
237
247
|
}
|
|
238
248
|
|
|
239
249
|
AsyncFunction("getPosition") {
|
|
@@ -302,7 +312,6 @@ class ExpoOrpheusModule : Module() {
|
|
|
302
312
|
checkPlayer()
|
|
303
313
|
player?.clearMediaItems()
|
|
304
314
|
durationCache.clear()
|
|
305
|
-
DownloadUtil.itemVolumeMap.clear()
|
|
306
315
|
}.runOnQueue(Queues.MAIN)
|
|
307
316
|
|
|
308
317
|
AsyncFunction("skipTo") { index: Int ->
|
|
@@ -396,7 +405,6 @@ class ExpoOrpheusModule : Module() {
|
|
|
396
405
|
if (clearQueue == true) {
|
|
397
406
|
p.clearMediaItems()
|
|
398
407
|
durationCache.clear()
|
|
399
|
-
DownloadUtil.itemVolumeMap.clear()
|
|
400
408
|
}
|
|
401
409
|
val initialSize = p.mediaItemCount
|
|
402
410
|
p.addMediaItems(mediaItems)
|
|
@@ -714,7 +722,7 @@ class ExpoOrpheusModule : Module() {
|
|
|
714
722
|
private fun saveCurrentPosition() {
|
|
715
723
|
val p = player ?: return
|
|
716
724
|
if (p.playbackState != Player.STATE_IDLE) {
|
|
717
|
-
|
|
725
|
+
GeneralStorage.savePosition(
|
|
718
726
|
p.currentMediaItemIndex,
|
|
719
727
|
p.currentPosition
|
|
720
728
|
)
|
|
@@ -2,6 +2,7 @@ package expo.modules.orpheus
|
|
|
2
2
|
|
|
3
3
|
import android.app.PendingIntent
|
|
4
4
|
import android.content.Intent
|
|
5
|
+
import android.util.Log
|
|
5
6
|
import androidx.annotation.OptIn
|
|
6
7
|
import androidx.core.net.toUri
|
|
7
8
|
import androidx.media3.common.AudioAttributes
|
|
@@ -15,10 +16,10 @@ import androidx.media3.session.MediaLibraryService
|
|
|
15
16
|
import androidx.media3.session.MediaSession
|
|
16
17
|
import com.google.common.util.concurrent.Futures
|
|
17
18
|
import com.google.common.util.concurrent.ListenableFuture
|
|
18
|
-
import expo.modules.orpheus.bilibili.VolumeData
|
|
19
19
|
import expo.modules.orpheus.utils.DownloadUtil
|
|
20
20
|
import expo.modules.orpheus.utils.SleepTimeController
|
|
21
|
-
import expo.modules.orpheus.utils.
|
|
21
|
+
import expo.modules.orpheus.utils.GeneralStorage
|
|
22
|
+
import expo.modules.orpheus.utils.LoudnessStorage
|
|
22
23
|
import expo.modules.orpheus.utils.calculateLoudnessGain
|
|
23
24
|
import expo.modules.orpheus.utils.fadeInTo
|
|
24
25
|
import kotlinx.coroutines.Job
|
|
@@ -61,7 +62,8 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
61
62
|
super.onCreate()
|
|
62
63
|
instance = this
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
GeneralStorage.initialize(this)
|
|
66
|
+
LoudnessStorage.initialize(this)
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
val dataSourceFactory = DownloadUtil.getPlayerDataSourceFactory(this)
|
|
@@ -111,18 +113,8 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
111
113
|
.setSessionActivity(contentIntent)
|
|
112
114
|
.build()
|
|
113
115
|
|
|
114
|
-
restorePlayerState(
|
|
116
|
+
restorePlayerState(GeneralStorage.isRestoreEnabled())
|
|
115
117
|
sleepTimerManager = SleepTimeController(player!!)
|
|
116
|
-
|
|
117
|
-
// 当有新的响度数据时,如果是当前这首歌的就直接应用,否则是预加载,等待 onMediaItemTransition 处理
|
|
118
|
-
scope.launch {
|
|
119
|
-
DownloadUtil.volumeResolvedEvent.collect { (uri, volumeData) ->
|
|
120
|
-
val currentUri = player?.currentMediaItem?.localConfiguration?.uri?.toString()
|
|
121
|
-
if (currentUri == uri) {
|
|
122
|
-
applyVolumeForCurrentItem(volumeData)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
118
|
}
|
|
127
119
|
|
|
128
120
|
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? {
|
|
@@ -187,15 +179,15 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
187
179
|
private fun restorePlayerState(restorePosition: Boolean) {
|
|
188
180
|
val player = player ?: return
|
|
189
181
|
|
|
190
|
-
val restoredItems =
|
|
182
|
+
val restoredItems = GeneralStorage.restoreQueue()
|
|
191
183
|
|
|
192
184
|
if (restoredItems.isNotEmpty()) {
|
|
193
185
|
player.setMediaItems(restoredItems)
|
|
194
186
|
|
|
195
|
-
val savedIndex =
|
|
196
|
-
val savedPosition =
|
|
197
|
-
val savedShuffleMode =
|
|
198
|
-
val savedRepeatMode =
|
|
187
|
+
val savedIndex = GeneralStorage.getSavedIndex()
|
|
188
|
+
val savedPosition = GeneralStorage.getSavedPosition()
|
|
189
|
+
val savedShuffleMode = GeneralStorage.getShuffleMode()
|
|
190
|
+
val savedRepeatMode = GeneralStorage.getRepeatMode()
|
|
199
191
|
|
|
200
192
|
if (savedIndex >= 0 && savedIndex < restoredItems.size) {
|
|
201
193
|
player.seekTo(savedIndex, if (restorePosition) savedPosition else C.TIME_UNSET)
|
|
@@ -206,7 +198,7 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
206
198
|
player.shuffleModeEnabled = savedShuffleMode
|
|
207
199
|
player.repeatMode = savedRepeatMode
|
|
208
200
|
|
|
209
|
-
player.playWhenReady =
|
|
201
|
+
player.playWhenReady = GeneralStorage.isAutoplayOnStartEnabled()
|
|
210
202
|
player.prepare()
|
|
211
203
|
}
|
|
212
204
|
}
|
|
@@ -221,10 +213,8 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
221
213
|
saveCurrentQueue()
|
|
222
214
|
val uri = mediaItem?.localConfiguration?.uri?.toString() ?: return
|
|
223
215
|
|
|
224
|
-
val volumeData =
|
|
225
|
-
|
|
226
|
-
applyVolumeForCurrentItem(volumeData)
|
|
227
|
-
}
|
|
216
|
+
val volumeData = LoudnessStorage.getLoudnessData(uri)
|
|
217
|
+
applyVolumeForCurrentItem(volumeData)
|
|
228
218
|
}
|
|
229
219
|
|
|
230
220
|
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
|
@@ -237,21 +227,20 @@ class OrpheusMusicService : MediaLibraryService() {
|
|
|
237
227
|
val player = player ?: return
|
|
238
228
|
val queue = List(player.mediaItemCount) { i -> player.getMediaItemAt(i) }
|
|
239
229
|
if (queue.isNotEmpty()) {
|
|
240
|
-
|
|
230
|
+
GeneralStorage.saveQueue(queue)
|
|
241
231
|
}
|
|
242
232
|
}
|
|
243
233
|
|
|
244
234
|
@OptIn(UnstableApi::class)
|
|
245
|
-
private fun applyVolumeForCurrentItem(
|
|
235
|
+
private fun applyVolumeForCurrentItem(measuredI: Double) {
|
|
236
|
+
Log.d("LoudnessNormalization", "measuredI: $measuredI")
|
|
246
237
|
val player = player ?: return
|
|
247
238
|
volumeFadeJob?.cancel()
|
|
248
|
-
val isLoudnessNormalizationEnabled =
|
|
239
|
+
val isLoudnessNormalizationEnabled = GeneralStorage.isLoudnessNormalizationEnabled()
|
|
249
240
|
if (!isLoudnessNormalizationEnabled) return
|
|
250
241
|
val gain = run {
|
|
251
|
-
val
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (measured == 0.0) 1.0f else calculateLoudnessGain(measured, target)
|
|
242
|
+
val target = -14.0 // bilibili 的这个值似乎是固定的
|
|
243
|
+
if (measuredI == 0.0) 1.0f else calculateLoudnessGain(measuredI, target)
|
|
255
244
|
}
|
|
256
245
|
|
|
257
246
|
val targetVol = 1.0f * gain
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package expo.modules.orpheus.utils
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
4
5
|
import androidx.core.net.toUri
|
|
5
6
|
import androidx.media3.common.util.UnstableApi
|
|
6
7
|
import androidx.media3.database.StandaloneDatabaseProvider
|
|
@@ -35,15 +36,6 @@ object DownloadUtil {
|
|
|
35
36
|
|
|
36
37
|
private var downloadNotificationHelper: DownloadNotificationHelper? = null
|
|
37
38
|
|
|
38
|
-
var itemVolumeMap: MutableMap<String, VolumeData> = mutableMapOf()
|
|
39
|
-
|
|
40
|
-
private val _volumeResolvedEvent = MutableSharedFlow<Pair<String, VolumeData>>(
|
|
41
|
-
replay = 0,
|
|
42
|
-
extraBufferCapacity = 1,
|
|
43
|
-
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
|
44
|
-
)
|
|
45
|
-
val volumeResolvedEvent = _volumeResolvedEvent.asSharedFlow()
|
|
46
|
-
|
|
47
39
|
@Synchronized
|
|
48
40
|
fun getDownloadManager(context: Context): DownloadManager {
|
|
49
41
|
if (downloadManager == null) {
|
|
@@ -68,10 +60,16 @@ object DownloadUtil {
|
|
|
68
60
|
val upstreamFactory = getUpstreamFactory()
|
|
69
61
|
|
|
70
62
|
val downloadCache = DownloadCache.getStableCache(context)
|
|
63
|
+
val lruCache = DownloadCache.getLruCache(context)
|
|
64
|
+
|
|
65
|
+
val cacheFactory = CacheDataSource.Factory()
|
|
66
|
+
.setCache(lruCache)
|
|
67
|
+
.setUpstreamDataSourceFactory(upstreamFactory)
|
|
68
|
+
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
|
|
71
69
|
|
|
72
70
|
val downloadFactory = CacheDataSource.Factory()
|
|
73
71
|
.setCache(downloadCache)
|
|
74
|
-
.setUpstreamDataSourceFactory(
|
|
72
|
+
.setUpstreamDataSourceFactory(cacheFactory)
|
|
75
73
|
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
|
|
76
74
|
.setCacheWriteDataSinkFactory(null)
|
|
77
75
|
|
|
@@ -109,10 +107,6 @@ object DownloadUtil {
|
|
|
109
107
|
return downloadNotificationHelper!!
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
suspend fun emitVolumeEvent(uri: String, data: VolumeData) {
|
|
113
|
-
_volumeResolvedEvent.emit(uri to data)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
110
|
private class BilibiliResolver :
|
|
117
111
|
ResolvingDataSource.Resolver {
|
|
118
112
|
override fun resolveDataSpec(dataSpec: DataSpec): DataSpec {
|
|
@@ -132,10 +126,8 @@ object DownloadUtil {
|
|
|
132
126
|
)
|
|
133
127
|
// 在这里保存响度均衡数据,并且直接发一个事件,在 OrpheusMusicService 监听
|
|
134
128
|
if (volume !== null) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
emitVolumeEvent(dataSpec.uri.toString(), volume)
|
|
138
|
-
}
|
|
129
|
+
Log.d("LoudnessNormalization", "uri: ${dataSpec.uri}, measuredI: ${volume.measuredI}")
|
|
130
|
+
LoudnessStorage.setLoudnessData(dataSpec.uri.toString(), volume.measuredI)
|
|
139
131
|
}
|
|
140
132
|
|
|
141
133
|
val headers = HashMap<String, String>()
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
package expo.modules.orpheus.utils
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
4
5
|
import androidx.annotation.OptIn
|
|
5
6
|
import androidx.media3.common.MediaItem
|
|
6
|
-
import androidx.media3.common.util.Log
|
|
7
|
-
import androidx.media3.common.util.UnstableApi
|
|
8
7
|
import com.google.gson.Gson
|
|
9
8
|
import com.google.gson.reflect.TypeToken
|
|
10
9
|
import com.tencent.mmkv.MMKV
|
|
11
10
|
import expo.modules.orpheus.models.TrackRecord
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
object Storage {
|
|
12
|
+
object GeneralStorage {
|
|
15
13
|
private var kv: MMKV? = null
|
|
16
14
|
private val gson = Gson()
|
|
17
15
|
private const val KEY_RESTORE_POSITION_ENABLED = "config_restore_position_enabled"
|
|
@@ -22,6 +20,7 @@ object Storage {
|
|
|
22
20
|
private const val KEY_SAVED_POSITION = "saved_position"
|
|
23
21
|
private const val KEY_SAVED_REPEAT_MODE = "saved_repeat_mode"
|
|
24
22
|
private const val KEY_SAVED_SHUFFLE_MODE = "saved_shuffle_mode"
|
|
23
|
+
private const val KEY_AUTOPLAY_ON_START_ENABLED = "config_autoplay_on_start_enabled"
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
@Synchronized
|
|
@@ -59,7 +58,18 @@ object Storage {
|
|
|
59
58
|
return safeKv.decodeBool(KEY_LOUDNESS_NORMALIZATION_ENABLED, true)
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
fun isAutoplayOnStartEnabled(): Boolean {
|
|
62
|
+
return safeKv.decodeBool(KEY_AUTOPLAY_ON_START_ENABLED, false)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fun setAutoplayOnStartEnabled(enabled: Boolean) {
|
|
66
|
+
try {
|
|
67
|
+
safeKv.encode(KEY_AUTOPLAY_ON_START_ENABLED, enabled)
|
|
68
|
+
} catch (e: Exception) {
|
|
69
|
+
Log.e("MediaItemStorer", "Failed to set autoplay on start enabled", e)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
fun saveQueue(mediaItems: List<MediaItem>) {
|
|
64
74
|
try {
|
|
65
75
|
val jsonList = mediaItems.mapNotNull { item ->
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
package expo.modules.orpheus.utils
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.tencent.mmkv.MMKV
|
|
6
|
+
|
|
7
|
+
object LoudnessStorage {
|
|
8
|
+
private var kv: MMKV? = null
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@Synchronized
|
|
12
|
+
fun initialize(context: Context) {
|
|
13
|
+
if (kv == null) {
|
|
14
|
+
MMKV.initialize(context)
|
|
15
|
+
kv = MMKV.mmkvWithID("loudness_normalization_store")
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private val safeKv: MMKV
|
|
20
|
+
get() = kv ?: throw IllegalStateException("LoudnessStorage not initialized")
|
|
21
|
+
|
|
22
|
+
fun setLoudnessData(key: String, measuredI: Double) {
|
|
23
|
+
try {
|
|
24
|
+
Log.d("LoudnessNormalization", "setLoudnessData: $key, $measuredI")
|
|
25
|
+
safeKv.encode(key, measuredI)
|
|
26
|
+
} catch (e: Exception) {
|
|
27
|
+
Log.e("LoudnessStorage", "Failed to set loudness data", e)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun getLoudnessData(key: String): Double {
|
|
32
|
+
try {
|
|
33
|
+
Log.d("LoudnessNormalization", "getLoudnessData: $key")
|
|
34
|
+
return safeKv.decodeDouble(key)
|
|
35
|
+
} catch (e: Exception) {
|
|
36
|
+
Log.e("LoudnessStorage", "Failed to get loudness data", e)
|
|
37
|
+
return 0.0
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -58,6 +58,7 @@ export type OrpheusEvents = {
|
|
|
58
58
|
declare class OrpheusModule extends NativeModule<OrpheusEvents> {
|
|
59
59
|
restorePlaybackPositionEnabled: boolean;
|
|
60
60
|
loudnessNormalizationEnabled: boolean;
|
|
61
|
+
autoplayOnStartEnabled: boolean;
|
|
61
62
|
/**
|
|
62
63
|
* 获取当前进度(秒)
|
|
63
64
|
*/
|
|
@@ -94,6 +95,7 @@ declare class OrpheusModule extends NativeModule<OrpheusEvents> {
|
|
|
94
95
|
setBilibiliCookie(cookie: string): void;
|
|
95
96
|
setRestorePlaybackPositionEnabled(enabled: boolean): void;
|
|
96
97
|
setLoudnessNormalizationEnabled(enabled: boolean): void;
|
|
98
|
+
setAutoplayOnStartEnabled(enabled: boolean): void;
|
|
97
99
|
play(): Promise<void>;
|
|
98
100
|
pause(): Promise<void>;
|
|
99
101
|
clear(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoOrpheusModule.d.ts","sourceRoot":"","sources":["../src/ExpoOrpheusModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtE,oBAAY,aAAa;IACvB,IAAI,IAAI;IACR,SAAS,IAAI;IACb,KAAK,IAAI;IACT,KAAK,IAAI;CACV;AAED,oBAAY,UAAU;IACpB,GAAG,IAAI;IACP,KAAK,IAAI;IACT,KAAK,IAAI;CACV;AAED,oBAAY,gBAAgB;IAC1B,MAAM,IAAI;IACV,IAAI,IAAI;IACR,IAAI,IAAI;IACR,gBAAgB,IAAI;CACrB;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAA;CACF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,sBAAsB,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9D,cAAc,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3E,eAAe,CAAC,KAAK,EAAE;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,aAAa,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9D,gBAAgB,CAAC,KAAK,EAAE;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,kBAAkB,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,aAAa,CAAC;IAE7D,8BAA8B,EAAE,OAAO,CAAC;IACxC,4BAA4B,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoOrpheusModule.d.ts","sourceRoot":"","sources":["../src/ExpoOrpheusModule.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoOrpheusModule.js","sourceRoot":"","sources":["../src/ExpoOrpheusModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAgB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,CAAN,IAAY,aAKX;AALD,WAAY,aAAa;IACvB,iDAAQ,CAAA;IACR,2DAAa,CAAA;IACb,mDAAS,CAAA;IACT,mDAAS,CAAA;AACX,CAAC,EALW,aAAa,KAAb,aAAa,QAKxB;AAED,MAAM,CAAN,IAAY,UAIX;AAJD,WAAY,UAAU;IACpB,yCAAO,CAAA;IACP,6CAAS,CAAA;IACT,6CAAS,CAAA;AACX,CAAC,EAJW,UAAU,KAAV,UAAU,QAIrB;AAED,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,2DAAU,CAAA;IACV,uDAAQ,CAAA;IACR,uDAAQ,CAAA;IACR,+EAAoB,CAAA;AACtB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;
|
|
1
|
+
{"version":3,"file":"ExpoOrpheusModule.js","sourceRoot":"","sources":["../src/ExpoOrpheusModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAgB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,CAAN,IAAY,aAKX;AALD,WAAY,aAAa;IACvB,iDAAQ,CAAA;IACR,2DAAa,CAAA;IACb,mDAAS,CAAA;IACT,mDAAS,CAAA;AACX,CAAC,EALW,aAAa,KAAb,aAAa,QAKxB;AAED,MAAM,CAAN,IAAY,UAIX;AAJD,WAAY,UAAU;IACpB,yCAAO,CAAA;IACP,6CAAS,CAAA;IACT,6CAAS,CAAA;AACX,CAAC,EAJW,UAAU,KAAV,UAAU,QAIrB;AAED,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,2DAAU,CAAA;IACV,uDAAQ,CAAA;IACR,uDAAQ,CAAA;IACR,+EAAoB,CAAA;AACtB,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AA0LD,MAAM,CAAN,IAAY,aAQX;AARD,WAAY,aAAa;IACvB,qDAAU,CAAA;IACV,uDAAW,CAAA;IACX,+DAAe,CAAA;IACf,2DAAa,CAAA;IACb,qDAAU,CAAA;IACV,yDAAY,CAAA;IACZ,6DAAc,CAAA;AAChB,CAAC,EARW,aAAa,KAAb,aAAa,QAQxB;AAWD,MAAM,CAAC,MAAM,OAAO,GAAG,mBAAmB,CAAgB,SAAS,CAAC,CAAC","sourcesContent":["import { requireNativeModule, NativeModule } from \"expo-modules-core\";\n\nexport enum PlaybackState {\n IDLE = 1,\n BUFFERING = 2,\n READY = 3,\n ENDED = 4,\n}\n\nexport enum RepeatMode {\n OFF = 0,\n TRACK = 1,\n QUEUE = 2,\n}\n\nexport enum TransitionReason {\n REPEAT = 0,\n AUTO = 1,\n SEEK = 2,\n PLAYLIST_CHANGED = 3,\n}\n\nexport interface Track {\n id: string;\n url: string;\n title?: string;\n artist?: string;\n artwork?: string;\n duration?: number;\n loudness?: {\n measured_i: number;\n target_i: number;\n }\n}\n\nexport type OrpheusEvents = {\n onPlaybackStateChanged(event: { state: PlaybackState }): void;\n onTrackStarted(event: { trackId: string; reason: TransitionReason }): void;\n onTrackFinished(event: {\n trackId: string;\n finalPosition: number;\n duration: number;\n }): void;\n onPlayerError(event: { code: string; message: string }): void;\n onPositionUpdate(event: {\n position: number;\n duration: number;\n buffered: number;\n }): void;\n onIsPlayingChanged(event: { status: boolean }): void;\n onDownloadUpdated(event: DownloadTask): void;\n};\n\ndeclare class OrpheusModule extends NativeModule<OrpheusEvents> {\n \n restorePlaybackPositionEnabled: boolean;\n loudnessNormalizationEnabled: boolean;\n autoplayOnStartEnabled: boolean;\n\n /**\n * 获取当前进度(秒)\n */\n getPosition(): Promise<number>;\n\n /**\n * 获取总时长(秒)\n */\n getDuration(): Promise<number>;\n\n /**\n * 获取缓冲进度(秒)\n */\n getBuffered(): Promise<number>;\n\n /**\n * 获取是否正在播放\n */\n getIsPlaying(): Promise<boolean>;\n\n /**\n * 获取当前播放索引\n */\n getCurrentIndex(): Promise<number>;\n\n /**\n * 获取当前播放的 Track 对象\n */\n getCurrentTrack(): Promise<Track | null>;\n\n /**\n * 获取随机模式状态\n */\n getShuffleMode(): Promise<boolean>;\n\n /**\n * 获取指定索引的 Track\n */\n getIndexTrack(index: number): Promise<Track | null>;\n\n getRepeatMode(): Promise<RepeatMode>;\n\n setBilibiliCookie(cookie: string): void;\n \n setRestorePlaybackPositionEnabled(enabled: boolean): void;\n setLoudnessNormalizationEnabled(enabled: boolean): void;\n setAutoplayOnStartEnabled(enabled: boolean): void;\n\n play(): Promise<void>;\n\n pause(): Promise<void>;\n\n clear(): Promise<void>;\n\n skipTo(index: number): Promise<void>;\n\n skipToNext(): Promise<void>;\n\n skipToPrevious(): Promise<void>;\n\n /**\n * 跳转进度\n * @param seconds 秒数\n */\n seekTo(seconds: number): Promise<void>;\n\n setRepeatMode(mode: RepeatMode): Promise<void>;\n\n setShuffleMode(enabled: boolean): Promise<void>;\n\n getQueue(): Promise<Track[]>;\n\n /**\n * 添加到队列末尾,且不去重。\n * @param tracks\n * @param startFromId 可选,添加后立即播放该 ID 的曲目\n * @param clearQueue 可选,是否清空当前队列\n */\n addToEnd(\n tracks: Track[],\n startFromId?: string,\n clearQueue?: boolean\n ): Promise<void>;\n\n /**\n * 播放下一首\n * @param track\n */\n playNext(track: Track): Promise<void>;\n\n removeTrack(index: number): Promise<void>;\n\n /**\n * 设置睡眠定时器\n * @param durationMs 单位毫秒\n */\n setSleepTimer(durationMs: number): Promise<void>;\n\n /**\n * 获取睡眠定时器结束时间\n * @returns 单位毫秒,如果没有设置则返回 null\n */\n getSleepTimerEndTime(): Promise<number | null>;\n\n cancelSleepTimer(): Promise<void>;\n\n /**\n * 下载单首歌曲\n */\n downloadTrack(track: Track): Promise<void>;\n\n /**\n * 移除下载任务\n */\n removeDownload(id: string): Promise<void>;\n\n /**\n * 批量下载歌曲\n */\n multiDownload(tracks: Track[]): Promise<void>;\n\n /**\n * 移除所有下载任务(包括已完成的及源文件)\n */\n removeAllDownloads(): Promise<void>;\n\n /**\n * 获取所有下载任务\n */\n getDownloads(): Promise<DownloadTask[]>;\n\n /**\n * 批量返回指定 ID 的下载状态\n */\n getDownloadStatusByIds(ids: string[]): Promise<Record<string, DownloadState>>;\n\n /**\n * 清除未完成的下载任务\n */\n clearUncompletedDownloadTasks(): Promise<void>;\n\n /**\n * 获取所有未完成的下载任务\n */\n getUncompletedDownloadTasks(): Promise<DownloadTask[]>;\n}\n\nexport enum DownloadState {\n QUEUED = 0,\n STOPPED = 1,\n DOWNLOADING = 2,\n COMPLETED = 3,\n FAILED = 4,\n REMOVING = 5,\n RESTARTING = 7,\n}\n\nexport interface DownloadTask {\n id: string;\n state: DownloadState;\n percentDownloaded: number;\n bytesDownloaded: number;\n contentLength: number;\n track?: Track;\n}\n\nexport const Orpheus = requireNativeModule<OrpheusModule>(\"Orpheus\");\n"]}
|
package/package.json
CHANGED
package/src/ExpoOrpheusModule.ts
CHANGED
|
@@ -55,6 +55,7 @@ declare class OrpheusModule extends NativeModule<OrpheusEvents> {
|
|
|
55
55
|
|
|
56
56
|
restorePlaybackPositionEnabled: boolean;
|
|
57
57
|
loudnessNormalizationEnabled: boolean;
|
|
58
|
+
autoplayOnStartEnabled: boolean;
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* 获取当前进度(秒)
|
|
@@ -102,6 +103,7 @@ declare class OrpheusModule extends NativeModule<OrpheusEvents> {
|
|
|
102
103
|
|
|
103
104
|
setRestorePlaybackPositionEnabled(enabled: boolean): void;
|
|
104
105
|
setLoudnessNormalizationEnabled(enabled: boolean): void;
|
|
106
|
+
setAutoplayOnStartEnabled(enabled: boolean): void;
|
|
105
107
|
|
|
106
108
|
play(): Promise<void>;
|
|
107
109
|
|