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-20 18:23:53 +08:00
parent b21ec91170
commit 641199aa1f
19 changed files with 364 additions and 291 deletions

View File

@ -11,7 +11,7 @@
"name": "yutent",
"email": "yutent.io@gmail.com"
},
"homepage": "https://yutent.me",
"homepage": "https://yutent.top",
"license": "MIT",
"dependencies": {
"crypto.js": "^2.0.2",

File diff suppressed because one or more lines are too long

View File

@ -39,41 +39,6 @@
z-index: 9;
height: 26px;
background: rgba(32, 32, 32, 0.5);
.btn-box {
display: inline-flex;
width: auto;
height: 12px;
padding: 0 8px;
.item {
display: inline-flex;
width: 12px;
height: 12px;
margin: 0 3px;
background: url(/images/btn-grey.svg) no-repeat;
background-size: cover;
}
&.focus {
.quit {
background-image: url(/images/btn-close.svg);
}
.min {
background-image: url(/images/btn-mini.svg);
}
// .max {background-image:url(/images/btn-maxi.svg);}
}
&:hover {
.quit {
background-image: url(/images/btn-close_a.svg);
}
.min {
background-image: url(/images/btn-mini_a.svg);
}
// .max {background-image:url(/images/btn-maxi_a.svg);}
}
}
}
.main-body {
@ -255,11 +220,22 @@
}
.progress {
position: relative;
flex: 1;
display: flex;
align-items: flex-start;
height: 3px;
background: #b2cfe3;
input {
position: absolute;
left: 0;
top: 0;
display: block;
width: 100%;
height: 3px;
opacity: 0;
}
}
.thumb {
@ -373,7 +349,46 @@
transition: background 0.1s ease-in-out;
cursor: pointer;
&.volume {
&.volume,
&.mute {
position: relative;
a {
display: block;
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-size: cover;
transition: background 0.1s ease-in-out;
}
.range {
visibility: hidden;
position: absolute;
right: -5px;
bottom: 20px;
width: 32px;
height: 128px;
padding: 14px;
border-radius: 3px;
background: rgba(128, 128, 128, 0.8);
input {
display: block;
width: 4px;
height: 100px;
border-radius: 3px;
appearance: slider-vertical;
}
}
&:hover {
.range {
visibility: visible;
}
}
}
&.volume a {
background-image: url(/images/ctrl/volume.png);
&:hover,
&:active {
@ -381,7 +396,7 @@
}
}
&.mute {
&.mute a {
background-image: url(/images/ctrl/mute.png);
&:hover,
&:active {

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #f55449;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_close_hover</title>
<g id="red">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36" transform="translate(512 1236.08) rotate(-135)"/>
<rect class="cls-3" x="191" y="475" width="640" height="72" rx="36" ry="36" transform="translate(1233.66 511) rotate(135)"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 899 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #dee1e3;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_grey</title>
<g id="grey">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 588 B

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #39ea49;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_max_hover</title>
<g id="green">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<path class="cls-3" d="M395.93,267.8a31.32,31.32,0,0,1,22.86-9.48l319.33-2.55A31.25,31.25,0,0,1,770,287.64l-2.5,319.29A32,32,0,0,1,758,629.79a31.06,31.06,0,0,1-22.86,9.48,30.67,30.67,0,0,1-22.72-9.15L395.65,313.33c-6.22-6.13-9.24-13.67-9.15-22.72a31.28,31.28,0,0,1,9.43-22.82ZM629.84,758A31.32,31.32,0,0,1,607,767.45L287.64,770a31.25,31.25,0,0,1-31.87-31.87l2.5-319.29A32,32,0,0,1,267.75,396a31.06,31.06,0,0,1,22.86-9.48,30.67,30.67,0,0,1,22.72,9.15L630.12,712.44c6.22,6.13,9.24,13.67,9.15,22.72A31.28,31.28,0,0,1,629.84,758Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 595 B

View File

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<defs>
<style>
.cls-1 {
fill: #fac536;
}
.cls-2 {
fill: #333;
opacity: 0.1;
}
.cls-3 {
opacity: 0.7;
}
</style>
</defs>
<title>btn_min_hover</title>
<g id="yellow">
<circle class="cls-1" cx="512" cy="512" r="512"/>
<path class="cls-2" d="M512,1024A512.13,512.13,0,0,1,312.7,40.24,512.13,512.13,0,0,1,711.3,983.76,508.81,508.81,0,0,1,512,1024Zm0-984A472.13,472.13,0,0,0,328.28,946.92,472.13,472.13,0,0,0,695.72,77.08,469,469,0,0,0,512,40Z"/>
<rect class="cls-3" x="192" y="476" width="640" height="72" rx="36" ry="36"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 723 B

View File

@ -16,11 +16,7 @@
</div>
<div class="title-bar app-drag">
<div class="btn-box app-nodrag focus">
<a class="item quit"></a>
<a class="item min"></a>
<a class="item max"></a>
</div>
</div>
<main class="main-body">
@ -57,8 +53,8 @@
<wc-scroll class="list">
<section
class="item"
@click="prviewSong(it, i)"
@dblclick="playSong(it, i)"
@click="previewSong(it, i)"
@dblclick="playSong(i)"
:class="{on: curr === i}"
:for="i it in list">
<span class="idx" :text="i + 1"></span>
@ -75,7 +71,8 @@
<section class="stat-bar">
<span class="time" :text="song.time | time"></span>
<div class="progress">
<span class="thumb" :css="{width: ~~((100 * song.time) / song.duration) + '%'}"></span>
<span class="thumb" :css="{width: ((100 * song.time) / song.duration).toFixed(1) + '%'}"></span>
<input type="range" max="712" min="0" step="1" :duplex="progress">
</div>
<span class="time" :text="song.duration | time"></span>
</section>
@ -87,22 +84,30 @@
</div>
<div class="play-btn">
<a class="item prev"></a>
<a class="item" :class="{on: isplaying, off: !isplaying}" @click="play"></a>
<a class="item next"></a>
<a class="item prev" @click="play(-1)"></a>
<a class="item"
:class="{on: isplaying, off: !isplaying}"
@click="play(0)">
</a>
<a class="item next" @click="play(1)"></a>
</div>
<div class="holder"></div>
<div class="play-action">
<a class="item" :class="{
<item class="item" :class="{
all: playmode === 1,
single: playmode === 2,
rand: playmode === 3,
}"
@click="switchMode">
</a>
<a class="item" :class="{mute: mute, volume: !mute}" @click="toggleMute"></a>
</item>
<item class="item" :class="{mute: mute || volume <= 0, volume: !mute && volume > 0}">
<div class="range">
<input type="range" max="100" min="0" step="1" :duplex="volume">
</div>
<a @click="toggleMute"></a>
</item>
</div>
</section>
</div>

View File

@ -15,6 +15,8 @@ import Player from '/js/lib/audio/index.js'
const id3 = require('music-metadata')
const MODE_DICT = { 1: 'all', 2: 'single', 3: 'random' }
var kb = new Keyboard()
var player = new Player()
@ -26,9 +28,10 @@ Anot({
$id: 'app',
state: {
defaultCover: '/images/disk.png',
isplaying: true,
playmode: 1,
isplaying: false,
playmode: +Anot.ls('app_mode') || 1,
mute: false,
volume: +Anot.ls('app_volume') || 50,
preview: {
name: '',
album: '',
@ -42,24 +45,13 @@ Anot({
time: 0,
duration: 0
},
curr: 2,
progress: 0,
curr: -1,
list: []
},
async mounted() {
var list = app.dispatch('scan-dir', { path: '/Volumes/extends/music' })
this.list = list
for (let it of this.list) {
let { album, artist, title, duration } = await this.getID3(it.path)
it.name = title || it.name
it.artist = artist
it.album = album
it.duration = duration
}
player.load(list.map(it => `sonist://${it.path}`))
var list = app.dispatch('get-all-songs')
// var list = app.dispatch('scan-dir', { dir: '/Volumes/extends/music' })
kb.on(['left'], ev => {
var time = this.song.time - 5
@ -75,17 +67,121 @@ Anot({
}
this.song.time = time
})
kb.on(['down'], ev => {
var vol = +this.volume - 5
if (vol < 0) {
vol = 0
}
this.volume = vol
})
kb.on(['up'], ev => {
var vol = +this.volume + 5
if (vol > 100) {
vol = 100
}
this.volume = vol
})
// for (let it of list) {
// let { album, artist, title, duration } = await this.getID3(it.file_path)
// it.name = title || it.name
// it.artist = artist
// it.album = album
// it.duration = duration
// app.dispatch('add-song', {
// name: it.name,
// artist,
// album,
// duration,
// file_path: it.path
// })
// }
this.list = list
player.volume = this.volume / 100
player.load(list.map(it => `sonist://${it.file_path}`))
player.on('play', time => {
this.song.time = time
})
player.on('stop', _ => {
var idx = this.curr
var repeat = false
switch (this.playmode) {
case 1: // all
idx++
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(this.list[idx], idx, repeat)
})
},
watch: {
volume(v) {
Anot.ls('app_volume', v)
player.volume = v / 100
},
playmode(v) {
Anot.ls('app_mode', v)
},
progress(v) {
var t = +(this.song.duration * (v / 712)).toFixed(2)
player.seek(t)
}
},
methods: {
load() {
// window.player = new Howl({
// src: [`sonist://${this.list[0].path}`]
// })
// window.player = this.__PLAYER__
},
play() {
this.isplaying = !this.isplaying
play(act) {
var idx = this.curr
var repeat = false
switch (act) {
case 0:
if (idx > -1) {
player.play(-1)
} else {
this.playSong(0)
}
this.isplaying = !this.isplaying
break
default:
switch (this.playmode) {
case 1: // all
idx += act
if (idx < 0) {
idx = this.list.length - 1
}
if (idx >= this.list.length) {
idx = 0
}
break
case 2: // single
if (idx === -1) {
idx = ~~(Math.random() * this.list.length)
}
repeat = true
break
case 3: // random
idx = ~~(Math.random() * this.list.length)
break
}
this.playSong(idx, repeat)
break
}
},
switchMode() {
var n = this.playmode + 1
if (n > 3) {
@ -105,18 +201,30 @@ Anot({
return { album, artist, title, duration: ~~duration }
})
},
prviewSong(it, i) {
previewSong(it) {
var { album, artist, name, cover } = it
Object.assign(this.preview, { album, artist, name, cover })
},
playSong(it, i) {
playSong(i, repeat) {
//
var it = this.list[i]
if (this.curr === i) {
if (repeat) {
this.song.time = 0
player.play(-1, repeat)
}
return
}
this.curr = i
this.song.name = it.name
this.song.artist = it.artist
this.song.duration = it.duration
this.song.src = `file://${it.path}`
this.song.src = `file://${it.file_path}`
this.song.time = 0
this.isplaying = true
this.previewSong(it)
player.play(i)
}
}
})

View File

@ -4,8 +4,12 @@
* @date 2020/11/19 17:32:19
*/
import $ from '../utils.js'
import fetch from '../fetch/index.js'
const { EventEmitter } = require('events')
const util = require('util')
function hide(target, key, value) {
Object.defineProperty(target, key, {
value,
@ -19,16 +23,39 @@ export default class Player {
constructor() {
hide(this, '__LIST__', [])
hide(this, '__AC__', new AudioContext())
hide(this, '__AUDIO__', new Audio())
hide(this, 'props', {
curr: '',
stat: 'ready',
volume: 0.5,
mode: 'all', // 循环模式, all, single, rand
time: 0,
duration: 0
})
hide(this, 'track', null)
hide(this, 'gain', null)
this.__main__()
}
__main__() {
hide(this, '__AUDIO__', new Audio())
this.__playFn = $.bind(this.__AUDIO__, 'timeupdate', _ => {
this.emit('play', this.__AUDIO__.currentTime)
})
this.__stopFn = $.bind(this.__AUDIO__, 'ended', _ => {
this.props.stat = 'paused'
this.emit('stop')
})
}
__destroy__() {
$.unbind(this.__AUDIO__, 'timeupdate', this.__playFn)
$.unbind(this.__AUDIO__, 'ended', this.__stopFn)
this.__AUDIO__.pause()
this.__AUDIO__.currentTime = 0
delete this.__playFn
delete this.__stopFn
}
load(list) {
@ -37,11 +64,16 @@ export default class Player {
}
async _getTrack(file) {
this.__main__()
this.__AUDIO__.src = URL.createObjectURL(
await fetch(file).then(r => r.blob())
)
console.log(this.__AUDIO__)
return this.__AC__.createMediaElementSource(this.__AUDIO__)
this.gain = this.__AC__.createGain()
this.gain.gain.value = this.volume
this.track = this.__AC__.createMediaElementSource(this.__AUDIO__)
this.track.connect(this.gain).connect(this.__AC__.destination)
}
get volume() {
@ -49,7 +81,7 @@ export default class Player {
}
set volume(val) {
val = +val || 0.5
val = +val
if (val < 0) {
val = 0
}
@ -57,31 +89,31 @@ export default class Player {
val = 1
}
this.props.volume = val
}
get mode() {
return this.props.mode
}
set mode(val) {
this.props.mode = val
if (this.gain) {
this.gain.gain.value = val
}
}
get time() {
return this.props.time
return this.__AUDIO__.currentTime
}
get stat() {
return this.props.stat
}
async play(id) {
var url, gain
if (id === undefined) {
/**
* id: 歌曲序号
* force: 强制重新播放
*/
play(id, force = false) {
if (id === -1) {
if (this.track) {
if (force) {
this.seek(0)
this.props.stat = 'paused'
}
if (this.stat === 'playing') {
this.props.time = this.track.context.currentTime
this.__AUDIO__.pause()
this.props.stat = 'paused'
} else if (this.stat === 'paused') {
@ -90,21 +122,17 @@ export default class Player {
}
}
} else {
url = this.__LIST__[id]
var url = this.__LIST__[id]
if (!url || this.props.curr === url) {
return
}
gain = this.__AC__.createGain()
if (this.track) {
this.stop()
}
gain.gain.value = this.volume
this.track = await this._getTrack(url)
this.track.connect(gain).connect(this.__AC__.destination)
this.props.curr = url
this._getTrack(url)
this.__AUDIO__.play()
this.props.stat = 'playing'
@ -112,15 +140,19 @@ export default class Player {
}
seek(time) {
this.props.time = time
if (this.track) {
this.__AUDIO__.currentTime = time
}
}
stop() {
if (this.track) {
this.__AUDIO__.pause()
this.track = null
this.props.time = 0
this.gain = null
this.__destroy__()
this.props.stat = 'stoped'
}
}
}
util.inherits(Player, EventEmitter)

View File

@ -7,7 +7,7 @@
const { ipcRenderer } = require('electron')
export default {
dispatch(type = '', params = {}) {
return ipcRenderer.sendSync('app', Object.assign(params, { type }))
dispatch(type = '', data = {}) {
return ipcRenderer.sendSync('app', { data, type })
}
}

View File

@ -7,12 +7,12 @@
const { app, session, protocol, globalShortcut } = require('electron')
const path = require('path')
const fs = require('iofs')
// const {exec} = require('child_process')
require('./tools/init.js')
const { createMainWindow, createMiniWindow } = require('./tools/windows.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',
@ -47,18 +47,17 @@ protocol.registerSchemesAsPrivileged([
// 初始化应用
app.once('ready', () => {
// 注册协议
protocol.registerBufferProtocol('app', (req, cb) => {
let file = decodeURIComponent(req.url.replace(/^app:\/\/local\//, ''))
let ext = path.extname(req.url)
let buff = fs.cat(path.resolve(__dirname, file))
protocol.registerBufferProtocol('app', function(req, cb) {
var file = decodeURIComponent(req.url.replace(/^app:\/\/local\//, ''))
var ext = path.extname(req.url)
var buff = fs.cat(path.resolve(__dirname, file))
cb({ data: buff, mimeType: MIME_TYPES[ext] })
})
protocol.registerBufferProtocol('sonist', (req, cb) => {
let file = decodeURIComponent(req.url.replace(/^sonist:[\/]+/, '/'))
let ext = path.extname(req.url)
let buff = fs.cat(file)
cb({ data: buff, mimeType: MIME_TYPES[ext] || MIME_TYPES.all })
protocol.registerBufferProtocol('sonist', function(req, cb) {
var file = decodeURIComponent(req.url.replace(/^sonist:[\/]+/, '/'))
var ext = path.extname(req.url)
cb({ data: fs.cat(file), mimeType: MIME_TYPES[ext] || MIME_TYPES.all })
})
// 修改app的UA
session.defaultSession.setUserAgent(
@ -74,7 +73,7 @@ app.once('ready', () => {
// mac专属事件,点击dock栏图标,可激活窗口
app.on('activate', _ => {
if (win) {
win.webContents.send('dock-click')
win.restore()
}
})
})

View File

@ -4,6 +4,7 @@
* @date 2020/07/14 18:17:59
*/
// 歌单
const TABLE_PLAYLIST = `
CREATE TABLE IF NOT EXISTS "playlist" (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
@ -11,15 +12,16 @@ CREATE TABLE IF NOT EXISTS "playlist" (
)
`
// aid: 歌手ID
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,
"artist" char(128) NOT NULL,
"album" char(128) NOT NULL,
"duration" integer NOT NULL,
"cover" char(256) NOT NULL,
"path" char(256) NOT NULL,
"file_path" char(256) NOT NULL,
"lrc" text NOT NULL
)
`
@ -28,15 +30,7 @@ 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
"pid" integer NOT NULL
)
`
@ -50,5 +44,4 @@ 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

@ -74,11 +74,63 @@ ipcMain.on('app', (ev, conn) => {
fs.echo(JSON.stringify(conn.data, null, 2), INIT_FILE)
break
case 'add-song':
var {
name,
artist = '',
album = '',
duration,
cover = '',
file_path,
lrc = ''
} = conn.data
db.query(
'INSERT INTO `songs` (name, artist, album, duration, cover, file_path, lrc) VALUES ($name, $artist, $album, $duration, $cover, $file_path, $lrc)',
{
$name: name,
$artist: artist,
$album: album,
$duration: duration,
$cover: cover,
$file_path: file_path,
$lrc: lrc
}
)
ev.returnValue = true
break
case 'get-all-songs':
db.getAll('SELECT id, name, duration, artist, file_path FROM songs')
.then(res => {
ev.returnValue = res
})
.catch(err => {
ev.returnValue = err
})
break
case 'get-songs':
db.getAll(
'SELECT id, name, duration, artist, file_path ' +
'FROM songs ' +
'WHERE id IN ' +
'(SELECT sid FROM relations WHERE pid = $pid)',
{ $pid: conn.pid }
)
.then(res => {
ev.returnValue = res
})
.catch(err => {
ev.returnValue = err
})
break
// 扫描目录
case 'scan-dir':
if (fs.isdir(conn.path)) {
var { dir } = conn.data
if (fs.isdir(dir)) {
let list = fs
.ls(conn.path, true)
.ls(dir, true)
.filter(it => {
if (fs.isdir(it)) {
return false
@ -97,7 +149,13 @@ ipcMain.on('app', (ev, conn) => {
end: 256,
encoding: 'base64'
})
return { name, path: it, artist: '', album: '', duration: '00:00' }
return {
name,
file_path: it,
artist: '',
album: '',
duration: '00:00'
}
})
ev.returnValue = list
} else {

View File

@ -15,7 +15,13 @@ module.exports = function(win) {
submenu: [
{ role: 'about', label: '关于 Sonist' },
{ type: 'separator' },
{ role: 'quit', label: '退出' }
{
label: '退出 Sonist',
accelerator: 'Command+Q',
click(a, b, ev) {
win.destroy()
}
}
]
},
{

View File

@ -16,6 +16,7 @@ exports.createMainWindow = function(icon) {
width: 820,
height: 460,
frame: false,
titleBarStyle: 'hiddenInset',
resizable: false,
maximizable: false,
icon,
@ -40,6 +41,11 @@ exports.createMainWindow = function(icon) {
win.openDevTools()
})
win.on('close', ev => {
ev.preventDefault()
win.hide()
})
return win
}