From 5353b47372c7f041c41058084167f60418a3c9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=AB=E5=A4=B4=E7=8C=AB?= Date: Tue, 6 Aug 2024 18:00:49 +0800 Subject: [PATCH] feat: remove react native file access --- package.json | 2 +- .../panels/types/musicItemOptions.tsx | 2 +- src/core/localMusicSheet.ts | 61 +++++++++++-------- src/core/pluginManager.ts | 20 +++--- src/pages/fileSelector/fileItem.tsx | 2 + src/utils/fileUtils.ts | 2 +- yarn.lock | 7 +-- 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 1cf8e28f..dc0d6078 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "dayjs": "^1.11.12", "deepmerge": "^4.3.1", "expo": "^51.0.0", + "expo-file-system": "^17.0.1", "expo-splash-screen": "~0.27.5", "he": "^1.2.0", "immer": "^10.1.1", @@ -60,7 +61,6 @@ "react-native-device-info": "^11.1.0", "react-native-document-picker": "^9.3.0", "react-native-fast-image": "^8.6.3", - "react-native-file-access": "^3.1.0", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "^2.18.1", "react-native-get-random-values": "^1.11.0", diff --git a/src/components/panels/types/musicItemOptions.tsx b/src/components/panels/types/musicItemOptions.tsx index c36d0a8f..9613af89 100644 --- a/src/components/panels/types/musicItemOptions.tsx +++ b/src/components/panels/types/musicItemOptions.tsx @@ -267,7 +267,7 @@ export default function MusicItemOptions(props: IMusicItemOptionsProps) { diff --git a/src/core/localMusicSheet.ts b/src/core/localMusicSheet.ts index 1da204e9..aae3d4e9 100644 --- a/src/core/localMusicSheet.ts +++ b/src/core/localMusicSheet.ts @@ -13,8 +13,9 @@ import StateMapper from '@/utils/stateMapper'; import {getStorage, setStorage} from '@/utils/storage'; import {nanoid} from 'nanoid'; import {useEffect, useState} from 'react'; -import {FileStat, FileSystem} from 'react-native-file-access'; import {unlink} from 'react-native-fs'; +import {getInfoAsync, readDirectoryAsync} from 'expo-file-system'; +import {addFileScheme, getFileName} from '@/utils/fileUtils.ts'; let localSheet: IMusic.IMusicItem[] = []; const localSheetStateMapper = new StateMapper(() => localSheet); @@ -28,7 +29,7 @@ export async function setup() { musicItem, InternalDataType.LOCALPATH, ); - if (localPath && (await FileSystem.exists(localPath))) { + if (localPath && (await getInfoAsync(localPath)).exists) { validSheet.push(musicItem); } } @@ -118,8 +119,8 @@ function parseFilename(fn: string): Partial | null { }; } -function localMediaFilter(_: FileStat) { - return supportLocalMediaType.some(ext => _.filename.endsWith(ext)); +function localMediaFilter(filename: string) { + return supportLocalMediaType.some(ext => filename.endsWith(ext)); } let importToken: string | null = null; @@ -127,27 +128,33 @@ let importToken: string | null = null; async function getMusicStats(folderPaths: string[]) { const _importToken = nanoid(); importToken = _importToken; - const musicList: FileStat[] = []; + const musicList: string[] = []; let peek: string | undefined; - let dirFiles: FileStat[] = []; + let dirFiles: string[] = []; while (folderPaths.length !== 0) { if (importToken !== _importToken) { throw new Error('Import Broken'); } peek = folderPaths.shift() as string; try { - dirFiles = await FileSystem.statDir(peek); + dirFiles = await readDirectoryAsync(peek); } catch { dirFiles = []; } - dirFiles.forEach(item => { - if (item.type === 'directory' && !folderPaths.includes(item.path)) { - folderPaths.push(item.path); - } else if (localMediaFilter(item)) { - musicList.push(item); - } - }); + await Promise.all( + dirFiles.map(async fileName => { + const stat = await getInfoAsync(peek + '/' + fileName); + if (!stat.exists) { + return; + } + if (stat.isDirectory && !folderPaths.includes(stat.uri)) { + folderPaths.push(stat.uri); + } else if (localMediaFilter(stat.uri)) { + musicList.push(stat.uri); + } + }), + ); } return {musicList, token: _importToken}; } @@ -159,8 +166,9 @@ function cancelImportLocal() { // 导入本地音乐 const groupNum = 25; async function importLocal(_folderPaths: string[]) { - const folderPaths = [..._folderPaths]; + const folderPaths = [..._folderPaths.map(it => addFileScheme(it))]; const {musicList, token} = await getMusicStats(folderPaths); + console.log('HI!!!', musicList, folderPaths, _folderPaths); if (token !== importToken) { throw new Error('Import Broken'); } @@ -170,36 +178,37 @@ async function importLocal(_folderPaths: string[]) { for (let i = 0; i < groups; ++i) { metas = metas.concat( await mp3Util.getMediaMeta( - musicList - .slice(i * groupNum, (i + 1) * groupNum) - .map(_ => _.path), + musicList.slice(i * groupNum, (i + 1) * groupNum), ), ); } if (token !== importToken) { throw new Error('Import Broken'); } - const musicItems = await Promise.all( - musicList.map(async (musicStat, index) => { + const musicItems: IMusic.IMusicItem[] = await Promise.all( + musicList.map(async (musicPath, index) => { let {platform, id, title, artist} = - parseFilename(musicStat.filename) ?? {}; + parseFilename(getFileName(musicPath, true)) ?? {}; const meta = metas[index]; if (!platform || !id) { platform = '本地'; - id = await FileSystem.hash(musicStat.path, 'MD5'); + const fileInfo = await getInfoAsync(musicPath, { + md5: true, + }); + id = fileInfo.exists ? fileInfo.md5 : nanoid(); } return { id, platform, - title: title ?? meta?.title ?? musicStat.filename, + title: title ?? meta?.title ?? getFileName(musicPath), artist: artist ?? meta?.artist ?? '未知歌手', - duration: parseInt(meta?.duration ?? '0') / 1000, + duration: parseInt(meta?.duration ?? '0', 10) / 1000, album: meta?.album ?? '未知专辑', artwork: '', [internalSerializeKey]: { - localPath: musicStat.path, + localPath: musicPath, }, - }; + } as IMusic.IMusicItem; }), ); if (token !== importToken) { diff --git a/src/core/pluginManager.ts b/src/core/pluginManager.ts index 97bfa2e5..32e0f9d9 100644 --- a/src/core/pluginManager.ts +++ b/src/core/pluginManager.ts @@ -39,7 +39,7 @@ import CookieManager from '@react-native-cookies/cookies'; import he from 'he'; import Network from './network'; import LocalMusicSheet from './localMusicSheet'; -import {FileSystem} from 'react-native-file-access'; +import {getInfoAsync} from 'expo-file-system'; import Mp3Util from '@/native/mp3Util'; import {PluginMeta} from './pluginMeta'; import {useEffect, useState} from 'react'; @@ -308,11 +308,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { LocalMusicSheet.isLocalMusic(musicItem), InternalDataType.LOCALPATH, ); - if ( - localPath && - (localPath.startsWith('content://') || - (await FileSystem.exists(localPath))) - ) { + if (localPath && (await getInfoAsync(localPath)).exists) { trace('本地播放', localPath); if (mediaExtra && mediaExtra.localPath !== localPath) { // 修正一下本地数据 @@ -935,13 +931,21 @@ const localFilePlugin = new Plugin(function () { try { meta = await Mp3Util.getBasicMeta(urlLike); } catch {} - const id = await FileSystem.hash(urlLike, 'MD5'); + const stat = await getInfoAsync(urlLike, { + md5: true, + }); + let id: string; + if (stat.exists) { + id = stat.md5 || nanoid(); + } else { + id = nanoid(); + } return { id: id, platform: '本地', title: meta?.title ?? getFileName(urlLike), artist: meta?.artist ?? '未知歌手', - duration: parseInt(meta?.duration ?? '0') / 1000, + duration: parseInt(meta?.duration ?? '0', 10) / 1000, album: meta?.album ?? '未知专辑', artwork: '', [internalSerializeKey]: { diff --git a/src/pages/fileSelector/fileItem.tsx b/src/pages/fileSelector/fileItem.tsx index 407b9500..afe31493 100644 --- a/src/pages/fileSelector/fileItem.tsx +++ b/src/pages/fileSelector/fileItem.tsx @@ -6,6 +6,7 @@ import useTextColor from '@/hooks/useTextColor'; import Checkbox from '@/components/base/checkbox'; import {TouchableOpacity} from 'react-native-gesture-handler'; import Icon from '@/components/base/icon.tsx'; +import {iconSizeConst} from '@/constants/uiConst.ts'; const ITEM_HEIGHT = rpx(96); @@ -46,6 +47,7 @@ function FileItem(props: IProps) { } color={textColor} style={styles.folderIcon} + size={iconSizeConst.light} />