feat: update electron app with touchbar and menu.

master
kunkka 4 years ago
parent 9b03403d9d
commit e030647720

@ -1,4 +1,4 @@
# 如果发现 npm / yarn 安装太慢,可以解除注释 # 如果发现 npm / yarn 安装太慢,可以解除注释
registry=https://registry.npm.taobao.org/ # registry=https://registry.npm.taobao.org/
ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron # ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs # phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs

@ -56,8 +56,7 @@
"vue-i18n": "^8.22.0", "vue-i18n": "^8.22.0",
"vue-router": "^3.4.3", "vue-router": "^3.4.3",
"vue-slider-component": "^3.2.5", "vue-slider-component": "^3.2.5",
"vuex": "^3.4.0", "vuex": "^3.4.0"
"vuex-electron": "^1.0.3"
}, },
"devDependencies": { "devDependencies": {
"@sentry/browser": "^5.27.0", "@sentry/browser": "^5.27.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1,6 +1,6 @@
<template> <template>
<div id="app"> <div id="app">
<Navbar /> <Navbar ref="navbar"/>
<main> <main>
<keep-alive> <keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view> <router-view v-if="$route.meta.keepAlive"></router-view>
@ -40,6 +40,7 @@ export default {
}, },
created() { created() {
if (this.isElectron) { if (this.isElectron) {
const self = this
// //
document.body.classList.add("is-electron"); document.body.classList.add("is-electron");
// ipc message channel // ipc message channel
@ -49,38 +50,42 @@ export default {
// inside this Vue instance, according to what path the main process requires. // inside this Vue instance, according to what path the main process requires.
// responds to Menu click() events at the main process and changes the route accordingly. // responds to Menu click() events at the main process and changes the route accordingly.
ipcRenderer.on("changeRouteTo", (event, path) => { ipcRenderer.on("changeRouteTo", (event, path) => {
console.log(event); self.$router.push(path);
this.$router.push(path);
}); });
ipcRenderer.on("search", () => {
//
self.$refs.navbar.$refs.searchInput.focus()
self.$refs.navbar.inputFocus = true
})
ipcRenderer.on("play", () => { ipcRenderer.on("play", () => {
this.$refs.player.play(); self.$refs.player.play();
}); });
ipcRenderer.on("next", () => { ipcRenderer.on("next", () => {
this.$refs.player.next(); self.$refs.player.next();
}); });
ipcRenderer.on("previous", () => { ipcRenderer.on("previous", () => {
this.$refs.player.previous(); self.$refs.player.previous();
}); });
ipcRenderer.on("increaseVolume", () => { ipcRenderer.on("increaseVolume", () => {
if (this.$refs.player.volume + 0.1 >= 1) { if (self.$refs.player.volume + 0.1 >= 1) {
return (this.$refs.player.volume = 1); return (self.$refs.player.volume = 1);
} }
this.$refs.player.volume += 0.1; self.$refs.player.volume += 0.1;
}); });
ipcRenderer.on("decreaseVolume", () => { ipcRenderer.on("decreaseVolume", () => {
if (this.$refs.player.volume - 0.1 <= 0) { if (self.$refs.player.volume - 0.1 <= 0) {
return (this.$refs.player.volume = 0); return (self.$refs.player.volume = 0);
} }
this.$refs.player.volume -= 0.1; self.$refs.player.volume -= 0.1;
}); });
ipcRenderer.on("like", () => { ipcRenderer.on("like", () => {
this.$refs.player.likeCurrentSong(); self.$refs.player.likeCurrentSong();
}); });
ipcRenderer.on("repeat", () => { ipcRenderer.on("repeat", () => {
this.$refs.player.repeat(); self.$refs.player.repeat();
}); });
ipcRenderer.on("shuffle", () => { ipcRenderer.on("shuffle", () => {
this.$refs.player.shuffle(); self.$refs.player.shuffle();
}); });
} }
}, },

@ -1,48 +1,59 @@
"use strict"; "use strict";
const path = require('path')
import path from "path";
// import { autoUpdater } from "electron-updater" // import { autoUpdater } from "electron-updater"
import { import {
app, app,
protocol, protocol,
BrowserWindow, BrowserWindow,
ipcMain, ipcMain,
dialog, globalShortcut
globalShortcut,
} from "electron"; } from "electron";
import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer"; import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
const isDevelopment = process.env.NODE_ENV !== "production"; const isDevelopment = process.env.NODE_ENV !== "production";
const { createTouchBar } = require("./electron/touchbar");
const { createMenu } = require("./electron/menu");
const { setAppBounced } = require("./electron/bounced")
const { setIcon } = require("./electron/setIcon")
// Keep a global reference of the window object, if you don't, the window will // Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected. // be closed automatically when the JavaScript object is garbage collected.
let win; let win;
let touchbar
const iconString = path.join(__static, "img/icons/apple-touch-icon.png");
// Make vuex copy for electron.
global.vuexCopy = null
// 同步 vuex 状态,由于 player 有循环引用问题,拷贝部分属性
ipcMain.on('vuex-state', (e, state) => {
global.vuexCopy = state
})
ipcMain.on("close", () => {
win.close();
app.quit();
});
ipcMain.on("minimize", () => {
win.minimize();
});
// Scheme must be registered before the app is ready // Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([ protocol.registerSchemesAsPrivileged([
{ scheme: "app", privileges: { secure: true, standard: true } }, { scheme: "app", privileges: { secure: true, standard: true } },
]); ]);
const iconString = path.join(__static, "img/icons/apple-touch-icon.png");
let bounceId = app.dock.bounce(); setAppBounced(app)
app.dock.setIcon(iconString); setIcon(app, iconString)
function createWindow() { function createWindow() {
const touchbar = require("./electron/touchbar.js");
const tray = require("./electron/tray.js"); const tray = require("./electron/tray.js");
const createMenu = require("./electron/menu.js");
tray.on("click", function () {
if (win.isVisible()) {
win.hide();
} else {
win.show();
}
});
win = new BrowserWindow({ win = new BrowserWindow({
width: 1440, width: 1440,
height: 768, height: 768,
icon: iconString, icon: iconString,
backgroundColor: '#2e2c29',
titleBarStyle: "default", titleBarStyle: "default",
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
@ -51,15 +62,13 @@ function createWindow() {
preload: path.join(__dirname, "./electron/preload.js"), preload: path.join(__dirname, "./electron/preload.js"),
}); });
try { tray.on("click", () => {
createMenu(win); if (win && win.isVisible()) {
win.setTouchBar(touchbar); win.hide();
win.setAutoHideCursor(true); } else {
app.dock.cancelBounce(bounceId); win.show();
// autoUpdater.checkForUpdatesAndNotify() }
} catch (error) { });
console.log(error);
}
if (process.env.WEBPACK_DEV_SERVER_URL) { if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode // Load the url of the dev server if in development mode
@ -74,6 +83,7 @@ function createWindow() {
win.on("closed", () => { win.on("closed", () => {
win = null; win = null;
}); });
return win
} }
// Quit when all windows are closed. // Quit when all windows are closed.
@ -112,14 +122,13 @@ app.on("ready", async () => {
win.webContents.openDevTools(); win.webContents.openDevTools();
}); });
createWindow(); createWindow();
}); win.once("ready-to-show", () => {
win.show()
ipcMain.on("close", () => { })
win.close(); createMenu(win);
app.quit(); touchbar = createTouchBar(win)
}); win.setTouchBar(touchbar);
ipcMain.on("minimize", () => { // autoUpdater.checkForUpdatesAndNotify()
win.minimize();
}); });
// autoUpdater.on("checking-for-update", () => {}); // autoUpdater.on("checking-for-update", () => {});

