修复导入歌单时可能会导致歌单数据存储异常的问题(#500)

This commit is contained in:
lyswhut 2024-05-25 19:55:02 +08:00
parent 0d996e2bf3
commit 8210da380a
5 changed files with 65 additions and 31 deletions

View File

@ -5,6 +5,7 @@
### 修复 ### 修复
- 修复重复的数据初始化调用 - 修复重复的数据初始化调用
- 修复导入歌单时可能会导致歌单数据存储异常的问题(#500
### 变更 ### 变更

View File

@ -2,7 +2,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import { log } from '@/utils/log' import { log } from '@/utils/log'
const partKeyPrefix = '@___PART___' const partKeyPrefix = '@___PART___'
const partKeyArrPrefix = '@___PART_A___'
const partKeyPrefixRxp = /^@___PART___/ const partKeyPrefixRxp = /^@___PART___/
const partKeyArrPrefixRxp = /^@___PART_A___/
const keySplit = ',' const keySplit = ','
const limit = 500000 const limit = 500000
@ -15,16 +17,25 @@ const buildData = (key: string, value: any, datas: Array<[string, string]>) => {
const partKeys = [] const partKeys = []
for (let i = 0, len = Math.floor(valueStr.length / limit); i <= len; i++) { for (let i = 0, len = Math.floor(valueStr.length / limit); i <= len; i++) {
let partKey = `${partKeyPrefix}${key}${i}` let partKey = `${partKeyArrPrefix}${key}${i}`
partKeys.push(partKey) partKeys.push(partKey)
datas.push([partKey, valueStr.substring(i * limit, (i + 1) * limit)]) datas.push([partKey, valueStr.substring(i * limit, (i + 1) * limit)])
} }
datas.push([key, `${partKeyPrefix}${partKeys.join(keySplit)}`]) datas.push([key, partKeyArrPrefix + JSON.stringify(partKeys)])
}
const handleGetDataOld = async<T>(partKeys: string): Promise<T> => {
const keys = partKeys.replace(partKeyPrefixRxp, '').split(keySplit)
return AsyncStorage.multiGet(keys).then(datas => {
return JSON.parse(datas.map(data => data[1]).join(''))
})
} }
const handleGetData = async<T>(partKeys: string): Promise<T> => { const handleGetData = async<T>(partKeys: string): Promise<T> => {
const keys = partKeys.replace(partKeyPrefixRxp, '').split(keySplit) if (partKeys.startsWith(partKeyPrefix)) return handleGetDataOld<T>(partKeys)
const keys = JSON.parse(partKeys.replace(partKeyArrPrefixRxp, '')) as string[]
return AsyncStorage.multiGet(keys).then(datas => { return AsyncStorage.multiGet(keys).then(datas => {
return JSON.parse(datas.map(data => data[1]).join('')) return JSON.parse(datas.map(data => data[1]).join(''))
}) })
@ -35,6 +46,7 @@ export const saveData = async(key: string, value: any) => {
buildData(key, value, datas) buildData(key, value, datas)
try { try {
await removeData(key)
await AsyncStorage.multiSet(datas) await AsyncStorage.multiSet(datas)
} catch (e: any) { } catch (e: any) {
// saving error // saving error
@ -52,7 +64,7 @@ export const getData = async<T = unknown>(key: string): Promise<T | null> => {
log.error('storage error[getData]:', key, e.message) log.error('storage error[getData]:', key, e.message)
throw e throw e
} }
if (value && partKeyPrefixRxp.test(value)) { if (value && (partKeyPrefixRxp.test(value) || partKeyArrPrefixRxp.test(value))) {
return handleGetData<T>(value) return handleGetData<T>(value)
} else if (value == null) return value } else if (value == null) return value
return JSON.parse(value) return JSON.parse(value)
@ -67,25 +79,39 @@ export const removeData = async(key: string) => {
log.error('storage error[removeData]:', key, e.message) log.error('storage error[removeData]:', key, e.message)
throw e throw e
} }
if (value && partKeyPrefixRxp.test(value)) { if (value) {
let partKeys = value.replace(partKeyPrefixRxp, '').split(keySplit) if (partKeyPrefixRxp.test(value)) {
partKeys.push(key) let partKeys = value.replace(partKeyPrefixRxp, '').split(keySplit)
try { partKeys.push(key)
await AsyncStorage.multiRemove(partKeys) try {
} catch (e: any) { await AsyncStorage.multiRemove(partKeys)
// remove error } catch (e: any) {
log.error('storage error[removeData]:', key, e.message) // remove error
throw e log.error('storage error[removeData]:', key, e.message)
} throw e
} else { }
try { return
await AsyncStorage.removeItem(key) } else if (partKeyArrPrefixRxp.test(value)) {
} catch (e: any) { let partKeys = JSON.parse(value.replace(partKeyArrPrefixRxp, '')) as string[]
// remove error partKeys.push(key)
log.error('storage error[removeData]:', key, e.message) try {
throw e await AsyncStorage.multiRemove(partKeys)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
return
} }
} }
try {
await AsyncStorage.removeItem(key)
} catch (e: any) {
// remove error
log.error('storage error[removeData]:', key, e.message)
throw e
}
} }
export const getAllKeys = async() => { export const getAllKeys = async() => {
@ -114,7 +140,7 @@ export const getDataMultiple = async<T extends readonly string[]>(keys: T) => {
} }
const promises: Array<Promise<ReadonlyArray<[unknown | null]>>> = [] const promises: Array<Promise<ReadonlyArray<[unknown | null]>>> = []
for (const [, value] of datas) { for (const [, value] of datas) {
if (value && partKeyPrefixRxp.test(value)) { if (value && (partKeyPrefixRxp.test(value) || partKeyArrPrefixRxp.test(value))) {
promises.push(handleGetData(value)) promises.push(handleGetData(value))
} else { } else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
@ -132,6 +158,7 @@ export const saveDataMultiple = async(datas: Array<[string, any]>) => {
buildData(key, value, allData) buildData(key, value, allData)
} }
try { try {
await removeDataMultiple(datas.map(k => k[0]))
await AsyncStorage.multiSet(allData) await AsyncStorage.multiSet(allData)
} catch (e: any) { } catch (e: any) {
// save error // save error
@ -147,8 +174,12 @@ export const removeDataMultiple = async(keys: string[]) => {
let allKeys = [] let allKeys = []
for (const [key, value] of datas) { for (const [key, value] of datas) {
allKeys.push(key) allKeys.push(key)
if (value && partKeyPrefixRxp.test(value)) { if (value) {
allKeys.push(...value.replace(partKeyPrefixRxp, '').split(keySplit)) if (partKeyPrefixRxp.test(value)) {
allKeys.push(...value.replace(partKeyPrefixRxp, '').split(keySplit))
} else if (partKeyArrPrefixRxp.test(value)) {
allKeys.push(...JSON.parse(value.replace(partKeyPrefixRxp, '')) as string[])
}
} }
} }
try { try {

View File

@ -48,7 +48,8 @@ export const handleImport = (path: string, position: number) => {
void readListData(path).then(async listData => { void readListData(path).then(async listData => {
if (listData == null) return if (listData == null) return
void handleImportListPart(listData, position) void handleImportListPart(listData, position)
}).catch(() => { }).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error')) toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
}) })
} }

View File

@ -103,7 +103,8 @@ export const handleImportListPart = async(listData: LX.ConfigFile.MyListInfoPart
position: Math.max(position, -1), position: Math.max(position, -1),
}).then(() => { }).then(() => {
toast(global.i18n.t('setting_backup_part_import_list_tip_success')) toast(global.i18n.t('setting_backup_part_import_list_tip_success'))
}).catch(() => { }).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error')) toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
}) })
} }
@ -160,7 +161,8 @@ export const handleImportList = (path: string) => {
void importPlayList(path).then((skipTip) => { void importPlayList(path).then((skipTip) => {
if (skipTip) return if (skipTip) return
toast(global.i18n.t('setting_backup_part_import_list_tip_success')) toast(global.i18n.t('setting_backup_part_import_list_tip_success'))
}).catch(() => { }).catch((err) => {
log.error(err)
toast(global.i18n.t('setting_backup_part_import_list_tip_error')) toast(global.i18n.t('setting_backup_part_import_list_tip_error'))
}) })
} }

