This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
appcat
/
sonist
Archived
1
0
Fork 0

一大波优化

2.x
宇天 2019-01-26 22:19:51 +08:00
parent b42d7ae19a
commit 4bcc894ec3
13 changed files with 306 additions and 238 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "sonist", "name": "sonist",
"version": "0.9.4", "version": "0.9.5",
"description": "Music Player", "description": "Music Player",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {
@ -29,11 +29,7 @@
"buildResources": "icons", "buildResources": "icons",
"output": "build" "output": "build"
}, },
"files": [ "files": ["src/**/*", "node_modules/iofs/*", "node_modules/crypto.js/*"],
"src/**/*",
"node_modules/iofs/*",
"node_modules/crypto.js/*"
],
"mac": { "mac": {
"category": "public.app-category.music", "category": "public.app-category.music",
"target": "dmg", "target": "dmg",

View File

@ -20,24 +20,18 @@ import Search from '/js/modules/search.js'
import KTV from '/js/modules/ktv.js' import KTV from '/js/modules/ktv.js'
import PLAYCTRL from '/js/modules/play-ctrl.js' import PLAYCTRL from '/js/modules/play-ctrl.js'
import {
createDesktopLrcWindow,
createMiniWindow
} from '/js/modules/extra-win.js'
const log = console.log const log = console.log
const fs = require('iofs') const fs = require('iofs')
const path = require('path') const path = require('path')
const { remote } = require('electron') const { remote, ipcRenderer } = require('electron')
const { createDesktopLrcWindow, createMiniWindow } = remote.app.windows
const WIN = remote.getCurrentWindow() const WIN = remote.getCurrentWindow()
const MAIN_SCREEN = remote.screen.getPrimaryDisplay() const MAIN_SCREEN = remote.screen.getPrimaryDisplay()
const HOME_PATH = remote.app.getPath('appData')
const APP_INI_PATH = path.join(HOME_PATH, 'app.ini')
const LYRICS_PATH = path.join(HOME_PATH, 'lyrics')
const PLAY_MODE = { const PLAY_MODE = {
0: 'all', 0: 'all',
1: 'single', 1: 'single',
@ -51,22 +45,20 @@ window.TS = store.collection('temp')
window.SONIST = new AudioPlayer() window.SONIST = new AudioPlayer()
window.LYRICS = new Lyrics() window.LYRICS = new Lyrics()
let appInit = fs.cat(APP_INI_PATH) let appInit = ipcRenderer.sendSync('get-init')
Anot.ss('app-init', appInit + '') Anot.ss('app-init', appInit)
appInit = JSON.parse(appInit)
const LRC_WIN = createDesktopLrcWindow(MAIN_SCREEN) const LRC_WIN = createDesktopLrcWindow(MAIN_SCREEN)
const MINI_WIN = createMiniWindow(MAIN_SCREEN, WIN) // const MINI_WIN = createMiniWindow(MAIN_SCREEN, WIN)
WIN.hide() // WIN.hide()
MINI_WIN.show() // MINI_WIN.show()
MINI_WIN.opener = WIN // MINI_WIN.opener = WIN
MINI_WIN.openDevTools() // MINI_WIN.openDevTools()
window.MINI_WIN = MINI_WIN // window.MINI_WIN = MINI_WIN
Anot({ Anot({
$id: 'app', $id: 'app',
state: { state: {
@ -330,7 +322,7 @@ Anot({
this.updateCurr(song) this.updateCurr(song)
this.isPlaying = true this.isPlaying = true
this.draw(true) this.draw(true)
LYRICS.__init__(song.lyrics) LYRICS.__init__(song.id)
} else { } else {
if (SONIST.stat === 'ready') { if (SONIST.stat === 'ready') {
let played = this.isPlaying let played = this.isPlaying
@ -351,7 +343,7 @@ Anot({
this.draw(true) this.draw(true)
// this.ktvMode = 1 // this.ktvMode = 1
LYRICS.__init__(it.lyrics) LYRICS.__init__(it.id)
}) })
} }
} }

View File

@ -1,53 +0,0 @@
/**
* 额外的小窗口
* @author yutent<yutent@doui.cc>
* @date 2019/01/21 21:24:04
*/
'use strict'
const {
remote: { BrowserWindow }
} = require('electron')
export const createDesktopLrcWindow = function(screen) {
let win = new BrowserWindow({
title: '',
width: 1024,
height: 100,
frame: false,
resizable: false,
alwaysOnTop: true,
x: (screen.size.width - 1024) / 2,
y: screen.size.height - 100,
skipTaskbar: true,
hasShadow: false,
thickFrame: false,
transparent: true,
show: false
})
// win.loadURL('http://127.0.0.1:10240/desktop-lrc.html')
win.loadURL('app://local/desktop-lrc.html')
return win
}
export const createMiniWindow = function(screen, pwin) {
let win = new BrowserWindow({
title: '',
width: 320,
height: 60,
frame: false,
// parent: pwin,
resizable: false,
alwaysOnTop: true,
x: screen.size.width - 320,
y: 0,
skipTaskbar: true,
show: false
})
// win.loadURL('http://127.0.0.1:10240/mini-win.html')
win.loadURL('app://local/mini-win.html')
return win
}

View File

@ -9,13 +9,7 @@
import Api from '/js/api.js' import Api from '/js/api.js'
import Local from '/js/modules/local.js' import Local from '/js/modules/local.js'
const fs = require('iofs') const { ipcRenderer } = require('electron')
const path = require('path')
const { app } = require('electron').remote
const HOME_PATH = app.getPath('appData')
const MUSIC_DB_PATH = path.join(HOME_PATH, 'music.db')
const LYRICS_PATH = path.join(HOME_PATH, 'lyrics')
const log = console.log const log = console.log
export default { export default {
@ -85,9 +79,8 @@ export default {
song.albumId = json.album_id song.albumId = json.album_id
song.kgHash = json.hash song.kgHash = json.hash
song.cover = json.img song.cover = json.img
song.lyrics = path.join(LYRICS_PATH, `${song.id}.lrc`)
LS.insert(song) LS.update(id, song)
Local.list.set(SONIST.__CURR__, song) Local.list.set(SONIST.__CURR__, song)
SONIST.clear() SONIST.clear()
@ -96,10 +89,10 @@ export default {
this.updateCurr(song) this.updateCurr(song)
this.draw(true) this.draw(true)
fs.echo(json.lyrics, song.lyrics) ipcRenderer.send('save-lrc', { id, lrc: json.lyrics })
fs.echo(JSON.stringify(LS.getAll(), '', 2), MUSIC_DB_PATH) ipcRenderer.send('set-music', LS.getAll())
LYRICS.__init__(song.lyrics) LYRICS.__init__(id)
layer.toast('歌词应用成功...') layer.toast('歌词应用成功...')

View File

@ -9,21 +9,14 @@
import Api from '/js/api.js' import Api from '/js/api.js'
import { ID3 } from '/lib/audio/index.js' import { ID3 } from '/lib/audio/index.js'
const fs = require('iofs')
const path = require('path') const path = require('path')
const crypto = require('crypto.js') const crypto = require('crypto.js')
const { app, dialog } = require('electron').remote const { remote, ipcRenderer } = require('electron')
const log = console.log const log = console.log
const HOME_PATH = app.getPath('appData')
const MUSIC_DB_PATH = path.join(HOME_PATH, 'music.db')
const LYRICS_PATH = path.join(HOME_PATH, 'lyrics')
const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac']
let appInit = {} let appInit = {}
let dbCache = fs.cat(MUSIC_DB_PATH) let dbCache = []
dbCache = JSON.parse(dbCache)
export default Anot({ export default Anot({
$id: 'local', $id: 'local',
@ -40,8 +33,10 @@ export default Anot({
} }
}, },
mounted() { mounted() {
LS.insert(dbCache)
appInit = JSON.parse(Anot.ss('app-init')) appInit = JSON.parse(Anot.ss('app-init'))
dbCache = ipcRenderer.sendSync('get-music')
LS.insert(dbCache)
dbCache = null dbCache = null
this.__APP__ = Anot.vmodels.app this.__APP__ = Anot.vmodels.app
@ -78,12 +73,6 @@ export default Anot({
if (!it.cover) { if (!it.cover) {
if (idx === undefined) { if (idx === undefined) {
idx = SONIST.__CURR__ idx = SONIST.__CURR__
// for (let i in this.list.$model) {
// if (this.list[i].id === it.id) {
// idx = i
// break
// }
// }
} }
let _P = Promise.resolve(true) let _P = Promise.resolve(true)
if (!it.kgHash) { if (!it.kgHash) {
@ -105,7 +94,6 @@ export default Anot({
it.albumId = json.album_id it.albumId = json.album_id
it.kgHash = json.hash it.kgHash = json.hash
it.cover = json.img it.cover = json.img
it.lyrics = path.join(LYRICS_PATH, `${it.id}.lrc`)
LS.insert(it) LS.insert(it)
this.list.set(idx, it) this.list.set(idx, it)
@ -116,10 +104,13 @@ export default Anot({
this.__APP__.updateCurr(it) this.__APP__.updateCurr(it)
this.__APP__.draw(true) this.__APP__.draw(true)
fs.echo(json.lyrics, it.lyrics) ipcRenderer.send('save-lrc', {
fs.echo(JSON.stringify(LS.getAll(), '', 2), MUSIC_DB_PATH) id: it.id,
lrc: json.lyrics
})
ipcRenderer.send('set-music', LS.getAll())
LYRICS.__init__(it.lyrics) LYRICS.__init__(it.id)
}) })
} }
}) })
@ -144,7 +135,7 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(dbCache) SONIST.push(dbCache)
fs.echo(JSON.stringify(dbCache, '', 2), MUSIC_DB_PATH) ipcRenderer.send('set-music', dbCache)
dbCache = null dbCache = null
layer.toast(`刷新缓存完成,新增${this.__NEW_NUM__}`) layer.toast(`刷新缓存完成,新增${this.__NEW_NUM__}`)
@ -160,6 +151,7 @@ export default Anot({
let item = LS.get(hash) let item = LS.get(hash)
if (item) { if (item) {
item.path = `file://${song}` item.path = `file://${song}`
delete item.lyrics
LS.update(hash, item) LS.update(hash, item)
return this.__checkSong__(el) return this.__checkSong__(el)
} }
@ -182,22 +174,12 @@ export default Anot({
return return
} }
if (appInit.musicPath) { if (appInit.musicPath) {
if (fs.isdir(appInit.musicPath)) { this.__LIST__ = ipcRenderer.sendSync('scan-dir', appInit.musicPath)
if (this.__LIST__) {
this.__APP__.loading = true this.__APP__.loading = true
this.__LIST__ = fs.ls(appInit.musicPath, true).filter(_ => {
if (fs.isdir(_)) {
return false
} else {
let { ext, name } = path.parse(_)
if (!ext || name.startsWith('.')) {
return false
}
return SUPPORTED_EXTS.includes(ext)
}
})
this.__WAIT_FOR_SCAN__ = this.__LIST__.length this.__WAIT_FOR_SCAN__ = this.__LIST__.length
this.__NEW_NUM__ = 0 this.__NEW_NUM__ = 0
ev.target.textContent = '正在扫描, 请稍候...' ev.target.textContent = '正在扫描, 请稍候...'
this.__checkSong__(ev.target) this.__checkSong__(ev.target)
} else { } else {
@ -238,7 +220,7 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(LS.getAll()) SONIST.push(LS.getAll())
fs.echo(JSON.stringify(LS.getAll(), '', 2), MUSIC_DB_PATH) ipcRenderer.send('set-music', LS.getAll())
}, },
handleMenu(it, idx, ev) { handleMenu(it, idx, ev) {
let that = this let that = this
@ -281,7 +263,7 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(LS.getAll()) SONIST.push(LS.getAll())
fs.echo(JSON.stringify(LS.getAll(), '', 2), MUSIC_DB_PATH) ipcRenderer.send('set-music', LS.getAll())
} }
) )
} else { } else {

View File

@ -8,17 +8,14 @@
import '/lib/form/index.js' import '/lib/form/index.js'
const fs = require('iofs') const {
const path = require('path') remote: { app, dialog },
const { app, dialog } = require('electron').remote ipcRenderer
} = require('electron')
const log = console.log const log = console.log
const HOME_PATH = app.getPath('appData') let appInit = ipcRenderer.sendSync('get-init')
const APP_INI_PATH = path.join(HOME_PATH, 'app.ini')
let appInit = fs.cat(APP_INI_PATH)
appInit = JSON.parse(appInit)
export default Anot({ export default Anot({
$id: 'profile', $id: 'profile',
@ -58,9 +55,9 @@ export default Anot({
Object.assign(appInit, setting) Object.assign(appInit, setting)
let cache = JSON.stringify(appInit, '', 2) ipcRenderer.send('set-init', appInit)
fs.echo(cache, APP_INI_PATH)
Anot.ss('app-init', cache) Anot.ss('app-init', appInit)
layer.toast('保存成功') layer.toast('保存成功')

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,13 +7,13 @@
'use strict' 'use strict'
const log = console.log const log = console.log
const fs = require('iofs')
const { EventEmitter } = require('events') const { EventEmitter } = require('events')
const util = require('util') const util = require('util')
const { ipcRenderer } = require('electron')
class Lyrics { class Lyrics {
// 歌词初始化 // 歌词初始化
__init__(lrcFile) { __init__(id) {
this.lib = [] this.lib = []
this.curr = [] this.curr = []
this.lrc = { this.lrc = {
@ -21,21 +21,16 @@ class Lyrics {
r: { bg: '', txt: '' } r: { bg: '', txt: '' }
} }
if (!lrcFile || !fs.exists(lrcFile)) { let lrc = ipcRenderer.sendSync('read-lrc', id)
log('no lrc file', lrcFile) if (!id || lrc === null) {
log('no lrc file', id)
return false return false
} }
this.__LRC__ = lrcFile this.__ID__ = id
// log(this.__LRC__)
let lrc = fs
.cat(lrcFile)
.toString('utf8')
.split('\n')
this.lib = lrc this.lib = lrc
.split('\n')
.map(it => { .map(it => {
if (it) { if (it) {
let matches = it.match(/^\[([0-9\.\:]+)\](.+)/) let matches = it.match(/^\[([0-9\.\:]+)\](.+)/)
@ -85,7 +80,7 @@ class Lyrics {
// 歌词向前调整指定时间 // 歌词向前调整指定时间
forward(time = 0) { forward(time = 0) {
if (!this.__LRC__) { if (!this.__ID__) {
return return
} }
clearTimeout(this.__TIMER__) clearTimeout(this.__TIMER__)
@ -109,7 +104,7 @@ class Lyrics {
// 延时3秒写入 // 延时3秒写入
this.__TIMER__ = setTimeout(() => { this.__TIMER__ = setTimeout(() => {
fs.echo(lrc, this.__LRC__) ipcRenderer.sendSync('save-lrc', { id: this.__ID__, lrc })
}, 3000) }, 3000)
} }

View File

@ -1 +1 @@
const __STORE__={};function parse$And(_){let t="";for(let e in _){let i=_[e];switch(Anot.type(i)){case"object":if(i.$has){t+=`it.${e}.indexOf(${JSON.stringify(i.$has)}) > -1`;break}if(i.$in){t+=`${JSON.stringify(i.$in)}.indexOf(it.${e}) > -1`;break}if(i.$regex){t+=`${i.$regex}.test(it.${e})`;break}if(i.$lt||i.$lte){t+=`it.${e} <${i.$lte?"=":""} ${i.$lt||i.$lte}`,(i.$gt||i.$gte)&&(t+=` && it.${e} >${i.$gte?"=":""} ${i.$gt||i.$gte}`);break}if(i.$gt||i.$gte){t+=`it.${e} >${i.$gte?"=":""} ${i.$gt||i.$gte}`;break}if(i.$eq){t+=`it.${e} === ${i.$eq}`;break}default:t+=`it.${e} === ${JSON.stringify(_[e])}`}t+=" && "}return(t=t.slice(0,-4))||(t="true"),t}function parse$Or(_){let t="";return _.forEach(_=>{t+="(",t+=parse$And(_),t+=") || "}),t.slice(0,-4)}class AnotStore{constructor(_){Anot.hideProperty(this,"__name__",_),Anot.hideProperty(this,"__LAST_QUERY__",""),Anot.hideProperty(this,"__QUERY_HISTORY__",[]),__STORE__[_]||(__STORE__[_]=[],__STORE__[`${_}Dict`]={})}static collection(_){return new this(_)}__MAKE_FN__(_){let t="\n let result = [];\n let num = 0;\n for (let it of arr) {\n if(";return _.$or?t+=parse$Or(_.$or):t+=parse$And(_),t+="){\n result.push(it)\n num++\n if(limit > 0 && num >= limit){\n break\n }\n }\n }\n return result;",Function("arr","limit",t)}clear(_){this.__QUERY_HISTORY__=[],this.__LAST_QUERY__="",_&&(__STORE__[this.__name__]=[],__STORE__[`${this.__name__}Dict`]={})}getAll({filter:_,limit:t=[]}={}){const e=__STORE__[this.__name__];let i=[],r=!1;if(!e||!e.length)return i;if(t.length<1&&(t=[0]),t.length<2&&_&&(r=!0,t[0]>0&&t.unshift(0)),_){let n=JSON.stringify(_);if(this.__LAST_QUERY__===n)i=this.__QUERY_HISTORY__.slice.apply(this.__QUERY_HISTORY__,t);else{i=this.__MAKE_FN__(_)(e,r&&t[1]||0),r||(this.__LAST_QUERY__=n,this.__QUERY_HISTORY__=i,i=this.__QUERY_HISTORY__.slice.apply(this.__QUERY_HISTORY__,t))}}else i=e.slice.apply(e,t);return Anot.deepCopy(i)}get(_){const t=__STORE__[`${this.__name__}Dict`];return Anot.deepCopy(t[_])||null}count({filter:_}={}){return _?this.__LAST_QUERY__===JSON.stringify(_)?this.__QUERY_HISTORY__.length:this.getAll({filter:_,limit:[0]}).length:__STORE__[this.__name__].length}__INSERT__(_,t){let e=__STORE__[this.__name__],i=__STORE__[`${this.__name__}Dict`],r=_[t||"id"];i[r]?this.update(r,_):(e.push(_),i[r]=_)}insert(_,t){Array.isArray(_)||(_=[_]),_.forEach(_=>{this.__INSERT__(_,t)}),this.clear()}sort(_,t,e){let i="";t&&window.Intl&&(i+="\n let col = new Intl.Collator('zh')\n "),i+=e?"return arr.sort((b, a) => {":"return arr.sort((a, b) => {",i+=`\n let filter = function(val) {\n try {\n return val.${_} || ''\n } catch (err) {\n return ''\n }\n }\n `,t?window.Intl?i+="return col.compare(filter(a), filter(b))":i+="return (filter(a) + '').localeCompare(filter(b), 'zh')":i+="return filter(a) - filter(b)",i+="\n})",Function("arr",i).call(this,__STORE__[this.__name__]),this.clear()}update(_,t){let e=__STORE__[this.__name__],i=__STORE__[`${this.__name__}Dict`],r=i[_],n=e.indexOf(r);Object.assign(r,t),e.splice(n,1,r),i[_]=r}remove(_){let t=__STORE__[this.__name__],e=__STORE__[`${this.__name__}Dict`],i=e[_],r=t.indexOf(i);t.splice(r,1),delete e[_]}}Anot.store=window.store=AnotStore;export default AnotStore; const __STORE__={};function parse$And(_){let t="";for(let e in _){let i=_[e];switch(Anot.type(i)){case"object":if(i.$has){t+=`it.${e}.indexOf(${JSON.stringify(i.$has)}) > -1`;break}if(i.$in){t+=`${JSON.stringify(i.$in)}.indexOf(it.${e}) > -1`;break}if(i.$regex){t+=`${i.$regex}.test(it.${e})`;break}if(i.$lt||i.$lte){t+=`it.${e} <${i.$lte?"=":""} ${i.$lt||i.$lte}`,(i.$gt||i.$gte)&&(t+=` && it.${e} >${i.$gte?"=":""} ${i.$gt||i.$gte}`);break}if(i.$gt||i.$gte){t+=`it.${e} >${i.$gte?"=":""} ${i.$gt||i.$gte}`;break}if(i.$eq){t+=`it.${e} === ${i.$eq}`;break}default:t+=`it.${e} === ${JSON.stringify(_[e])}`}t+=" && "}return(t=t.slice(0,-4))||(t="true"),t}function parse$Or(_){let t="";return _.forEach(_=>{t+="(",t+=parse$And(_),t+=") || "}),t.slice(0,-4)}class AnotStore{constructor(_){Anot.hideProperty(this,"__name__",_),Anot.hideProperty(this,"__LAST_QUERY__",""),Anot.hideProperty(this,"__QUERY_HISTORY__",[]),__STORE__[_]||(__STORE__[_]=[],__STORE__[`${_}Dict`]={})}static collection(_){return new this(_)}__MAKE_FN__(_){let t="\n let result = [];\n let num = 0;\n for (let it of arr) {\n if(";return _.$or?t+=parse$Or(_.$or):t+=parse$And(_),t+="){\n result.push(it)\n num++\n if(limit > 0 && num >= limit){\n break\n }\n }\n }\n return result;",Function("arr","limit",t)}clear(_){this.__QUERY_HISTORY__=[],this.__LAST_QUERY__="",_&&(__STORE__[this.__name__]=[],__STORE__[`${this.__name__}Dict`]={})}getAll({filter:_,limit:t=[]}={}){const e=__STORE__[this.__name__];let i=[],r=!1;if(!e||!e.length)return i;if(t.length<1&&(t=[0]),t.length<2&&_&&(r=!0,t[0]>0&&t.unshift(0)),_){let n=JSON.stringify(_);if(this.__LAST_QUERY__===n)i=this.__QUERY_HISTORY__.slice.apply(this.__QUERY_HISTORY__,t);else{i=this.__MAKE_FN__(_)(e,r&&t[1]||0),r||(this.__LAST_QUERY__=n,this.__QUERY_HISTORY__=i,i=this.__QUERY_HISTORY__.slice.apply(this.__QUERY_HISTORY__,t))}}else i=e.slice.apply(e,t);return Anot.deepCopy(i)}get(_){const t=__STORE__[`${this.__name__}Dict`];return Anot.deepCopy(t[_])||null}count({filter:_}={}){return _?this.__LAST_QUERY__===JSON.stringify(_)?this.__QUERY_HISTORY__.length:this.getAll({filter:_,limit:[0]}).length:__STORE__[this.__name__].length}__INSERT__(_,t){let e=__STORE__[this.__name__],i=__STORE__[`${this.__name__}Dict`],r=_[t||"id"];i[r]?this.update(r,_):(e.push(_),i[r]=_)}insert(_,t){Array.isArray(_)||(_=[_]),_.forEach(_=>{this.__INSERT__(_,t)}),this.clear()}sort(_,t,e){let i="";t&&window.Intl&&(i+="\n let col = new Intl.Collator('zh')\n "),i+=e?"return arr.sort((b, a) => {":"return arr.sort((a, b) => {",i+=`\n let filter = function(val) {\n try {\n return val.${_} || ''\n } catch (err) {\n return ''\n }\n }\n `,t?window.Intl?i+="return col.compare(filter(a), filter(b))":i+="return (filter(a) + '').localeCompare(filter(b), 'zh')":i+="return filter(a) - filter(b)",i+="\n})",Function("arr",i).call(this,__STORE__[this.__name__]),this.clear()}update(_,t){let e=__STORE__[this.__name__],i=__STORE__[`${this.__name__}Dict`],r=i[_],n=e.indexOf(r);e.splice(n,1,t),i[_]=t}remove(_){let t=__STORE__[this.__name__],e=__STORE__[`${this.__name__}Dict`],i=e[_],r=t.indexOf(i);t.splice(r,1),delete e[_]}}Anot.store=window.store=AnotStore;export default AnotStore;

View File

@ -22,112 +22,55 @@ const MIME_TYPES = {
'.ico': 'image/ico' '.ico': 'image/ico'
} }
require('./tools/init')
const createTray = require('./tools/tray') const createTray = require('./tools/tray')
const createMenu = require('./tools/menu') const createMenu = require('./tools/menu')
const {
createMainWindow,
createErrorWindow,
createDesktopLrcWindow,
createMiniWindow
} = require('./tools/windows')
/* ******************************* */ app.windows = { createDesktopLrcWindow, createMiniWindow }
/* **********修复环境变量*********** */
/* ******************************* */
let PATH_SET = new Set()
process.env.PATH.split(':').forEach(_ => {
PATH_SET.add(_)
})
PATH_SET.add('/usr/local/bin')
PATH_SET.add('/usr/local/sbin')
process.env.PATH = Array.from(PATH_SET).join(':')
PATH_SET = null
const ROOT = __dirname const ROOT = __dirname
const HOME = app.getPath('home')
/* ----------------------------------------------------- */ /* ----------------------------------------------------- */
app.commandLine.appendSwitch('--lang', 'zh-CN') app.commandLine.appendSwitch('--lang', 'zh-CN')
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required') app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
app.setPath('appData', path.resolve(HOME, '.sonist/'))
protocol.registerStandardSchemes(['app'], { secure: true }) protocol.registerStandardSchemes(['app'], { secure: true })
let appPath = app.getPath('appData')
if (!fs.exists(appPath)) {
fs.mkdir(appPath)
fs.mkdir(path.join(appPath, 'lyrics'))
fs.mkdir(path.join(appPath, 'cache'))
fs.echo('{}', path.join(appPath, 'app.ini'))
fs.echo('[]', path.join(appPath, 'music.db'))
}
/* ----------------------------------------------------- */ /* ----------------------------------------------------- */
function createWindow() { // 初始化应用
// 创建浏览器窗口
let win = new BrowserWindow({
title: 'sonist',
width: 1024,
height: 640,
frame: false,
resizable: false,
icon: path.resolve(ROOT, './images/app.png'),
webPreferences: {
webSecurity: false,
experimentalFeatures: true
},
show: false
})
// 然后加载应用的 index.html。
win.loadURL('app://local/index.html')
win.on('ready-to-show', _ => {
win.show()
win.openDevTools()
})
return win
}
/* ****************************************** */
/* ************* init ******************* */
/* ****************************************** */
// 创建窗口
app.once('ready', () => { app.once('ready', () => {
// 注册协议
protocol.registerBufferProtocol('app', (req, cb) => { protocol.registerBufferProtocol('app', (req, cb) => {
let file = req.url.replace(/^app:\/\/local\//, '') let file = req.url.replace(/^app:\/\/local\//, '')
let ext = path.extname(req.url) let ext = path.extname(req.url)
let buff = fs.cat(path.resolve(ROOT, file)) let buff = fs.cat(path.resolve(ROOT, file))
cb({ data: buff, mimeType: MIME_TYPES[ext] }) cb({ data: buff, mimeType: MIME_TYPES[ext] })
}) })
exec('which ffprobe', (err, res) => { // 修改app的UA
if (res) {
session.defaultSession.setUserAgent( session.defaultSession.setUserAgent(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
) )
let win = createWindow() // 判断依赖
exec('which ffprobe', (err, res) => {
if (res) {
let win = createMainWindow(path.resolve(ROOT, './images/app.png'))
createTray(win) createTray(win)
createMenu(win) createMenu(win)
} else { // mac专属事件,点击dock栏图标,可激活窗口
win = new BrowserWindow({ app.on('activate', _ => {
width: 600,
height: 360,
skipTaskbar: true,
maximizable: false,
minimizable: false,
resizable: false,
titleBarStyle: 'hiddenInset'
})
win.setMenuBarVisibility(false)
win.loadURL('app://local/depends.html')
win.on('closed', _ => {
app.exit()
})
}
})
})
app.on('activate', _ => {
if (win) { if (win) {
win.show() win.show()
} }
})
} else {
createErrorWindow()
}
})
}) })

117
src/tools/init.js Normal file
View File

@ -0,0 +1,117 @@
/**
* 配置/DB通讯
* @author yutent<yutent@doui.cc>
* @date 2019/01/26 18:11:26
*/
'use strict'
const { app, ipcMain } = require('electron')
const path = require('path')
const fs = require('iofs')
/* ********** 修复环境变量 start *********** */
let PATH_SET = new Set()
process.env.PATH.split(':').forEach(_ => {
PATH_SET.add(_)
})
PATH_SET.add('/usr/local/bin')
PATH_SET.add('/usr/local/sbin')
process.env.PATH = Array.from(PATH_SET).join(':')
PATH_SET = null
/* ********** 修复环境变量 end *********** */
const HOME = app.getPath('home')
const APP_ROOT = path.resolve(HOME, '.sonist/')
const LRC_DIR = path.join(APP_ROOT, 'lyrics')
const CACHE_DIR = path.join(APP_ROOT, 'cache')
const INIT_FILE = path.join(APP_ROOT, 'app.ini')
const DB_FILE = path.join(APP_ROOT, 'music.db')
if (!fs.exists(APP_ROOT)) {
fs.mkdir(APP_ROOT)
fs.mkdir(LRC_DIR)
fs.mkdir(CACHE_DIR)
fs.echo('{}', INIT_FILE)
fs.echo('[]', DB_FILE)
}
const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac']
/* ----------------------------------------------------------------- */
/* --------------------- 事件开始 ------------------------- */
/* ---------------------------------------------------------------- */
// 获取应用配置
ipcMain.on('get-init', (ev, val) => {
let cache = fs.cat(INIT_FILE).toString('utf-8')
cache = JSON.parse(cache)
ev.returnValue = cache
})
// 设置应用配置
ipcMain.on('set-init', (ev, val) => {
fs.echo(JSON.stringify(val), INIT_FILE)
})
// 获取音乐数据库
ipcMain.on('get-music', (ev, val) => {
let cache = fs.cat(DB_FILE).toString('utf-8')
cache = JSON.parse(cache)
ev.returnValue = cache
})
// 更新音乐数据库
ipcMain.on('set-music', (ev, val) => {
fs.echo(JSON.stringify(val), DB_FILE)
})
/**
* 保存歌词文件
*/
ipcMain.on('save-lrc', (ev, obj) => {
fs.echo(obj.lrc, path.join(LRC_DIR, `${obj.id}.lrc`))
})
/**
* 读取歌词文件
*/
ipcMain.on('read-lrc', (ev, id) => {
let file = path.join(LRC_DIR, `${id}.lrc`)
if (fs.exists(file)) {
ev.returnValue = fs.cat(file).toString('utf8')
} else {
ev.returnValue = null
}
})
/**
* 保存音乐文件
*/
ipcMain.on('save-cache', (ev, obj) => {
fs.echo(obj.data, path.join(CACHE_DIR, obj.file))
})
/**
* 扫描目录
*/
ipcMain.on('scan-dir', (ev, dir) => {
if (fs.isdir(dir)) {
let list = fs.ls(dir, true).filter(_ => {
if (fs.isdir(_)) {
return false
} else {
let { ext, name } = path.parse(_)
if (!ext || name.startsWith('.')) {
return false
}
return SUPPORTED_EXTS.includes(ext)
}
})
ev.returnValue = list
} else {
ev.returnValue = null
}
})

106
src/tools/windows.js Normal file
View File

@ -0,0 +1,106 @@
/**
* 各种窗口创建
* @author yutent<yutent@doui.cc>
* @date 2019/01/26 18:28:22
*/
'use strict'
const { BrowserWindow } = require('electron')
/**
* 应用主窗口
*/
exports.createMainWindow = function(icon) {
// 创建浏览器窗口
let win = new BrowserWindow({
title: 'sonist',
width: 1024,
height: 640,
frame: false,
resizable: false,
icon,
webPreferences: {
webSecurity: false,
experimentalFeatures: true
},
show: false
})
// 然后加载应用的 index.html。
win.loadURL('app://local/index.html')
win.on('ready-to-show', _ => {
win.show()
win.openDevTools()
})
return win
}
/**
* 依赖异常显示窗口
*/
exports.createErrorWindow = function() {
let win = new BrowserWindow({
width: 600,
height: 360,
skipTaskbar: true,
maximizable: false,
minimizable: false,
resizable: false,
titleBarStyle: 'hiddenInset'
})
win.setMenuBarVisibility(false)
win.loadURL('app://local/depends.html')
win.on('closed', _ => {
app.exit()
})
}
/**
* 桌面歌词窗口
*/
exports.createDesktopLrcWindow = function(screen) {
let win = new BrowserWindow({
title: '',
width: 1024,
height: 100,
frame: false,
resizable: false,
alwaysOnTop: true,
x: (screen.size.width - 1024) / 2,
y: screen.size.height - 100,
skipTaskbar: true,
hasShadow: false,
thickFrame: false,
transparent: true,
show: false
})
win.loadURL('app://local/desktop-lrc.html')
return win
}
/**
* 应用迷你窗口
*/
exports.createMiniWindow = function(screen) {
let win = new BrowserWindow({
title: '',
width: 320,
height: 60,
frame: false,
resizable: false,
alwaysOnTop: true,
x: screen.size.width - 320,
y: 0,
skipTaskbar: true,
show: false
})
// win.loadURL('http://127.0.0.1:10240/mini-win.html')
win.loadURL('app://local/mini-win.html')
return win
}