diff --git a/package.json b/package.json index 6c5699c0..3e4235e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MusicFree", - "version": "0.4.1", + "version": "0.4.2", "private": true, "license": "AGPL", "author": { diff --git a/src/components/musicBar/index.tsx b/src/components/musicBar/index.tsx index 8aebd13c..0145ce0c 100644 --- a/src/components/musicBar/index.tsx +++ b/src/components/musicBar/index.tsx @@ -37,6 +37,12 @@ function CircularPlayBtn() { accessibilityLabel={'播放或暂停歌曲'} name={isPaused ? 'play' : 'pause'} sizeType={'normal'} + hitSlop={{ + top: 10, + left: 10, + right: 10, + bottom: 10, + }} color={colors.musicBarText} onPress={async () => { if (isPaused) { diff --git a/src/constants/pathConst.ts b/src/constants/pathConst.ts index 5d08f512..3c6a9327 100644 --- a/src/constants/pathConst.ts +++ b/src/constants/pathConst.ts @@ -16,6 +16,7 @@ export default { imageCachePath: CachesDirectoryPath + '/image_manager_disk_cache', localLrcPath: `${basePath}/local_lrc/`, lrcCachePath: `${basePath}/cache/lrc/`, + downloadCachePath: `${basePath}/cache/download/`, downloadPath: `${basePath}/download/`, downloadMusicPath: `${basePath}/download/music/`, mmkvPath: `${basePath}/mmkv`, diff --git a/src/core/download.ts b/src/core/download.ts index 8ad87a97..31d3db6f 100644 --- a/src/core/download.ts +++ b/src/core/download.ts @@ -11,7 +11,7 @@ import StateMapper from '@/utils/stateMapper'; import Toast from '@/utils/toast'; import {produce} from 'immer'; import {InteractionManager} from 'react-native'; -import {downloadFile, exists} from 'react-native-fs'; +import {copyFile, downloadFile, exists, unlink} from 'react-native-fs'; import Config from './config'; import LocalMusicSheet from './localMusicSheet'; @@ -25,6 +25,7 @@ import { hideDialog, showDialog, } from '@/components/dialogs/useDialog'; +import {nanoid} from 'nanoid'; /** 队列中的元素 */ interface IDownloadMusicOptions { @@ -68,7 +69,7 @@ const getExtensionName = (url: string) => { }; /** 生成下载文件 */ -const getDownloadPath = (fileName?: string) => { +const getDownloadPath = (fileName: string) => { const dlPath = Config.get('setting.basic.downloadPath') ?? pathConst.downloadMusicPath; if (!dlPath.endsWith('/')) { @@ -77,6 +78,14 @@ const getDownloadPath = (fileName?: string) => { return fileName ? dlPath + fileName : dlPath; }; +const getCacheDownloadPath = (fileName: string) => { + const cachePath = pathConst.downloadCachePath; + if (!cachePath.endsWith('/')) { + return `${cachePath}/${fileName ?? ''}`; + } + return fileName ? cachePath + fileName : cachePath; +}; + /** 从待下载中移除 */ function removeFromPendingQueue(item: IDownloadMusicOptions) { const targetIndex = pendingMusicQueue.findIndex(_ => @@ -222,12 +231,15 @@ async function downloadNext() { extension = 'mp3'; } /** 目标下载地址 */ + const cacheDownloadPath = addFileScheme( + getCacheDownloadPath(`${nanoid()}.${extension}`), + ); const targetDownloadPath = addFileScheme( getDownloadPath(`${nextDownloadItem.filename}.${extension}`), ); const {promise, jobId} = downloadFile({ fromUrl: url ?? '', - toFile: targetDownloadPath, + toFile: cacheDownloadPath, headers: headers, background: true, begin(res) { @@ -256,8 +268,10 @@ async function downloadNext() { if (!folderExists) { await mkdirR(folder); } + try { await promise; + await copyFile(cacheDownloadPath, targetDownloadPath); /** 下载完成 */ LocalMusicSheet.addMusicDraft({ ...musicItem, @@ -301,6 +315,8 @@ async function downloadNext() { targetDownloadPath: targetDownloadPath, }); hasError = true; + } finally { + await unlink(cacheDownloadPath); } removeFromDownloadingQueue(nextDownloadItem); downloadingProgress = produce(downloadingProgress, draft => { diff --git a/src/core/localMusicSheet.ts b/src/core/localMusicSheet.ts index 65ab1acb..59043299 100644 --- a/src/core/localMusicSheet.ts +++ b/src/core/localMusicSheet.ts @@ -126,7 +126,6 @@ function localMediaFilter(filename: string) { let importToken: string | null = null; // 获取本地的文件列表 async function getMusicStats(folderPaths: string[]) { - console.log('GGGG', folderPaths); const _importToken = nanoid(); importToken = _importToken; const musicList: string[] = []; diff --git a/src/core/pluginManager.ts b/src/core/pluginManager.ts index db7530d8..f22abaf2 100644 --- a/src/core/pluginManager.ts +++ b/src/core/pluginManager.ts @@ -3,6 +3,7 @@ import RNFS, { exists, readDir, readFile, + stat, unlink, writeFile, } from 'react-native-fs'; @@ -39,7 +40,6 @@ import CookieManager from '@react-native-cookies/cookies'; import he from 'he'; import Network from './network'; import LocalMusicSheet from './localMusicSheet'; -import {getInfoAsync} from 'expo-file-system'; import Mp3Util from '@/native/mp3Util'; import {PluginMeta} from './pluginMeta'; import {useEffect, useState} from 'react'; @@ -253,15 +253,18 @@ export class Plugin { return true; } } + //#endregion //#region 基于插件类封装的方法,供给APP侧直接调用 /** 有缓存等信息 */ class PluginMethods implements IPlugin.IPluginInstanceMethods { private plugin; + constructor(plugin: Plugin) { this.plugin = plugin; } + /** 搜索 */ async search( query: string, @@ -308,7 +311,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { LocalMusicSheet.isLocalMusic(musicItem), InternalDataType.LOCALPATH, ); - if (localPath && (await getInfoAsync(localPath)).exists) { + if (localPath && (await exists(localPath))) { trace('本地播放', localPath); if (mediaExtra && mediaExtra.localPath !== localPath) { // 修正一下本地数据 @@ -801,6 +804,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { return []; } } + /** 导入单曲 */ async importMusicItem(urlLike: string): Promise { try { @@ -818,6 +822,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { return null; } } + /** 获取榜单 */ async getTopLists(): Promise { try { @@ -831,6 +836,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { return []; } } + /** 获取榜单详情 */ async getTopListDetail( topListItem: IMusic.IMusicSheetItemBase, @@ -879,6 +885,7 @@ class PluginMethods implements IPlugin.IPluginInstanceMethods { }; } } + /** 获取某个tag的推荐歌单 */ async getRecommendSheetsByTag( tagItem: ICommon.IUnique, @@ -1018,18 +1025,19 @@ const localFilePlugin = new Plugin(function () { }, async importMusicItem(urlLike) { let meta: any = {}; + let id: string; + try { meta = await Mp3Util.getBasicMeta(urlLike); - } catch {} - const stat = await getInfoAsync(urlLike, { - md5: true, - }); - let id: string; - if (stat.exists) { - id = stat.md5 || nanoid(); - } else { + const fileStat = await stat(urlLike); + id = + CryptoJs.MD5(fileStat.originalFilepath).toString( + CryptoJs.enc.Hex, + ) || nanoid(); + } catch { id = nanoid(); } + return { id: id, platform: '本地', diff --git a/src/core/trackPlayer/internal/playList.ts b/src/core/trackPlayer/internal/playList.ts index d2821e73..e3281855 100644 --- a/src/core/trackPlayer/internal/playList.ts +++ b/src/core/trackPlayer/internal/playList.ts @@ -10,6 +10,7 @@ let playListIndexMap: Record> = {}; /** * 设置播放队列 * @param newPlayList 新的播放队列 + * @param shouldSave 是否保存到本地 */ export function setPlayList( newPlayList: IMusic.IMusicItem[], diff --git a/src/entry/bootstrap.ts b/src/entry/bootstrap.ts index efb3c072..a0b73c0b 100644 --- a/src/entry/bootstrap.ts +++ b/src/entry/bootstrap.ts @@ -172,6 +172,7 @@ async function setupFolder() { checkAndCreateDir(pathConst.cachePath), checkAndCreateDir(pathConst.pluginPath), checkAndCreateDir(pathConst.lrcCachePath), + checkAndCreateDir(pathConst.downloadCachePath), checkAndCreateDir(pathConst.localLrcPath), checkAndCreateDir(pathConst.downloadPath).then(() => { checkAndCreateDir(pathConst.downloadMusicPath);