仰望星辰工作室

starmusic-pc

starmusic-pc/ frontend/src/common/utils.js 3.9 KB · 129 行 原始文件
1/**
2 * 通用工具函数(迁移自 lx-music-mobile/src/utils)
3 */
4
5/** 随机整数 [min, max) */
6export const getRandom = (min, max) => Math.floor(Math.random() * (max - min)) + min
7
8/** 数字补零 */
9export const numFix = (n) => (n < 10 ? '0' + n : String(n))
10
11/** 秒数格式化为 m:ss */
12export const formatPlayTime = (time) => {
13 const m = Math.trunc(time / 60)
14 const s = Math.trunc(time % 60)
15 return m == 0 && s == 0 ? '--/--' : numFix(m) + ':' + numFix(s)
16}
17
18/** 秒数格式化为 mm:ss */
19export const formatPlayTime2 = (time) => {
20 const m = Math.trunc(time / 60)
21 const s = Math.trunc(time % 60)
22 return numFix(m) + ':' + numFix(s)
23}
24
25/** 是否 http url */
26export const isUrl = (path) => /https?:\/\//.test(path)
27
28/** HTML 实体解码(替代 he 库,覆盖常用实体) */
29const entityMap = {
30 amp: '&',
31 lt: '<',
32 gt: '>',
33 quot: '"',
34 apos: "'",
35 nbsp: ' ',
36 copy: '©',
37 reg: '®',
38 trade: '™',
39}
40export const decodeName = (str = '') => {
41 if (!str) return ''
42 return String(str)
43 .replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num)))
44 .replace(/&#x([0-9a-f]+);/gi, (_, num) => String.fromCharCode(parseInt(num, 16)))
45 .replace(/&([a-z]+);/gi, (match, name) => (entityMap[name.toLowerCase()] != null ? entityMap[name.toLowerCase()] : match))
46}
47
48/** 格式化歌手:& 换成 、 */
49export const formatSinger = (rawData) => String(rawData || '').replace(/&/g, '、')
50
51/** 防抖 */
52export const debounce = (func, wait = 200) => {
53 let timeout = null
54 return (...args) => {
55 if (timeout) clearTimeout(timeout)
56 timeout = setTimeout(() => {
57 timeout = null
58 func(...args)
59 }, wait)
60 }
61}
62
63/** 节流 */
64export const throttle = (func, wait = 200) => {
65 let last = 0
66 return (...args) => {
67 const now = Date.now()
68 if (now - last >= wait) {
69 last = now
70 func(...args)
71 }
72 }
73}
74
75/** 格式化播放量 */
76export const formatPlayCount = (num) => {
77 num = Number(num) || 0
78 if (num > 100000000) return `${Math.trunc(num / 10000000) / 10}亿`
79 if (num > 10000) return `${Math.trunc(num / 1000) / 10}万`
80 return String(num)
81}
82
83/** 字节数格式化 */
84export const sizeFormate = (size) => {
85 if (!size) return '0 B'
86 const units = ['B', 'KB', 'MB', 'GB', 'TB']
87 const number = Math.floor(Math.log(size) / Math.log(1024))
88 return `${(size / Math.pow(1024, Math.floor(number))).toFixed(2)} ${units[number]}`
89}
90
91/**
92 * 解析用户源脚本头部信息
93 * @param {string} scriptInfo 头部注释块
94 */
95export const matchScriptInfo = (scriptInfo) => {
96 const infoArr = scriptInfo.split(/\r?\n/)
97 const rxp = /^\s?\*\s?@(\w+)\s(.+)$/
98 const infos = {}
99 for (const info of infoArr) {
100 const result = rxp.exec(info)
101 if (!result) continue
102 const key = result[1]
103 infos[key] = result[2].trim()
104 }
105 return infos
106}
107
108/** 生成音乐信息 id(模仿原项目 hash) */
109export const createMusicInfoId = (musicInfo) => {
110 if (musicInfo.source == 'local') return `local_${musicInfo.name}_${musicInfo.singer}`
111 const key = musicInfo.songmid || musicInfo.songId || musicInfo.id
112 if (key) return `${musicInfo.source}_${key}`
113 // 部分歌曲缺少平台 id(如跨源匹配结果),此时按 源+歌名+歌手 生成稳定 id,
114 // 避免所有无 id 歌曲共享 "source_undefined" 导致删除/播放/批量操作互相联动
115 return `${musicInfo.source}_${musicInfo.name || 'unknown'}_${musicInfo.singer || ''}`
116}
117
118/**
119 * 时间戳归一化为毫秒
120 *
121 * 服务端(account.php / api.php)返回的是**秒级**时间戳(PHP time() 语义),
122 * 而 JS 的 Date 与时间差运算都按毫秒。数量级判定:小于 1e11 视为秒并 ×1000
123 * (1e11 毫秒≈1973 年,1e11 秒≈5138 年,区间不会重叠)。
124 * 字符串(如 "2026-09-25 10:00:00")原样返回,由调用方自行解析。
125 */
126export const normalizeTimestamp = (ts) => {
127 if (typeof ts !== 'number' || !Number.isFinite(ts)) return ts
128 return ts < 1e11 ? ts * 1000 : ts
129}