15500cea7S猫头猫import produce from 'immer'; 25500cea7S猫头猫import ReactNativeTrackPlayer, { 35500cea7S猫头猫 Event, 45500cea7S猫头猫 State, 55500cea7S猫头猫 Track, 65500cea7S猫头猫 TrackMetadataBase, 75500cea7S猫头猫 usePlaybackState, 85500cea7S猫头猫 useProgress, 95500cea7S猫头猫} from 'react-native-track-player'; 105500cea7S猫头猫import shuffle from 'lodash.shuffle'; 115500cea7S猫头猫import Config from '../config'; 125500cea7S猫头猫import { 135500cea7S猫头猫 EDeviceEvents, 145500cea7S猫头猫 internalFakeSoundKey, 155500cea7S猫头猫 sortIndexSymbol, 165500cea7S猫头猫 timeStampSymbol, 175500cea7S猫头猫} from '@/constants/commonConst'; 185500cea7S猫头猫import {GlobalState} from '@/utils/stateMapper'; 195500cea7S猫头猫import delay from '@/utils/delay'; 205500cea7S猫头猫import { 215500cea7S猫头猫 isSameMediaItem, 225500cea7S猫头猫 mergeProps, 235500cea7S猫头猫 sortByTimestampAndIndex, 245500cea7S猫头猫} from '@/utils/mediaItem'; 255500cea7S猫头猫import Network from '../network'; 265500cea7S猫头猫import LocalMusicSheet from '../localMusicSheet'; 275500cea7S猫头猫import {SoundAsset} from '@/constants/assetsConst'; 285500cea7S猫头猫import {getQualityOrder} from '@/utils/qualities'; 295500cea7S猫头猫import musicHistory from '../musicHistory'; 305500cea7S猫头猫import getUrlExt from '@/utils/getUrlExt'; 315500cea7S猫头猫import {DeviceEventEmitter} from 'react-native'; 325500cea7S猫头猫import LyricManager from '../lyricManager'; 335500cea7S猫头猫import {MusicRepeatMode} from './common'; 345500cea7S猫头猫import { 355500cea7S猫头猫 getMusicIndex, 365500cea7S猫头猫 getPlayList, 375500cea7S猫头猫 getPlayListMusicAt, 385500cea7S猫头猫 isInPlayList, 395500cea7S猫头猫 isPlayListEmpty, 405500cea7S猫头猫 setPlayList, 415500cea7S猫头猫 usePlayList, 425500cea7S猫头猫} from './internal/playList'; 435500cea7S猫头猫import {createMediaIndexMap} from '@/utils/mediaIndexMap'; 445500cea7S猫头猫import PluginManager from '../pluginManager'; 455500cea7S猫头猫import {musicIsPaused} from '@/utils/trackUtils'; 46f511aee9S猫头猫import Toast from '@/utils/toast'; 475500cea7S猫头猫 485500cea7S猫头猫/** 当前播放 */ 495500cea7S猫头猫const currentMusicStore = new GlobalState<IMusic.IMusicItem | null>(null); 505500cea7S猫头猫 515500cea7S猫头猫/** 播放模式 */ 525500cea7S猫头猫const repeatModeStore = new GlobalState<MusicRepeatMode>(MusicRepeatMode.QUEUE); 535500cea7S猫头猫 545500cea7S猫头猫/** 音质 */ 555500cea7S猫头猫const qualityStore = new GlobalState<IMusic.IQualityKey>('standard'); 565500cea7S猫头猫 575500cea7S猫头猫let currentIndex = -1; 585500cea7S猫头猫 595500cea7S猫头猫// TODO: 下个版本最大限制调大一些 605500cea7S猫头猫const maxMusicQueueLength = 1500; // 当前播放最大限制 615500cea7S猫头猫const halfMaxMusicQueueLength = Math.floor(maxMusicQueueLength / 2); 625500cea7S猫头猫const shrinkPlayListToSize = ( 635500cea7S猫头猫 queue: IMusic.IMusicItem[], 645500cea7S猫头猫 targetIndex = currentIndex, 655500cea7S猫头猫) => { 665500cea7S猫头猫 // 播放列表上限,太多无法缓存状态 675500cea7S猫头猫 if (queue.length > maxMusicQueueLength) { 685500cea7S猫头猫 if (targetIndex < halfMaxMusicQueueLength) { 695500cea7S猫头猫 queue = queue.slice(0, maxMusicQueueLength); 705500cea7S猫头猫 } else { 715500cea7S猫头猫 const right = Math.min( 725500cea7S猫头猫 queue.length, 735500cea7S猫头猫 targetIndex + halfMaxMusicQueueLength, 745500cea7S猫头猫 ); 755500cea7S猫头猫 const left = Math.max(0, right - maxMusicQueueLength); 765500cea7S猫头猫 queue = queue.slice(left, right); 775500cea7S猫头猫 } 785500cea7S猫头猫 } 795500cea7S猫头猫 return queue; 805500cea7S猫头猫}; 815500cea7S猫头猫 82*7aed04d4S猫头猫let hasSetupListener = false; 83*7aed04d4S猫头猫 845500cea7S猫头猫async function setupTrackPlayer() { 855500cea7S猫头猫 const config = Config.get('status.music') ?? {}; 865500cea7S猫头猫 const {rate, repeatMode, musicQueue, progress, track} = config; 875500cea7S猫头猫 885500cea7S猫头猫 // 状态恢复 895500cea7S猫头猫 if (rate) { 905500cea7S猫头猫 await ReactNativeTrackPlayer.setRate(+rate / 100); 915500cea7S猫头猫 } 925500cea7S猫头猫 935500cea7S猫头猫 if (musicQueue && Array.isArray(musicQueue)) { 945500cea7S猫头猫 addAll(musicQueue, undefined, repeatMode === MusicRepeatMode.SHUFFLE); 955500cea7S猫头猫 } 965500cea7S猫头猫 975500cea7S猫头猫 const currentQuality = 985500cea7S猫头猫 Config.get('setting.basic.defaultPlayQuality') ?? 'standard'; 995500cea7S猫头猫 1005500cea7S猫头猫 if (track && isInPlayList(track)) { 1015500cea7S猫头猫 const newSource = await PluginManager.getByMedia( 1025500cea7S猫头猫 track, 1035500cea7S猫头猫 )?.methods.getMediaSource(track, currentQuality, 0); 1045500cea7S猫头猫 // 重新初始化 获取最新的链接 1055500cea7S猫头猫 track.url = newSource?.url || track.url; 1065500cea7S猫头猫 track.headers = newSource?.headers || track.headers; 1075500cea7S猫头猫 1085500cea7S猫头猫 await setTrackSource(track as Track, false); 1095500cea7S猫头猫 setCurrentMusic(track); 1105500cea7S猫头猫 1115500cea7S猫头猫 if (config?.progress) { 1125500cea7S猫头猫 await ReactNativeTrackPlayer.seekTo(progress!); 1135500cea7S猫头猫 } 1145500cea7S猫头猫 } 1155500cea7S猫头猫 116*7aed04d4S猫头猫 if (!hasSetupListener) { 1175500cea7S猫头猫 ReactNativeTrackPlayer.addEventListener( 1185500cea7S猫头猫 Event.PlaybackActiveTrackChanged, 1195500cea7S猫头猫 async evt => { 1205500cea7S猫头猫 if ( 1215500cea7S猫头猫 evt.index === 1 && 1225500cea7S猫头猫 evt.lastIndex === 0 && 1235500cea7S猫头猫 evt.track?.$ === internalFakeSoundKey 1245500cea7S猫头猫 ) { 1255500cea7S猫头猫 if (repeatModeStore.getValue() === MusicRepeatMode.SINGLE) { 1265500cea7S猫头猫 await play(null, true); 1275500cea7S猫头猫 } else { 1285500cea7S猫头猫 // 当前生效的歌曲是下一曲的标记 1295500cea7S猫头猫 await skipToNext('队列结尾'); 1305500cea7S猫头猫 } 1315500cea7S猫头猫 } 1325500cea7S猫头猫 }, 1335500cea7S猫头猫 ); 1345500cea7S猫头猫 135*7aed04d4S猫头猫 ReactNativeTrackPlayer.addEventListener( 136*7aed04d4S猫头猫 Event.PlaybackError, 137*7aed04d4S猫头猫 async () => { 1385500cea7S猫头猫 // 只关心第一个元素 139*7aed04d4S猫头猫 if ( 140*7aed04d4S猫头猫 (await ReactNativeTrackPlayer.getActiveTrackIndex()) === 0 141*7aed04d4S猫头猫 ) { 1425500cea7S猫头猫 failToPlay(); 1435500cea7S猫头猫 } 144*7aed04d4S猫头猫 }, 145*7aed04d4S猫头猫 ); 146*7aed04d4S猫头猫 147*7aed04d4S猫头猫 hasSetupListener = true; 148*7aed04d4S猫头猫 } 1495500cea7S猫头猫} 1505500cea7S猫头猫 1515500cea7S猫头猫/** 1525500cea7S猫头猫 * 获取自动播放的下一个track 1535500cea7S猫头猫 */ 1545500cea7S猫头猫const getFakeNextTrack = () => { 1555500cea7S猫头猫 let track: Track | undefined; 1565500cea7S猫头猫 const repeatMode = repeatModeStore.getValue(); 1575500cea7S猫头猫 if (repeatMode === MusicRepeatMode.SINGLE) { 1585500cea7S猫头猫 // 单曲循环 1595500cea7S猫头猫 track = getPlayListMusicAt(currentIndex) as Track; 1605500cea7S猫头猫 } else { 1615500cea7S猫头猫 // 下一曲 1625500cea7S猫头猫 track = getPlayListMusicAt(currentIndex + 1) as Track; 1635500cea7S猫头猫 } 1645500cea7S猫头猫 1655500cea7S猫头猫 if (track) { 1665500cea7S猫头猫 return produce(track, _ => { 1675500cea7S猫头猫 _.url = SoundAsset.fakeAudio; 1685500cea7S猫头猫 _.$ = internalFakeSoundKey; 1695500cea7S猫头猫 }); 1705500cea7S猫头猫 } else { 1715500cea7S猫头猫 // 只有列表长度为0时才会出现的特殊情况 1725500cea7S猫头猫 return {url: SoundAsset.fakeAudio, $: internalFakeSoundKey} as Track; 1735500cea7S猫头猫 } 1745500cea7S猫头猫}; 1755500cea7S猫头猫 1765500cea7S猫头猫/** 播放失败时的情况 */ 1775500cea7S猫头猫async function failToPlay(reason?: string) { 1785500cea7S猫头猫 // 如果自动跳转下一曲, 500s后自动跳转 1795500cea7S猫头猫 if (!Config.get('setting.basic.autoStopWhenError')) { 1805500cea7S猫头猫 await ReactNativeTrackPlayer.reset(); 1815500cea7S猫头猫 await delay(500); 1825500cea7S猫头猫 await skipToNext('播放失败' + reason); 1835500cea7S猫头猫 } 1845500cea7S猫头猫} 1855500cea7S猫头猫 1865500cea7S猫头猫// 播放模式相关 1875500cea7S猫头猫const _toggleRepeatMapping = { 1885500cea7S猫头猫 [MusicRepeatMode.SHUFFLE]: MusicRepeatMode.SINGLE, 1895500cea7S猫头猫 [MusicRepeatMode.SINGLE]: MusicRepeatMode.QUEUE, 1905500cea7S猫头猫 [MusicRepeatMode.QUEUE]: MusicRepeatMode.SHUFFLE, 1915500cea7S猫头猫}; 1925500cea7S猫头猫/** 切换下一个模式 */ 1935500cea7S猫头猫const toggleRepeatMode = () => { 1945500cea7S猫头猫 setRepeatMode(_toggleRepeatMapping[repeatModeStore.getValue()]); 1955500cea7S猫头猫}; 1965500cea7S猫头猫 1975500cea7S猫头猫/** 设置音源 */ 1985500cea7S猫头猫const setTrackSource = async (track: Track, autoPlay = true) => { 1995500cea7S猫头猫 await ReactNativeTrackPlayer.setQueue([track, getFakeNextTrack()]); 2005500cea7S猫头猫 if (autoPlay) { 2015500cea7S猫头猫 await ReactNativeTrackPlayer.play(); 2025500cea7S猫头猫 } 2035500cea7S猫头猫 // 写缓存 TODO: MMKV 2045500cea7S猫头猫 Config.set('status.music.track', track as IMusic.IMusicItem, false); 2055500cea7S猫头猫 Config.set('status.music.progress', 0, false); 2065500cea7S猫头猫}; 2075500cea7S猫头猫 2085500cea7S猫头猫/** 2095500cea7S猫头猫 * 添加到播放列表 2105500cea7S猫头猫 * @param musicItems 目标歌曲 2115500cea7S猫头猫 * @param beforeIndex 在第x首歌曲前添加 2125500cea7S猫头猫 * @param shouldShuffle 随机排序 2135500cea7S猫头猫 */ 2145500cea7S猫头猫const addAll = ( 2155500cea7S猫头猫 musicItems: Array<IMusic.IMusicItem> = [], 2165500cea7S猫头猫 beforeIndex?: number, 2175500cea7S猫头猫 shouldShuffle?: boolean, 2185500cea7S猫头猫) => { 2195500cea7S猫头猫 const now = Date.now(); 2205500cea7S猫头猫 let newPlayList: IMusic.IMusicItem[] = []; 2215500cea7S猫头猫 let currentPlayList = getPlayList(); 2225500cea7S猫头猫 const _musicItems = musicItems.map((item, index) => 2235500cea7S猫头猫 produce(item, draft => { 2245500cea7S猫头猫 draft[timeStampSymbol] = now; 2255500cea7S猫头猫 draft[sortIndexSymbol] = index; 2265500cea7S猫头猫 }), 2275500cea7S猫头猫 ); 2285500cea7S猫头猫 if (beforeIndex === undefined || beforeIndex < 0) { 2295500cea7S猫头猫 // 1.1. 添加到歌单末尾,并过滤掉已有的歌曲 2305500cea7S猫头猫 newPlayList = currentPlayList.concat( 2315500cea7S猫头猫 _musicItems.filter(item => !isInPlayList(item)), 2325500cea7S猫头猫 ); 2335500cea7S猫头猫 } else { 2345500cea7S猫头猫 // 1.2. 新的播放列表,插入 2355500cea7S猫头猫 const indexMap = createMediaIndexMap(_musicItems); 2365500cea7S猫头猫 const beforeDraft = currentPlayList 2375500cea7S猫头猫 .slice(0, beforeIndex) 2385500cea7S猫头猫 .filter(item => !indexMap.has(item)); 2395500cea7S猫头猫 const afterDraft = currentPlayList 2405500cea7S猫头猫 .slice(beforeIndex) 2415500cea7S猫头猫 .filter(item => !indexMap.has(item)); 2425500cea7S猫头猫 2435500cea7S猫头猫 newPlayList = [...beforeDraft, ..._musicItems, ...afterDraft]; 2445500cea7S猫头猫 } 24515900d05S猫头猫 2465500cea7S猫头猫 // 如果太长了 2475500cea7S猫头猫 if (newPlayList.length > maxMusicQueueLength) { 2485500cea7S猫头猫 newPlayList = shrinkPlayListToSize( 2495500cea7S猫头猫 newPlayList, 2505500cea7S猫头猫 beforeIndex ?? newPlayList.length - 1, 2515500cea7S猫头猫 ); 2525500cea7S猫头猫 } 2535500cea7S猫头猫 2545500cea7S猫头猫 // 2. 如果需要随机 2555500cea7S猫头猫 if (shouldShuffle) { 2565500cea7S猫头猫 newPlayList = shuffle(newPlayList); 2575500cea7S猫头猫 } 2585500cea7S猫头猫 // 3. 设置播放列表 2595500cea7S猫头猫 setPlayList(newPlayList); 2605500cea7S猫头猫 const currentMusicItem = currentMusicStore.getValue(); 2615500cea7S猫头猫 2625500cea7S猫头猫 // 4. 重置下标 2635500cea7S猫头猫 if (currentMusicItem) { 2645500cea7S猫头猫 currentIndex = getMusicIndex(currentMusicItem); 2655500cea7S猫头猫 } 2665500cea7S猫头猫 2675500cea7S猫头猫 // TODO: 更新播放队列信息 2685500cea7S猫头猫 // 5. 存储更新的播放列表信息 2695500cea7S猫头猫}; 2705500cea7S猫头猫 2715500cea7S猫头猫/** 追加到队尾 */ 2725500cea7S猫头猫const add = ( 2735500cea7S猫头猫 musicItem: IMusic.IMusicItem | IMusic.IMusicItem[], 2745500cea7S猫头猫 beforeIndex?: number, 2755500cea7S猫头猫) => { 2765500cea7S猫头猫 addAll(Array.isArray(musicItem) ? musicItem : [musicItem], beforeIndex); 2775500cea7S猫头猫}; 2785500cea7S猫头猫 2795500cea7S猫头猫/** 2805500cea7S猫头猫 * 下一首播放 2815500cea7S猫头猫 * @param musicItem 2825500cea7S猫头猫 */ 2835500cea7S猫头猫const addNext = (musicItem: IMusic.IMusicItem | IMusic.IMusicItem[]) => { 2845500cea7S猫头猫 const shouldPlay = isPlayListEmpty(); 2855500cea7S猫头猫 add(musicItem, currentIndex + 1); 2865500cea7S猫头猫 if (shouldPlay) { 2875500cea7S猫头猫 play(Array.isArray(musicItem) ? musicItem[0] : musicItem); 2885500cea7S猫头猫 } 2895500cea7S猫头猫}; 2905500cea7S猫头猫 2915500cea7S猫头猫const isCurrentMusic = (musicItem: IMusic.IMusicItem) => { 2925500cea7S猫头猫 return isSameMediaItem(musicItem, currentMusicStore.getValue()) ?? false; 2935500cea7S猫头猫}; 2945500cea7S猫头猫 2955500cea7S猫头猫const remove = async (musicItem: IMusic.IMusicItem) => { 2965500cea7S猫头猫 const playList = getPlayList(); 2975500cea7S猫头猫 let newPlayList: IMusic.IMusicItem[] = []; 2985500cea7S猫头猫 let currentMusic: IMusic.IMusicItem | null = currentMusicStore.getValue(); 2995500cea7S猫头猫 const targetIndex = getMusicIndex(musicItem); 3005500cea7S猫头猫 let shouldPlayCurrent: boolean | null = null; 3015500cea7S猫头猫 if (targetIndex === -1) { 3025500cea7S猫头猫 // 1. 这种情况应该是出错了 3035500cea7S猫头猫 return; 3045500cea7S猫头猫 } 3055500cea7S猫头猫 // 2. 移除的是当前项 3065500cea7S猫头猫 if (currentIndex === targetIndex) { 3075500cea7S猫头猫 // 2.1 停止播放,移除当前项 3085500cea7S猫头猫 newPlayList = produce(playList, draft => { 3095500cea7S猫头猫 draft.splice(targetIndex, 1); 3105500cea7S猫头猫 }); 3115500cea7S猫头猫 // 2.2 设置新的播放列表,并更新当前音乐 3125500cea7S猫头猫 if (newPlayList.length === 0) { 3135500cea7S猫头猫 currentMusic = null; 3145500cea7S猫头猫 shouldPlayCurrent = false; 3155500cea7S猫头猫 } else { 3165500cea7S猫头猫 currentMusic = newPlayList[currentIndex % newPlayList.length]; 3175500cea7S猫头猫 try { 3185500cea7S猫头猫 const state = (await ReactNativeTrackPlayer.getPlaybackState()) 3195500cea7S猫头猫 .state; 3205500cea7S猫头猫 if (musicIsPaused(state)) { 3215500cea7S猫头猫 shouldPlayCurrent = false; 3225500cea7S猫头猫 } else { 3235500cea7S猫头猫 shouldPlayCurrent = true; 3245500cea7S猫头猫 } 3255500cea7S猫头猫 } catch { 3265500cea7S猫头猫 shouldPlayCurrent = false; 3275500cea7S猫头猫 } 3285500cea7S猫头猫 } 3295500cea7S猫头猫 } else { 3305500cea7S猫头猫 // 3. 删除 3315500cea7S猫头猫 newPlayList = produce(playList, draft => { 3325500cea7S猫头猫 draft.splice(targetIndex, 1); 3335500cea7S猫头猫 }); 3345500cea7S猫头猫 } 3355500cea7S猫头猫 3365500cea7S猫头猫 setPlayList(newPlayList); 3375500cea7S猫头猫 setCurrentMusic(currentMusic); 3385500cea7S猫头猫 Config.set('status.music.musicQueue', playList, false); 3395500cea7S猫头猫 if (shouldPlayCurrent === true) { 3405500cea7S猫头猫 await play(currentMusic, true); 3415500cea7S猫头猫 } else if (shouldPlayCurrent === false) { 3425500cea7S猫头猫 await ReactNativeTrackPlayer.reset(); 3435500cea7S猫头猫 } 3445500cea7S猫头猫}; 3455500cea7S猫头猫 3465500cea7S猫头猫/** 3475500cea7S猫头猫 * 设置播放模式 3485500cea7S猫头猫 * @param mode 播放模式 3495500cea7S猫头猫 */ 3505500cea7S猫头猫const setRepeatMode = (mode: MusicRepeatMode) => { 3515500cea7S猫头猫 const playList = getPlayList(); 3525500cea7S猫头猫 let newPlayList; 3535500cea7S猫头猫 if (mode === MusicRepeatMode.SHUFFLE) { 3545500cea7S猫头猫 newPlayList = shuffle(playList); 3555500cea7S猫头猫 } else { 3565500cea7S猫头猫 newPlayList = produce(playList, draft => { 3575500cea7S猫头猫 return sortByTimestampAndIndex(draft); 3585500cea7S猫头猫 }); 3595500cea7S猫头猫 } 3605500cea7S猫头猫 3615500cea7S猫头猫 setPlayList(newPlayList); 3625500cea7S猫头猫 const currentMusicItem = currentMusicStore.getValue(); 3635500cea7S猫头猫 currentIndex = getMusicIndex(currentMusicItem); 3645500cea7S猫头猫 repeatModeStore.setValue(mode); 3655500cea7S猫头猫 // 更新下一首歌的信息 3665500cea7S猫头猫 ReactNativeTrackPlayer.updateMetadataForTrack(1, getFakeNextTrack()); 3675500cea7S猫头猫 // 记录 3685500cea7S猫头猫 Config.set('status.music.repeatMode', mode, false); 3695500cea7S猫头猫}; 3705500cea7S猫头猫 3715500cea7S猫头猫/** 清空播放列表 */ 3725500cea7S猫头猫const clear = async () => { 3735500cea7S猫头猫 setPlayList([]); 3745500cea7S猫头猫 setCurrentMusic(null); 3755500cea7S猫头猫 3765500cea7S猫头猫 await ReactNativeTrackPlayer.reset(); 3775500cea7S猫头猫 Config.set('status.music', { 3785500cea7S猫头猫 repeatMode: repeatModeStore.getValue(), 3795500cea7S猫头猫 }); 3805500cea7S猫头猫}; 3815500cea7S猫头猫 3825500cea7S猫头猫/** 暂停 */ 3835500cea7S猫头猫const pause = async () => { 3845500cea7S猫头猫 await ReactNativeTrackPlayer.pause(); 3855500cea7S猫头猫}; 3865500cea7S猫头猫 3875500cea7S猫头猫const setCurrentMusic = (musicItem?: IMusic.IMusicItem | null) => { 3885500cea7S猫头猫 if (!musicItem) { 3895500cea7S猫头猫 currentIndex = -1; 390f511aee9S猫头猫 currentMusicStore.setValue(null); 3915500cea7S猫头猫 } 3925500cea7S猫头猫 currentIndex = getMusicIndex(musicItem); 393f511aee9S猫头猫 currentMusicStore.setValue(musicItem!); 3945500cea7S猫头猫}; 3955500cea7S猫头猫 3965500cea7S猫头猫/** 3975500cea7S猫头猫 * 播放 3985500cea7S猫头猫 * 3995500cea7S猫头猫 * 当musicItem 为空时,代表暂停/播放 4005500cea7S猫头猫 * 4015500cea7S猫头猫 * @param musicItem 4025500cea7S猫头猫 * @param forcePlay 4035500cea7S猫头猫 * @returns 4045500cea7S猫头猫 */ 4055500cea7S猫头猫const play = async ( 4065500cea7S猫头猫 musicItem?: IMusic.IMusicItem | null, 4075500cea7S猫头猫 forcePlay?: boolean, 4085500cea7S猫头猫) => { 4095500cea7S猫头猫 try { 4105500cea7S猫头猫 if (!musicItem) { 4115500cea7S猫头猫 musicItem = currentMusicStore.getValue(); 4125500cea7S猫头猫 } 4135500cea7S猫头猫 if (!musicItem) { 4145500cea7S猫头猫 throw new Error(PlayFailReason.PLAY_LIST_IS_EMPTY); 4155500cea7S猫头猫 } 4165500cea7S猫头猫 // 1. 移动网络禁止播放 4175500cea7S猫头猫 if ( 4185500cea7S猫头猫 Network.isCellular() && 4195500cea7S猫头猫 !Config.get('setting.basic.useCelluarNetworkPlay') && 4205500cea7S猫头猫 !LocalMusicSheet.isLocalMusic(musicItem) 4215500cea7S猫头猫 ) { 4225500cea7S猫头猫 await ReactNativeTrackPlayer.reset(); 4235500cea7S猫头猫 throw new Error(PlayFailReason.FORBID_CELLUAR_NETWORK_PLAY); 4245500cea7S猫头猫 } 4255500cea7S猫头猫 4265500cea7S猫头猫 // 2. 如果是当前正在播放的音频 4275500cea7S猫头猫 if (isCurrentMusic(musicItem)) { 4285500cea7S猫头猫 const currentTrack = await ReactNativeTrackPlayer.getTrack(0); 4295500cea7S猫头猫 // 2.1 如果当前有源 4305500cea7S猫头猫 if ( 4315500cea7S猫头猫 currentTrack?.url && 4325500cea7S猫头猫 isSameMediaItem(musicItem, currentTrack as IMusic.IMusicItem) 4335500cea7S猫头猫 ) { 4345500cea7S猫头猫 const currentActiveIndex = 4355500cea7S猫头猫 await ReactNativeTrackPlayer.getActiveTrackIndex(); 4365500cea7S猫头猫 if (currentActiveIndex !== 0) { 4375500cea7S猫头猫 await ReactNativeTrackPlayer.skip(0); 4385500cea7S猫头猫 } 4395500cea7S猫头猫 if (forcePlay) { 4405500cea7S猫头猫 // 2.1.1 强制重新开始 4415500cea7S猫头猫 await ReactNativeTrackPlayer.seekTo(0); 4425500cea7S猫头猫 } else if ( 4435500cea7S猫头猫 (await ReactNativeTrackPlayer.getPlaybackState()).state !== 4445500cea7S猫头猫 State.Playing 4455500cea7S猫头猫 ) { 4465500cea7S猫头猫 // 2.1.2 恢复播放 4475500cea7S猫头猫 await ReactNativeTrackPlayer.play(); 4485500cea7S猫头猫 } 4495500cea7S猫头猫 // 这种情况下,播放队列和当前歌曲都不需要变化 4505500cea7S猫头猫 return; 4515500cea7S猫头猫 } 4525500cea7S猫头猫 // 2.2 其他情况:重新获取源 4535500cea7S猫头猫 } 4545500cea7S猫头猫 4555500cea7S猫头猫 // 3. 如果没有在播放列表中,添加到队尾;同时更新列表状态 4565500cea7S猫头猫 const inPlayList = isInPlayList(musicItem); 4575500cea7S猫头猫 if (!inPlayList) { 4585500cea7S猫头猫 add(musicItem); 4595500cea7S猫头猫 } 4605500cea7S猫头猫 4615500cea7S猫头猫 // 4. 更新列表状态和当前音乐 4625500cea7S猫头猫 setCurrentMusic(musicItem); 4635500cea7S猫头猫 4645500cea7S猫头猫 // 5. 获取音源 4655500cea7S猫头猫 let track: IMusic.IMusicItem; 4665500cea7S猫头猫 4675500cea7S猫头猫 // 5.1 通过插件获取音源 4685500cea7S猫头猫 const plugin = PluginManager.getByName(musicItem.platform); 4695500cea7S猫头猫 // 5.2 获取音质排序 4705500cea7S猫头猫 const qualityOrder = getQualityOrder( 4715500cea7S猫头猫 Config.get('setting.basic.defaultPlayQuality') ?? 'standard', 4725500cea7S猫头猫 Config.get('setting.basic.playQualityOrder') ?? 'asc', 4735500cea7S猫头猫 ); 4745500cea7S猫头猫 // 5.3 插件返回音源 4755500cea7S猫头猫 let source: IPlugin.IMediaSourceResult | null = null; 4765500cea7S猫头猫 for (let quality of qualityOrder) { 4775500cea7S猫头猫 if (isCurrentMusic(musicItem)) { 4785500cea7S猫头猫 source = 4795500cea7S猫头猫 (await plugin?.methods?.getMediaSource( 4805500cea7S猫头猫 musicItem, 4815500cea7S猫头猫 quality, 4825500cea7S猫头猫 )) ?? null; 4835500cea7S猫头猫 // 5.3.1 获取到真实源 4845500cea7S猫头猫 if (source) { 4855500cea7S猫头猫 qualityStore.setValue(quality); 4865500cea7S猫头猫 break; 4875500cea7S猫头猫 } 4885500cea7S猫头猫 } else { 4895500cea7S猫头猫 // 5.3.2 已经切换到其他歌曲了, 4905500cea7S猫头猫 return; 4915500cea7S猫头猫 } 4925500cea7S猫头猫 } 4935500cea7S猫头猫 4945500cea7S猫头猫 if (!isCurrentMusic(musicItem)) { 4955500cea7S猫头猫 return; 4965500cea7S猫头猫 } 4975500cea7S猫头猫 4985500cea7S猫头猫 if (!source) { 4995500cea7S猫头猫 // 5.4 没有返回源 5005500cea7S猫头猫 if (!musicItem.url) { 5015500cea7S猫头猫 throw new Error(PlayFailReason.INVALID_SOURCE); 5025500cea7S猫头猫 } 5035500cea7S猫头猫 source = { 5045500cea7S猫头猫 url: musicItem.url, 5055500cea7S猫头猫 }; 5065500cea7S猫头猫 qualityStore.setValue('standard'); 5075500cea7S猫头猫 } 5085500cea7S猫头猫 5095500cea7S猫头猫 // 6. 特殊类型源 5105500cea7S猫头猫 if (getUrlExt(source.url) === '.m3u8') { 5115500cea7S猫头猫 // @ts-ignore 5125500cea7S猫头猫 source.type = 'hls'; 5135500cea7S猫头猫 } 5145500cea7S猫头猫 // 7. 合并结果 5155500cea7S猫头猫 track = mergeProps(musicItem, source) as IMusic.IMusicItem; 5165500cea7S猫头猫 5175500cea7S猫头猫 // 8. 新增历史记录 5185500cea7S猫头猫 musicHistory.addMusic(musicItem); 5195500cea7S猫头猫 5205500cea7S猫头猫 // 9. 设置音源 5215500cea7S猫头猫 await setTrackSource(track as Track); 5225500cea7S猫头猫 5235500cea7S猫头猫 // 10. 获取补充信息 5245500cea7S猫头猫 let info: Partial<IMusic.IMusicItem> | null = null; 5255500cea7S猫头猫 try { 5265500cea7S猫头猫 info = (await plugin?.methods?.getMusicInfo?.(musicItem)) ?? null; 5275500cea7S猫头猫 } catch {} 5285500cea7S猫头猫 5295500cea7S猫头猫 // 11. 设置补充信息 5305500cea7S猫头猫 if (info && isCurrentMusic(musicItem)) { 5315500cea7S猫头猫 const mergedTrack = mergeProps(track, info); 5325500cea7S猫头猫 currentMusicStore.setValue(mergedTrack as IMusic.IMusicItem); 5335500cea7S猫头猫 await ReactNativeTrackPlayer.updateMetadataForTrack( 5345500cea7S猫头猫 0, 5355500cea7S猫头猫 mergedTrack as TrackMetadataBase, 5365500cea7S猫头猫 ); 5375500cea7S猫头猫 } 5385500cea7S猫头猫 5395500cea7S猫头猫 // 12. 刷新歌词信息 5405500cea7S猫头猫 if ( 5415500cea7S猫头猫 !isSameMediaItem( 5425500cea7S猫头猫 LyricManager.getLyricState()?.lyricParser?.getCurrentMusicItem?.(), 5435500cea7S猫头猫 musicItem, 5445500cea7S猫头猫 ) 5455500cea7S猫头猫 ) { 5465500cea7S猫头猫 DeviceEventEmitter.emit(EDeviceEvents.REFRESH_LYRIC, true); 5475500cea7S猫头猫 } 5485500cea7S猫头猫 } catch (e: any) { 5495500cea7S猫头猫 const message = e?.message; 5505500cea7S猫头猫 if ( 5515500cea7S猫头猫 message === 'The player is not initialized. Call setupPlayer first.' 5525500cea7S猫头猫 ) { 5535500cea7S猫头猫 await ReactNativeTrackPlayer.setupPlayer(); 5545500cea7S猫头猫 play(musicItem, forcePlay); 5555500cea7S猫头猫 } else if (message === PlayFailReason.FORBID_CELLUAR_NETWORK_PLAY) { 556f511aee9S猫头猫 Toast.warn( 557f511aee9S猫头猫 '当前禁止移动网络播放音乐,如需播放请去侧边栏-基本设置中修改', 558f511aee9S猫头猫 ); 5595500cea7S猫头猫 } else if (message === PlayFailReason.INVALID_SOURCE) { 5605500cea7S猫头猫 await failToPlay('无效源'); 5615500cea7S猫头猫 } else if (message === PlayFailReason.PLAY_LIST_IS_EMPTY) { 5625500cea7S猫头猫 // 队列是空的,不应该出现这种情况 5635500cea7S猫头猫 } 5645500cea7S猫头猫 } 5655500cea7S猫头猫}; 5665500cea7S猫头猫 5675500cea7S猫头猫/** 5685500cea7S猫头猫 * 播放音乐,同时替换播放队列 5695500cea7S猫头猫 * @param musicItem 音乐 5705500cea7S猫头猫 * @param newPlayList 替代列表 5715500cea7S猫头猫 */ 5725500cea7S猫头猫const playWithReplacePlayList = async ( 5735500cea7S猫头猫 musicItem: IMusic.IMusicItem, 5745500cea7S猫头猫 newPlayList: IMusic.IMusicItem[], 5755500cea7S猫头猫) => { 5765500cea7S猫头猫 if (newPlayList.length !== 0) { 5775500cea7S猫头猫 const now = Date.now(); 5785500cea7S猫头猫 if (newPlayList.length > maxMusicQueueLength) { 5795500cea7S猫头猫 newPlayList = shrinkPlayListToSize( 5805500cea7S猫头猫 newPlayList, 5815500cea7S猫头猫 newPlayList.findIndex(it => isSameMediaItem(it, musicItem)), 5825500cea7S猫头猫 ); 5835500cea7S猫头猫 } 5845500cea7S猫头猫 const playListItems = newPlayList.map((item, index) => 5855500cea7S猫头猫 produce(item, draft => { 5865500cea7S猫头猫 draft[timeStampSymbol] = now; 5875500cea7S猫头猫 draft[sortIndexSymbol] = index; 5885500cea7S猫头猫 }), 5895500cea7S猫头猫 ); 5905500cea7S猫头猫 setPlayList( 5915500cea7S猫头猫 repeatModeStore.getValue() === MusicRepeatMode.SHUFFLE 5925500cea7S猫头猫 ? shuffle(playListItems) 5935500cea7S猫头猫 : playListItems, 5945500cea7S猫头猫 ); 5955500cea7S猫头猫 await play(musicItem, true); 5965500cea7S猫头猫 } 5975500cea7S猫头猫}; 5985500cea7S猫头猫 5995500cea7S猫头猫const skipToNext = async (reason?: string) => { 6005500cea7S猫头猫 console.log( 6015500cea7S猫头猫 'SkipToNext', 6025500cea7S猫头猫 reason, 6035500cea7S猫头猫 await ReactNativeTrackPlayer.getActiveTrack(), 6045500cea7S猫头猫 ); 6055500cea7S猫头猫 if (isPlayListEmpty()) { 6065500cea7S猫头猫 setCurrentMusic(null); 6075500cea7S猫头猫 return; 6085500cea7S猫头猫 } 6095500cea7S猫头猫 6105500cea7S猫头猫 await play(getPlayListMusicAt(currentIndex + 1), true); 6115500cea7S猫头猫}; 6125500cea7S猫头猫 6135500cea7S猫头猫const skipToPrevious = async () => { 6145500cea7S猫头猫 if (isPlayListEmpty()) { 6155500cea7S猫头猫 setCurrentMusic(null); 6165500cea7S猫头猫 return; 6175500cea7S猫头猫 } 6185500cea7S猫头猫 6195500cea7S猫头猫 await play(getPlayListMusicAt(currentIndex === -1 ? 0 : currentIndex - 1)); 6205500cea7S猫头猫}; 6215500cea7S猫头猫 6225500cea7S猫头猫/** 修改当前播放的音质 */ 6235500cea7S猫头猫const changeQuality = async (newQuality: IMusic.IQualityKey) => { 6245500cea7S猫头猫 // 获取当前的音乐和进度 6255500cea7S猫头猫 if (newQuality === qualityStore.getValue()) { 6265500cea7S猫头猫 return true; 6275500cea7S猫头猫 } 6285500cea7S猫头猫 6295500cea7S猫头猫 // 获取当前歌曲 6305500cea7S猫头猫 const musicItem = currentMusicStore.getValue(); 6315500cea7S猫头猫 if (!musicItem) { 6325500cea7S猫头猫 return false; 6335500cea7S猫头猫 } 6345500cea7S猫头猫 try { 6355500cea7S猫头猫 const progress = await ReactNativeTrackPlayer.getProgress(); 6365500cea7S猫头猫 const plugin = PluginManager.getByMedia(musicItem); 6375500cea7S猫头猫 const newSource = await plugin?.methods?.getMediaSource( 6385500cea7S猫头猫 musicItem, 6395500cea7S猫头猫 newQuality, 6405500cea7S猫头猫 ); 6415500cea7S猫头猫 if (!newSource?.url) { 6425500cea7S猫头猫 throw new Error(PlayFailReason.INVALID_SOURCE); 6435500cea7S猫头猫 } 6445500cea7S猫头猫 if (isCurrentMusic(musicItem)) { 6455500cea7S猫头猫 const playingState = ( 6465500cea7S猫头猫 await ReactNativeTrackPlayer.getPlaybackState() 6475500cea7S猫头猫 ).state; 6485500cea7S猫头猫 await setTrackSource( 6495500cea7S猫头猫 mergeProps(musicItem, newSource) as unknown as Track, 6505500cea7S猫头猫 !musicIsPaused(playingState), 6515500cea7S猫头猫 ); 6525500cea7S猫头猫 6535500cea7S猫头猫 await ReactNativeTrackPlayer.seekTo(progress.position ?? 0); 6545500cea7S猫头猫 qualityStore.setValue(newQuality); 6555500cea7S猫头猫 } 6565500cea7S猫头猫 return true; 6575500cea7S猫头猫 } catch { 6585500cea7S猫头猫 // 修改失败 6595500cea7S猫头猫 return false; 6605500cea7S猫头猫 } 6615500cea7S猫头猫}; 6625500cea7S猫头猫 6635500cea7S猫头猫enum PlayFailReason { 6645500cea7S猫头猫 /** 禁止移动网络播放 */ 6655500cea7S猫头猫 FORBID_CELLUAR_NETWORK_PLAY = 'FORBID_CELLUAR_NETWORK_PLAY', 6665500cea7S猫头猫 /** 播放列表为空 */ 6675500cea7S猫头猫 PLAY_LIST_IS_EMPTY = 'PLAY_LIST_IS_EMPTY', 6685500cea7S猫头猫 /** 无效源 */ 6695500cea7S猫头猫 INVALID_SOURCE = 'INVALID_SOURCE', 6705500cea7S猫头猫 /** 非当前音乐 */ 6715500cea7S猫头猫} 6725500cea7S猫头猫 6735500cea7S猫头猫function useMusicState() { 6745500cea7S猫头猫 const playbackState = usePlaybackState(); 6755500cea7S猫头猫 6765500cea7S猫头猫 return playbackState.state; 6775500cea7S猫头猫} 6785500cea7S猫头猫 679f511aee9S猫头猫function getPreviousMusic() { 680f511aee9S猫头猫 const currentMusicItem = currentMusicStore.getValue(); 681f511aee9S猫头猫 if (!currentMusicItem) { 682f511aee9S猫头猫 return null; 683f511aee9S猫头猫 } 684f511aee9S猫头猫 685f511aee9S猫头猫 return getPlayListMusicAt(currentIndex - 1); 686f511aee9S猫头猫} 687f511aee9S猫头猫 688f511aee9S猫头猫function getNextMusic() { 689f511aee9S猫头猫 const currentMusicItem = currentMusicStore.getValue(); 690f511aee9S猫头猫 if (!currentMusicItem) { 691f511aee9S猫头猫 return null; 692f511aee9S猫头猫 } 693f511aee9S猫头猫 694f511aee9S猫头猫 return getPlayListMusicAt(currentIndex + 1); 695f511aee9S猫头猫} 696f511aee9S猫头猫 6975500cea7S猫头猫const TrackPlayer = { 6985500cea7S猫头猫 setupTrackPlayer, 6995500cea7S猫头猫 usePlayList, 7005500cea7S猫头猫 getPlayList, 7015500cea7S猫头猫 addAll, 7025500cea7S猫头猫 add, 7035500cea7S猫头猫 addNext, 7045500cea7S猫头猫 skipToNext, 7055500cea7S猫头猫 skipToPrevious, 7065500cea7S猫头猫 play, 7075500cea7S猫头猫 playWithReplacePlayList, 7085500cea7S猫头猫 pause, 7095500cea7S猫头猫 remove, 7105500cea7S猫头猫 clear, 7115500cea7S猫头猫 useCurrentMusic: currentMusicStore.useValue, 7125500cea7S猫头猫 getCurrentMusic: currentMusicStore.getValue, 7135500cea7S猫头猫 useRepeatMode: repeatModeStore.useValue, 7145500cea7S猫头猫 getRepeatMode: repeatModeStore.getValue, 7155500cea7S猫头猫 toggleRepeatMode, 7165500cea7S猫头猫 usePlaybackState, 7175500cea7S猫头猫 getProgress: ReactNativeTrackPlayer.getProgress, 7185500cea7S猫头猫 useProgress: useProgress, 7195500cea7S猫头猫 seekTo: ReactNativeTrackPlayer.seekTo, 7205500cea7S猫头猫 changeQuality, 7215500cea7S猫头猫 useCurrentQuality: qualityStore.useValue, 7225500cea7S猫头猫 getCurrentQuality: qualityStore.getValue, 7235500cea7S猫头猫 getRate: ReactNativeTrackPlayer.getRate, 7245500cea7S猫头猫 setRate: ReactNativeTrackPlayer.setRate, 7255500cea7S猫头猫 useMusicState, 7265500cea7S猫头猫 reset: ReactNativeTrackPlayer.reset, 727f511aee9S猫头猫 getPreviousMusic, 728f511aee9S猫头猫 getNextMusic, 7295500cea7S猫头猫}; 7305500cea7S猫头猫 7315500cea7S猫头猫export default TrackPlayer; 7325500cea7S猫头猫export {MusicRepeatMode, State as MusicState}; 733