新增将我的列表保存为TXT、CSV格式
This commit is contained in:
@@ -104,7 +104,7 @@ export default {
|
||||
message: Buffer.from('e69cace8bdafe4bbb6e5ae8ce585a8e5858de8b4b9e4b894e5bc80e6ba90efbc8ce5a682e69e9ce4bda0e698afe88ab1e992b1e8b4ade4b9b0e79a84efbc8ce8afb7e79bb4e68ea5e7bb99e5b7aee8af84efbc810a0a5468697320736f667477617265206973206672656520616e64206f70656e20736f757263652e', 'hex').toString(),
|
||||
confirmButtonText: Buffer.from('e5a5bde79a8420284f4b29', 'hex').toString(),
|
||||
})
|
||||
}, 2000)
|
||||
}, 2e3)
|
||||
},
|
||||
handleClose(isExit) {
|
||||
if (isExit) return rendererSend(NAMES.mainWindow.close, true)
|
||||
@@ -116,7 +116,7 @@ export default {
|
||||
startTimeout() {
|
||||
setTimeout(() => {
|
||||
if (--this.time > 0) this.startTimeout()
|
||||
}, 1000)
|
||||
}, 1e3)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,50 +11,50 @@ const defaultOptions = {
|
||||
confirmButtonText: '',
|
||||
}
|
||||
|
||||
const dialog = {
|
||||
install(Vue, options) {
|
||||
const dialog = function(options) {
|
||||
const { message, showCancel, cancelButtonText, confirmButtonText } =
|
||||
Object.assign({}, defaultOptions, typeof options == 'string' ? { message: options } : options || {})
|
||||
return new Promise((resolve, reject) => {
|
||||
let app = createApp(Dialog, {
|
||||
afterLeave() {
|
||||
app?.unmount()
|
||||
app = null
|
||||
},
|
||||
}).use(i18n).use(store)
|
||||
export const dialog = function(options) {
|
||||
const { message, showCancel, cancelButtonText, confirmButtonText } =
|
||||
Object.assign({}, defaultOptions, typeof options == 'string' ? { message: options } : options || {})
|
||||
return new Promise((resolve, reject) => {
|
||||
let app = createApp(Dialog, {
|
||||
afterLeave() {
|
||||
app?.unmount()
|
||||
app = null
|
||||
},
|
||||
}).use(i18n).use(store)
|
||||
|
||||
let instance = app.mount(document.createElement('div'))
|
||||
let instance = app.mount(document.createElement('div'))
|
||||
|
||||
// 属性设置
|
||||
instance.visible = true
|
||||
instance.message = message
|
||||
instance.showCancel = showCancel
|
||||
instance.cancelButtonText = cancelButtonText
|
||||
instance.confirmButtonText = confirmButtonText
|
||||
// 属性设置
|
||||
instance.visible = true
|
||||
instance.message = message
|
||||
instance.showCancel = showCancel
|
||||
instance.cancelButtonText = cancelButtonText
|
||||
instance.confirmButtonText = confirmButtonText
|
||||
|
||||
// 挂载
|
||||
document.getElementById('container').appendChild(instance.$el)
|
||||
// 挂载
|
||||
document.getElementById('container').appendChild(instance.$el)
|
||||
|
||||
instance.handleCancel = () => {
|
||||
instance.visible = false
|
||||
resolve(false)
|
||||
}
|
||||
|
||||
instance.handleComfirm = () => {
|
||||
instance.visible = false
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
instance.handleCancel = () => {
|
||||
instance.visible = false
|
||||
resolve(false)
|
||||
}
|
||||
dialog.confirm = options => dialog(
|
||||
typeof options == 'string'
|
||||
? { message: options, showCancel: true }
|
||||
: { ...options, showCancel: true },
|
||||
)
|
||||
|
||||
instance.handleComfirm = () => {
|
||||
instance.visible = false
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
dialog.confirm = options => dialog(
|
||||
typeof options == 'string'
|
||||
? { message: options, showCancel: true }
|
||||
: { ...options, showCancel: true },
|
||||
)
|
||||
|
||||
const dialogPlugin = {
|
||||
install(Vue, options) {
|
||||
Vue.config.globalProperties.$dialog = dialog
|
||||
},
|
||||
}
|
||||
|
||||
export default dialog
|
||||
export default dialogPlugin
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'path'
|
||||
import { shell, clipboard } from 'electron'
|
||||
import crypto from 'crypto'
|
||||
import { rendererSend, rendererInvoke, NAMES } from '@common/ipc'
|
||||
import { log } from '@common/utils'
|
||||
import iconv from 'iconv-lite'
|
||||
import { gzip, gunzip } from 'zlib'
|
||||
import { proxy, qualityList } from '@renderer/core/share'
|
||||
@@ -480,6 +481,16 @@ export const readLxConfigFile = async path => {
|
||||
return data
|
||||
}
|
||||
|
||||
export const saveStrToFile = (path, str) => new Promise((resolve, reject) => {
|
||||
fs.writeFile(path, str, err => {
|
||||
if (err) {
|
||||
log.error(err)
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
||||
const fileNameRxp = /[\\/:*?#"<>|]/g
|
||||
export const filterFileName = name => name.replace(fileNameRxp, '')
|
||||
|
||||
@@ -12,6 +12,11 @@ dd
|
||||
div
|
||||
base-btn.btn.gap-left(min @click="handleImportAllData") {{$t('setting__backup_all_import')}}
|
||||
base-btn.btn.gap-left(min @click="handleExportAllData") {{$t('setting__backup_all_export')}}
|
||||
dd
|
||||
h3#backup_other {{$t('setting__backup_other')}}
|
||||
div
|
||||
base-btn.btn.gap-left(min @click="handleExportPlayListToText") {{$t('setting__backup_other_export_list_text')}}
|
||||
base-btn.btn.gap-left(min @click="handleExportPlayListToCsv") {{$t('setting__backup_other_export_list_csv')}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -26,9 +31,14 @@ import {
|
||||
readLxConfigFile,
|
||||
selectDir,
|
||||
openSaveDir,
|
||||
filterFileName,
|
||||
saveStrToFile,
|
||||
} from '@renderer/utils'
|
||||
import { currentStting } from '../setting'
|
||||
import { getList } from '@renderer/core/share/utils'
|
||||
import path from 'path'
|
||||
import { dialog } from '@renderer/plugins/Dialog'
|
||||
import iconv from 'iconv-lite'
|
||||
|
||||
export default {
|
||||
name: 'SettingUpdate',
|
||||
@@ -214,6 +224,106 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
const exportPlayListToText = async(savePath, isMerge) => {
|
||||
const lists = JSON.parse(JSON.stringify([
|
||||
{ ...toRaw(defaultList), list: toRaw(getList(defaultList.id)) },
|
||||
{ ...toRaw(loveList), list: toRaw(getList(loveList.id)) },
|
||||
...userLists.map(l => ({ ...toRaw(l), list: toRaw(getList(l.id)) })),
|
||||
]))
|
||||
for await (const list of lists) {
|
||||
for await (const item of list.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
if (isMerge) {
|
||||
saveStrToFile(savePath, iconv.encode(lists.map(l => l.list.map(m => `${m.name} ${m.singer} ${m.albumName}`).join('\n')).join('\n\n'), 'utf8', { addBOM: true }))
|
||||
} else {
|
||||
for await (const list of lists) {
|
||||
await saveStrToFile(path.join(savePath, `lx_list_${filterFileName(list.name)}.txt`), iconv.encode(list.list.map(m => `${m.name} ${m.singer} ${m.albumName}`).join('\n'), 'utf8', { addBOM: true }))
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleExportPlayListToText = async() => {
|
||||
const confirm = await dialog.confirm({
|
||||
message: t('setting__backup_other_export_list_text_confirm'),
|
||||
cancelButtonText: t('cancel_button_text'),
|
||||
confirmButtonText: t('confirm_button_text'),
|
||||
})
|
||||
if (confirm) {
|
||||
openSaveDir({
|
||||
title: t('setting__backup_other_export_dir'),
|
||||
defaultPath: 'lx_list_all.txt',
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
let path = result.filePath
|
||||
if (!path.endsWith('.txt')) path += '.txt'
|
||||
exportPlayListToText(path, true)
|
||||
})
|
||||
} else {
|
||||
selectDir({
|
||||
title: t('setting__backup_other_export_dir'),
|
||||
defaultPath: currentStting.value.download.savePath,
|
||||
properties: ['openDirectory'],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
exportPlayListToText(result.filePaths[0], false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const exportPlayListToCsv = async(savePath, isMerge) => {
|
||||
const lists = JSON.parse(JSON.stringify([
|
||||
{ ...toRaw(defaultList), list: toRaw(getList(defaultList.id)) },
|
||||
{ ...toRaw(loveList), list: toRaw(getList(loveList.id)) },
|
||||
...userLists.map(l => ({ ...toRaw(l), list: toRaw(getList(l.id)) })),
|
||||
]))
|
||||
for await (const list of lists) {
|
||||
for await (const item of list.list) {
|
||||
if (item.otherSource) delete item.otherSource
|
||||
}
|
||||
}
|
||||
const filterStr = str => {
|
||||
if (!str) return ''
|
||||
str = str.replace(/"/g, '""')
|
||||
if (/,/.test(str)) str = `"${str}"`
|
||||
return str
|
||||
}
|
||||
if (isMerge) {
|
||||
saveStrToFile(savePath, iconv.encode(lists.map(l => l.list.map(m => `${filterStr(m.name)},${filterStr(m.singer)},${filterStr(m.albumName)}`).join('\n')).join('\n'), 'utf8', { addBOM: true }))
|
||||
} else {
|
||||
for await (const list of lists) {
|
||||
await saveStrToFile(path.join(savePath, `lx_list_${filterFileName(list.name)}.csv`), iconv.encode(list.list.map(m => `${filterStr(m.name)},${filterStr(m.singer)},${filterStr(m.albumName)}`).join('\n'), 'utf8', { addBOM: true }))
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleExportPlayListToCsv = async() => {
|
||||
const confirm = await dialog.confirm({
|
||||
message: t('setting__backup_other_export_list_text_confirm'),
|
||||
cancelButtonText: t('cancel_button_text'),
|
||||
confirmButtonText: t('confirm_button_text'),
|
||||
})
|
||||
if (confirm) {
|
||||
openSaveDir({
|
||||
title: t('setting__backup_other_export_dir'),
|
||||
defaultPath: 'lx_list_all.csv',
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
let path = result.filePath
|
||||
if (!path.endsWith('.csv')) path += '.csv'
|
||||
exportPlayListToCsv(path, true)
|
||||
})
|
||||
} else {
|
||||
selectDir({
|
||||
title: t('setting__backup_other_export_dir'),
|
||||
defaultPath: currentStting.value.download.savePath,
|
||||
properties: ['openDirectory'],
|
||||
}).then(result => {
|
||||
if (result.canceled) return
|
||||
exportPlayListToCsv(result.filePaths[0], false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
window.eventHub.on(eventBaseName.set_config, handleUpdateSetting)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -228,6 +338,8 @@ export default {
|
||||
handleImportSetting,
|
||||
handleExportAllData,
|
||||
handleImportAllData,
|
||||
handleExportPlayListToText,
|
||||
handleExportPlayListToCsv,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user