From b394ec0899cbf9f7f7d09e26eb5d05e8ff31e536 Mon Sep 17 00:00:00 2001
From: njzy <njzydark@gmail.com>
Date: Wed, 9 Dec 2020 19:50:50 +0800
Subject: [PATCH 1/3] refactor: hide window when close app (#78)

fix: window can't be close by shortcutkey

fix: tray does not show in Windows
---
 build/icons/menu@88.png | Bin 0 -> 1084 bytes
 src/background.js       |  29 +++++++++++++++++++++++++----
 src/electron/ipcMain.js |   5 +++--
 src/electron/menu.js    |   1 +
 src/electron/tray.js    |  16 +++++++++++++++-
 5 files changed, 44 insertions(+), 7 deletions(-)
 create mode 100644 build/icons/menu@88.png

diff --git a/build/icons/menu@88.png b/build/icons/menu@88.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc14a82d1a3de3faff2aaea28d180e0885c8780a
GIT binary patch
literal 1084
zcmV-C1jGA@P)<h;3K|Lk000e1NJLTq003A3003AB1^@s6ag{JM00009a7bBm000XU
z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yP<VFdsH1JOxDK~#7F?VS5j
z6hRQizq9-w$Y9R|ga|xLKqUdrOF%RLgalL)kRt*#0X-71h(MMIzzEjv+F6}iEURZ<
z^Xgw!*B;c~9sAMy?M%-DP!vT`6h%=KMH!eGMxv^!f)3jh&CzCyVh7W5NAaumyRtdX
zX0s9_Fqpt3D2w=pVnMNshoZ*7hT@81J(l3kaHdjRR_SqG)pwy+aSti(s&wINJ46p2
zp(FIg@9D5coK_%(uWfCvbvpP9tNiFBvhzcZzGz8@bL7dA9)1@tZ*UU0s!jhp>ZVtY
z+*8a+<a_BGTs`;%`HIW1Cp%e%ID<|0-auh?q*?5e!3!u2OX-O&0>6OLu#leUB5(nv
z;i>dQ7Z0wW{DC}Jq@5^(E8l(c0Fi1UwTJ>r8&Qd%b1gzqfrCIQ@R${|4aGji+#J6k
z4z?sGJ%)_21m>ZdefwvMjVUN`m$X2*h=#m;Tg2JFeo3P9kPT<_DI&+W!2j*flgKBi
zt;4BA7!tuh<aqFrVo5Jw<L@YNG(^*iXxWCi9_yRvnrX&1_7I6}MA;6uY=ea^5in7A
zG)UxYV;jNxa}qr{_Gi?cl;CV5nXa;B8=TQ40wK9tgb_Lz+eo(9W7)<TJd$)Z&1CZt
zYTNKqM9QLEX@q`7FW+KfvZ@D@$e%EA0V&6V`=nNpqrj6+x0M#$qsPoT;Gy;<CM1gt
zHsbOK#0QFAGuB;6I)kmOT~K_6haspW1-2$3ar>8rUBDB`S%hT=Clo*7c~)pL8Qgk^
zvDXH%I4AlHW??B<Sgt#KLv0DctbB?(gDpEarZ_@?L@BUk2fTEbC#VFwRxc`nTg|Rx
zM6j$O8i6f4_=t%+3w;P)U@L3c3fK~&TDlvoz#;U)hQPPLycGiM<qjeB!iK<iJD6|z
zxoOL`dtpD2z}h@%29GrtjQx~52RzDfi+Xt!i(VjnO36vS1!f8)yn0{PXpD?h;Ap0c
zPRO8(!Byagh&0iqv>h3ADXpP|(r_(3(M8}ZC=IvL6I}$}Kxw#?p6KGi+$rEKWm4WJ
zO+rbrH~5w^<^#P>1r3A?mtCjC8O)e0<{H5qd2&PIK5==2Q%}zP=MGSB6`LE}OnnYV
zoV&`rvWE`u%opYYU+b#Nmbaa15oUV+3VCEQU*pPLvolT}+{Wx%!{x@J?`H)Hx0(*j
zUE7IX)ig6TpG|*d{+JYOgRg<J_LQ%!kGRaBSw|WDk`B*M7tVJPPn)?!UA*Dm>Q@qV
zgMk?Rwzjsv+D15nm(7b=D0{UlilQirq9}@@Ncjziaia>bFNlf&0000<MNUMnLSTYc
CN$Z~g

literal 0
HcmV?d00001

diff --git a/src/background.js b/src/background.js
index 4a84b61..b3649e7 100644
--- a/src/background.js
+++ b/src/background.js
@@ -17,6 +17,10 @@ const isDevelopment = process.env.NODE_ENV !== "production";
 // 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.
 let win;
+// eslint-disable-next-line no-unused-vars
+let tray;
+
+let willQuitApp = false;
 
 // ipcMain
 initIpcMain(win);
@@ -39,7 +43,7 @@ function createWindow() {
   win.setMenuBarVisibility(false);
 
   if (process.platform !== "darwin") {
-    createTray(win);
+    tray = createTray(win);
   }
 
   if (process.env.WEBPACK_DEV_SERVER_URL) {
@@ -60,9 +64,19 @@ function createWindow() {
     e.preventDefault();
     shell.openExternal(url);
   });
-  win.on("closed", () => {
-    win = null;
+  win.on("close", (e) => {
+    if (willQuitApp) {
+      /* the user tried to quit the app */
+      win = null;
+    } else {
+      /* the user only tried to close the window */
+      e.preventDefault();
+      win.hide();
+    }
   });
+  // win.on("closed", () => {
+  //   win = null;
+  // });
   return win;
 }
 
@@ -71,7 +85,7 @@ app.on("window-all-closed", () => {
   // On macOS it is common for applications and their menu bar
   // to stay active until the user quits explicitly with Cmd + Q
   if (process.platform !== "darwin") {
-    app.quit();
+    // app.quit();
   }
 });
 
@@ -80,9 +94,16 @@ app.on("activate", () => {
   // dock icon is clicked and there are no other windows open.
   if (win === null) {
     createWindow();
+  } else {
+    win.show();
   }
 });
 
+/**
+ * 'before-quit' is emitted when Electron receives the signal to exit and wants to start closing windows
+ */
+app.on("before-quit", () => (willQuitApp = true));
+
 // This method will be called when Electron has finished
 // initialization and is ready to create browser windows.
 // Some APIs can only be used after this event occurs.
diff --git a/src/electron/ipcMain.js b/src/electron/ipcMain.js
index a631697..83a3ae8 100644
--- a/src/electron/ipcMain.js
+++ b/src/electron/ipcMain.js
@@ -33,8 +33,9 @@ export function initIpcMain(win) {
   });
 
   ipcMain.on("close", () => {
-    win.close();
-    app.quit();
+    win.hide();
+    // win.close();
+    // app.quit();
   });
 
   ipcMain.on("minimize", () => {
diff --git a/src/electron/menu.js b/src/electron/menu.js
index 972fc9b..7965400 100644
--- a/src/electron/menu.js
+++ b/src/electron/menu.js
@@ -128,6 +128,7 @@ export function createMenu(win) {
     {
       label: "Window",
       submenu: [
+        { role: "close" },
         { role: "minimize" },
         { role: "zoom" },
         { role: "reload" },
diff --git a/src/electron/tray.js b/src/electron/tray.js
index e624c19..04ab51f 100644
--- a/src/electron/tray.js
+++ b/src/electron/tray.js
@@ -1,5 +1,6 @@
+/* global __static */
 import path from "path";
-import { nativeImage, Tray } from "electron";
+import { app, nativeImage, Tray, Menu } from "electron";
 
 export function createTray(win) {
   let icon = nativeImage
@@ -17,5 +18,18 @@ export function createTray(win) {
       win.show();
     }
   });
+
+  tray.on("right-click", () => {
+    const contextMenu = Menu.buildFromTemplate([
+      {
+        label: "Quit",
+        click: () => {
+          app.exit();
+        },
+      },
+    ]);
+    tray.popUpContextMenu(contextMenu);
+  });
+
   return tray;
 }

From 993f86710983ba98d3a63cf77a0ad34d63824e9e Mon Sep 17 00:00:00 2001
From: njzy <njzydark@gmail.com>
Date: Wed, 9 Dec 2020 19:51:35 +0800
Subject: [PATCH 2/3] fix: the initial music sort error when restart app (#79)

---
 src/store/index.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/store/index.js b/src/store/index.js
index 4caa429..c6a5c9b 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -43,10 +43,11 @@ Howler.volume(store.state.player.volume);
 // 防止软件第一次打开资源加载2次
 Howler.autoUnlock = false;
 
-const currentTrackId = store.state?.player?.currentTrack?.id;
-if (currentTrackId) {
+const currentTrack = store.state?.player?.currentTrack;
+if (currentTrack?.id) {
   store.dispatch("switchTrack", {
-    id: currentTrackId,
+    id: currentTrack.id,
+    sort: currentTrack.sort,
     autoplay: false,
   });
 }

From 4128d9ac3b0a389bb27731e5eae5c0f31ba51e79 Mon Sep 17 00:00:00 2001
From: njzy <njzydark@gmail.com>
Date: Wed, 9 Dec 2020 19:53:19 +0800
Subject: [PATCH 3/3] feat: support cache songs (#80)

fix: cache unblock song

refactor: play first and then cache it
---
 src/store/actions.js | 66 ++++++++++++++++++++++++++++++--------------
 src/utils/db.js      | 34 ++++++++++-------------
 2 files changed, 61 insertions(+), 39 deletions(-)

diff --git a/src/store/actions.js b/src/store/actions.js
index 76305b7..4200e0c 100644
--- a/src/store/actions.js
+++ b/src/store/actions.js
@@ -1,5 +1,5 @@
 import { updateMediaSessionMetaData } from "@/utils/mediaSession";
-import { getTrackDetail, scrobble, getMP3 } from "@/api/track";
+import { getTrackDetail, scrobble, getMP3 as getMP3Api } from "@/api/track";
 import { isAccountLoggedIn } from "@/utils/auth";
 import { updateHttps } from "@/utils/common";
 import localforage from "localforage";
@@ -40,17 +40,18 @@ export default {
       updateMediaSessionMetaData(track);
       document.title = `${track.name} · ${track.ar[0].name} - YesPlayMusic`;
 
+      let unblockSongUrl = null;
       if (track.playable === false) {
         let res = undefined;
         if (process.env.IS_ELECTRON === true) {
           res = ipcRenderer.sendSync("unblock-music", track);
         }
         if (res?.url) {
-          commitMP3(res.url);
+          unblockSongUrl = res.url;
         } else {
           dispatch("nextTrack");
+          return;
         }
-        return;
       }
 
       function commitMP3(mp3) {
@@ -59,31 +60,56 @@ export default {
           dispatch("nextTrack");
         });
       }
+
+      function getMP3(id) {
+        return getMP3Api(id).then((data) => {
+          // 未知情况下会没有返回数据导致报错,增加防范逻辑
+          if (data.data[0]) {
+            const url = updateHttps(data.data[0].url);
+            commitMP3(url);
+            return url;
+          }
+        });
+      }
+
       if (isAccountLoggedIn()) {
         if (store.state.settings.automaticallyCacheSongs === true) {
           let tracks = localforage.createInstance({
             name: "tracks",
           });
-          tracks.getItem(`${track.id}`).then((t) => {
-            if (t !== null) {
-              commitMP3(URL.createObjectURL(t.mp3));
-            } else {
-              cacheTrack(`${track.id}`).then((t) => {
-                commitMP3(URL.createObjectURL(t.mp3));
-              });
-            }
-          });
+          tracks
+            .getItem(`${track.id}`)
+            .then((t) => {
+              if (t !== null) {
+                const blob = new Blob([t.mp3]);
+                commitMP3(URL.createObjectURL(blob));
+              } else {
+                if (unblockSongUrl) {
+                  commitMP3(unblockSongUrl);
+                  cacheTrack(`${track.id}`, unblockSongUrl);
+                } else {
+                  getMP3(track.id).then((url) => {
+                    cacheTrack(`${track.id}`, url);
+                  });
+                }
+              }
+            })
+            .catch((err) => {
+              console.log(err.messaeg);
+              if (unblockSongUrl) {
+                commitMP3(unblockSongUrl);
+              } else {
+                getMP3(track.id);
+              }
+            });
         } else {
-          getMP3(track.id).then((data) => {
-            // 未知情况下会没有返回数据导致报错,增加防范逻辑
-            if (data.data[0]) {
-              const url = updateHttps(data.data[0].url);
-              commitMP3(url);
-            }
-          });
+          getMP3(track.id);
         }
       } else {
-        commitMP3(`https://music.163.com/song/media/outer/url?id=${track.id}`);
+        commitMP3(
+          unblockSongUrl ||
+            `https://music.163.com/song/media/outer/url?id=${track.id}`
+        );
       }
     });
   },
