diff --git a/package-lock.json b/package-lock.json index 7bcd3f5..ffc8067 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,6 @@ "electron-builder": "^24.6.3", "eslint": "^8.47.0", "prettier": "^3.0.2" - }, - "optionalDependencies": { - "dmg-license": "^1.0.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -691,6 +688,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", + "dev": true, "optional": true, "dependencies": { "@types/node": "*", @@ -709,6 +707,7 @@ "version": "1.10.6", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", + "dev": true, "optional": true }, "node_modules/@types/yauzl": { @@ -724,7 +723,7 @@ "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=10.0.0" } @@ -772,7 +771,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "devOptional": true, + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -797,7 +796,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" } @@ -806,7 +805,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -922,6 +921,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, "optional": true, "engines": { "node": ">=0.8" @@ -931,6 +931,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, "optional": true, "engines": { "node": ">=8" @@ -976,7 +977,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -1059,6 +1060,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -1276,6 +1278,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, "optional": true, "dependencies": { "slice-ansi": "^3.0.0", @@ -1317,7 +1320,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1329,7 +1332,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1381,12 +1384,14 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, "optional": true }, "node_modules/crc": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, "optional": true, "dependencies": { "buffer": "^5.1.0" @@ -1626,6 +1631,7 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, "optional": true, "os": [ "darwin" @@ -1821,7 +1827,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true + "dev": true }, "node_modules/end-of-stream": { "version": "1.4.4", @@ -2128,6 +2134,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, "engines": [ "node >=0.6.0" ], @@ -2137,7 +2144,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "devOptional": true + "dev": true }, "node_modules/fast-diff": { "version": "1.3.0", @@ -2177,7 +2184,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "devOptional": true + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -2649,6 +2656,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, "optional": true, "os": [ "darwin" @@ -2677,6 +2685,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -2783,7 +2792,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" } @@ -2954,7 +2963,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "devOptional": true + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -3237,6 +3246,7 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, "optional": true }, "node_modules/normalize-url": { @@ -3448,7 +3458,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "devOptional": true, + "dev": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -3515,7 +3525,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } @@ -3903,6 +3913,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, "optional": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -3917,6 +3928,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, "optional": true, "engines": { "node": ">= 6.0.0", @@ -3961,7 +3973,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3975,7 +3987,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4232,7 +4244,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "devOptional": true, + "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -4247,6 +4259,7 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, "optional": true, "dependencies": { "assert-plus": "^1.0.0", @@ -4298,7 +4311,7 @@ "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.0" } diff --git a/package.json b/package.json index a32bedb..4e3dfa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "um-react-electron", - "version": "1.0.0", + "version": "1.0.1", "description": "Unlock Music 音乐解锁 (React) Electron App", "main": "./src/main.js", "author": "dreamfly", diff --git a/src/main.js b/src/main.js index 554477f..b49f075 100644 --- a/src/main.js +++ b/src/main.js @@ -1,87 +1,163 @@ // Modules to control application life and create native browser window -const { app, shell, BrowserWindow } = require('electron') +const { app, shell, BrowserWindow, Menu, dialog } = require('electron') const path = require('path') const { electronApp, optimizer } = require('@electron-toolkit/utils') -function createWindow() { - // Create the browser window. - const mainWindow = new BrowserWindow({ - width: 900, - height: 670, - show: false, - autoHideMenuBar: true, - // frame: false, // 隐藏标题栏 - ...(process.platform === 'linux' || process.platform === 'win32' - ? { - icon: path.join(__dirname, '../resources/icon.png') - } - : {}), - ...(process.platform === 'darwin' - ? { - titleBarStyle: 'hidden' // 隐藏 macOS 的默认标题栏 - } - : {}), - webPreferences: { - preload: path.join(__dirname, 'preload.js'), - sandbox: false, - nodeIntegration: true +let name, mainWindow; + +function downloadURL(url, filepath) { + return new Promise((resolve, reject) => { + name = filepath; + mainWindow.webContents.downloadURL(url); + mainWindow.webContents.session.once('will-download', (event, item) => { + item.once('done', (event, state) => { + if (state === 'completed') { + resolve(); + } else { + reject(new Error(`Download failed: ${state}`)); + } + }); + }); + }); +} + +async function downloadAll() { + console.log('保存全部') + const links = await mainWindow.webContents.executeJavaScript(` + Array.from(document.querySelectorAll('a[href^="blob"]')).map(a => ({ + href: a.href, + download: a.download + })) + `); + console.log(links); + // Ask for a directory to save all files + const { filePaths } = await dialog.showOpenDialog({ + properties: ['openDirectory'] + }); + if (!filePaths || !filePaths.length) { + return; } - }) + for (const link of links) { + try { + await downloadURL(link.href, path.join(filePaths[0], link.download)); + console.log(`Downloaded ${link.download}`); + } catch (error) { + console.log(`Failed to download ${link.download}: ${error.message}`); + } + } +} - mainWindow.on('ready-to-show', () => { - mainWindow.show() - }) +function createWindow() { + // Create the browser window. + mainWindow = new BrowserWindow({ + width: 900, + height: 670, + show: false, + // autoHideMenuBar: true, + // frame: false, // 隐藏标题栏 + ...(process.platform === 'linux' || process.platform === 'win32' ? { + icon: path.join(__dirname, '../resources/icon.png') + } : {}), + ...(process.platform === 'darwin' ? { + titleBarStyle: 'hidden' // 隐藏 macOS 的默认标题栏 + } : {}), + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + sandbox: false, + nodeIntegration: true + } + }) - mainWindow.webContents.setWindowOpenHandler((details) => { - shell.openExternal(details.url) - return { action: 'deny' } - }) + mainWindow.on('ready-to-show', () => { + mainWindow.show() + }) - // and load the index.html of the app. - // console.log(app.isPackaged) - // if (!app.isPackaged) { - // mainWindow.loadURL('http://localhost:5173') - // } else { - mainWindow.loadFile(path.join(__dirname, 'renderer/index.html')) - // } + mainWindow.webContents.setWindowOpenHandler((details) => { + shell.openExternal(details.url) + return { action: 'deny' } + }) - mainWindow.on('closed', () => { - // 在窗口关闭时触发before-quit事件以结束进程 - app.quit() - }) + mainWindow.webContents.session.on('will-download', (event, item) => { + if (name) { + item.setSavePath(name); + name = null; + } + + item.on('updated', (event, state) => { + if (state === 'interrupted') { + console.log('Download is interrupted but can be resumed') + } else if (state === 'progressing') { + if (item.isPaused()) { + console.log('Download is paused') + } else { + console.log(`Received bytes: ${item.getReceivedBytes()}`) + } + } + }); + + item.once('done', (event, state) => { + if (state === 'completed') { + console.log('Download successfully') + } else { + console.log(`Download failed: ${state}`) + } + }); + }); + + // and load the index.html of the app. + // console.log(app.isPackaged) + // if (!app.isPackaged) { + // mainWindow.loadURL('http://localhost:5173') + // } else { + mainWindow.loadFile(path.join(__dirname, 'renderer/index.html')) + // } + + mainWindow.on('closed', () => { + // 在窗口关闭时触发before-quit事件以结束进程 + app.quit() + }) } // 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. app.whenReady().then(() => { - // Set app user model id for windows - electronApp.setAppUserModelId('com.dreamfly.um') + // Set app user model id for windows + electronApp.setAppUserModelId('com.dreamfly.um') - // Default open or close DevTools by F12 in development - // and ignore CommandOrControl + R in production. - // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils - app.on('browser-window-created', (_, window) => { - optimizer.watchWindowShortcuts(window) - }) + // Default open or close DevTools by F12 in development + // and ignore CommandOrControl + R in production. + // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils + app.on('browser-window-created', (_, window) => { + optimizer.watchWindowShortcuts(window) + }) - app.on('NSApplicationDelegate.applicationSupportsSecureRestorableState', () => true) + app.on('NSApplicationDelegate.applicationSupportsSecureRestorableState', () => true) - createWindow() + const menu = Menu.buildFromTemplate([{ + label: '文件', + submenu: [{ + label: '保存全部', + click: downloadAll + }] + }]); + Menu.setApplicationMenu(menu); - app.on('activate', function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) createWindow() - }) + createWindow() + + app.on('activate', function() { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) createWindow() + }) }) // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. -app.on('window-all-closed', function () { - if (process.platform !== 'darwin') { - app.quit() - } +app.on('window-all-closed', function() { + if (process.platform !== 'darwin') { + app.quit() + } })