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