diff --git a/src/utils/db.js b/src/utils/db.js
index 7c8ffc6..e7bb5bf 100644
--- a/src/utils/db.js
+++ b/src/utils/db.js
@@ -1,11 +1,10 @@
-// import axios from "axios";
+import axios from "axios";
 import localforage from "localforage";
-import { getMP3 } from "@/api/track";
 
-export function cacheTrack(id) {
-  // let tracks = localforage.createInstance({
-  //   name: "tracks",
-  // });
+export function cacheTrack(id, url) {
+  let tracks = localforage.createInstance({
+    name: "tracks",
+  });
 
   // TODO: limit cache songs number
   // tracks.length().then(function (length) {
@@ -17,17 +16,14 @@ export function cacheTrack(id) {
   // });
 
   // TODO: cache track details
-  return getMP3(id).then((data) => {
-    // return axios
-    //   .get(data.data[0].url.replace(/^http:/, "https:"), {
-    //     responseType: "blob",
-    //   })
-    //   .then((data) => {
-    //     tracks.setItem(`${id}`, { mp3: data.data });
-    //     return { mp3: data.data };
-    //   });
-    return { mp3: data.data[0].url.replace(/^http:/, "https:") };
-  });
+  return axios
+    .get(url, {
+      responseType: "arraybuffer",
+    })
+    .then((response) => {
+      tracks.setItem(`${id}`, { mp3: response.data });
+      return { mp3: response.data };
+    });
 }
 
 export function countDBSize(dbName) {
@@ -37,11 +33,11 @@ export function countDBSize(dbName) {
   let trackSizes = [];
   return db
     .iterate((value) => {
-      trackSizes.push(value.mp3.size);
+      trackSizes.push(value.mp3.byteLength);
     })
     .then(() => {
       return {
-        bytes: trackSizes.reduce((s1, s2) => s1 + s2),
+        bytes: trackSizes.reduce((s1, s2) => s1 + s2, 0),
         length: trackSizes.length,
       };
     })