starmusic-pc
1/**
2 * 数据存储层(迁移自 lx-music-mobile/src/utils/data.ts)
3 * 使用 uni.setStorageSync / uni.getStorageSync
4 */
5import { STORAGE_KEYS, DEFAULT_SETTING } from './constants.js'
6import { matchScriptInfo } from './utils.js'
7import event from './event.js'
8
9/** 保存数据 */
10export const saveData = (key, value) => {
11 try {
12 uni.setStorageSync(key, value)
13 } catch (err) {
14 console.error('[saveData]', key, err)
15 }
16}
17
18/** 读取数据 */
19export const getData = (key, defaultValue = null) => {
20 try {
21 const value = uni.getStorageSync(key)
22 if (value === '' || value === null || value === undefined) return defaultValue
23 return value
24 } catch (err) {
25 return defaultValue
26 }
27}
28
29/** 删除数据 */
30export const removeData = (key) => {
31 try {
32 uni.removeStorageSync(key)
33 } catch (err) {
34 console.error('[removeData]', key, err)
35 }
36}
37
38// ============ 设置 ============
39
40export const getSetting = () => {
41 const setting = getData(STORAGE_KEYS.SETTING, {})
42 return { ...DEFAULT_SETTING, ...setting }
43}
44
45export const saveSetting = (setting) => {
46 const current = getSetting()
47 const newSetting = { ...current, ...setting }
48 saveData(STORAGE_KEYS.SETTING, newSetting)
49 return newSetting
50}
51
52// ============ 用户自定义源 ============
53
54/** 获取用户源列表 */
55export const getUserApiList = () => {
56 const list = getData(STORAGE_KEYS.USER_API_LIST, [])
57 // 清理可能误存到列表中的脚本
58 let updated = false
59 for (const info of list) {
60 if (info.script != null) {
61 delete info.script
62 updated = true
63 }
64 }
65 if (updated) saveData(STORAGE_KEYS.USER_API_LIST, list)
66 return [...list]
67}
68
69/** 获取用户源脚本 */
70export const getUserApiScript = (id) => {
71 return getData(`${STORAGE_KEYS.USER_API_SCRIPT_PREFIX}${id}`, '')
72}
73
74/** 解析脚本头部注释 */
75const parseScriptHeader = (script) => {
76 const result = /^\/\*[\S|\s]+?\*\//.exec(script)
77 if (!result) return null
78 const infos = matchScriptInfo(result[0])
79 const INFO_LIMITS = {
80 name: 24,
81 description: 36,
82 author: 56,
83 homepage: 1024,
84 version: 36,
85 }
86 const finalInfo = {}
87 for (const [key, len] of Object.entries(INFO_LIMITS)) {
88 let value = infos[key] || ''
89 if (value.length > len) value = value.substring(0, len) + '...'
90 finalInfo[key] = value
91 }
92 return finalInfo
93}
94
95/** 添加用户源 */
96export const addUserApi = (script) => {
97 const scriptInfo = parseScriptHeader(script)
98 if (!scriptInfo) throw new Error('脚本格式错误:缺少头部注释 @name 等')
99 scriptInfo.name ||= `用户源_${Date.now()}`
100 const apiInfo = {
101 id: `user_api_${Math.random().toString().substring(2, 5)}_${Date.now()}`,
102 ...scriptInfo,
103 allowShowUpdateAlert: true,
104 }
105 const list = getUserApiList()
106 list.push(apiInfo)
107 saveData(STORAGE_KEYS.USER_API_LIST, list)
108 saveData(`${STORAGE_KEYS.USER_API_SCRIPT_PREFIX}${apiInfo.id}`, script)
109 // 通知外部(如自动备份)音源列表已变化
110 event.emit('userApiUpdated')
111 return apiInfo
112}
113
114/**
115 * 原地更新已有音源的脚本与头部信息(保持 id 不变,避免产生重复音源)
116 * @param {string} id 音源 id(user_api_xxx)
117 * @param {string} script 新脚本文本
118 */
119export const updateUserApiScript = (id, script) => {
120 const scriptInfo = parseScriptHeader(script)
121 // 解析不出头部说明内容不是合法音源脚本,直接抛错,避免写入垃圾
122 if (!scriptInfo) throw new Error('脚本格式错误:缺少头部注释 @name 等')
123 const list = getUserApiList()
124 const target = list.find((api) => api.id === id)
125 if (!target) throw new Error('音源不存在:' + id)
126 // 新值非空才覆盖;name 保留原值,替换后用户仍能一眼认出这个源
127 if (scriptInfo.author) target.author = scriptInfo.author
128 if (scriptInfo.version) target.version = scriptInfo.version
129 if (scriptInfo.description) target.description = scriptInfo.description
130 if (scriptInfo.homepage) target.homepage = scriptInfo.homepage
131 saveData(STORAGE_KEYS.USER_API_LIST, list)
132 saveData(`${STORAGE_KEYS.USER_API_SCRIPT_PREFIX}${id}`, script)
133 // 通知外部(如自动备份)音源列表已变化
134 event.emit('userApiUpdated')
135 return { ...target }
136}
137
138/** 删除用户源 */
139export const removeUserApi = (ids) => {
140 let list = getUserApiList()
141 const removeIds = new Set(ids)
142 list = list.filter((api) => !removeIds.has(api.id))
143 saveData(STORAGE_KEYS.USER_API_LIST, list)
144 for (const id of ids) {
145 removeData(`${STORAGE_KEYS.USER_API_SCRIPT_PREFIX}${id}`)
146 }
147 // 通知外部(如自动备份)音源列表已变化
148 event.emit('userApiUpdated')
149 return [...list]
150}
151
152/** 设置是否允许显示更新提醒 */
153export const setUserApiAllowShowUpdateAlert = (id, enable) => {
154 const list = getUserApiList()
155 const target = list.find((api) => api.id == id)
156 if (!target) return
157 target.allowShowUpdateAlert = enable
158 saveData(STORAGE_KEYS.USER_API_LIST, list)
159}
160
161// ============ 搜索历史 ============
162
163export const getSearchHistory = () => getData(STORAGE_KEYS.SEARCH_HISTORY, [])
164
165export const saveSearchHistory = (list) => saveData(STORAGE_KEYS.SEARCH_HISTORY, list)
166
167// ============ 播放列表 ============
168
169export const getListById = (listId) => getData(`${STORAGE_KEYS.DEFAULT_LIST}__${listId}`, [])
170
171export const saveListById = (listId, list) => saveData(`${STORAGE_KEYS.DEFAULT_LIST}__${listId}`, list)