View File

@ -37,7 +37,7 @@ export const clearLogs = async() => {
export const log = { export const log = {
info(...msgs: any[]) { info(...msgs: any[]) {
// console.info(...msgs) // console.info(...msgs)
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ') const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
if (msg.startsWith('%c')) return if (msg.startsWith('%c')) return
const time = new Date().toLocaleString() const time = new Date().toLocaleString()
if (logTools.tempLog) { if (logTools.tempLog) {
@ -46,15 +46,14 @@ export const log = {
}, },
warn(...msgs: any[]) { warn(...msgs: any[]) {
// console.warn(...msgs) // console.warn(...msgs)
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ') const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
const time = new Date().toLocaleString() const time = new Date().toLocaleString()
if (logTools.tempLog) { if (logTools.tempLog) {
logTools.tempLog.push({ type: 'WARN', time, text: msg }) logTools.tempLog.push({ type: 'WARN', time, text: msg })
} else logTools.writeLog(`${time} WARN ${msg}`) } else logTools.writeLog(`${time} WARN ${msg}`)
}, },
error(...msgs: any[]) { error(...msgs: any[]) {
// console.error...(msgs) const msg = msgs.map(m => typeof m == 'string' ? m : m instanceof Error ? m.stack ?? m.message : JSON.stringify(m)).join(' ')
const msg = msgs.map(m => typeof m == 'string' ? m : JSON.stringify(m)).join(' ')
const time = new Date().toLocaleString() const time = new Date().toLocaleString()
if (logTools.tempLog) { if (logTools.tempLog) {
logTools.tempLog.push({ type: 'ERROR', time, text: msg }) logTools.tempLog.push({ type: 'ERROR', time, text: msg })