458 lines
10 KiB
JavaScript
458 lines
10 KiB
JavaScript
/**
|
|
* {sonist app}
|
|
* @author yutent<yutent@doui.cc>
|
|
* @date 2018/12/16 17:15:57
|
|
*/
|
|
|
|
import '/lib/anot.next.js'
|
|
import layer from '/lib/layer/index.js'
|
|
import store from '/lib/store/index.js'
|
|
import AudioPlayer from '/lib/audio/index.js'
|
|
import Lyrics from '/lib/lyrics/index.js'
|
|
|
|
import Api from '/js/api.js'
|
|
|
|
import Artist from '/js/modules/artist.js'
|
|
import Local from '/js/modules/local.js'
|
|
import Profile from '/js/modules/profile.js'
|
|
|
|
import KTV from '/js/modules/ktv.js'
|
|
|
|
const log = console.log
|
|
|
|
const fs = require('iofs')
|
|
const path = require('path')
|
|
|
|
const { remote } = require('electron')
|
|
|
|
const WIN = remote.getCurrentWindow()
|
|
const CURR_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 = {
|
|
0: 'all',
|
|
1: 'single',
|
|
2: 'random'
|
|
}
|
|
const COLORS = [
|
|
{
|
|
title: '#62778d',
|
|
lrc: '#98acae',
|
|
bar1: '#dae1e9',
|
|
bar2: '#3fc2a7'
|
|
},
|
|
{
|
|
title: '#fff',
|
|
lrc: '#d7d8db',
|
|
bar1: '#454545',
|
|
bar2: '#fff'
|
|
}
|
|
]
|
|
|
|
const FONTS_NAME =
|
|
' Helvetica, Arial,"WenQuanYi Micro Hei","PingFang SC","Hiragino Sans GB","Segoe UI", "Microsoft Yahei", sans-serif'
|
|
|
|
// 本地音乐和试用音乐列表
|
|
window.LS = store.collection('local')
|
|
window.TS = store.collection('temp')
|
|
// 音乐播放器
|
|
window.SONIST = new AudioPlayer()
|
|
window.LYRICS = new Lyrics()
|
|
|
|
let appInit = fs.cat(APP_INI_PATH)
|
|
|
|
Anot.ss('app-init', appInit + '')
|
|
|
|
appInit = JSON.parse(appInit)
|
|
|
|
const LRC_WIN = new remote.BrowserWindow({
|
|
title: '',
|
|
width: 1024,
|
|
height: 100,
|
|
frame: false,
|
|
resizable: false,
|
|
alwaysOnTop: true,
|
|
x: (CURR_SCREEN.size.width - 1024) / 2,
|
|
y: CURR_SCREEN.size.height - 100,
|
|
skipTaskbar: true,
|
|
hasShadow: false,
|
|
thickFrame: false,
|
|
transparent: true,
|
|
show: false
|
|
})
|
|
// LRC_WIN.setIgnoreMouseEvents(true)
|
|
window.LRC_WIN = LRC_WIN
|
|
|
|
LRC_WIN.loadURL('app://sonist/desktop-lrc.html')
|
|
|
|
Anot({
|
|
$id: 'app',
|
|
state: {
|
|
theme: appInit.theme || 1, // 1:macos, 2: deepin
|
|
winFocus: false,
|
|
mod: 'local',
|
|
playMode: Anot.ls('play-mode') >>> 0, // 0:all | 1:single | 2:random
|
|
ktvMode: 0,
|
|
isPlaying: false,
|
|
optBoxShow: false,
|
|
volumeCtrlShow: false,
|
|
volume: Anot.ls('volume') || 70,
|
|
curr: {
|
|
id: '',
|
|
title: '',
|
|
artist: '',
|
|
album: '',
|
|
time: 0,
|
|
duration: 0
|
|
},
|
|
ctrlLrc: '暂无歌词...',
|
|
...KTV.data
|
|
},
|
|
skip: [],
|
|
computed: {
|
|
views() {
|
|
if (!this.mod) {
|
|
return
|
|
}
|
|
return '/views/' + this.mod + '.htm'
|
|
},
|
|
coverBG() {
|
|
if (this.curr.cover) {
|
|
return `url(${this.curr.cover})`
|
|
} else {
|
|
return 'none'
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
mod(val) {
|
|
this.activeModule(val)
|
|
}
|
|
},
|
|
mounted() {
|
|
let canvas = this.$refs.player
|
|
|
|
// 画布放大4倍, 以解决模糊的问题
|
|
this.__WIDTH__ = canvas.clientWidth * 4
|
|
this.__HEIGHT__ = canvas.clientHeight * 4
|
|
|
|
canvas.width = this.__WIDTH__
|
|
canvas.height = this.__HEIGHT__
|
|
this.__CTX__ = canvas.getContext('2d')
|
|
|
|
this.draw(true)
|
|
|
|
// 修改歌曲进度
|
|
canvas.addEventListener(
|
|
'click',
|
|
ev => {
|
|
if (!this.curr.id) {
|
|
return
|
|
}
|
|
let rect = canvas.getBoundingClientRect()
|
|
let aw = rect.width
|
|
let ax = ev.pageX - rect.left
|
|
let ay = ev.pageY - rect.top
|
|
|
|
if (ax < 80) {
|
|
this.ktvMode = this.ktvMode ^ 1
|
|
return
|
|
}
|
|
if (ax > 124 && ay > 55 && ay < 64) {
|
|
let pp = (ax - 124) / (aw - 124)
|
|
this.curr.time = pp * this.curr.duration
|
|
SONIST.seek(this.curr.time)
|
|
LYRICS.seek(this.curr.time)
|
|
if (!this.isPlaying) {
|
|
this.draw()
|
|
}
|
|
}
|
|
},
|
|
false
|
|
)
|
|
|
|
// 设置循环模式
|
|
SONIST.mode = PLAY_MODE[this.playMode]
|
|
SONIST.volume = this.volume
|
|
|
|
SONIST.on('play', time => {
|
|
this.curr.time = time
|
|
LYRICS.update(time)
|
|
})
|
|
|
|
SONIST.on('end', time => {
|
|
this.nextSong(1)
|
|
})
|
|
|
|
// 控制条的单行歌词
|
|
LYRICS.on('ctrl-lrc', lrc => {
|
|
this.ctrlLrc = lrc
|
|
})
|
|
|
|
// ktv模式的歌词
|
|
LYRICS.on('ktv-lrc', lrc => {
|
|
this.lrc = lrc
|
|
LRC_WIN.emit('ktv-lrc', lrc)
|
|
})
|
|
|
|
// ktv模式的歌词
|
|
LYRICS.on('view-all', lrc => {
|
|
this.allLrc = lrc
|
|
})
|
|
|
|
this.activeModule(this.mod)
|
|
|
|
remote.app.on('browser-window-focus', _ => {
|
|
this.winFocus = true
|
|
})
|
|
remote.app.on('browser-window-blur', _ => {
|
|
this.winFocus = false
|
|
})
|
|
},
|
|
methods: {
|
|
quit(force) {
|
|
if (force) {
|
|
remote.app.exit()
|
|
} else {
|
|
if (appInit.allowPlayOnBack) {
|
|
WIN.hide()
|
|
} else {
|
|
remote.app.exit()
|
|
}
|
|
}
|
|
},
|
|
minimize() {},
|
|
maximize() {},
|
|
|
|
activeModule(mod) {
|
|
switch (mod) {
|
|
case 'artist':
|
|
Artist.__init__()
|
|
break
|
|
case 'local':
|
|
Local.__init__()
|
|
break
|
|
case 'profile':
|
|
Profile.__init__()
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
},
|
|
|
|
toggleOptBox() {
|
|
this.optBoxShow = !this.optBoxShow
|
|
},
|
|
toggleDesktopLrc() {
|
|
if (LRC_WIN.isVisible()) {
|
|
LRC_WIN.hide()
|
|
} else {
|
|
LRC_WIN.show()
|
|
}
|
|
},
|
|
toggleModule(mod) {
|
|
if ('mv' === mod) {
|
|
return
|
|
}
|
|
this.optBoxShow = false
|
|
this.mod = mod
|
|
},
|
|
// 设置保存 回调
|
|
onProfileSaved() {
|
|
this.toggleModule('local')
|
|
appInit = Anot.ss('app-init')
|
|
},
|
|
|
|
togglePlayMode() {
|
|
let mod = this.playMode
|
|
mod++
|
|
if (mod > 2) {
|
|
mod = 0
|
|
}
|
|
this.playMode = mod
|
|
SONIST.mode = PLAY_MODE[mod]
|
|
Anot.ls('play-mode', mod)
|
|
},
|
|
|
|
// 修改音量
|
|
changeValume(ev) {
|
|
let volume = 575 - ev.pageY
|
|
if (volume < 0) {
|
|
volume = 0
|
|
}
|
|
if (volume > 100) {
|
|
volume = 100
|
|
}
|
|
this.volume = volume
|
|
SONIST.volume = volume
|
|
Anot.ls('volume', volume)
|
|
},
|
|
|
|
__draw__() {
|
|
let play = this.isPlaying
|
|
let rx = (play ? 112 : 40) + this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标X
|
|
let ry = this.__HEIGHT__ / 2 // 旋转唱片的圆心坐标Y
|
|
let pw = this.__WIDTH__ - this.__HEIGHT__ - 180 // 进度条总长度
|
|
let wl = this.__HEIGHT__ + 180 // 文字的坐标X
|
|
|
|
let { time, duration, title, artist } = this.curr
|
|
let lrc = this.ctrlLrc
|
|
let pp = time / duration // 进度百分比
|
|
time = Anot.filters.time(time)
|
|
duration = Anot.filters.time(duration)
|
|
|
|
this.__CTX__.clearRect(0, 0, this.__WIDTH__, this.__HEIGHT__)
|
|
this.__CTX__.save()
|
|
|
|
// 将原点移到唱片圆心, 旋转完再回到初始值
|
|
this.__CTX__.translate(rx, ry)
|
|
this.__CTX__.rotate(this.__DEG__ * Math.PI)
|
|
this.__CTX__.translate(-rx, -ry)
|
|
|
|
this.__CTX__.drawImage(
|
|
this.__img1__,
|
|
play ? 112 : 40,
|
|
0,
|
|
this.__HEIGHT__,
|
|
this.__HEIGHT__
|
|
)
|
|
|
|
this.__CTX__.restore()
|
|
|
|
this.__CTX__.drawImage(
|
|
this.__img2__,
|
|
0,
|
|
0,
|
|
this.__HEIGHT__,
|
|
this.__HEIGHT__
|
|
)
|
|
|
|
// 歌曲标题和歌手
|
|
this.__CTX__.fillStyle = COLORS[this.ktvMode].title
|
|
this.__CTX__.font = '56px' + FONTS_NAME
|
|
this.__CTX__.fillText(`${title} - ${artist}`, wl, 100)
|
|
|
|
// 时间
|
|
this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
|
|
this.__CTX__.font = '48px' + FONTS_NAME
|
|
this.__CTX__.fillText(`${time} / ${duration}`, this.__WIDTH__ - 280, 100)
|
|
|
|
// 歌词
|
|
this.__CTX__.fillStyle = COLORS[this.ktvMode].lrc
|
|
this.__CTX__.font = '48px' + FONTS_NAME
|
|
this.__CTX__.fillText(lrc, wl, 180)
|
|
|
|
// 进度条
|
|
this.__CTX__.fillStyle = COLORS[this.ktvMode].bar1
|
|
this.__CTX__.fillRect(wl, 230, pw, 16)
|
|
this.__CTX__.fillStyle = COLORS[this.ktvMode].bar2
|
|
this.__CTX__.fillRect(wl, 230, pw * pp, 16)
|
|
|
|
this.__DEG__ += 0.01
|
|
},
|
|
|
|
draw(force) {
|
|
if (force) {
|
|
this.__img1__ = new Image()
|
|
this.__img2__ = new Image()
|
|
|
|
let p1 = Promise.defer()
|
|
let p2 = Promise.defer()
|
|
|
|
this.__img1__.onload = p1.resolve
|
|
this.__img2__.onload = p2.resolve
|
|
this.__img1__.src = '/images/disk.png'
|
|
this.__img2__.src = this.curr.cover || '/images/album.png'
|
|
|
|
Promise.all([p1.promise, p2.promise]).then(_ => {
|
|
clearInterval(this.timer)
|
|
this.__DEG__ = 0.01
|
|
if (this.isPlaying) {
|
|
this.timer = setInterval(_ => {
|
|
this.__draw__()
|
|
}, 20)
|
|
} else {
|
|
this.__draw__()
|
|
}
|
|
})
|
|
} else {
|
|
clearInterval(this.timer)
|
|
if (this.isPlaying) {
|
|
this.timer = setInterval(_ => {
|
|
this.__draw__()
|
|
}, 20)
|
|
} else {
|
|
this.__draw__()
|
|
}
|
|
}
|
|
},
|
|
|
|
nextSong(step) {
|
|
let _p = null
|
|
if (step > 0) {
|
|
_p = SONIST.next()
|
|
} else {
|
|
_p = SONIST.prev()
|
|
}
|
|
this.isPlaying = false
|
|
_p.then(it => {
|
|
if (this.mod === 'local') {
|
|
Local.__updateSong__(it)
|
|
}
|
|
// 通知子模块歌曲已经改变
|
|
this.$fire('child!curr', it.id)
|
|
this.play(it)
|
|
})
|
|
},
|
|
|
|
pause() {
|
|
this.isPlaying = false
|
|
},
|
|
|
|
updateCurr(obj) {
|
|
let old = this.curr.$model
|
|
this.curr = Object.assign(old, obj)
|
|
},
|
|
|
|
play(song) {
|
|
// 有参数的,说明是播放回调通知
|
|
// 此时仅更新播放控制条的信息即可
|
|
if (song) {
|
|
song.time = 0
|
|
this.ctrlLrc = '暂无歌词...'
|
|
this.updateCurr(song)
|
|
this.isPlaying = true
|
|
this.draw(true)
|
|
LYRICS.__init__(song.lyrics)
|
|
} else {
|
|
if (SONIST.stat === 'ready') {
|
|
let played = this.isPlaying
|
|
this.isPlaying = !this.isPlaying
|
|
if (this.curr.id) {
|
|
if (played) {
|
|
SONIST.pause()
|
|
} else {
|
|
SONIST.play()
|
|
}
|
|
this.draw()
|
|
} else {
|
|
let lastPlay = Anot.ls('last-play') || 0
|
|
SONIST.play(lastPlay).then(it => {
|
|
it.time = 0
|
|
this.ctrlLrc = '暂无歌词...'
|
|
this.updateCurr(it)
|
|
this.draw(true)
|
|
// this.ktvMode = 1
|
|
|
|
LYRICS.__init__(it.lyrics)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
},
|
|
...KTV.methods
|
|
}
|
|
})
|
JavaScript
60.1%
SCSS
19.2%
HTML
16.9%
CSS
3.8%