@roitium/expo-orpheus 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.
Files changed (61) hide show
  1. package/.eslintrc.js +5 -0
  2. package/README.md +11 -0
  3. package/android/build.gradle +51 -0
  4. package/android/src/main/AndroidManifest.xml +21 -0
  5. package/android/src/main/java/expo/modules/orpheus/ExpoOrpheusModule.kt +365 -0
  6. package/android/src/main/java/expo/modules/orpheus/NetworkModule.kt +46 -0
  7. package/android/src/main/java/expo/modules/orpheus/OrpheusConfig.kt +5 -0
  8. package/android/src/main/java/expo/modules/orpheus/OrpheusService.kt +142 -0
  9. package/android/src/main/java/expo/modules/orpheus/TrackRecord.kt +28 -0
  10. package/android/src/main/java/expo/modules/orpheus/bilibili/BilibiliApi.kt +24 -0
  11. package/android/src/main/java/expo/modules/orpheus/bilibili/BilibiliModels.kt +68 -0
  12. package/android/src/main/java/expo/modules/orpheus/bilibili/BilibiliRepository.kt +144 -0
  13. package/android/src/main/java/expo/modules/orpheus/bilibili/WbiUtil.kt +73 -0
  14. package/build/ExpoOrpheusModule.d.ts +98 -0
  15. package/build/ExpoOrpheusModule.d.ts.map +1 -0
  16. package/build/ExpoOrpheusModule.js +23 -0
  17. package/build/ExpoOrpheusModule.js.map +1 -0
  18. package/build/hooks/index.d.ts +7 -0
  19. package/build/hooks/index.d.ts.map +1 -0
  20. package/build/hooks/index.js +7 -0
  21. package/build/hooks/index.js.map +1 -0
  22. package/build/hooks/useCurrentTrack.d.ts +6 -0
  23. package/build/hooks/useCurrentTrack.d.ts.map +1 -0
  24. package/build/hooks/useCurrentTrack.js +41 -0
  25. package/build/hooks/useCurrentTrack.js.map +1 -0
  26. package/build/hooks/useIsPlaying.d.ts +2 -0
  27. package/build/hooks/useIsPlaying.d.ts.map +1 -0
  28. package/build/hooks/useIsPlaying.js +22 -0
  29. package/build/hooks/useIsPlaying.js.map +1 -0
  30. package/build/hooks/useOrpheus.d.ts +10 -0
  31. package/build/hooks/useOrpheus.d.ts.map +1 -0
  32. package/build/hooks/useOrpheus.js +20 -0
  33. package/build/hooks/useOrpheus.js.map +1 -0
  34. package/build/hooks/usePlaybackState.d.ts +3 -0
  35. package/build/hooks/usePlaybackState.d.ts.map +1 -0
  36. package/build/hooks/usePlaybackState.js +18 -0
  37. package/build/hooks/usePlaybackState.js.map +1 -0
  38. package/build/hooks/useProgress.d.ts +6 -0
  39. package/build/hooks/useProgress.d.ts.map +1 -0
  40. package/build/hooks/useProgress.js +59 -0
  41. package/build/hooks/useProgress.js.map +1 -0
  42. package/build/hooks/useShuffleMode.d.ts +2 -0
  43. package/build/hooks/useShuffleMode.d.ts.map +1 -0
  44. package/build/hooks/useShuffleMode.js +22 -0
  45. package/build/hooks/useShuffleMode.js.map +1 -0
  46. package/build/index.d.ts +3 -0
  47. package/build/index.d.ts.map +1 -0
  48. package/build/index.js +3 -0
  49. package/build/index.js.map +1 -0
  50. package/expo-module.config.json +6 -0
  51. package/package.json +44 -0
  52. package/src/ExpoOrpheusModule.ts +114 -0
  53. package/src/hooks/index.ts +6 -0
  54. package/src/hooks/useCurrentTrack.ts +46 -0
  55. package/src/hooks/useIsPlaying.ts +25 -0
  56. package/src/hooks/useOrpheus.ts +21 -0
  57. package/src/hooks/usePlaybackState.ts +21 -0
  58. package/src/hooks/useProgress.ts +71 -0
  59. package/src/hooks/useShuffleMode.ts +26 -0
  60. package/src/index.ts +2 -0
  61. package/tsconfig.json +9 -0
