xref: /MusicFree/src/core/localMusicSheet.ts (revision 0e4173cd50b15efbe5ce9954996c3697547338df)
1*0e4173cdS猫头猫import {internalSerializeKey, StorageKeys} from '@/constants/commonConst';
2afb5c234S猫头猫import mp3Util, {IBasicMeta} from '@/native/mp3Util';
3afb5c234S猫头猫import {isSameMediaItem} from '@/utils/mediaItem';
4afb5c234S猫头猫import StateMapper from '@/utils/stateMapper';
5afb5c234S猫头猫import {getStorage, setStorage} from '@/utils/storage';
6*0e4173cdS猫头猫import {useEffect, useState} from 'react';
7afb5c234S猫头猫import {FileSystem} from 'react-native-file-access';
8afb5c234S猫头猫
9afb5c234S猫头猫let localSheet: IMusic.IMusicItem[] = [];
10afb5c234S猫头猫const localSheetStateMapper = new StateMapper(() => localSheet);
11afb5c234S猫头猫
12afb5c234S猫头猫export async function setup() {
13afb5c234S猫头猫    const sheets = await getStorage(StorageKeys.LocalMusicSheet);
14afb5c234S猫头猫    if (sheets) {
15afb5c234S猫头猫        localSheet = sheets;
16afb5c234S猫头猫    } else {
17afb5c234S猫头猫        await setStorage(StorageKeys.LocalMusicSheet, []);
18afb5c234S猫头猫    }
19afb5c234S猫头猫    localSheetStateMapper.notify();
20afb5c234S猫头猫}
21afb5c234S猫头猫
22afb5c234S猫头猫export async function addMusic(
23afb5c234S猫头猫    musicItem: IMusic.IMusicItem | IMusic.IMusicItem[],
24afb5c234S猫头猫) {
25afb5c234S猫头猫    if (!Array.isArray(musicItem)) {
26afb5c234S猫头猫        musicItem = [musicItem];
27afb5c234S猫头猫    }
28afb5c234S猫头猫    let newSheet = [...localSheet];
29afb5c234S猫头猫    musicItem.forEach(mi => {
30afb5c234S猫头猫        if (localSheet.findIndex(_ => isSameMediaItem(mi, _)) === -1) {
31afb5c234S猫头猫            newSheet.push(mi);
32afb5c234S猫头猫        }
33afb5c234S猫头猫    });
34afb5c234S猫头猫    await setStorage(StorageKeys.LocalMusicSheet, newSheet);
35afb5c234S猫头猫    localSheet = newSheet;
36afb5c234S猫头猫    localSheetStateMapper.notify();
37afb5c234S猫头猫}
38afb5c234S猫头猫
39afb5c234S猫头猫export async function removeMusic(
40afb5c234S猫头猫    musicItem: IMusic.IMusicItem,
41afb5c234S猫头猫    deleteOriginalFile = false,
42afb5c234S猫头猫) {
43afb5c234S猫头猫    const idx = localSheet.findIndex(_ => isSameMediaItem(_, musicItem));
44afb5c234S猫头猫    let newSheet = [...localSheet];
45afb5c234S猫头猫    if (idx !== -1) {
46afb5c234S猫头猫        newSheet.splice(idx, 1);
47*0e4173cdS猫头猫        if (deleteOriginalFile && musicItem[internalSerializeKey]?.localPath) {
48*0e4173cdS猫头猫            await FileSystem.unlink(musicItem[internalSerializeKey].localPath);
49afb5c234S猫头猫        }
50afb5c234S猫头猫    }
51afb5c234S猫头猫    localSheet = newSheet;
52afb5c234S猫头猫    localSheetStateMapper.notify();
53afb5c234S猫头猫}
54afb5c234S猫头猫
55afb5c234S猫头猫function parseFilename(fn: string): Partial<IMusic.IMusicItem> | null {
56afb5c234S猫头猫    const data = fn.slice(0, fn.lastIndexOf('.')).split('@');
57afb5c234S猫头猫    const [platform, id, title, artist] = data;
58afb5c234S猫头猫    if (!platform || !id) {
59afb5c234S猫头猫        return null;
60afb5c234S猫头猫    }
61afb5c234S猫头猫    return {
62afb5c234S猫头猫        id,
63afb5c234S猫头猫        platform,
64afb5c234S猫头猫        title,
65afb5c234S猫头猫        artist,
66afb5c234S猫头猫    };
67afb5c234S猫头猫}
68afb5c234S猫头猫
69*0e4173cdS猫头猫/** 从文件夹导入 */
70*0e4173cdS猫头猫async function importFolder(folderPath: string) {
71afb5c234S猫头猫    const dirFiles = await FileSystem.statDir(folderPath);
72afb5c234S猫头猫    const musicFiles = dirFiles.filter(
73afb5c234S猫头猫        _ => _.type === 'file' && _.filename.endsWith('.mp3'),
74afb5c234S猫头猫    );
75afb5c234S猫头猫
76afb5c234S猫头猫    const musicItems: IMusic.IMusicItem[] = await Promise.all(
77afb5c234S猫头猫        musicFiles.map(async mf => {
78afb5c234S猫头猫            let {platform, id, title, artist} =
79afb5c234S猫头猫                parseFilename(mf.filename) ?? {};
80afb5c234S猫头猫
81afb5c234S猫头猫            let meta: IBasicMeta | null;
82afb5c234S猫头猫            try {
83afb5c234S猫头猫                meta = await mp3Util.getBasicMeta(mf.path);
84afb5c234S猫头猫            } catch {
85afb5c234S猫头猫                meta = null;
86afb5c234S猫头猫            }
87afb5c234S猫头猫            if (!platform || !id) {
88afb5c234S猫头猫                platform = '本地';
89afb5c234S猫头猫                id = await FileSystem.hash(mf.path, 'MD5');
90afb5c234S猫头猫            }
91afb5c234S猫头猫            return {
92afb5c234S猫头猫                id,
93afb5c234S猫头猫                platform,
94afb5c234S猫头猫                title: title ?? meta?.title ?? '未知名称',
95afb5c234S猫头猫                artist: artist ?? meta?.artist ?? '未知歌手',
96afb5c234S猫头猫                duration: parseInt(meta?.duration ?? '0') / 1000,
97afb5c234S猫头猫                album: meta?.album ?? '',
98afb5c234S猫头猫                artwork: '',
99*0e4173cdS猫头猫                [internalSerializeKey]: {
100*0e4173cdS猫头猫                    localPath: mf.path,
101afb5c234S猫头猫                },
102afb5c234S猫头猫            };
103afb5c234S猫头猫        }),
104afb5c234S猫头猫    );
105afb5c234S猫头猫    addMusic(musicItems);
106afb5c234S猫头猫}
107*0e4173cdS猫头猫
108*0e4173cdS猫头猫/** 是否为本地音乐 */
109*0e4173cdS猫头猫function isLocalMusic(
110*0e4173cdS猫头猫    musicItem: ICommon.IMediaBase | null,
111*0e4173cdS猫头猫): IMusic.IMusicItem | undefined {
112*0e4173cdS猫头猫    return musicItem
113*0e4173cdS猫头猫        ? localSheet.find(_ => isSameMediaItem(_, musicItem))
114*0e4173cdS猫头猫        : undefined;
115*0e4173cdS猫头猫}
116*0e4173cdS猫头猫
117*0e4173cdS猫头猫/** 状态-是否为本地音乐 */
118*0e4173cdS猫头猫function useIsLocal(musicItem: IMusic.IMusicItem | null) {
119*0e4173cdS猫头猫    const localMusicState = localSheetStateMapper.useMappedState();
120*0e4173cdS猫头猫    const [isLocal, setIsLocal] = useState<boolean>(!!isLocalMusic(musicItem));
121*0e4173cdS猫头猫    useEffect(() => {
122*0e4173cdS猫头猫        if (!musicItem) {
123*0e4173cdS猫头猫            setIsLocal(false);
124*0e4173cdS猫头猫        } else {
125*0e4173cdS猫头猫            setIsLocal(!!isLocalMusic(musicItem));
126*0e4173cdS猫头猫        }
127*0e4173cdS猫头猫    }, [localMusicState, musicItem]);
128*0e4173cdS猫头猫    return isLocal;
129*0e4173cdS猫头猫}
130*0e4173cdS猫头猫
131*0e4173cdS猫头猫const LocalMusicSheet = {
132*0e4173cdS猫头猫    setup,
133*0e4173cdS猫头猫    addMusic,
134*0e4173cdS猫头猫    removeMusic,
135*0e4173cdS猫头猫    importFolder,
136*0e4173cdS猫头猫    isLocalMusic,
137*0e4173cdS猫头猫    useIsLocal,
138*0e4173cdS猫头猫    useMusicList: localSheetStateMapper.useMappedState,
139*0e4173cdS猫头猫};
140*0e4173cdS猫头猫
141*0e4173cdS猫头猫export default LocalMusicSheet;
142