1import produce from 'immer'; 2import ReactNativeTrackPlayer, { 3 Event, 4 State, 5 Track, 6 TrackMetadataBase, 7 usePlaybackState, 8 useProgress, 9} from 'react-native-track-player'; 10import shuffle from 'lodash.shuffle'; 11import Config from '../config'; 12import { 13 EDeviceEvents, 14 internalFakeSoundKey, 15 sortIndexSymbol, 16 timeStampSymbol, 17} from '@/constants/commonConst'; 18import {GlobalState} from '@/utils/stateMapper'; 19import delay from '@/utils/delay'; 20import { 21 isSameMediaItem, 22 mergeProps, 23 sortByTimestampAndIndex, 24} from '@/utils/mediaItem'; 25import Network from '../network'; 26import LocalMusicSheet from '../localMusicSheet'; 27import {SoundAsset} from '@/constants/assetsConst'; 28import {getQualityOrder} from '@/utils/qualities'; 29import musicHistory from '../musicHistory'; 30import getUrlExt from '@/utils/getUrlExt'; 31import {DeviceEventEmitter} from 'react-native'; 32import LyricManager from '../lyricManager'; 33import {MusicRepeatMode} from './common'; 34import { 35 getMusicIndex, 36 getPlayList, 37 getPlayListMusicAt, 38 isInPlayList, 39 isPlayListEmpty, 40 setPlayList, 41 usePlayList, 42} from './internal/playList'; 43import {createMediaIndexMap} from '@/utils/mediaIndexMap'; 44import PluginManager from '../pluginManager'; 45import {musicIsPaused} from '@/utils/trackUtils'; 46import Toast from '@/utils/toast'; 47import {trace} from '@/utils/log'; 48import PersistStatus from '../persistStatus'; 49 50/** 当前播放 */ 51const currentMusicStore = new GlobalState<IMusic.IMusicItem | null>(null); 52 53/** 播放模式 */ 54const repeatModeStore = new GlobalState<MusicRepeatMode>(MusicRepeatMode.QUEUE); 55 56/** 音质 */ 57const qualityStore = new GlobalState<IMusic.IQualityKey>('standard'); 58 59let currentIndex = -1; 60 61// TODO: 下个版本最大限制调大一些 62const maxMusicQueueLength = 1500; // 当前播放最大限制 63const halfMaxMusicQueueLength = Math.floor(maxMusicQueueLength / 2); 64const shrinkPlayListToSize = ( 65 queue: IMusic.IMusicItem[], 66 targetIndex = currentIndex, 67) => { 68 // 播放列表上限,太多无法缓存状态 69 if (queue.length > maxMusicQueueLength) { 70 if (targetIndex < halfMaxMusicQueueLength) { 71 queue = queue.slice(0, maxMusicQueueLength); 72 } else { 73 const right = Math.min( 74 queue.length, 75 targetIndex + halfMaxMusicQueueLength, 76 ); 77 const left = Math.max(0, right - maxMusicQueueLength); 78 queue = queue.slice(left, right); 79 } 80 } 81 return queue; 82}; 83 84let hasSetupListener = false; 85 86// TODO: 删除 87function migrate() { 88 const config = Config.get('status.music'); 89 if (!config) { 90 return; 91 } 92 const {rate, repeatMode, musicQueue, progress, track} = config; 93 PersistStatus.set('music.rate', rate); 94 PersistStatus.set('music.repeatMode', repeatMode); 95 PersistStatus.set('music.playList', musicQueue); 96 PersistStatus.set('music.progress', progress); 97 PersistStatus.set('music.musicItem', track); 98 Config.set('status.music', undefined); 99} 100 101async function setupTrackPlayer() { 102 migrate(); 103 104 const rate = PersistStatus.get('music.rate'); 105 const musicQueue = PersistStatus.get('music.playList'); 106 const repeatMode = PersistStatus.get('music.repeatMode'); 107 const progress = PersistStatus.get('music.progress'); 108 const track = PersistStatus.get('music.musicItem'); 109 const quality = 110 PersistStatus.get('music.quality') || 111 Config.get('setting.basic.defaultPlayQuality') || 112 'standard'; 113 114 // 状态恢复 115 if (rate) { 116 await ReactNativeTrackPlayer.setRate(+rate / 100); 117 } 118 119 if (musicQueue && Array.isArray(musicQueue)) { 120 addAll(musicQueue, undefined, repeatMode === MusicRepeatMode.SHUFFLE); 121 } 122 123 if (track && isInPlayList(track)) { 124 const newSource = await PluginManager.getByMedia( 125 track, 126 )?.methods.getMediaSource(track, quality, 0); 127 // 重新初始化 获取最新的链接 128 track.url = newSource?.url || track.url; 129 track.headers = newSource?.headers || track.headers; 130 if (!Config.get('setting.basic.autoPlayWhenAppStart')) { 131 track.isInit = true; 132 } 133 134 await setTrackSource(track as Track, false); 135 setCurrentMusic(track); 136 137 if (progress) { 138 await ReactNativeTrackPlayer.seekTo(progress); 139 } 140 } 141 142 if (!hasSetupListener) { 143 ReactNativeTrackPlayer.addEventListener( 144 Event.PlaybackActiveTrackChanged, 145 async evt => { 146 if ( 147 evt.index === 1 && 148 evt.lastIndex === 0 && 149 evt.track?.$ === internalFakeSoundKey 150 ) { 151 trace('队列末尾,播放下一首'); 152 if (repeatModeStore.getValue() === MusicRepeatMode.SINGLE) { 153 await play(null, true); 154 } else { 155 // 当前生效的歌曲是下一曲的标记 156 await skipToNext(); 157 } 158 } 159 }, 160 ); 161 162 ReactNativeTrackPlayer.addEventListener( 163 Event.PlaybackError, 164 async e => { 165 // WARNING: 不稳定,报错的时候有可能track已经变到下一首歌去了 166 const currentTrack = 167 await ReactNativeTrackPlayer.getActiveTrack(); 168 if (currentTrack?.isInit) { 169 // HACK: 避免初始失败的情况 170 ReactNativeTrackPlayer.updateMetadataForTrack(0, { 171 ...currentTrack, 172 // @ts-ignore 173 isInit: undefined, 174 }); 175 return; 176 } 177 178 if ( 179 (await ReactNativeTrackPlayer.getActiveTrackIndex()) === 180 0 && 181 e.message && 182 e.message !== 'android-io-file-not-found' 183 ) { 184 trace('播放出错', { 185 message: e.message, 186 code: e.code, 187 }); 188 189 failToPlay(); 190 } 191 }, 192 ); 193 194 hasSetupListener = true; 195 } 196} 197 198/** 199 * 获取自动播放的下一个track 200 */ 201const getFakeNextTrack = () => { 202 let track: Track | undefined; 203 const repeatMode = repeatModeStore.getValue(); 204 if (repeatMode === MusicRepeatMode.SINGLE) { 205 // 单曲循环 206 track = getPlayListMusicAt(currentIndex) as Track; 207 } else { 208 // 下一曲 209 track = getPlayListMusicAt(currentIndex + 1) as Track; 210 } 211 212 if (track) { 213 return produce(track, _ => { 214 _.url = SoundAsset.fakeAudio; 215 _.$ = internalFakeSoundKey; 216 if (!_.artwork?.trim()?.length) { 217 _.artwork = undefined; 218 } 219 }); 220 } else { 221 // 只有列表长度为0时才会出现的特殊情况 222 return {url: SoundAsset.fakeAudio, $: internalFakeSoundKey} as Track; 223 } 224}; 225 226/** 播放失败时的情况 */ 227async function failToPlay() { 228 // 如果自动跳转下一曲, 500s后自动跳转 229 if (!Config.get('setting.basic.autoStopWhenError')) { 230 await ReactNativeTrackPlayer.reset(); 231 await delay(500); 232 await skipToNext(); 233 } 234} 235 236// 播放模式相关 237const _toggleRepeatMapping = { 238 [MusicRepeatMode.SHUFFLE]: MusicRepeatMode.SINGLE, 239 [MusicRepeatMode.SINGLE]: MusicRepeatMode.QUEUE, 240 [MusicRepeatMode.QUEUE]: MusicRepeatMode.SHUFFLE, 241}; 242/** 切换下一个模式 */ 243const toggleRepeatMode = () => { 244 setRepeatMode(_toggleRepeatMapping[repeatModeStore.getValue()]); 245}; 246 247/** 248 * 添加到播放列表 249 * @param musicItems 目标歌曲 250 * @param beforeIndex 在第x首歌曲前添加 251 * @param shouldShuffle 随机排序 252 */ 253const addAll = ( 254 musicItems: Array<IMusic.IMusicItem> = [], 255 beforeIndex?: number, 256 shouldShuffle?: boolean, 257) => { 258 const now = Date.now(); 259 let newPlayList: IMusic.IMusicItem[] = []; 260 let currentPlayList = getPlayList(); 261 const _musicItems = musicItems.map((item, index) => 262 produce(item, draft => { 263 draft[timeStampSymbol] = now; 264 draft[sortIndexSymbol] = index; 265 }), 266 ); 267 if (beforeIndex === undefined || beforeIndex < 0) { 268 // 1.1. 添加到歌单末尾,并过滤掉已有的歌曲 269 newPlayList = currentPlayList.concat( 270 _musicItems.filter(item => !isInPlayList(item)), 271 ); 272 } else { 273 // 1.2. 新的播放列表,插入 274 const indexMap = createMediaIndexMap(_musicItems); 275 const beforeDraft = currentPlayList 276 .slice(0, beforeIndex) 277 .filter(item => !indexMap.has(item)); 278 const afterDraft = currentPlayList 279 .slice(beforeIndex) 280 .filter(item => !indexMap.has(item)); 281 282 newPlayList = [...beforeDraft, ..._musicItems, ...afterDraft]; 283 } 284 285 // 如果太长了 286 if (newPlayList.length > maxMusicQueueLength) { 287 newPlayList = shrinkPlayListToSize( 288 newPlayList, 289 beforeIndex ?? newPlayList.length - 1, 290 ); 291 } 292 293 // 2. 如果需要随机 294 if (shouldShuffle) { 295 newPlayList = shuffle(newPlayList); 296 } 297 // 3. 设置播放列表 298 setPlayList(newPlayList); 299 const currentMusicItem = currentMusicStore.getValue(); 300 301 // 4. 重置下标 302 if (currentMusicItem) { 303 currentIndex = getMusicIndex(currentMusicItem); 304 } 305 306 // TODO: 更新播放队列信息 307 // 5. 存储更新的播放列表信息 308}; 309 310/** 追加到队尾 */ 311const add = ( 312 musicItem: IMusic.IMusicItem | IMusic.IMusicItem[], 313 beforeIndex?: number, 314) => { 315 addAll(Array.isArray(musicItem) ? musicItem : [musicItem], beforeIndex); 316}; 317 318/** 319 * 下一首播放 320 * @param musicItem 321 */ 322const addNext = (musicItem: IMusic.IMusicItem | IMusic.IMusicItem[]) => { 323 const shouldPlay = isPlayListEmpty(); 324 add(musicItem, currentIndex + 1); 325 if (shouldPlay) { 326 play(Array.isArray(musicItem) ? musicItem[0] : musicItem); 327 } 328}; 329 330const isCurrentMusic = (musicItem: IMusic.IMusicItem) => { 331 return isSameMediaItem(musicItem, currentMusicStore.getValue()) ?? false; 332}; 333 334const remove = async (musicItem: IMusic.IMusicItem) => { 335 const playList = getPlayList(); 336 let newPlayList: IMusic.IMusicItem[] = []; 337 let currentMusic: IMusic.IMusicItem | null = currentMusicStore.getValue(); 338 const targetIndex = getMusicIndex(musicItem); 339 let shouldPlayCurrent: boolean | null = null; 340 if (targetIndex === -1) { 341 // 1. 这种情况应该是出错了 342 return; 343 } 344 // 2. 移除的是当前项 345 if (currentIndex === targetIndex) { 346 // 2.1 停止播放,移除当前项 347 newPlayList = produce(playList, draft => { 348 draft.splice(targetIndex, 1); 349 }); 350 // 2.2 设置新的播放列表,并更新当前音乐 351 if (newPlayList.length === 0) { 352 currentMusic = null; 353 shouldPlayCurrent = false; 354 } else { 355 currentMusic = newPlayList[currentIndex % newPlayList.length]; 356 try { 357 const state = (await ReactNativeTrackPlayer.getPlaybackState()) 358 .state; 359 if (musicIsPaused(state)) { 360 shouldPlayCurrent = false; 361 } else { 362 shouldPlayCurrent = true; 363 } 364 } catch { 365 shouldPlayCurrent = false; 366 } 367 } 368 } else { 369 // 3. 删除 370 newPlayList = produce(playList, draft => { 371 draft.splice(targetIndex, 1); 372 }); 373 } 374 375 setPlayList(newPlayList); 376 setCurrentMusic(currentMusic); 377 if (shouldPlayCurrent === true) { 378 await play(currentMusic, true); 379 } else if (shouldPlayCurrent === false) { 380 await ReactNativeTrackPlayer.reset(); 381 } 382}; 383 384/** 385 * 设置播放模式 386 * @param mode 播放模式 387 */ 388const setRepeatMode = (mode: MusicRepeatMode) => { 389 const playList = getPlayList(); 390 let newPlayList; 391 if (mode === MusicRepeatMode.SHUFFLE) { 392 newPlayList = shuffle(playList); 393 } else { 394 newPlayList = produce(playList, draft => { 395 return sortByTimestampAndIndex(draft); 396 }); 397 } 398 399 setPlayList(newPlayList); 400 const currentMusicItem = currentMusicStore.getValue(); 401 currentIndex = getMusicIndex(currentMusicItem); 402 repeatModeStore.setValue(mode); 403 // 更新下一首歌的信息 404 ReactNativeTrackPlayer.updateMetadataForTrack(1, getFakeNextTrack()); 405 // 记录 406 PersistStatus.set('music.repeatMode', mode); 407}; 408 409/** 清空播放列表 */ 410const clear = async () => { 411 setPlayList([]); 412 setCurrentMusic(null); 413 414 await ReactNativeTrackPlayer.reset(); 415 PersistStatus.set('music.musicItem', undefined); 416 PersistStatus.set('music.progress', 0); 417}; 418 419/** 暂停 */ 420const pause = async () => { 421 await ReactNativeTrackPlayer.pause(); 422}; 423 424/** 设置音源 */ 425const setTrackSource = async (track: Track, autoPlay = true) => { 426 if (!track.artwork?.trim()?.length) { 427 track.artwork = undefined; 428 } 429 await ReactNativeTrackPlayer.setQueue([track, getFakeNextTrack()]); 430 PersistStatus.set('music.musicItem', track as IMusic.IMusicItem); 431 PersistStatus.set('music.progress', 0); 432 if (autoPlay) { 433 await ReactNativeTrackPlayer.play(); 434 } 435}; 436 437const setCurrentMusic = (musicItem?: IMusic.IMusicItem | null) => { 438 if (!musicItem) { 439 currentIndex = -1; 440 currentMusicStore.setValue(null); 441 PersistStatus.set('music.musicItem', undefined); 442 PersistStatus.set('music.progress', 0); 443 return; 444 } 445 currentIndex = getMusicIndex(musicItem); 446 currentMusicStore.setValue(musicItem); 447}; 448 449const setQuality = (quality: IMusic.IQualityKey) => { 450 qualityStore.setValue(quality); 451 PersistStatus.set('music.quality', quality); 452}; 453/** 454 * 播放 455 * 456 * 当musicItem 为空时,代表暂停/播放 457 * 458 * @param musicItem 459 * @param forcePlay 460 * @returns 461 */ 462const play = async ( 463 musicItem?: IMusic.IMusicItem | null, 464 forcePlay?: boolean, 465) => { 466 try { 467 if (!musicItem) { 468 musicItem = currentMusicStore.getValue(); 469 } 470 if (!musicItem) { 471 throw new Error(PlayFailReason.PLAY_LIST_IS_EMPTY); 472 } 473 // 1. 移动网络禁止播放 474 if ( 475 Network.isCellular() && 476 !Config.get('setting.basic.useCelluarNetworkPlay') && 477 !LocalMusicSheet.isLocalMusic(musicItem) 478 ) { 479 await ReactNativeTrackPlayer.reset(); 480 throw new Error(PlayFailReason.FORBID_CELLUAR_NETWORK_PLAY); 481 } 482 483 // 2. 如果是当前正在播放的音频 484 if (isCurrentMusic(musicItem)) { 485 const currentTrack = await ReactNativeTrackPlayer.getTrack(0); 486 // 2.1 如果当前有源 487 if ( 488 currentTrack?.url && 489 isSameMediaItem(musicItem, currentTrack as IMusic.IMusicItem) 490 ) { 491 const currentActiveIndex = 492 await ReactNativeTrackPlayer.getActiveTrackIndex(); 493 if (currentActiveIndex !== 0) { 494 await ReactNativeTrackPlayer.skip(0); 495 } 496 if (forcePlay) { 497 // 2.1.1 强制重新开始 498 await ReactNativeTrackPlayer.seekTo(0); 499 } 500 const currentState = ( 501 await ReactNativeTrackPlayer.getPlaybackState() 502 ).state; 503 if (currentState === State.Stopped) { 504 await setTrackSource(currentTrack); 505 } 506 if (currentState !== State.Playing) { 507 // 2.1.2 恢复播放 508 await ReactNativeTrackPlayer.play(); 509 } 510 // 这种情况下,播放队列和当前歌曲都不需要变化 511 return; 512 } 513 // 2.2 其他情况:重新获取源 514 } 515 516 // 3. 如果没有在播放列表中,添加到队尾;同时更新列表状态 517 const inPlayList = isInPlayList(musicItem); 518 if (!inPlayList) { 519 add(musicItem); 520 } 521 522 // 4. 更新列表状态和当前音乐 523 setCurrentMusic(musicItem); 524 await ReactNativeTrackPlayer.reset(); 525 526 // 4.1 刷新歌词信息 527 if ( 528 !isSameMediaItem( 529 LyricManager.getLyricState()?.lyricParser?.getCurrentMusicItem?.(), 530 musicItem, 531 ) 532 ) { 533 DeviceEventEmitter.emit(EDeviceEvents.REFRESH_LYRIC, true); 534 } 535 536 // 5. 获取音源 537 let track: IMusic.IMusicItem; 538 539 // 5.1 通过插件获取音源 540 const plugin = PluginManager.getByName(musicItem.platform); 541 // 5.2 获取音质排序 542 const qualityOrder = getQualityOrder( 543 Config.get('setting.basic.defaultPlayQuality') ?? 'standard', 544 Config.get('setting.basic.playQualityOrder') ?? 'asc', 545 ); 546 // 5.3 插件返回音源 547 let source: IPlugin.IMediaSourceResult | null = null; 548 for (let quality of qualityOrder) { 549 if (isCurrentMusic(musicItem)) { 550 source = 551 (await plugin?.methods?.getMediaSource( 552 musicItem, 553 quality, 554 )) ?? null; 555 // 5.3.1 获取到真实源 556 if (source) { 557 setQuality(quality); 558 break; 559 } 560 } else { 561 // 5.3.2 已经切换到其他歌曲了, 562 return; 563 } 564 } 565 566 if (!isCurrentMusic(musicItem)) { 567 return; 568 } 569 570 if (!source) { 571 // 如果有source 572 if (musicItem.source) { 573 for (let quality of qualityOrder) { 574 if (musicItem.source[quality]?.url) { 575 source = musicItem.source[quality]!; 576 setQuality(quality); 577 578 break; 579 } 580 } 581 } 582 583 // 5.4 没有返回源 584 if (!source && !musicItem.url) { 585 throw new Error(PlayFailReason.INVALID_SOURCE); 586 } else { 587 source = { 588 url: musicItem.url, 589 }; 590 setQuality('standard'); 591 } 592 } 593 594 // 6. 特殊类型源 595 if (getUrlExt(source.url) === '.m3u8') { 596 // @ts-ignore 597 source.type = 'hls'; 598 } 599 // 7. 合并结果 600 track = mergeProps(musicItem, source) as IMusic.IMusicItem; 601 602 // 8. 新增历史记录 603 musicHistory.addMusic(musicItem); 604 605 trace('获取音源成功', track); 606 // 9. 设置音源 607 await setTrackSource(track as Track); 608 609 // 10. 获取补充信息 610 let info: Partial<IMusic.IMusicItem> | null = null; 611 try { 612 info = (await plugin?.methods?.getMusicInfo?.(musicItem)) ?? null; 613 } catch {} 614 615 // 11. 设置补充信息 616 if (info && isCurrentMusic(musicItem)) { 617 const mergedTrack = mergeProps(track, info); 618 currentMusicStore.setValue(mergedTrack as IMusic.IMusicItem); 619 await ReactNativeTrackPlayer.updateMetadataForTrack( 620 0, 621 mergedTrack as TrackMetadataBase, 622 ); 623 } 624 } catch (e: any) { 625 const message = e?.message; 626 console.log(message, 'MMM', e); 627 if ( 628 message === 'The player is not initialized. Call setupPlayer first.' 629 ) { 630 await ReactNativeTrackPlayer.setupPlayer(); 631 play(musicItem, forcePlay); 632 } else if (message === PlayFailReason.FORBID_CELLUAR_NETWORK_PLAY) { 633 Toast.warn( 634 '当前禁止移动网络播放音乐,如需播放请去侧边栏-基本设置中修改', 635 ); 636 } else if (message === PlayFailReason.INVALID_SOURCE) { 637 trace('音源为空,播放失败'); 638 await failToPlay(); 639 } else if (message === PlayFailReason.PLAY_LIST_IS_EMPTY) { 640 // 队列是空的,不应该出现这种情况 641 } 642 } 643}; 644 645/** 646 * 播放音乐,同时替换播放队列 647 * @param musicItem 音乐 648 * @param newPlayList 替代列表 649 */ 650const playWithReplacePlayList = async ( 651 musicItem: IMusic.IMusicItem, 652 newPlayList: IMusic.IMusicItem[], 653) => { 654 if (newPlayList.length !== 0) { 655 const now = Date.now(); 656 if (newPlayList.length > maxMusicQueueLength) { 657 newPlayList = shrinkPlayListToSize( 658 newPlayList, 659 newPlayList.findIndex(it => isSameMediaItem(it, musicItem)), 660 ); 661 } 662 const playListItems = newPlayList.map((item, index) => 663 produce(item, draft => { 664 draft[timeStampSymbol] = now; 665 draft[sortIndexSymbol] = index; 666 }), 667 ); 668 setPlayList( 669 repeatModeStore.getValue() === MusicRepeatMode.SHUFFLE 670 ? shuffle(playListItems) 671 : playListItems, 672 ); 673 await play(musicItem, true); 674 } 675}; 676 677const skipToNext = async () => { 678 if (isPlayListEmpty()) { 679 setCurrentMusic(null); 680 return; 681 } 682 683 await play(getPlayListMusicAt(currentIndex + 1), true); 684}; 685 686const skipToPrevious = async () => { 687 if (isPlayListEmpty()) { 688 setCurrentMusic(null); 689 return; 690 } 691 692 await play( 693 getPlayListMusicAt(currentIndex === -1 ? 0 : currentIndex - 1), 694 true, 695 ); 696}; 697 698/** 修改当前播放的音质 */ 699const changeQuality = async (newQuality: IMusic.IQualityKey) => { 700 // 获取当前的音乐和进度 701 if (newQuality === qualityStore.getValue()) { 702 return true; 703 } 704 705 // 获取当前歌曲 706 const musicItem = currentMusicStore.getValue(); 707 if (!musicItem) { 708 return false; 709 } 710 try { 711 const progress = await ReactNativeTrackPlayer.getProgress(); 712 const plugin = PluginManager.getByMedia(musicItem); 713 const newSource = await plugin?.methods?.getMediaSource( 714 musicItem, 715 newQuality, 716 ); 717 if (!newSource?.url) { 718 throw new Error(PlayFailReason.INVALID_SOURCE); 719 } 720 if (isCurrentMusic(musicItem)) { 721 const playingState = ( 722 await ReactNativeTrackPlayer.getPlaybackState() 723 ).state; 724 await setTrackSource( 725 mergeProps(musicItem, newSource) as unknown as Track, 726 !musicIsPaused(playingState), 727 ); 728 729 await ReactNativeTrackPlayer.seekTo(progress.position ?? 0); 730 setQuality(newQuality); 731 } 732 return true; 733 } catch { 734 // 修改失败 735 return false; 736 } 737}; 738 739enum PlayFailReason { 740 /** 禁止移动网络播放 */ 741 FORBID_CELLUAR_NETWORK_PLAY = 'FORBID_CELLUAR_NETWORK_PLAY', 742 /** 播放列表为空 */ 743 PLAY_LIST_IS_EMPTY = 'PLAY_LIST_IS_EMPTY', 744 /** 无效源 */ 745 INVALID_SOURCE = 'INVALID_SOURCE', 746 /** 非当前音乐 */ 747} 748 749function useMusicState() { 750 const playbackState = usePlaybackState(); 751 752 return playbackState.state; 753} 754 755function getPreviousMusic() { 756 const currentMusicItem = currentMusicStore.getValue(); 757 if (!currentMusicItem) { 758 return null; 759 } 760 761 return getPlayListMusicAt(currentIndex - 1); 762} 763 764function getNextMusic() { 765 const currentMusicItem = currentMusicStore.getValue(); 766 if (!currentMusicItem) { 767 return null; 768 } 769 770 return getPlayListMusicAt(currentIndex + 1); 771} 772 773const TrackPlayer = { 774 setupTrackPlayer, 775 usePlayList, 776 getPlayList, 777 addAll, 778 add, 779 addNext, 780 skipToNext, 781 skipToPrevious, 782 play, 783 playWithReplacePlayList, 784 pause, 785 remove, 786 clear, 787 useCurrentMusic: currentMusicStore.useValue, 788 getCurrentMusic: currentMusicStore.getValue, 789 useRepeatMode: repeatModeStore.useValue, 790 getRepeatMode: repeatModeStore.getValue, 791 toggleRepeatMode, 792 usePlaybackState, 793 getProgress: ReactNativeTrackPlayer.getProgress, 794 useProgress: useProgress, 795 seekTo: ReactNativeTrackPlayer.seekTo, 796 changeQuality, 797 useCurrentQuality: qualityStore.useValue, 798 getCurrentQuality: qualityStore.getValue, 799 getRate: ReactNativeTrackPlayer.getRate, 800 setRate: ReactNativeTrackPlayer.setRate, 801 useMusicState, 802 reset: ReactNativeTrackPlayer.reset, 803 getPreviousMusic, 804 getNextMusic, 805}; 806 807export default TrackPlayer; 808export {MusicRepeatMode, State as MusicState}; 809