@@ -0,0 +1,24 @@
1
+ package expo.modules.orpheus.bilibili
2
+
3
+ import retrofit2.Call
4
+ import retrofit2.http.GET
5
+ import retrofit2.http.Header
6
+ import retrofit2.http.Query
7
+ import retrofit2.http.QueryMap
8
+
9
+ interface BilibiliApi {
10
+ @GET("/x/web-interface/nav")
11
+ fun getNavInfo(): Call<BilibiliNavResponse>
12
+
13
+ @GET("/x/player/wbi/playurl")
14
+ fun getPlayUrl(
15
+ @Header("Cookie") cookie: String? = null,
16
+ @QueryMap params: Map<String, String>
17
+ ): Call<BilibiliApiResponse<BilibiliAudioStreamResponse>>
18
+
19
+ @GET("/x/player/pagelist")
20
+ fun getPageList(
21
+ @Header("Cookie") cookie: String? = null,
22
+ @Query("bvid") bvid: String
23
+ ): Call<BilibiliApiResponse<List<BilibiliPageListResponse>>>
24
+ }
@@ -0,0 +1,68 @@
1
+ package expo.modules.orpheus.bilibili
2
+
3
+ import com.google.gson.annotations.SerializedName
4
+
5
+ data class BilibiliApiResponse<TData>(
6
+ val code: Int,
7
+ val message: String,
8
+ val data: TData?
9
+ )
10
+
11
+ data class BilibiliAudioStreamResponse(
12
+ val durl: List<DurlItem>?,
13
+
14
+ val dash: DashData?,
15
+
16
+ val volume: VolumeData?
17
+ )
18
+
19
+ data class DurlItem(
20
+ val order: Int,
21
+ val url: String,
22
+ @SerializedName("backup_url") val backupUrl: List<String>?
23
+ )
24
+
25
+ data class DashData(
26
+ val audio: List<DashAudioItem>?,
27
+ val dolby: DolbyData?,
28
+ val flac: FlacData?
29
+ )
30
+
31
+ data class DashAudioItem(
32
+ val id: Int,
33
+ @SerializedName("base_url") val baseUrl: String,
34
+ @SerializedName("backup_url") val backupUrl: List<String>?
35
+ )
36
+
37
+ data class DolbyData(
38
+ val type: Int,
39
+ val audio: List<DashAudioItem>?
40
+ )
41
+
42
+ data class FlacData(
43
+ val display: Boolean,
44
+ val audio: DashAudioItem?
45
+ )
46
+
47
+ data class VolumeData(
48
+ @SerializedName("measured_i") val measuredI: Double,
49
+ @SerializedName("target_i") val targetI: Double
50
+ )
51
+
52
+ data class BilibiliNavResponse(
53
+ val code: Int,
54
+ val data: NavData?
55
+ )
56
+
57
+ data class NavData(
58
+ @SerializedName("wbi_img") val wbiImg: WbiImgData?
59
+ )
60
+
61
+ data class WbiImgData(
62
+ @SerializedName("img_url") val imgUrl: String,
63
+ @SerializedName("sub_url") val subUrl: String
64
+ )
65
+
66
+ data class BilibiliPageListResponse(
67
+ val cid: Long
68
+ )
@@ -0,0 +1,144 @@
1
+ package expo.modules.orpheus.bilibili
2
+
3
+ import android.util.Log
4
+ import expo.modules.orpheus.NetworkModule
5
+ import java.io.IOException
6
+ import java.text.SimpleDateFormat
7
+ import java.util.Date
8
+ import java.util.Locale
9
+
10
+ object BilibiliRepository {
11
+ val TAG = "Orpheus/BilibiliRepo"
12
+
13
+ private val api: BilibiliApi by lazy {
14
+ NetworkModule.retrofit.create(BilibiliApi::class.java)
15
+ }
16
+
17
+ private var cachedImgKey: String? = null
18
+ private var cachedSubKey: String? = null
19
+ private var cachedDateStr: String? = null
20
+
21
+ private fun getTodayDateStr(): String {
22
+ val sdf = SimpleDateFormat("yyyyMMdd", Locale.getDefault())
23
+ return sdf.format(Date())
24
+ }
25
+
26
+ @Synchronized
27
+ private fun getWbiKeys(): Pair<String, String> {
28
+ val today = getTodayDateStr()
29
+
30
+ if (cachedImgKey != null && cachedSubKey != null && today == cachedDateStr) {
31
+ return cachedImgKey!! to cachedSubKey!!
32
+ }
33
+
34
+ val response = api.getNavInfo().execute()
35
+ val wbiData = response.body()?.data?.wbiImg
36
+
37
+ if (!response.isSuccessful || wbiData == null) {
38
+ throw IOException("Failed to fetch Wbi Keys: ${response.code()}")
39
+ }
40
+
41
+ val imgKey = WbiUtil.extractKey(wbiData.imgUrl)
42
+ val subKey = WbiUtil.extractKey(wbiData.subUrl)
43
+
44
+ cachedImgKey = imgKey
45
+ cachedSubKey = subKey
46
+ cachedDateStr = today
47
+
48
+ return imgKey to subKey
49
+ }
50
+
51
+ /**
52
+ * 解析音频 URL
53
+ */
54
+ fun resolveAudioUrl(
55
+ bvid: String,
56
+ cid: Long?,
57
+ audioQuality: Int,
58
+ enableDolby: Boolean,
59
+ enableHiRes: Boolean,
60
+ cookie: String?
61
+ ): String {
62
+ var cidInternal = cid
63
+ val (imgKey, subKey) = getWbiKeys()
64
+ if (cidInternal === null) {
65
+ cidInternal = getFirstCid(bvid, cookie)
66
+ }
67
+
68
+ Log.e(TAG, "resolve url: bvid: $bvid, cid: $cid, enableDolby: ")
69
+
70
+ val rawParams = mapOf(
71
+ "bvid" to bvid,
72
+ "cid" to cidInternal,
73
+ "fnval" to 4048,
74
+ "fnver" to 0,
75
+ "fourk" to 1,
76
+ "qlt" to audioQuality,
77
+ "voice_balance" to 1
78
+ )
79
+
80
+ val signedParams = WbiUtil.sign(rawParams, imgKey, subKey)
81
+
82
+ val call = api.getPlayUrl(cookie, signedParams)
83
+ val response = call.execute()
84
+
85
+ if (!response.isSuccessful) {
86
+ throw IOException("Bilibili API Http Error: ${response.code()}")
87
+ }
88
+
89
+ val apiResponse = response.body()
90
+ if (apiResponse?.code != 0 || apiResponse.data == null) {
91
+ throw IOException("Bilibili API Logic Error: code=${apiResponse?.code} msg=${apiResponse?.message}")
92
+ }
93
+
94
+ val data = apiResponse.data
95
+ val dash = data.dash
96
+ val durl = data.durl
97
+
98
+ if (dash == null) {
99
+ if (durl.isNullOrEmpty()) {
100
+ throw IOException("AudioStreamError: 请求到的流数据不包含 dash 或 durl 任一字段")
101
+ }
102
+ return durl[0].url
103
+ }
104
+
105
+ if (enableDolby && dash.dolby?.audio?.isNotEmpty() == true) {
106
+ Log.d(TAG, "select dolby source")
107
+ return dash.dolby.audio[0].baseUrl
108
+ }
109
+
110
+ if (enableHiRes && dash.flac?.audio != null) {
111
+ Log.d(TAG, "select hires source")
112
+ return dash.flac.audio.baseUrl
113
+ }
114
+
115
+ if (dash.audio.isNullOrEmpty()) {
116
+ throw IOException("AudioStreamError: 未找到有效的音频流数据")
117
+ }
118
+
119
+ val targetAudio = dash.audio.find { it.id == audioQuality }
120
+
121
+ if (targetAudio != null) {
122
+ return targetAudio.baseUrl
123
+ } else {
124
+ val highestQualityAudio = dash.audio[0]
125
+ return highestQualityAudio.baseUrl
126
+ }
127
+ }
128
+
129
+ fun getFirstCid(bvid: String, cookie: String?): Long {
130
+ val call = api.getPageList(cookie = cookie, bvid = bvid)
131
+ val response = call.execute()
132
+
133
+ if (!response.isSuccessful) {
134
+ throw IOException("Bilibili API Http Error: ${response.code()}")
135
+ }
136
+
137
+ val apiResponse = response.body()
138
+ if (apiResponse?.code != 0 || apiResponse.data == null) {
139
+ throw IOException("Bilibili API Logic Error: code=${apiResponse?.code} msg=${apiResponse?.message}")
140
+ }
141
+
142
+ return apiResponse.data[0].cid
143
+ }
144
+ }
@@ -0,0 +1,73 @@
1
+ package expo.modules.orpheus.bilibili
2
+
3
+ import java.net.URLEncoder
4
+ import java.security.MessageDigest
5
+ import java.util.TreeMap
6
+
7
+ object WbiUtil {
8
+ private val mixinKeyEncTab = intArrayOf(
9
+ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
10
+ 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
11
+ 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
12
+ 36, 20, 34, 44, 52
13
+ )
14
+
15
+ private fun String.toMD5(): String {
16
+ val md = MessageDigest.getInstance("MD5")
17
+ val digest = md.digest(this.toByteArray())
18
+ return digest.joinToString("") { "%02x".format(it) }
19
+ }
20
+
21
+ private fun Any?.encodeURIComponent(): String {
22
+ if (this == null) return ""
23
+ return URLEncoder.encode(this.toString(), "UTF-8")
24
+ .replace("+", "%20")
25
+ .replace("*", "%2A")
26
+ .replace("%7E", "~")
27
+ }
28
+
29
+ /**
30
+ * 计算 WBI 混淆键
31
+ */
32
+ private fun getMixinKey(orig: String): String {
33
+ return buildString {
34
+ repeat(32) {
35
+ if (it < mixinKeyEncTab.size && mixinKeyEncTab[it] < orig.length) {
36
+ append(orig[mixinKeyEncTab[it]])
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ /**
43
+ * 核心签名方法
44
+ * @param params 原始参数 Map
45
+ * @param imgKey 来自 /nav 接口
46
+ * @param subKey 来自 /nav 接口
47
+ * @return 包含 w_rid 和 wts 的完整参数 Map
48
+ */
49
+ fun sign(params: Map<String, Any?>, imgKey: String, subKey: String): Map<String, String> {
50
+ val mixinKey = getMixinKey(imgKey + subKey)
51
+ val currTime = System.currentTimeMillis() / 1000
52
+
53
+ val sortedParams = TreeMap<String, Any?>()
54
+ params.forEach { (k, v) -> if (v != null) sortedParams[k] = v }
55
+ sortedParams["wts"] = currTime
56
+
57
+ val queryStr = sortedParams.entries.joinToString("&") { (k, v) ->
58
+ "${k.encodeURIComponent()}=${v.encodeURIComponent()}"
59
+ }
60
+
61
+ val w_rid = (queryStr + mixinKey).toMD5()
62
+
63
+ val finalMap = HashMap<String, String>()
64
+ sortedParams.forEach { (k, v) -> finalMap[k] = v.toString() }
65
+ finalMap["w_rid"] = w_rid
66
+
67
+ return finalMap
68
+ }
69
+
70
+ fun extractKey(url: String): String {
71
+ return url.substringAfterLast("/").substringBeforeLast(".")
72
+ }
73
+ }
@@ -0,0 +1,98 @@
1
+ import { NativeModule } from "expo-modules-core";
2
+ export declare enum PlaybackState {
3
+ IDLE = 1,
4
+ BUFFERING = 2,
5
+ READY = 3,
6
+ ENDED = 4
7
+ }
8
+ export declare enum RepeatMode {
9
+ OFF = 0,
10
+ TRACK = 1,
11
+ QUEUE = 2
12
+ }
13
+ export declare enum TransitionReason {
14
+ REPEAT = 0,
15
+ AUTO = 1,
16
+ SEEK = 2,
17
+ PLAYLIST_CHANGED = 3
18
+ }
19
+ export interface Track {
20
+ id: string;
21
+ url: string;
22
+ title?: string;
23
+ artist?: string;
24
+ artwork?: string;
25
+ duration?: number;
26
+ [key: string]: any;
27
+ }
28
+ export type OrpheusEvents = {
29
+ onPlaybackStateChanged(event: {
30
+ state: PlaybackState;
31
+ }): void;
32
+ onTrackTransition(event: {
33
+ currentTrackId: string;
34
+ previousTrackId?: string;
35
+ reason: TransitionReason;
36
+ }): void;
37
+ onPlayerError(event: {
38
+ code: string;
39
+ message: string;
40
+ }): void;
41
+ onPositionUpdate(event: {
42
+ position: number;
43
+ duration: number;
44
+ buffered: number;
45
+ }): void;
46
+ onIsPlayingChanged(event: {
47
+ status: boolean;
48
+ }): void;
49
+ };
50
+ declare class OrpheusModule extends NativeModule<OrpheusEvents> {
51
+ /**
52
+ * 获取当前进度(秒)
53
+ */
54
+ getPosition(): Promise<number>;
55
+ /**
56
+ * 获取总时长(秒)
57
+ */
58
+ getDuration(): Promise<number>;
59
+ /**
60
+ * 获取是否正在播放
61
+ */
62
+ getIsPlaying(): Promise<boolean>;
63
+ /**
64
+ * 获取当前播放索引
65
+ */
66
+ getCurrentIndex(): Promise<number>;
67
+ /**
68
+ * 获取当前播放的 Track 对象
69
+ */
70
+ getCurrentTrack(): Promise<Track | null>;
71
+ /**
72
+ * 获取随机模式状态
73
+ */
74
+ getShuffleMode(): Promise<boolean>;
75
+ /**
76
+ * 获取指定索引的 Track
77
+ */
78
+ getIndexTrack(index: number): Promise<Track | null>;
79
+ setBilibiliCookie(cookie: string): void;
80
+ play(): Promise<void>;
81
+ pause(): Promise<void>;
82
+ clear(): Promise<void>;
83
+ skipTo(index: number): Promise<void>;
84
+ skipToNext(): Promise<void>;
85
+ skipToPrevious(): Promise<void>;
86
+ /**
87
+ * 跳转进度
88
+ * @param seconds 秒数
89
+ */
90
+ seekTo(seconds: number): Promise<void>;
91
+ setRepeatMode(mode: RepeatMode): Promise<void>;
92
+ setShuffleMode(enabled: boolean): Promise<void>;
93
+ getQueue(): Promise<Track[]>;
94
+ add(tracks: Track[]): Promise<void>;
95
+ }
96
+ export declare const Orpheus: OrpheusModule;
97
+ export {};
98
+ //# sourceMappingURL=ExpoOrpheusModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoOrpheusModule.d.ts","sourceRoot":"","sources":["../src/ExpoOrpheusModule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { requireNativeModule } from "expo-modules-core";
2
+ export var PlaybackState;
3
+ (function (PlaybackState) {
4
+ PlaybackState[PlaybackState["IDLE"] = 1] = "IDLE";
5
+ PlaybackState[PlaybackState["BUFFERING"] = 2] = "BUFFERING";
6
+ PlaybackState[PlaybackState["READY"] = 3] = "READY";
7
+ PlaybackState[PlaybackState["ENDED"] = 4] = "ENDED";
8
+ })(PlaybackState || (PlaybackState = {}));
9
+ export var RepeatMode;
10
+ (function (RepeatMode) {
11
+ RepeatMode[RepeatMode["OFF"] = 0] = "OFF";
12
+ RepeatMode[RepeatMode["TRACK"] = 1] = "TRACK";
13
+ RepeatMode[RepeatMode["QUEUE"] = 2] = "QUEUE";
14
+ })(RepeatMode || (RepeatMode = {}));
15
+ export var TransitionReason;
16
+ (function (TransitionReason) {
17
+ TransitionReason[TransitionReason["REPEAT"] = 0] = "REPEAT";
18
+ TransitionReason[TransitionReason["AUTO"] = 1] = "AUTO";
19
+ TransitionReason[TransitionReason["SEEK"] = 2] = "SEEK";
20
+ TransitionReason[TransitionReason["PLAYLIST_CHANGED"] = 3] = "PLAYLIST_CHANGED";
21
+ })(TransitionReason || (TransitionReason = {}));
22
+ export const Orpheus = requireNativeModule("Orpheus");
23
+ //# sourceMappingURL=ExpoOrpheusModule.js.map
@@ -0,0 +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;AA6FD,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 [key: string]: any;\n}\n\nexport type OrpheusEvents = {\n onPlaybackStateChanged(event: { state: PlaybackState }): void;\n onTrackTransition(event: {\n currentTrackId: string;\n previousTrackId?: string;\n reason: TransitionReason;\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};\n\ndeclare class OrpheusModule extends NativeModule<OrpheusEvents> {\n /**\n * 获取当前进度(秒)\n */\n getPosition(): Promise<number>;\n\n /**\n * 获取总时长(秒)\n */\n getDuration(): 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 setBilibiliCookie(cookie: string): 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 add(tracks: Track[]): Promise<void>;\n}\n\nexport const Orpheus = requireNativeModule<OrpheusModule>(\"Orpheus\");\n"]}
@@ -0,0 +1,7 @@
1
+ export * from './useProgress';
2
+ export * from './usePlaybackState';
3
+ export * from './useIsPlaying';
4
+ export * from './useCurrentTrack';
5
+ export * from './useShuffleMode';
6
+ export * from './useOrpheus';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from './useProgress';
2
+ export * from './usePlaybackState';
3
+ export * from './useIsPlaying';
4
+ export * from './useCurrentTrack';
5
+ export * from './useShuffleMode';
6
+ export * from './useOrpheus';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC","sourcesContent":["export * from './useProgress';\nexport * from './usePlaybackState';\nexport * from './useIsPlaying';\nexport * from './useCurrentTrack';\nexport * from './useShuffleMode';\nexport * from './useOrpheus';"]}
@@ -0,0 +1,6 @@
1
+ import { Track } from "../ExpoOrpheusModule";
2
+ export declare function useCurrentTrack(): {
3
+ track: Track | null;
4
+ index: number;
5
+ };
6
+ //# sourceMappingURL=useCurrentTrack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCurrentTrack.d.ts","sourceRoot":"","sources":["../../src/hooks/useCurrentTrack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAW,MAAM,sBAAsB,CAAC;AAEtD,wBAAgB,eAAe;;;EA0C9B"}
@@ -0,0 +1,41 @@
1
+ import { useState, useEffect } from "react";
2
+ import { Orpheus } from "../ExpoOrpheusModule";
3
+ export function useCurrentTrack() {
4
+ const [track, setTrack] = useState(null);
5
+ const [index, setIndex] = useState(-1);
6
+ const fetchTrack = async () => {
7
+ try {
8
+ const [currentTrack, currentIndex] = await Promise.all([
9
+ Orpheus.getCurrentTrack(),
10
+ Orpheus.getCurrentIndex(),
11
+ ]);
12
+ return { currentTrack, currentIndex };
13
+ }
14
+ catch (e) {
15
+ console.warn("Failed to fetch current track", e);
16
+ return { currentTrack: null, currentIndex: -1 };
17
+ }
18
+ };
19
+ useEffect(() => {
20
+ let isMounted = true;
21
+ fetchTrack().then(({ currentTrack, currentIndex }) => {
22
+ if (isMounted) {
23
+ setTrack(currentTrack);
24
+ setIndex(currentIndex);
25
+ }
26
+ });
27
+ const sub = Orpheus.addListener("onTrackTransition", async () => {
28
+ const { currentTrack, currentIndex } = await fetchTrack();
29
+ if (isMounted) {
30
+ setTrack(currentTrack);
31
+ setIndex(currentIndex);
32
+ }
33
+ });
34
+ return () => {
35
+ isMounted = false;
36
+ sub.remove();
37
+ };
38
+ }, []);
39
+ return { track, index };
40
+ }
41
+ //# sourceMappingURL=useCurrentTrack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCurrentTrack.js","sourceRoot":"","sources":["../../src/hooks/useCurrentTrack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAS,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,KAAK,IAAI,EAAE;QAC5B,IAAI,CAAC;YACH,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACrD,OAAO,CAAC,eAAe,EAAE;gBACzB,OAAO,CAAC,eAAe,EAAE;aAC1B,CAAC,CAAC;YACH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,+BAA+B,EAAE,CAAC,CAAC,CAAC;YACjD,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC;QAClD,CAAC;IACH,CAAC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,EAAE;YACnD,IAAI,SAAS,EAAE,CAAC;gBACd,QAAQ,CAAC,YAAY,CAAC,CAAC;gBACvB,QAAQ,CAAC,YAAY,CAAC,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,MAAM,UAAU,EAAE,CAAC;YAC1D,IAAI,SAAS,EAAE,CAAC;gBACd,QAAQ,CAAC,YAAY,CAAC,CAAC;gBACvB,QAAQ,CAAC,YAAY,CAAC,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC1B,CAAC","sourcesContent":["import { useState, useEffect } from \"react\";\nimport { Track, Orpheus } from \"../ExpoOrpheusModule\";\n\nexport function useCurrentTrack() {\n const [track, setTrack] = useState<Track | null>(null);\n const [index, setIndex] = useState<number>(-1);\n\n const fetchTrack = async () => {\n try {\n const [currentTrack, currentIndex] = await Promise.all([\n Orpheus.getCurrentTrack(),\n Orpheus.getCurrentIndex(),\n ]);\n return { currentTrack, currentIndex };\n } catch (e) {\n console.warn(\"Failed to fetch current track\", e);\n return { currentTrack: null, currentIndex: -1 };\n }\n };\n\n useEffect(() => {\n let isMounted = true;\n\n fetchTrack().then(({ currentTrack, currentIndex }) => {\n if (isMounted) {\n setTrack(currentTrack);\n setIndex(currentIndex);\n }\n });\n\n const sub = Orpheus.addListener(\"onTrackTransition\", async () => {\n const { currentTrack, currentIndex } = await fetchTrack();\n if (isMounted) {\n setTrack(currentTrack);\n setIndex(currentIndex);\n }\n });\n\n return () => {\n isMounted = false;\n sub.remove();\n };\n }, []);\n\n return { track, index };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function useIsPlaying(): boolean;
2
+ //# sourceMappingURL=useIsPlaying.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsPlaying.d.ts","sourceRoot":"","sources":["../../src/hooks/useIsPlaying.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,YAqB3B"}
@@ -0,0 +1,22 @@
1
+ import { useState, useEffect } from "react";
2
+ import { Orpheus } from "../ExpoOrpheusModule";
3
+ export function useIsPlaying() {
4
+ const [isPlaying, setIsPlaying] = useState(false);
5
+ useEffect(() => {
6
+ let isMounted = true;
7
+ Orpheus.getIsPlaying().then((val) => {
8
+ if (isMounted)
9
+ setIsPlaying(val);
10
+ });
11
+ const sub = Orpheus.addListener("onIsPlayingChanged", (event) => {
12
+ if (isMounted)
13
+ setIsPlaying(event.status);
14
+ });
15
+ return () => {
16
+ isMounted = false;
17
+ sub.remove();
18
+ };
19
+ }, []);
20
+ return isPlaying;
21
+ }
22
+ //# sourceMappingURL=useIsPlaying.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsPlaying.js","sourceRoot":"","sources":["../../src/hooks/useIsPlaying.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,MAAM,UAAU,YAAY;IAC1B,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,OAAO,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAClC,IAAI,SAAS;gBAAE,YAAY,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;YAC9D,IAAI,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import { useState, useEffect } from \"react\";\nimport { Orpheus } from \"../ExpoOrpheusModule\";\n\nexport function useIsPlaying() {\n const [isPlaying, setIsPlaying] = useState(false);\n\n useEffect(() => {\n let isMounted = true;\n\n Orpheus.getIsPlaying().then((val) => {\n if (isMounted) setIsPlaying(val);\n });\n\n const sub = Orpheus.addListener(\"onIsPlayingChanged\", (event) => {\n if (isMounted) setIsPlaying(event.status);\n });\n\n return () => {\n isMounted = false;\n sub.remove();\n };\n }, []);\n\n return isPlaying;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export declare function useOrpheus(): {
2
+ state: import("..").PlaybackState;
3
+ isPlaying: boolean;
4
+ position: number;
5
+ duration: number;
6
+ buffered: number;
7
+ currentTrack: import("..").Track | null;
8
+ currentIndex: number;
9
+ };
10
+ //# sourceMappingURL=useOrpheus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOrpheus.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrpheus.ts"],"names":[],"mappings":"AAKA,wBAAgB,UAAU;;;;;;;;EAezB"}
@@ -0,0 +1,20 @@
1
+ import { useCurrentTrack } from "./useCurrentTrack";
2
+ import { useIsPlaying } from "./useIsPlaying";
3
+ import { usePlaybackState } from "./usePlaybackState";
4
+ import { useProgress } from "./useProgress";
5
+ export function useOrpheus() {
6
+ const state = usePlaybackState();
7
+ const isPlaying = useIsPlaying();
8
+ const progress = useProgress();
9
+ const { track, index } = useCurrentTrack();
10
+ return {
11
+ state,
12
+ isPlaying,
13
+ position: progress.position,
14
+ duration: progress.duration,
15
+ buffered: progress.buffered,
16
+ currentTrack: track,
17
+ currentIndex: index,
18
+ };
19
+ }
20
+ //# sourceMappingURL=useOrpheus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOrpheus.js","sourceRoot":"","sources":["../../src/hooks/useOrpheus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,eAAe,EAAE,CAAC;IAE3C,OAAO;QACL,KAAK;QACL,SAAS;QACT,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,KAAK;KACpB,CAAC;AACJ,CAAC","sourcesContent":["import { useCurrentTrack } from \"./useCurrentTrack\";\nimport { useIsPlaying } from \"./useIsPlaying\";\nimport { usePlaybackState } from \"./usePlaybackState\";\nimport { useProgress } from \"./useProgress\";\n\nexport function useOrpheus() {\n const state = usePlaybackState();\n const isPlaying = useIsPlaying();\n const progress = useProgress();\n const { track, index } = useCurrentTrack();\n\n return {\n state,\n isPlaying,\n position: progress.position,\n duration: progress.duration,\n buffered: progress.buffered,\n currentTrack: track,\n currentIndex: index,\n };\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { PlaybackState } from "../ExpoOrpheusModule";
2
+ export declare function usePlaybackState(): PlaybackState;
3
+ //# sourceMappingURL=usePlaybackState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlaybackState.d.ts","sourceRoot":"","sources":["../../src/hooks/usePlaybackState.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE9D,wBAAgB,gBAAgB,kBAiB/B"}
@@ -0,0 +1,18 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Orpheus, PlaybackState } from "../ExpoOrpheusModule";
3
+ export function usePlaybackState() {
4
+ const [state, setState] = useState(PlaybackState.IDLE);
5
+ useEffect(() => {
6
+ let isMounted = true;
7
+ const sub = Orpheus.addListener("onPlaybackStateChanged", (event) => {
8
+ if (isMounted)
9
+ setState(event.state);
10
+ });
11
+ return () => {
12
+ isMounted = false;
13
+ sub.remove();
14
+ };
15
+ }, []);
16
+ return state;
17
+ }
18
+ //# sourceMappingURL=usePlaybackState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePlaybackState.js","sourceRoot":"","sources":["../../src/hooks/usePlaybackState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,UAAU,gBAAgB;IAC9B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,aAAa,CAAC,IAAI,CAAC,CAAC;IAEtE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE;YAClE,IAAI,SAAS;gBAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,KAAK,CAAC;YAClB,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { Orpheus, PlaybackState } from \"../ExpoOrpheusModule\";\n\nexport function usePlaybackState() {\n const [state, setState] = useState<PlaybackState>(PlaybackState.IDLE);\n\n useEffect(() => {\n let isMounted = true;\n\n const sub = Orpheus.addListener(\"onPlaybackStateChanged\", (event) => {\n if (isMounted) setState(event.state);\n });\n\n return () => {\n isMounted = false;\n sub.remove();\n };\n }, []);\n\n return state;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export declare function useProgress(): {
2
+ position: number;
3
+ duration: number;
4
+ buffered: number;
5
+ };
6
+ //# sourceMappingURL=useProgress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useProgress.d.ts","sourceRoot":"","sources":["../../src/hooks/useProgress.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW;;;;EAgE1B"}