这一个章节会用到我写的一个插件,electron-auto-setting 当我们传递配置项,它可以自动生成页面。
它的原理其实就是从配置项里面读取到表单的选项,构造出表单,然后当修改的时候,通过统一的 IPC 信道,将修改的 key 和 value 发送回去。
npm install electron-auto-setting --save
declare module 'electron-auto-setting' {
const all: any
const init: any
const store: any
export { init, store }
export default all
}
store 的方法全部来自于 electron-store
新建 tray.ts 文件,将配置项传递给 init,而 create 则是创建窗口的函数,第一个是参数配置项,第二个参数是是否开启 DEBUG 模式,这里我们开启它。
import create, { init, store } from 'electron-auto-setting'
import { app, Tray, Menu } from 'electron'
import { resolve } from 'path'
import { fromEvent } from 'rxjs'
let win = null
let tray = null
let setting = [
{
icon: 'icon-setting',
label: '普通设置',
configs: {
SAVA_PATH: {
type: 'path',
label: ' 输出路径',
defaultValue: resolve(app.getPath('home'), 'xiaoshuo')
},
CONCURRENCE: {
type: 'string',
label: '下载并发量',
defaultValue: 5
}
}
},
{
icon: 'icon-download',
label: 'API 设置',
configs: {
APP_ID: {
type: 'string',
label: '应用 ID',
defaultValue: ''
},
API_KEY: {
type: 'string',
label: '应用秘钥',
defaultValue: ''
},
SECRET_KEY: {
type: 'string',
label: '加密秘钥',
defaultValue: ''
}
}
}
]
init(setting)
function opensetting() {
win = create({}, true)
}
fromEvent(<any>app, 'ready').subscribe(() => {
tray = new Tray(resolve(__dirname, 'tray_w24h24.png'))
const contextMenu = Menu.buildFromTemplate([
{ label: '设置', click: opensetting }
])
tray.setContextMenu(contextMenu)
})
export { store, tray, win }
当 app
ready
的时候,我们创建一个通知栏的小图标,这个小图标是 24x24 大小的 png 图片,我是从 http://www.iconfont.cn/ 下载的,大家可以自行下载。
然后设置通知栏菜单,通过 setContextMenu
进行初始化,这是 Electron API 提供的方法。
在 index.ts 中 ,记得使用了 await ,一定要给函数加上 async 哦。此时可以把 on 函数提取到 helper 里面去,具体请看仓库源码。
import { store } from './tray'
const { folderName, url } = args
const savePath = resolve(
store.get('SAVA_PATH', app.getPath('music')),
folderName
)
await download(url, plugin, { path: savePath })
await transform(savePath)
修改 download.ts ,让默认的配置项可以读取到并发量。
opts = Object.assign(
{},
{
concurrence: store.get('CONCURRENCE', 5)
},
defaultOptions,
crawl.opts,
opts
)
修改 TTS.ts ,我们不希望每次设置完要重启,这里我们订阅一下更新。
let APP_ID = store.get('APP_ID')
let API_KEY = store.get('API_KEY')
let SECRET_KEY = store.get('SECRET_KEY')
// 每次修改更新值
store.onDidChange('APP_ID', (newValue: any) => (APP_ID = newValue))
store.onDidChange('API_KEY', (newValue: any) => (API_KEY = newValue))
store.onDidChange('SECRET_KEY', (newValue: any) => (SECRET_KEY = newValue))
let client: any = null
try {
client = new speech(APP_ID, API_KEY, SECRET_KEY)
} catch (e) {
log({ type: 'audio', step: 'new_speech_error', message: e.message })
}
新建 Download.svelte , 然后像之前一样,在 App.svlete 里面引入并设置值。
<input bind:value=folderName />
<input bind:value=url />
<button on:click="download()">下载</button>
<button on:click="stop()">中断</button>
<script>
import {
ipcRenderer
} from 'electron'
export default {
data() {
return {
url: '',
folderName: ''
}
},
methods: {
download() {
const {
url,
folderName
} = this.get()
if (url.length && folderName.length) {
console.log(url);
console.log(folderName);
ipcRenderer.send('download', {
url,
folderName
})
}
},
stop() {
ipcRenderer.send('stop') // 发送停止事件信号
}
}
}
</script>
此时我们是没有办法对正在下载的进行中断想要实现中断,必须要有一个信号量来控制跳出循环,我们可以把这个信号量放到 store 里面去,只要我们不配置 setting 的配置项,其实他是不会显示出来的。
修改 helper.ts,此时你的 helper 应该会像下面这样,中断了的时候,我们应该把下载的给删除,这样可以保证目录的干净。
import { Subject } from 'rxjs'
import { ipcMain as ipc } from 'electron'
import { store } from './tray'
import { remove } from 'fs-extra'
import log from './statusLog'
function ready() {
let resolveFN, rejectFN
let promise = new Promise(
(resolve, reject) => ([resolveFN, rejectFN] = [resolve, reject])
)
return [resolveFN, rejectFN, promise]
}
interface CombineEvent {
event: any
args: any
}
function on(channel: string): Subject<CombineEvent> {
const eventListner = new Subject<CombineEvent>()
ipc.on(channel, (event: any, args: any) => eventListner.next({ event, args }))
return eventListner
}
async function handleStop(path: string, logInfo: any) {
if (store.get('stop')) {
path && (await remove(path)) // 删除目录
log(logInfo)
return true
}
return false
}
export { ready, on, handleStop }
修改 download.ts 在循环里面添加跳出代码,并发送消息显示给前端
if (
await handleStop(resolve(path, 'text'), {
type: 'stop',
message: '已停止该队列'
})
) {
// 每次循环都要判断是否有停止信号量。
break
}
同样修改 TTS.ts ,添加跳出代码
if (
await handleStop(chapterSavePath, {
type: 'stop',
message: '已停止该队列'
})
) {
break
}
那么在开始与结束的时候应该添加重置信号量的代码
store.set('stop', false)
await download(url, plugin, { path: savePath })
await transform(savePath)
store.set('stop', false)
以及设置信号量为停止的代码
on('stop').subscribe(() => {
store.set('stop', true)
})