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
宇天 2020-11-18 16:32:33 +08:00
parent cb66f9b287
commit ea4bdddffa
14 changed files with 261 additions and 246 deletions

View File

@ -1,6 +1,6 @@
{
"name": "top.yutent.sonist",
"version": "2.0.0-alpha-1",
"version": "2.0.0-alpha-2",
"description": "Music Player",
"main": "src/main.js",
"scripts": {
@ -15,7 +15,8 @@
"license": "MIT",
"dependencies": {
"crypto.js": "^2.0.2",
"iofs": "^1.5.1"
"iofs": "^1.5.1",
"sqlite3": "^5.0.0"
},
"devDependencies": {
"electron": "^10.0.0",
@ -33,7 +34,12 @@
"version": "10.1.5",
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"files": ["src/**/*", "node_modules/iofs/*", "node_modules/crypto.js/*"],
"files": [
"src/**/*",
"node_modules/iofs/*",
"node_modules/sqlite3/*",
"node_modules/crypto.js/*"
],
"mac": {
"category": "public.app-category.music",
"target": "dmg",

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@
position:relative;
display:flex;flex-direction:column;
width:100%;height:100%;
background:rgba(0, 0, 0, .2);
background:rgba(0, 0, 0, .3);
//
.title-bar {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -53,71 +53,11 @@
<div class="scroll-box">
<wc-scroll class="list">
<section class="item">
<span class="idx">01</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">02</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item on">
<span class="idx">03</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">04</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">05</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">06</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">07</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">08</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">09</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">10</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
</section>
<section class="item">
<span class="idx">11</span>
<span class="name">情是何物</span>
<span class="artist">周深</span>
<span class="duration">04:23</span>
<section class="item" :for="i it in list">
<span class="idx" :text="i + 1"></span>
<span class="name" :text="it.name"></span>
<span class="artist" :text="it.artist"></span>
<span class="duration" :text="it.duration"></span>
</section>
</wc-scroll>

View File

@ -6,13 +6,23 @@
import Anot from '/js/lib/anot.js'
import '/js/lib/scroll/index.js'
import app from '/js/lib/socket.js'
// const {} from ''
Anot({
$id: 'app',
state: {
isplaying: true,
playmode: 1,
mute: false
mute: false,
list: []
},
mounted() {
var list = app.dispatch('scan-dir', { path: '/Volumes/extends/music' })
this.list = list
console.log(list)
},
methods: {
play() {

13
src/js/lib/socket.js Normal file
View File

@ -0,0 +1,13 @@
/**
* 与主进程的通讯
* @author yutent<yutent.io@gmail.com>
* @date 2020/07/14 11:42:02
*/
const { ipcRenderer } = require('electron')
export default {
dispatch(type = '', params = {}) {
return ipcRenderer.sendSync('app', Object.assign(params, { type }))
}
}

View File

@ -1,16 +1,19 @@
/**
* 主入口
* @author yutent<yutent@doui.cc>
* @date 2019/12/13 00:37:04
* @author yutent<yutent.io@gmail.com>
* @date 2020/11/18 09:27:09
*/
'use strict'
const { app, session, protocol, globalShortcut } = require('electron')
const path = require('path')
const fs = require('iofs')
const { exec } = require('child_process')
const log = console.log
// const {exec} = require('child_process')
require('./tools/init.js')
const { createAppTray, createLrcTray } = require('./tools/tray.js')
const createMenu = require('./tools/menu.js')
const { createMainWindow, createMiniWindow } = require('./tools/windows.js')
const MIME_TYPES = {
'.js': 'text/javascript',
'.html': 'text/html',
@ -23,14 +26,6 @@ const MIME_TYPES = {
'.ico': 'image/ico'
}
require('./tools/init')
const createTray = require('./tools/tray')
const createMenu = require('./tools/menu')
const { createMainWindow, createErrorWindow } = require('./tools/windows')
const ROOT = __dirname
/* ----------------------------------------------------- */
app.commandLine.appendSwitch('--lang', 'zh-CN')
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
@ -47,7 +42,7 @@ app.once('ready', () => {
protocol.registerBufferProtocol('app', (req, cb) => {
let file = req.url.replace(/^app:\/\/local\//, '')
let ext = path.extname(req.url)
let buff = fs.cat(path.resolve(ROOT, file))
let buff = fs.cat(path.resolve(__dirname, file))
cb({ data: buff, mimeType: MIME_TYPES[ext] })
})
// 修改app的UA
@ -55,13 +50,12 @@ app.once('ready', () => {
'KugouMusic/2.9.5 (Mac OS X Version 10.15.7 (Build 19H2))'
)
let win = createMainWindow(path.resolve(ROOT, './images/app.png'))
let win = createMainWindow(path.resolve(__dirname, './images/app.png'))
createTray(win)
createAppTray(win)
createLrcTray(win)
createMenu(win)
app.__MAIN__ = win
// mac专属事件,点击dock栏图标,可激活窗口
app.on('activate', _ => {
if (win) {

63
src/tools/db/index.js Normal file
View File

@ -0,0 +1,63 @@
/**
*
* @authors yutent (yutent.io@gmail.com)
* @date 2018-08-06 15:55:54
*/
Promise.defer = function() {
var obj = {}
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve
obj.reject = reject
})
return obj
}
const sqlite3 = require('sqlite3').verbose()
class Sqlite {
constructor(db) {
this.db = new sqlite3.Database(db)
}
query(sql, ...param) {
let defer = Promise.defer()
param.unshift(sql)
param.push(err => {
if (err) {
return defer.reject(err)
}
defer.resolve(true)
})
this.db.run.apply(this.db, param)
return defer.promise
}
getAll(sql, ...param) {
let defer = Promise.defer()
param.unshift(sql)
param.push((err, row) => {
if (err) {
return defer.reject(err)
}
defer.resolve(row)
})
this.db.all.apply(this.db, param)
return defer.promise
}
get(sql, ...param) {
let defer = Promise.defer()
param.unshift(sql)
param.push((err, row) => {
if (err) {
return defer.reject(err)
}
defer.resolve(row)
})
this.db.get.apply(this.db, param)
return defer.promise
}
}
module.exports = Sqlite

53
src/tools/db/init.js Normal file
View File

@ -0,0 +1,53 @@
/**
* 初始化数据库
* @author yutent<yutent.io@gmail.com>
* @date 2020/07/14 18:17:59
*/
const TABLE_PLAYLIST = `
CREATE TABLE IF NOT EXISTS "playlist" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" char(128) NOT NULL
)
`
const TABLE_SONGS = `
CREATE TABLE IF NOT EXISTS "songs" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"pid" integer NOT NULL,
"aid" integer NOT NULL,
"name" char(128) NOT NULL,
"album" char(128) NOT NULL,
"cover" char(256) NOT NULL,
"lrc" text NOT NULL,
)
`
// 歌曲和播放列表的关系表(多对多)
const TABLE_RELATIONS = `
CREATE TABLE IF NOT EXISTS "relations" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"sid" integer NOT NULL,
"pid" integer NOT NULL,
)
`
const TABLE_ARTISTS = `
CREATE TABLE IF NOT EXISTS "artists" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" integer NOT NULL,
"avatar" char(256) NOT NULL
)
`
function error(err) {
console.log('----------------------------------------')
console.error(err)
console.log('----------------------------------------')
}
module.exports = function(db) {
db.query(TABLE_PLAYLIST).catch(error)
db.query(TABLE_SONGS).catch(error)
db.query(TABLE_RELATIONS).catch(error)
db.query(TABLE_ARTISTS).catch(error)
}

View File

@ -4,12 +4,15 @@
* @date 2019/01/26 18:11:26
*/
'use strict'
const { app, ipcMain, globalShortcut: GS } = require('electron')
const path = require('path')
const fs = require('iofs')
const Shortcut = require('./shortcut')
const Sqlite = require('./db')
const dbinit = require('./db/init')
const HOME = path.resolve(app.getPath('userData'))
/* ********** 修复环境变量 start *********** */
let PATH_SET = new Set()
@ -24,105 +27,75 @@ 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')
const TEMP_DB = path.join(APP_ROOT, 'temp.db')
if (!fs.exists(APP_ROOT)) {
fs.mkdir(APP_ROOT)
fs.mkdir(LRC_DIR)
fs.mkdir(CACHE_DIR)
fs.echo('{}', INIT_FILE)
fs.echo('[]', TEMP_DB)
fs.echo('[]', DB_FILE)
}
const DB_FILE = path.join(HOME, 'sqlite3.cache')
const INIT_FILE = path.join(HOME, 'app.ini')
const CACHE_DIR = path.join(HOME, 'other_cache')
const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac']
const DB = {
read(file) {
let cache = (fs.cat(file) || '[]').toString('utf-8')
try {
return JSON.parse(cache)
} catch (err) {
return cache
}
},
save(file, data) {
fs.echo(JSON.stringify(data), file)
}
var isFirstTimeLaunch = false
var db = null
if (!fs.exists(DB_FILE)) {
fs.echo('{}', INIT_FILE)
fs.mkdir(CACHE_DIR)
isFirstTimeLaunch = true
}
db = new Sqlite(DB_FILE)
if (isFirstTimeLaunch) {
dbinit(db)
}
/* ----------------------------------------------------------------- */
/* --------------------- 事件开始 ------------------------- */
/* ---------------------------------------------------------------- */
ipcMain.on('sonist', (ev, conn) => {
ipcMain.on('app', (ev, conn) => {
switch (conn.type) {
// 获取所有书籍
case 'get-playlist':
db.getAll('SELECT * FROM `playlist`')
.then(res => {
ev.returnValue = res
})
.catch(err => {
ev.returnValue = err
})
break
// 获取应用配置
case 'get-init':
ev.returnValue = DB.read(INIT_FILE)
var ini = fs.cat(INIT_FILE).toString('')
ev.returnValue = JSON.parse(ini)
break
// 设置应用配置
case 'set-init':
DB.save(INIT_FILE, conn.data)
break
// 获取音乐数据库
case 'get-music':
ev.returnValue = DB.read(DB_FILE)
break
// 更新音乐数据库
case 'set-music':
DB.save(DB_FILE, conn.data)
break
// 获取临时音乐数据库
case 'get-temp':
ev.returnValue = DB.read(TEMP_DB)
break
// 更新临时音乐数据库
case 'set-temp':
DB.save(TEMP_DB, conn.data)
break
// 读取歌词文件
case 'read-lrc':
let lrc = path.join(LRC_DIR, `${conn.id}.lrc`)
if (fs.exists(lrc)) {
ev.returnValue = DB.read(lrc)
} else {
ev.returnValue = null
}
break
// 保存歌词文件
case 'save-lrc':
fs.echo(conn.data, path.join(LRC_DIR, `${conn.id}.lrc`))
break
// 保存音乐文件
case 'save-cache':
let file = path.join(CACHE_DIR, conn.file)
fs.echo(conn.data, file)
ev.returnValue = `file://${file}`
fs.echo(JSON.stringify(conn.data, null, 2), INIT_FILE)
break
// 扫描目录
case 'scan-dir':
if (fs.isdir(conn.path)) {
let list = fs.ls(conn.path, true).filter(_ => {
if (fs.isdir(_)) {
return false
} else {
let { ext, name } = path.parse(_)
if (!ext || name.startsWith('.')) {
let list = fs
.ls(conn.path, true)
.filter(it => {
if (fs.isdir(it)) {
return false
} else {
let { ext, name } = path.parse(it)
if (!ext || name.startsWith('.')) {
return false
}
return SUPPORTED_EXTS.includes(ext)
}
return SUPPORTED_EXTS.includes(ext)
}
})
})
.map(it => {
var { ext, name } = path.parse(it)
return { uuid: '', ext, name, path: it }
})
ev.returnValue = list
} else {
ev.returnValue = null
ev.returnValue = []
}
break

View File

@ -6,31 +6,44 @@
'use strict'
const { app, Tray, Menu } = require('electron')
const { ipcMain, Tray, Menu, nativeImage } = require('electron')
const path = require('path')
const ROOT = __dirname
module.exports = function(win) {
app.__TRAY__ = new Tray(path.join(ROOT, '../images/trays/trayTemplate.png'))
let menuList = Menu.buildFromTemplate([
{
label: '显示主窗口',
click() {
win.webContents.send('dock-click')
}
},
{ type: 'separator' },
{ label: '退出', role: 'quit' }
])
if (process.platform === 'darwin') {
app.__TRAY__.on('click', _ => {
win.webContents.send('dock-click')
})
app.__TRAY__.on('right-click', _ => {
app.__TRAY__.popUpContextMenu(menuList)
})
} else {
app.__TRAY__.setContextMenu(menuList)
}
function ctrlTrayBtn() {
var prev = new Tray(path.join(__dirname, '../images/ctrl/prev.png'))
var next = new Tray(path.join(__dirname, '../images/ctrl/next.png'))
}
exports.createAppTray = function(win) {
var tray = new Tray(path.join(__dirname, '../images/trays/trayTemplate.png'))
tray.setIgnoreDoubleClickEvents(true)
// let menuList = Menu.buildFromTemplate([
// {
// label: '显示主窗口',
// click() {
// win.webContents.send('dock-click')
// }
// },
// { type: 'separator' },
// { label: '退出', role: 'quit' }
// ])
// if (process.platform === 'darwin') {
// tray.on('click', _ => {
// win.webContents.send('dock-click')
// })
// tray.on('right-click', _ => {
// tray.popUpContextMenu(menuList)
// })
// } else {
// tray.setContextMenu(menuList)
// }
}
exports.createLrcTray = function(win) {
var nullImage = nativeImage.createEmpty()
var topbarLrc = new Tray(nullImage)
topbarLrc.setTitle('这是顶栏歌词, blablablabla...')
topbarLrc.setIgnoreDoubleClickEvents(true)
// ctrlTrayBtn()
}

View File

@ -4,8 +4,6 @@
* @date 2019/01/26 18:28:22
*/
'use strict'
const { BrowserWindow } = require('electron')
/**
@ -45,55 +43,6 @@ exports.createMainWindow = function(icon) {
return win
}
/**
* 依赖异常显示窗口
*/
exports.createErrorWindow = function() {
let win = new BrowserWindow({
width: 600,
height: 360,
skipTaskbar: true,
maximizable: false,
minimizable: false,
resizable: false,
webPreferences: {
devTools: false
}
})
win.setMenuBarVisibility(false)
win.loadURL('app://local/depends.html')
win.on('closed', _ => {
app.exit()
})
}
/**
* 桌面歌词窗口
*/
exports.createDesktopLrcWindow = function(screen) {
let win = new BrowserWindow({
title: '',
width: 1000,
height: 100,
frame: false,
resizable: false,
alwaysOnTop: true,
skipTaskbar: true,
x: (screen.width - 1024) / 2,
y: screen.height - 100,
transparent: true,
hasShadow: false,
thickFrame: false,
show: false,
webPreferences: {
nodeIntegration: true
}
})
win.loadURL('app://local/desktop-lrc.html')
return win
}
/**
* 应用迷你窗口
*/
@ -108,9 +57,10 @@ exports.createMiniWindow = function(screen) {
skipTaskbar: true,
x: screen.width - 320,
y: 0,
thickFrame: false,
show: false,
webPreferences: {
webSecurity: false,
experimentalFeatures: true,
nodeIntegration: true
}
})