@ -35,6 +35,7 @@
<svg-icon icon-class="search" /> <svg-icon icon-class="search" />
<div class="input"> <div class="input">
<input <input
ref="searchInput"
:placeholder="inputFocus ? '' : $t('nav.search')" :placeholder="inputFocus ? '' : $t('nav.search')"
v-model="keywords" v-model="keywords"
@keydown.enter="goToSearchPage" @keydown.enter="goToSearchPage"

@ -18,7 +18,7 @@
<div class="controls"> <div class="controls">
<div class="playing"> <div class="playing">
<img <img
:src="currentTrack.al.picUrl | resizeImage(224)" :src="currentTrack.al && currentTrack.al.picUrl | resizeImage(224)"
@click="goToAlbum" @click="goToAlbum"
/> />
<div class="track-info"> <div class="track-info">
@ -141,9 +141,12 @@ export default {
oldVolume: 0.5, oldVolume: 0.5,
}; };
}, },
created() { mounted() {
setInterval(() => { setInterval(() => {
this.progress = ~~this.howler.seek(); // fix _id
if (this.howler && this.howler._sounds[0]._id) {
this.progress = ~~this.howler.seek();
}
}, 1000); }, 1000);
if (isAccountLoggedIn()) { if (isAccountLoggedIn()) {
userLikedSongsIDs(this.data.user.userId).then((data) => { userLikedSongsIDs(this.data.user.userId).then((data) => {
@ -167,9 +170,12 @@ export default {
}, },
playing() { playing() {
if (this.howler.state() === "loading") { if (this.howler.state() === "loading") {
this.updatePlayerState({ key: "playing", value: true });
return true; return true;
} }
return this.howler.playing(); const status = this.howler.playing()
this.updatePlayerState({ key: "playing", value: status });
return status
}, },
progressMax() { progressMax() {
let max = ~~(this.currentTrack.dt / 1000); let max = ~~(this.currentTrack.dt / 1000);

@ -0,0 +1,6 @@
export function setAppBounced(app) {
let bounceId = app.dock.bounce();
app.on('ready', () => {
app.dock.cancelBounce(bounceId);
})
};

@ -4,7 +4,7 @@ const { app, Menu } = require("electron");
const isMac = process.platform === "darwin"; const isMac = process.platform === "darwin";
function createMenu(win) { export function createMenu(win) {
let menu = null; let menu = null;
const template = [ const template = [
...(isMac ...(isMac
@ -196,5 +196,3 @@ function createMenu(win) {
menu = Menu.buildFromTemplate(template); menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu); Menu.setApplicationMenu(menu);
} }
module.exports = createMenu;

@ -0,0 +1,3 @@
export function setIcon(app, iconString) {
app.dock.setIcon(iconString);
}

@ -1,104 +1,131 @@
// 运用 ipdMain 请求用户喜欢的歌手的数据,随机抽几个歌手进行随机 const { TouchBar, nativeImage, ipcMain } = require("electron");
const { TouchBar } = require("electron"); const path = require("path");
const { TouchBarLabel, TouchBarButton, TouchBarSpacer } = TouchBar; const {
TouchBarButton,
let spinning = false; TouchBarGroup,
TouchBarSpacer,
// Reel labels TouchBarSegmentedControl,
const reel1 = new TouchBarLabel(); } = TouchBar;
const reel2 = new TouchBarLabel();
const reel3 = new TouchBarLabel(); function getNativeIcon(name, width = 24, height = 24) {
return nativeImage
// Spin result label .createFromPath(path.join(__static, "img/icons/touchbar/", name))
const result = new TouchBarLabel(); .resize({
width,
// Spin button height,
const spin = new TouchBarButton({ });
label: "🎰 Spin", }
backgroundColor: "#7851A9",
click: () => { export function createSegmentedControl(renderer) {
// Ignore clicks if already spinning const segments = [
if (spinning) { {
return; icon: getNativeIcon("previous.png"),
} },
{
spinning = true; icon: getNativeIcon("play.png", 20, 20),
result.label = ""; },
{
let timeout = 10; icon: getNativeIcon("next.png"),
const spinLength = 4 * 1000; // 4 seconds },
const startTime = Date.now(); ];
const segmentedControl = new TouchBarSegmentedControl({
const spinReels = () => { segments,
updateReels(); change: (selectedIndex) => {
const temp = Object.assign([], segmentedControl.segments);
if (Date.now() - startTime >= spinLength) { if (selectedIndex === 0) {
finishSpin(); renderer.send("previous");
} else {
// Slow down a bit on each spin
timeout *= 1.1;
setTimeout(spinReels, timeout);
} }
}; if (selectedIndex === 1) {
ipcMain.on("vuex-state", (e, { player }) => {
spinReels(); const playing = player.playing;
}, if (playing === true) {
}); // To be paused
temp[1].icon = getNativeIcon("play.png", 20, 20);
const getRandomValue = () => { segmentedControl.segments = temp;
const values = ["🍒", "💎", "7⃣", "🍊", "🔔", "⭐", "🍇", "🍀"]; } else {
return values[Math.floor(Math.random() * values.length)]; temp[1].icon = getNativeIcon("play.png", 20, 20);
}; segmentedControl.segments = temp;
}
const updateReels = () => { });
reel1.label = getRandomValue(); renderer.send("play");
reel2.label = getRandomValue(); }
reel3.label = getRandomValue(); if (selectedIndex === 2) {
}; renderer.send("next");
}
const finishSpin = () => { },
const uniqueValues = new Set([reel1.label, reel2.label, reel3.label]).size; mode: "buttons",
if (uniqueValues === 1) { });
// All 3 values are the same return segmentedControl;
result.label = "💰 Jackpot!"; }
result.textColor = "#FDFF00";
} else if (uniqueValues === 2) { export function createPreferGroup(renderer) {
// 2 values are the same const search = new TouchBarButton({
result.label = "😍 Winner!"; icon: getNativeIcon("search.png", 22, 22),
result.textColor = "#FDFF00"; click: () => {
} else { renderer.send("search");
// No values are the same },
result.label = "🙁 Spin Again"; });
result.textColor = null; const like = new TouchBarButton({
} icon: getNativeIcon("like.png"),
spinning = false; click: () => {
}; ipcMain.on("vuex-state", (e, { liked, player }) => {
const currentTrack = player.currentTrack;
const touchBar = new TouchBar({ if (liked.songs.includes(currentTrack.id)) {
items: [ like.icon = getNativeIcon("liked.png");
spin, } else {
new TouchBarSpacer({ size: "large" }), like.icon = getNativeIcon("like.png");
reel1, }
new TouchBarSpacer({ size: "small" }), });
reel2, renderer.send("like");
new TouchBarSpacer({ size: "small" }), },
reel3, });
new TouchBarSpacer({ size: "large" }), const repeat = new TouchBarButton({
result, icon: getNativeIcon("repeat.png"),
], click: () => {
}); ipcMain.on("vuex-state", (e, { player }) => {
const repeat = player.repeat;
// let window if (repeat === "on") {
repeat.icon = getNativeIcon("repeat.png");
// app.whenReady().then(() => { } else if (repeat === "one") {
// window = new BrowserWindow({ repeat.icon = getNativeIcon("repeat.png");
// frame: false, } else {
// titleBarStyle: 'hiddenInset', repeat.icon = getNativeIcon("repeat.png");
// backgroundColor: '#000' }
// }) });
// window.loadURL('about:blank') renderer.send("repeat");
// window.setTouchBar(touchBar) },
// }) });
const shuffle = new TouchBarButton({
module.exports = touchBar; icon: getNativeIcon("shuffle.png"),
click: () => {
ipcMain.on("vuex-state", (e, { player }) => {
const shuffle = player.shuffle;
if (shuffle === true) {
shuffle.icon = getNativeIcon("shuffle.png");
} else {
shuffle.icon = getNativeIcon("shuffle.png");
}
});
renderer.send("shuffle");
},
});
return new TouchBar({
items: [search, like, repeat, shuffle],
});
}
export function createTouchBar(window) {
const renderer = window.webContents;
const segmentedControl = createSegmentedControl(renderer);
const preferGroup = createPreferGroup(renderer);
const touchBar = new TouchBar({
items: [
new TouchBarGroup({ items: preferGroup }),
new TouchBarSpacer({ size: "large" }),
segmentedControl,
new TouchBarSpacer({ size: "large" }),
],
});
return touchBar;
}

@ -0,0 +1,104 @@
// 运用 ipdMain 请求用户喜欢的歌手的数据,随机抽几个歌手进行随机
const { TouchBar } = require("electron");
const { TouchBarLabel, TouchBarButton, TouchBarSpacer } = TouchBar;
let spinning = false;
// Reel labels
const reel1 = new TouchBarLabel();
const reel2 = new TouchBarLabel();
const reel3 = new TouchBarLabel();
// Spin result label
const result = new TouchBarLabel();
// Spin button
const spin = new TouchBarButton({
label: "🎰 Spin",
backgroundColor: "#7851A9",
click: () => {
// Ignore clicks if already spinning
if (spinning) {
return;
}
spinning = true;
result.label = "";
let timeout = 10;
const spinLength = 4 * 1000; // 4 seconds
const startTime = Date.now();
const spinReels = () => {
updateReels();
if (Date.now() - startTime >= spinLength) {
finishSpin();
} else {
// Slow down a bit on each spin
timeout *= 1.1;
setTimeout(spinReels, timeout);
}
};
spinReels();
},
});
const getRandomValue = () => {
const values = ["🍒", "💎", "7⃣", "🍊", "🔔", "⭐", "🍇", "🍀"];
return values[Math.floor(Math.random() * values.length)];
};
const updateReels = () => {
reel1.label = getRandomValue();
reel2.label = getRandomValue();
reel3.label = getRandomValue();
};
const finishSpin = () => {
const uniqueValues = new Set([reel1.label, reel2.label, reel3.label]).size;
if (uniqueValues === 1) {
// All 3 values are the same
result.label = "💰 Jackpot!";
result.textColor = "#FDFF00";
} else if (uniqueValues === 2) {
// 2 values are the same
result.label = "😍 Winner!";
result.textColor = "#FDFF00";
} else {
// No values are the same
result.label = "🙁 Spin Again";
result.textColor = null;
}
spinning = false;
};
const touchBar = new TouchBar({
items: [
spin,
new TouchBarSpacer({ size: "large" }),
reel1,
new TouchBarSpacer({ size: "small" }),
reel2,
new TouchBarSpacer({ size: "small" }),
reel3,
new TouchBarSpacer({ size: "large" }),
result,
],
});
// let window
// app.whenReady().then(() => {
// window = new BrowserWindow({
// frame: false,
// titleBarStyle: 'hiddenInset',
// backgroundColor: '#000'
// })
// window.loadURL('about:blank')
// window.setTouchBar(touchBar)
// })
module.exports = touchBar;

@ -1,12 +1,14 @@
const path = require("path"); const path = require("path");
const { Menu, Tray } = require("electron"); const { nativeImage, Tray } = require("electron");
let tray = null; function getNativeIcon(name, width = 24, height = 24) {
return nativeImage.createFromPath(path.join(__static, 'img/icons/', name)).resize({
width,
height,
})
}
const macIcon = path.join(__static, "img/icons/menu.png"); let tray = new Tray(getNativeIcon('menu@88.png', 20, 20));;
const winIcon = path.join(__static, "img/icons/icon.ico");
tray = new Tray(macIcon);
// Temporary no need for menu. // Temporary no need for menu.
// const contextMenu = Menu.buildFromTemplate([ // const contextMenu = Menu.buildFromTemplate([

@ -3,7 +3,7 @@ import { getTrackDetail, scrobble } from "@/api/track";
import { isAccountLoggedIn } from "@/utils/auth"; import { isAccountLoggedIn } from "@/utils/auth";
// import { updateHttps } from "@/utils/common"; // import { updateHttps } from "@/utils/common";
import localforage from "localforage"; import localforage from "localforage";
import { cacheTrack } from "@/utils/db"; // import { cacheTrack } from "@/utils/db";
export default { export default {
switchTrack({ state, dispatch, commit }, basicTrack) { switchTrack({ state, dispatch, commit }, basicTrack) {
@ -51,11 +51,13 @@ export default {
}); });
tracks.getItem(`${track.id}`).then((t) => { tracks.getItem(`${track.id}`).then((t) => {
if (t !== null) { if (t !== null) {
commitMP3(URL.createObjectURL(t.mp3)); // commitMP3(URL.createObjectURL(t.mp3));
commitMP3(t.mp3);
} else { } else {
cacheTrack(`${track.id}`).then((t) => { commitMP3(t.mp3);
commitMP3(URL.createObjectURL(t.mp3)); // cacheTrack(`${track.id}`).then((t) => {
}); // commitMP3(URL.createObjectURL(t.mp3));
// });
} }
}); });
} else { } else {

@ -7,42 +7,43 @@ import initLocalStorage from "./initLocalStorage";
import { Howl, Howler } from "howler"; import { Howl, Howler } from "howler";
import { changeAppearance } from "@/utils/common"; import { changeAppearance } from "@/utils/common";
import updateApp from "@/utils/updateApp"; import updateApp from "@/utils/updateApp";
import pack from "../../package.json"; import pkg from "../../package.json";
// vuex 自定义插件
import vuexBroadCast from './plugins/broadcast'
import saveToLocalStorage from './plugins/localStorage'
if (localStorage.getItem("appVersion") === null) { if (localStorage.getItem("appVersion") === null) {
localStorage.setItem("player", JSON.stringify(initLocalStorage.player)); localStorage.setItem("player", JSON.stringify(initLocalStorage.player));
localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings)); localStorage.setItem("settings", JSON.stringify(initLocalStorage.settings));
localStorage.setItem("data", JSON.stringify(initLocalStorage.data)); localStorage.setItem("data", JSON.stringify(initLocalStorage.data));
localStorage.setItem("appVersion", pack.version); localStorage.setItem("appVersion", pkg.version);
window.location.reload(); window.location.reload();
} }
updateApp(); updateApp();
const saveToLocalStorage = (store) => {
store.subscribe((mutation, state) => {
// console.log(mutation);
localStorage.setItem("player", JSON.stringify(state.player));
localStorage.setItem("settings", JSON.stringify(state.settings));
localStorage.setItem("data", JSON.stringify(state.data));
});
};
Vue.use(Vuex); Vue.use(Vuex);
const store = new Vuex.Store({
state: state, const options = {
state,
mutations, mutations,
actions, actions,
plugins: [saveToLocalStorage], plugins: [
}); saveToLocalStorage,
vuexBroadCast,
],
}
const store = new Vuex.Store(options);
store.state.howler = new Howl({ store.state.howler = new Howl({
src: [ src: [
`https://music.163.com/song/media/outer/url?id=${store.state.player.currentTrack.id}`, `https://music.163.com/song/media/outer/url?id=${store.state.player.currentTrack.id}`,
], ],
html5: true, html5: true,
format: ["mp3"], format: ["webm", "mp3"],
}); });
Howler.volume(store.state.player.volume); Howler.volume(store.state.player.volume);
if ([undefined, null].includes(store.state.settings.lang)) { if ([undefined, null].includes(store.state.settings.lang)) {
@ -53,6 +54,7 @@ if ([undefined, null].includes(store.state.settings.lang)) {
} }
changeAppearance(store.state.settings.appearance); changeAppearance(store.state.settings.appearance);
window window
.matchMedia("(prefers-color-scheme: dark)") .matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", () => { .addEventListener("change", () => {

@ -14,6 +14,7 @@ export default {
src: [mp3], src: [mp3],
autoplay: true, autoplay: true,
html5: true, html5: true,
format: ["webm", "mp3"],
}); });
state.howler.play(); state.howler.play();
}, },

@ -0,0 +1,17 @@
// const electron = import('electron')
const electron = window.require("electron");
const ipcRenderer = electron.ipcRenderer;
export default store => {
// 第一行初始化第一次的状态
ipcRenderer.send('vuex-state', store.state)
store.subscribe((mutation, { data = '', settings = '', player = {}, contextMenu = {}, liked = {} }) => {
const copyState = { data, settings, player, contextMenu, liked }
ipcRenderer.send('vuex-state', copyState)
})
store.subscribe((mutation, state) => {
if (mutation.type === 'updateData') {
ipcRenderer.send('updateData', state.data)
}
})
}

@ -0,0 +1,8 @@
export default (store) => {
store.subscribe((mutation, state) => {
// console.log(mutation);
localStorage.setItem("player", JSON.stringify(state.player));
localStorage.setItem("settings", JSON.stringify(state.settings));
localStorage.setItem("data", JSON.stringify(state.data));
});
};

@ -1,11 +1,11 @@
import axios from "axios"; // import axios from "axios";
import localforage from "localforage"; import localforage from "localforage";
import { getMP3 } from "@/api/track"; import { getMP3 } from "@/api/track";
export function cacheTrack(id) { export function cacheTrack(id) {
let tracks = localforage.createInstance({ // let tracks = localforage.createInstance({
name: "tracks", // name: "tracks",
}); // });
// TODO: limit cache songs number // TODO: limit cache songs number
// tracks.length().then(function (length) { // tracks.length().then(function (length) {
@ -18,14 +18,15 @@ export function cacheTrack(id) {
// TODO: cache track details // TODO: cache track details
return getMP3(id).then((data) => { return getMP3(id).then((data) => {
return axios // return axios
.get(data.data[0].url.replace(/^http:/, "https:"), { // .get(data.data[0].url.replace(/^http:/, "https:"), {
responseType: "blob", // responseType: "blob",
}) // })
.then((data) => { // .then((data) => {
tracks.setItem(`${id}`, { mp3: data.data }); // tracks.setItem(`${id}`, { mp3: data.data });
return { mp3: data.data }; // return { mp3: data.data };
}); // });
return { mp3: data.data[0].url.replace(/^http:/, "https:")}
}); });
} }

@ -78,7 +78,7 @@ module.exports = {
// window 的 icon 头标 // window 的 icon 头标
win: { win: {
publisherName: 'Yes Play Music', publisherName: 'Yes Play Music',
icon: 'public/img/icons/512x512.png', icon: 'build/icons/icon.ico',
publish: [ publish: [
"github" "github"
], ],
@ -89,7 +89,8 @@ module.exports = {
"AppImage", "AppImage",
"tar.gz", "tar.gz",
"deb" "deb"
] ],
icon: "build/icons"
}, },
"dmg": { "dmg": {
"icon": "build/icons/icon.icns" "icon": "build/icons/icon.icns"

@ -3459,17 +3459,6 @@ concat-stream@^1.5.0, concat-stream@^1.6.2:
readable-stream "^2.2.2" readable-stream "^2.2.2"
typedarray "^0.0.6" typedarray "^0.0.6"
conf@^2.0.0:
version "2.2.0"
resolved "https://registry.npm.taobao.org/conf/download/conf-2.2.0.tgz#ee282efafc1450b61e205372041ad7d866802d9a"
integrity sha1-7igu+vwUULYeIFNyBBrX2GaALZo=
dependencies:
dot-prop "^4.1.0"
env-paths "^1.0.0"
make-dir "^1.0.0"
pkg-up "^2.0.0"
write-file-atomic "^2.3.0"
config-chain@^1.1.11: config-chain@^1.1.11:
version "1.1.12" version "1.1.12"
resolved "https://registry.npm.taobao.org/config-chain/download/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" resolved "https://registry.npm.taobao.org/config-chain/download/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
@ -3979,11 +3968,6 @@ deepmerge@^1.5.2:
resolved "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" resolved "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
integrity sha1-EEmdhohEza1P7ghC34x/bwyVp1M= integrity sha1-EEmdhohEza1P7ghC34x/bwyVp1M=
deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.npm.taobao.org/deepmerge/download/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha1-XT/yKgHAD2RUBaL7wX0HeKGAEXA=
default-gateway@^4.2.0: default-gateway@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.npm.taobao.org/default-gateway/download/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" resolved "https://registry.npm.taobao.org/default-gateway/download/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"
@ -4227,13 +4211,6 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0" dom-serializer "0"
domelementtype "1" domelementtype "1"
dot-prop@^4.1.0:
version "4.2.1"
resolved "https://registry.npm.taobao.org/dot-prop/download/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4"
integrity sha1-RYhBlKcfws2nHLtLzrOk3S9DO6Q=
dependencies:
is-obj "^1.0.0"
dot-prop@^5.2.0: dot-prop@^5.2.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.npm.taobao.org/dot-prop/download/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" resolved "https://registry.npm.taobao.org/dot-prop/download/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@ -4414,13 +4391,6 @@ electron-publish@22.9.1:
lazy-val "^1.0.4" lazy-val "^1.0.4"
mime "^2.4.6" mime "^2.4.6"
electron-store@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/electron-store/download/electron-store-2.0.0.tgz#1035cca2a95409d1f54c7466606345852450d64a"
integrity sha1-EDXMoqlUCdH1THRmYGNFhSRQ1ko=
dependencies:
conf "^2.0.0"
electron-to-chromium@^1.3.570: electron-to-chromium@^1.3.570:
version "1.3.570" version "1.3.570"
resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.570.tgz#3f5141cc39b4e3892a276b4889980dabf1d29c7f" resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.570.tgz#3f5141cc39b4e3892a276b4889980dabf1d29c7f"
@ -4512,11 +4482,6 @@ entities@^2.0.0:
resolved "https://registry.npm.taobao.org/entities/download/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" resolved "https://registry.npm.taobao.org/entities/download/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha1-XEh+V0Krk8Fau12iJ1m4WQ7AO38= integrity sha1-XEh+V0Krk8Fau12iJ1m4WQ7AO38=
env-paths@^1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/env-paths/download/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=
env-paths@^2.2.0: env-paths@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.npm.taobao.org/env-paths/download/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" resolved "https://registry.npm.taobao.org/env-paths/download/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43"
@ -5187,13 +5152,6 @@ find-up@^1.0.0:
path-exists "^2.0.0" path-exists "^2.0.0"
pinkie-promise "^2.0.0" pinkie-promise "^2.0.0"
find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.npm.taobao.org/find-up/download/find-up-2.1.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
dependencies:
locate-path "^2.0.0"
find-up@^3.0.0: find-up@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" resolved "https://registry.npm.taobao.org/find-up/download/find-up-3.0.0.tgz?cache=0&sync_timestamp=1597169842138&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@ -6410,7 +6368,7 @@ is-number@^7.0.0:
resolved "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" resolved "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss= integrity sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=
is-obj@^1.0.0, is-obj@^1.0.1: is-obj@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npm.taobao.org/is-obj/download/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" resolved "https://registry.npm.taobao.org/is-obj/download/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
@ -7024,14 +6982,6 @@ localforage@^1.9.0:
dependencies: dependencies:
lie "3.1.1" lie "3.1.1"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/locate-path/download/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
dependencies:
p-locate "^2.0.0"
path-exists "^3.0.0"
locate-path@^3.0.0: locate-path@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" resolved "https://registry.npm.taobao.org/locate-path/download/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@ -7168,13 +7118,6 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
make-dir@^1.0.0:
version "1.3.0"
resolved "https://registry.npm.taobao.org/make-dir/download/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
integrity sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=
dependencies:
pify "^3.0.0"
make-dir@^2.0.0: make-dir@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" resolved "https://registry.npm.taobao.org/make-dir/download/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@ -7963,13 +7906,6 @@ p-finally@^2.0.0:
resolved "https://registry.npm.taobao.org/p-finally/download/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" resolved "https://registry.npm.taobao.org/p-finally/download/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561"
integrity sha1-vW/KqcVZoJa2gIBvTWV7Pw8kBWE= integrity sha1-vW/KqcVZoJa2gIBvTWV7Pw8kBWE=
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.npm.taobao.org/p-limit/download/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
integrity sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1, p-limit@^2.3.0: p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1, p-limit@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz?cache=0&sync_timestamp=1594559734248&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-limit%2Fdownload%2Fp-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" resolved "https://registry.npm.taobao.org/p-limit/download/p-limit-2.3.0.tgz?cache=0&sync_timestamp=1594559734248&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-limit%2Fdownload%2Fp-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@ -7984,13 +7920,6 @@ p-limit@^3.0.2:
dependencies: dependencies:
p-try "^2.0.0" p-try "^2.0.0"
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/p-locate/download/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
dependencies:
p-limit "^1.1.0"
p-locate@^3.0.0: p-locate@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" resolved "https://registry.npm.taobao.org/p-locate/download/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
@ -8036,11 +7965,6 @@ p-retry@^3.0.1:
dependencies: dependencies:
retry "^0.12.0" retry "^0.12.0"
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
p-try@^2.0.0: p-try@^2.0.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" resolved "https://registry.npm.taobao.org/p-try/download/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@ -8368,13 +8292,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
dependencies: dependencies:
find-up "^4.0.0" find-up "^4.0.0"
pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/pkg-up/download/pkg-up-2.0.0.tgz?cache=0&sync_timestamp=1589682752621&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpkg-up%2Fdownload%2Fpkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
dependencies:
find-up "^2.1.0"
please-upgrade-node@^3.2.0: please-upgrade-node@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.npm.taobao.org/please-upgrade-node/download/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" resolved "https://registry.npm.taobao.org/please-upgrade-node/download/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
@ -11180,14 +11097,6 @@ vue@^2.6.11:
resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1600443052956&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" resolved "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1600443052956&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
integrity sha1-9evU+mvShpQD4pqJau1JBEVskSM= integrity sha1-9evU+mvShpQD4pqJau1JBEVskSM=
vuex-electron@^1.0.3:
version "1.0.3"
resolved "https://registry.npm.taobao.org/vuex-electron/download/vuex-electron-1.0.3.tgz#c8ce0fd06ed9d3beb8881bce39bf76c9fcdc5611"
integrity sha1-yM4P0G7Z0764iBvOOb92yfzcVhE=
dependencies:
deepmerge "^2.1.1"
electron-store "^2.0.0"
vuex@^3.4.0: vuex@^3.4.0:
version "3.5.1" version "3.5.1"
resolved "https://registry.npm.taobao.org/vuex/download/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d" resolved "https://registry.npm.taobao.org/vuex/download/vuex-3.5.1.tgz#f1b8dcea649bc25254cf4f4358081dbf5da18b3d"
@ -11598,19 +11507,10 @@ wrappy@1:
resolved "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
write-file-atomic@^2.3.0:
version "2.4.3"
resolved "https://registry.npm.taobao.org/write-file-atomic/download/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481"
integrity sha1-H9Lprh3z51uNjDZ0Q8aS1MqB9IE=
dependencies:
graceful-fs "^4.1.11"
imurmurhash "^0.1.4"
signal-exit "^3.0.2"
write-file-atomic@^3.0.0: write-file-atomic@^3.0.0:
version "3.0.3" version "3.0.3"
resolved "https://registry.npm.taobao.org/write-file-atomic/download/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
integrity sha1-Vr1cWlxwSBzRnFcb05q5ZaXeVug= integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
dependencies: dependencies:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
is-typedarray "^1.0.0" is-typedarray "^1.0.0"

Loading…
Cancel
Save