mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
152 lines
4.9 KiB
JavaScript
152 lines
4.9 KiB
JavaScript
import { parseFile } from "music-metadata";
|
||
import fs from "fs/promises";
|
||
import path from "path";
|
||
|
||
/**
|
||
* 从指定文件夹中递归读取音乐文件,并将它们以数组形式返回
|
||
* @param {string} directoryPath - 要读取的文件夹路径
|
||
* @param {number} fileLimit - 返回的音乐文件数量限制
|
||
* @returns {Array} - 包含音乐文件信息的数组
|
||
*/
|
||
export const readDirAsync = async (directoryPath, fileLimit = 5000) => {
|
||
const result = [];
|
||
// 递归读取文件夹中的项目
|
||
const readItem = async (item) => {
|
||
const itemPath = path.join(directoryPath, item);
|
||
const stats = await fs.stat(itemPath);
|
||
// 若为音频文件
|
||
if (stats.isFile() && isAudioFile(itemPath)) {
|
||
try {
|
||
const { common, format } = await parseFile(itemPath);
|
||
// 音乐文件信息
|
||
const fileInfo = {
|
||
id: generateId(itemPath),
|
||
name: common.title,
|
||
path: itemPath,
|
||
size: (stats.size / (1024 * 1024)).toFixed(2),
|
||
time: stats.mtime?.getTime(),
|
||
artists: common.artists?.[0],
|
||
album: common.album,
|
||
alia: common.comment?.[0],
|
||
duration: formatDuration(format.duration),
|
||
};
|
||
result.push(fileInfo);
|
||
} catch (error) {
|
||
console.error("解析音乐文件元数据时出错:", error);
|
||
}
|
||
}
|
||
// 若为文件夹
|
||
if (stats.isDirectory()) {
|
||
// 读取子文件夹中的项目
|
||
const subItems = await fs.readdir(itemPath);
|
||
for (const subItem of subItems) {
|
||
await readItem(path.join(item, subItem));
|
||
}
|
||
}
|
||
};
|
||
// 从根目录开始读取
|
||
await readItem("");
|
||
// 返回不超过上限的音乐文件列表
|
||
return result.slice(0, fileLimit);
|
||
};
|
||
|
||
/**
|
||
* 递归地读取文件夹内容,包括文件和子文件夹的信息
|
||
* @param {string} directoryPath - 要读取的文件夹路径
|
||
* @param {number} depth - 递归深度(默认为 -1,无限递归)
|
||
* @param {number} fileLimit - 文件总数
|
||
* @returns {Promise<Array>} 包含文件和子文件夹信息的树形数组
|
||
*/
|
||
export const readDirTreeAsync = async (directoryPath, depth = -1, fileLimit = 5000) => {
|
||
const result = [];
|
||
|
||
const readItem = async (item) => {
|
||
const itemPath = path.join(directoryPath, item);
|
||
const stats = await fs.stat(itemPath);
|
||
|
||
const fileInfo = {
|
||
id: generateId(item),
|
||
name: item,
|
||
path: itemPath,
|
||
type: stats.isFile() ? "song" : "dir",
|
||
size: (stats.size / (1024 * 1024)).toFixed(2), // 文件大小
|
||
modified: stats.mtime, // 修改日期
|
||
};
|
||
|
||
if (stats.isFile() && isAudioFile(itemPath)) {
|
||
try {
|
||
const { common, format } = await parseFile(itemPath);
|
||
fileInfo.metadata = {
|
||
name: common.title,
|
||
artists: common.artists,
|
||
album: common.album,
|
||
date: common.date,
|
||
alia: common.comment?.[0],
|
||
year: common.year,
|
||
duration: formatDuration(format.duration),
|
||
};
|
||
} catch (error) {
|
||
console.error("解析音乐文件元数据时出错:", error);
|
||
}
|
||
}
|
||
|
||
if (stats.isDirectory() && (depth === -1 || depth > 0)) {
|
||
// 如果是文件夹且未达到递归深度限制,且文件数量未达到上限,则递归读取文件夹内容
|
||
if (fileInfo.type === "dir" && result.length < fileLimit) {
|
||
fileInfo.children = await readDirAsync(
|
||
itemPath,
|
||
depth === -1 ? -1 : depth - 1,
|
||
fileLimit - result.length,
|
||
);
|
||
}
|
||
}
|
||
|
||
result.push(fileInfo);
|
||
};
|
||
|
||
const items = await fs.readdir(directoryPath);
|
||
await Promise.all(items.map(readItem));
|
||
|
||
return result.slice(0, fileLimit); // 返回不超过上限的文件列表
|
||
};
|
||
|
||
/**
|
||
* 歌曲时长时间戳转换
|
||
* @param {number} mss 毫秒数
|
||
* @returns {string} 格式为 "mm:ss" 的字符串
|
||
*/
|
||
const formatDuration = (seconds) => {
|
||
const minutes = Math.floor(seconds / 60);
|
||
const remainingSeconds = Math.round(seconds % 60);
|
||
const formattedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
||
const formattedSeconds = remainingSeconds < 10 ? `0${remainingSeconds}` : `${remainingSeconds}`;
|
||
return `${formattedMinutes}:${formattedSeconds}`;
|
||
};
|
||
|
||
/**
|
||
* 判断文件是否为音频文件
|
||
* @param {string} filePath - 文件路径
|
||
* @returns {boolean} - 是否为音频文件
|
||
*/
|
||
const isAudioFile = (filePath) => {
|
||
const audioExtensions = [".flac", ".mp3"];
|
||
const extension = path.extname(filePath).toLowerCase();
|
||
return audioExtensions.includes(extension);
|
||
};
|
||
|
||
/**
|
||
* 从文件名生成数字 ID
|
||
* @param {string} fileName - 文件名
|
||
* @returns {number} - 生成的数字ID
|
||
*/
|
||
const generateId = (fileName) => {
|
||
// 将文件名转换为哈希值
|
||
let hash = 0;
|
||
for (let i = 0; i < fileName.length; i++) {
|
||
hash = (hash << 5) - hash + fileName.charCodeAt(i);
|
||
}
|
||
// 将哈希值转换为正整数
|
||
const numericId = Math.abs(hash % 10000000000);
|
||
return numericId;
|
||
};
|