diff --git a/package.json b/package.json index aefc11f..f8ffd99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sonist", - "version": "0.9.3", + "version": "0.9.4", "description": "Music Player", "main": "src/main.js", "scripts": { diff --git a/src/css/desktop-lrc.css b/src/css/desktop-lrc.css index 1e97196..1720cac 100644 --- a/src/css/desktop-lrc.css +++ b/src/css/desktop-lrc.css @@ -1 +1 @@ -.do-fn-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.do-fn-nodrag{-webkit-app-region:no-drag}html{font-size:62.5%}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;background:transparent;font-size:1.4rem;color:#62778d}body .lrc-box{position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem}body .lrc-box section{flex:1;display:flex}body .lrc-box section.left{justify-content:flex-start}body .lrc-box section.right{justify-content:flex-end}body .lrc-box section span{-webkit-background-clip:text !important;background-clip:text !important;color:transparent}body .lrc-box section span.shadow{text-shadow:0 0 0.5rem rgba(0,0,0,0.5)}body .touch-bar{position:absolute;left:0;top:0;z-index:9;width:100%;height:2rem}body .quit,body .lock{visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold}body .quit:hover,body .lock:hover{color:#ff5061}body .lock{right:4rem}body .lock.actived{color:#dae1e9}body:hover{background:rgba(29,35,44,0.2)}body:hover .quit,body:hover .lock{visibility:visible} +.do-fn-drag{-webkit-app-region:drag;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.do-fn-nodrag{-webkit-app-region:no-drag}html{font-size:62.5%}body{position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-height:1.5;background:transparent;font-size:1.4rem;color:#62778d}body .lrc-box{position:absolute;left:0;top:0;display:flex;flex-flow:column wrap;width:100%;height:100%;padding:0 5rem;line-height:5rem;color:#fff;font-size:3rem}body .lrc-box section{flex:1;display:flex}body .lrc-box section.left{justify-content:flex-start}body .lrc-box section.right{justify-content:flex-end}body .lrc-box section span{-webkit-background-clip:text !important;background-clip:text !important;color:transparent}body .lrc-box section span.shadow{text-shadow:0 0 0.5rem rgba(0,0,0,0.5)}body .touch-bar{position:absolute;left:0;top:0;z-index:9;width:100%;height:2rem}body .quit,body .lock{visibility:hidden;position:absolute;right:1rem;top:.5rem;font-size:1.8rem;font-weight:bold}body .quit:hover,body .lock:hover{color:#ff5061}body .lock{right:4rem}body .lock.active{color:#dae1e9}body:hover{background:rgba(29,35,44,0.2)}body:hover .quit,body:hover .lock{visibility:visible} diff --git a/src/css/desktop-lrc.scss b/src/css/desktop-lrc.scss index 2751240..1c22fdf 100644 --- a/src/css/desktop-lrc.scss +++ b/src/css/desktop-lrc.scss @@ -38,7 +38,7 @@ body {position:fixed;left:0;top:0;display:flex;width:100%;height:100%;line-heigh } .lock {right:4rem; - &.actived {color:nth($cp, 3)} + &.active {color:nth($cp, 3)} } diff --git a/src/desktop-lrc.html b/src/desktop-lrc.html index 855012c..ae10ac2 100644 --- a/src/desktop-lrc.html +++ b/src/desktop-lrc.html @@ -28,7 +28,7 @@
- +
diff --git a/src/js/app.js b/src/js/app.js index 6d16434..f097e73 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -18,6 +18,12 @@ import Profile from '/js/modules/profile.js' import Search from '/js/modules/search.js' import KTV from '/js/modules/ktv.js' +import PLAYCTRL from '/js/modules/play-ctrl.js' + +import { + createDesktopLrcWindow, + createMiniWindow +} from '/js/modules/extra-win.js' const log = console.log @@ -27,7 +33,7 @@ const path = require('path') const { remote } = require('electron') const WIN = remote.getCurrentWindow() -const CURR_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') @@ -37,23 +43,6 @@ const PLAY_MODE = { 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') @@ -68,23 +57,7 @@ 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.loadURL('app://sonist/desktop-lrc.html') +const LRC_WIN = createDesktopLrcWindow(MAIN_SCREEN) Anot({ $id: 'app', @@ -307,105 +280,6 @@ Anot({ } }, - __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) { @@ -469,6 +343,7 @@ Anot({ } } }, + ...PLAYCTRL.methods, ...KTV.methods } }) diff --git a/src/js/modules/extra-win.js b/src/js/modules/extra-win.js new file mode 100644 index 0000000..5b16bf1 --- /dev/null +++ b/src/js/modules/extra-win.js @@ -0,0 +1,50 @@ +/** + * 额外的小窗口 + * @author yutent + * @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('app://sonist/desktop-lrc.html') + return win +} + +export const createMiniWindow = function(screen) { + let win = new BrowserWindow({ + title: '', + width: 480, + height: 60, + frame: false, + resizable: false, + alwaysOnTop: true, + x: screen.size.width - 480, + y: 0, + skipTaskbar: true, + show: false + }) + + win.loadURL('app://sonist/mini-win.html') + return win +} diff --git a/src/js/modules/play-ctrl.js b/src/js/modules/play-ctrl.js new file mode 100644 index 0000000..d8d9ad3 --- /dev/null +++ b/src/js/modules/play-ctrl.js @@ -0,0 +1,128 @@ +/** + * 播放器控制条 + * @author yutent + * @date 2019/01/21 21:16:29 + */ + +'use strict' + +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' + +export default { + methods: { + __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__() + } + } + } + } +} diff --git a/src/main.js b/src/main.js index 6be9268..602deb3 100644 --- a/src/main.js +++ b/src/main.js @@ -1,16 +1,12 @@ -const { - app, - BrowserWindow, - protocol, - Tray, - Menu, - session -} = require('electron') +const { app, BrowserWindow, protocol, session, ipcMain } = require('electron') const path = require('path') const fs = require('iofs') const { exec } = require('child_process') const log = console.log +const createTray = require('./tools/tray') +const createMenu = require('./tools/menu') + /* ******************************* */ /* **********修复环境变量*********** */ /* ******************************* */ @@ -36,63 +32,23 @@ const MIME_TYPES = { gif: 'image/gif' } -let win = null -let tray = null - /* ----------------------------------------------------- */ +app.commandLine.appendSwitch('--lang', 'zh-CN') +app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required') -const TRAYMENU_TMPL = [ - { - label: '显示主窗口', - click: () => { - win.show() - } - }, - { - type: 'separator' - }, - { - label: '退出', - role: 'quit' - } -] -const MENUBAR_TMPL = [ - { - label: 'Edit', - submenu: [ - { role: 'undo' }, - { role: 'redo' }, - { type: 'separator' }, - { role: 'cut' }, - { role: 'copy' }, - { role: 'paste' }, - { role: 'selectall' } - ] - }, - { - label: 'View', - submenu: [{ role: 'zoomin' }, { role: 'zoomout' }] - }, - { - role: 'window', - submenu: [{ role: 'minimize' }, { role: 'close' }] - } -] +app.setPath('appData', path.resolve(HOME, '.sonist/')) +protocol.registerStandardSchemes(['app'], { secure: true }) -if (process.platform === 'darwin') { - MENUBAR_TMPL.unshift({ - label: 'Sonist', - submenu: [{ role: 'about' }, { type: 'separator' }, { role: 'quit' }] - }) - - // Window menu - MENUBAR_TMPL[3].submenu = [{ role: 'minimize' }] +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')) } - -let traymenuList = Menu.buildFromTemplate(TRAYMENU_TMPL) -let menubarList = Menu.buildFromTemplate(MENUBAR_TMPL) - /* ----------------------------------------------------- */ +let win = null function createWindow() { // 创建浏览器窗口 @@ -113,19 +69,9 @@ function createWindow() { // 然后加载应用的 index.html。 win.loadURL('app://sonist/index.html') } -app.commandLine.appendSwitch('--lang', 'zh-CN') -app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required') - -app.setPath('appData', path.resolve(HOME, '.sonist/')) -protocol.registerStandardSchemes(['app'], { secure: true }) - -let appPath = app.getPath('appData') -if (!fs.exists(appPath)) { - fs.mkdir(appPath) - fs.mkdir(path.join(appPath, 'lyrics')) - fs.echo('{}', path.join(appPath, 'app.ini')) - fs.echo('[]', path.join(appPath, 'music.db')) -} +/* ****************************************** */ +/* ************* init ******************* */ +/* ****************************************** */ // 创建窗口 app.once('ready', () => { @@ -138,30 +84,19 @@ app.once('ready', () => { exec('which ffprobe', (err, res) => { if (res) { - tray = new Tray(path.resolve(ROOT, './images/trays/trayTemplate.png')) - - if (process.platform === 'darwin') { - tray.on('click', _ => { - win.show() - }) - tray.on('right-click', _ => { - tray.popUpContextMenu(traymenuList) - }) - } else { - tray.setContextMenu(traymenuList) - } - Menu.setApplicationMenu(menubarList) - 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' ) createWindow() + createTray(win) + createMenu(win) + win.on('ready-to-show', _ => { win.show() }) - // win.openDevTools() + win.openDevTools() } else { win = new BrowserWindow({ width: 600, diff --git a/src/mini-win.html b/src/mini-win.html new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/menu.js b/src/tools/menu.js new file mode 100644 index 0000000..5e17c0b --- /dev/null +++ b/src/tools/menu.js @@ -0,0 +1,69 @@ +/** + * 菜单项 + * @author yutent + * @date 2019/01/21 20:34:04 + */ + +'use strict' + +const { Tray, Menu, shell } = require('electron') + +module.exports = function(win) { + let menuList = Menu.buildFromTemplate([ + { + label: 'Sonist', + submenu: [ + { role: 'about', label: '关于 Sonist' }, + { type: 'separator' }, + { role: 'quit', label: '退出' } + ] + }, + { + label: '编辑', + submenu: [ + { role: 'undo', label: '撤消重做' }, + { role: 'redo', label: '重做' }, + { type: 'separator' }, + { role: 'cut', label: '剪切' }, + { role: 'copy', label: '复制' }, + { role: 'paste', label: '粘贴' }, + { role: 'selectall', label: '全选' } + ] + }, + { + label: '显示', + submenu: [ + { label: '显示桌面歌词' }, + { + type: 'separator' + }, + { label: '迷你模式' } + ] + }, + { + label: '窗口', + submenu: [ + { + role: 'minimize', + label: '最小化', + click() { + win.minimize() + } + } + ] + }, + { + role: 'help', + label: '帮助', + submenu: [ + { + label: '官网', + click() { + shell.openExternal('https://github.com/yutent/sonist') + } + } + ] + } + ]) + Menu.setApplicationMenu(menuList) +} diff --git a/src/tools/tray.js b/src/tools/tray.js new file mode 100644 index 0000000..9746dee --- /dev/null +++ b/src/tools/tray.js @@ -0,0 +1,38 @@ +/** + * 托盘 + * @author yutent + * @date 2019/01/21 20:42:07 + */ + +'use strict' + +const { Tray, Menu } = require('electron') +const path = require('path') +const ROOT = __dirname + +module.exports = function(win) { + let tray = new Tray( + path.resolve(__dirname, '../images/trays/trayTemplate.png') + ) + let menuList = Menu.buildFromTemplate([ + { + label: '显示主窗口', + click() { + win.show() + } + }, + { type: 'separator' }, + { label: '退出', role: 'quit' } + ]) + + if (process.platform === 'darwin') { + tray.on('click', _ => { + win.show() + }) + tray.on('right-click', _ => { + tray.popUpContextMenu(menuList) + }) + } else { + tray.setContextMenu(menuList) + } +}