Repository: listen1/listen1_desktop Branch: master Commit: bbc38654bd59 Files: 14 Total size: 38.9 KB Directory structure: gitextract_vfoos4bu/ ├── .github/ │ └── workflows/ │ └── release.yml ├── .gitignore ├── .gitmodules ├── .prettierrc ├── LICENSE.md ├── README.md ├── app/ │ ├── floatingWindow.html │ ├── functions.js │ ├── main.js │ ├── package.json │ └── preload.js ├── build/ │ ├── disk.icns │ └── icon.icns └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/release.yml ================================================ name: Build/release on: push jobs: release: runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] steps: - name: Check out Git repository uses: actions/checkout@v2 with: submodules: true - name: Install Node.js, NPM and Yarn uses: actions/setup-node@v2 with: node-version: 16 - name: Build/release Electron app uses: samuelmeuli/action-electron-builder@v1 with: # GitHub token, automatically provided to the action # (No need to define this secret in the repo settings) github_token: ${{ secrets.github_token }} # If the commit is tagged with a version (e.g. "v1.0.0"), # release the app after building release: ${{ startsWith(github.ref, 'refs/tags/v') }} ================================================ FILE: .gitignore ================================================ node_modules dist yarn.lock .vscode .DS_Store ================================================ FILE: .gitmodules ================================================ [submodule "app/listen1_chrome_extension"] path = app/listen1_chrome_extension url = https://github.com/listen1/listen1_chrome_extension.git ================================================ FILE: .prettierrc ================================================ { "trailingComma": "es5", "tabWidth": 2, "useTabs": false, "semi": true, "singleQuote": false, "arrowParens": "always", "endOfLine": "lf", "bracketSpacing": true } ================================================ FILE: LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2016 Listen 1 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Listen 1 音乐播放器(桌面版) Listen 1 可以搜索和播放来自多个主流音乐网站的歌曲,让你的曲库更全面。并支持收藏功能,方便的创建自己的歌单。 支持音乐平台 - 网易云音乐 - QQ 音乐 - 酷狗音乐 - 酷我音乐 - bilibili - 咪咕音乐 - 千千音乐 [![imgur](http://i.imgur.com/Ae6ItmA.png)]() - 支持 Windows,Mac,Linux 平台 # 安装方式 访问 github 主页下载安装包安装 网址:https://listen1.github.io/listen1 ## 生成完整代码 项目中包含了 listen1_chrome_extension 的引用,在 checkout 后需要把引用库初始化 git submodule update --init --recursive ## 运行 npm run start ## 生成安装包 全平台安装包 npm run dist Windows 安装包 npm run dist:win32 npm run dist:win64 Mac 安装包 npm run dist:mac Linux 安装包 npm run dist:linux32 npm run dist:linux64 ================================================ FILE: app/floatingWindow.html ================================================ Listen1
Listen1
================================================ FILE: app/functions.js ================================================ const { existsSync } = require("fs"); const { readFile } = require("fs/promises"); const { parseFile } = require("music-metadata"); const { basename, extname, parse, format } = require("path"); const { detect } = require("chardet"); module.exports = { async readAudioTags(filePath) { const fileName = basename(filePath, extname(filePath)); try { const metaData = await parseFile(filePath); metaData.common.title ||= fileName; const lyric_url = format({ ...parse(filePath), ext: ".lrc", base: undefined, }); //if metadata doesn't include lyric, then try to read from local lyric file if (!metaData.common.lyrics && existsSync(lyric_url)) { metaData.common.lyrics = []; const fileBuffer = await readFile(lyric_url); const encoding = detect(fileBuffer); const decoder = new TextDecoder(encoding); metaData.common.lyrics[0] = decoder.decode(fileBuffer); } return metaData; } catch (error) { return { error, common: { title: fileName, album: "", artist: "", }, }; } }, }; ================================================ FILE: app/main.js ================================================ const electron = require("electron"); const { app, BrowserWindow, globalShortcut, ipcMain, Menu, session, screen, Tray, } = electron; const Store = require("electron-store"); const { autoUpdater } = require("electron-updater"); const remoteMain = require("@electron/remote/main"); const { join } = require("path"); const store = new Store(); const iconPath = join(__dirname, "/listen1_chrome_extension/images/logo.png"); autoUpdater.checkForUpdatesAndNotify(); let floatingWindowCssKey = undefined, appIcon = null, willQuitApp = false, transparent = false, trayIconPath; /** @type {electron.BrowserWindow} */ let mainWindow; /** @type {electron.BrowserWindow} */ let floatingWindow; /** @type {electron.Tray} */ let appTray; //platform-specific switch (process.platform) { case "darwin": trayIconPath = join(__dirname, "/resources/logo_16.png"); transparent = true; break; case "linux": trayIconPath = join(__dirname, "/resources/logo_32.png"); // fix transparent window not working in linux bug app.disableHardwareAcceleration(); break; case "win32": trayIconPath = join(__dirname, "/resources/logo_32.png"); break; default: break; } // 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. /** @type {{ width: number; height: number; maximized: boolean; zoomLevel: number}} */ const windowState = store.get("windowState") || { width: 1000, height: 670, maximized: false, zoomLevel: 0, }; /** @type {electron.Config} */ let proxyConfig = store.get("proxyConfig") || { mode: "system", }; const globalShortcutMapping = { "CmdOrCtrl+Alt+Left": "left", "CmdOrCtrl+Alt+Right": "right", "CmdOrCtrl+Alt+Space": "space", MediaNextTrack: "right", MediaPreviousTrack: "left", MediaPlayPause: "space", }; /** * @param {electron.BrowserWindow} mainWindow * @param {{ title: string; artist: string; }} [track] */ function initialTray(mainWindow, track) { track ||= { title: "暂无歌曲", artist: " ", }; let nowPlayingTitle = `${track.title}`; let nowPlayingArtist = `歌手: ${track.artist}`; function toggleVisiable() { mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show(); } const menuTemplate = [ { label: nowPlayingTitle, click() { mainWindow.show(); }, }, { label: nowPlayingArtist, click() { mainWindow.show(); }, }, { type: "separator" }, { label: "播放/暂停", click() { mainWindow.webContents.send("globalShortcut", "space"); }, }, { label: "上一首", click() { mainWindow.webContents.send("globalShortcut", "left"); }, }, { label: "下一首", click() { mainWindow.webContents.send("globalShortcut", "right"); }, }, { label: "显示/隐藏窗口", click() { toggleVisiable(); }, }, { label: "退出", click() { app.quit(); }, }, ]; const contextMenu = Menu.buildFromTemplate(menuTemplate); if (appTray?.destroy != undefined) { // appTray had create, just refresh tray menu here appTray?.setContextMenu(contextMenu); return; } appTray = new Tray(trayIconPath); appTray.setContextMenu(contextMenu); appTray.on("click", () => { toggleVisiable(); }); } /** * @param {string | electron.Accelerator} key * @param {string} message */ function setKeyMapping(key, message) { globalShortcut.register(key, () => { mainWindow.webContents.send("globalShortcut", message); }); } function enableGlobalShortcuts() { // initial global shortcuts for (const [key, value] of Object.entries(globalShortcutMapping)) { setKeyMapping(key, value); } } function disableGlobalShortcuts() { globalShortcut.unregisterAll(); } /** * @param {string} cssStyle */ async function updateFloatingWindow(cssStyle) { if (cssStyle === undefined) { return; } try { const newCssKey = await floatingWindow.webContents.insertCSS(cssStyle, { cssOrigin: "author", }); if (floatingWindowCssKey !== undefined) { await floatingWindow.webContents.removeInsertedCSS(floatingWindowCssKey); } floatingWindowCssKey = newCssKey; } catch (err) { console.log(err); } } /** * @param {electron.Config} params */ async function updateProxyConfig(params) { proxyConfig = params; await mainWindow.webContents.session.setProxy(params); await mainWindow.webContents.session.forceReloadProxyConfig(); } /** * @param {string} cssStyle */ function createFloatingWindow(cssStyle) { const display = screen.getPrimaryDisplay(); if (process.platform === "linux") { // fix transparent window not working in linux bug floatingWindow?.destroy(); floatingWindow = null; } if (!floatingWindow) { /** @type {Electron.Rectangle} */ const winBounds = store.get("floatingWindowBounds"); floatingWindow = new BrowserWindow({ width: 1000, minWidth: 640, maxWidth: 1920, height: 70, titleBarStyle: "hidden", transparent: true, frame: false, resizable: true, hasShadow: false, alwaysOnTop: true, webPreferences: { sandbox: true, preload: join(__dirname, "preload.js"), }, ...winBounds, }); if (winBounds === undefined) { floatingWindow.setPosition( floatingWindow.getPosition()[0], display.bounds.height - 150 ); } floatingWindow.setVisibleOnAllWorkspaces(true); floatingWindow.setSkipTaskbar(true); floatingWindow.loadURL(`file://${__dirname}/floatingWindow.html`); floatingWindow.setAlwaysOnTop(true, "floating"); floatingWindow.setIgnoreMouseEvents(false); // NOTICE: setResizable should be set, otherwise mouseleave event won't trigger in windows environment floatingWindow.webContents.on("did-finish-load", async () => { await updateFloatingWindow(cssStyle); }); floatingWindow.on("closed", () => { floatingWindow = null; }); // floatingWindow.webContents.openDevTools(); } floatingWindow.showInactive(); } const previousButton = { tooltip: "Previous", icon: join(__dirname, "/resources/prev-song.png"), click() { mainWindow.webContents.send("globalShortcut", "left"); }, }; const nextButton = { tooltip: "Next", icon: join(__dirname, "/resources/next-song.png"), click() { mainWindow.webContents.send("globalShortcut", "right"); }, }; const playButton = { tooltip: "Play", icon: join(__dirname, "/resources/play-song.png"), click() { mainWindow.webContents.send("globalShortcut", "space"); }, }; const pauseButton = { tooltip: "Pause", icon: join(__dirname, "/resources/pause-song.png"), click() { mainWindow.webContents.send("globalShortcut", "space"); }, }; const setThumbarPause = () => { mainWindow?.setThumbarButtons([previousButton, playButton, nextButton]); }; const setThumbbarPlay = () => { mainWindow?.setThumbarButtons([previousButton, pauseButton, nextButton]); }; function createWindow() { const filter = { urls: [ "*://*.music.163.com/*", "*://music.163.com/*", "*://*.xiami.com/*", "*://i.y.qq.com/*", "*://c.y.qq.com/*", "*://*.kugou.com/*", "*://*.kuwo.cn/*", "*://*.bilibili.com/*", "*://*.bilivideo.com/*", "*://*.bilivideo.cn/*", "*://*.migu.cn/*", "*://*.githubusercontent.com/*", "https://listen1.github.io/listen1/callback.html?code=*", ], }; session.defaultSession.webRequest.onBeforeSendHeaders( filter, (details, callback) => { if ( details.url.startsWith( "https://listen1.github.io/listen1/callback.html?code=" ) ) { const { url } = details; const code = url.split("=")[1]; mainWindow.webContents.executeJavaScript( 'GithubClient.github.handleCallback("' + code + '");' ); } else { hack_referer_header(details); } callback({ cancel: false, requestHeaders: details.requestHeaders }); } ); // Create the browser window. mainWindow = new BrowserWindow({ width: windowState.width, height: windowState.height, minHeight: 300, minWidth: 600, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false, }, icon: iconPath, titleBarStyle: "hiddenInset", transparent: transparent, vibrancy: "light", frame: false, hasShadow: true, }); mainWindow.on("ready-to-show", () => { if (windowState.maximized) { mainWindow.maximize(); } mainWindow.webContents.send("setZoomLevel", windowState.zoomLevel); }); mainWindow.on("resized", () => { if (!mainWindow.isMaximized() && !mainWindow.isFullScreen()) { const [width, height] = mainWindow.getSize(); windowState.width = width; windowState.height = height; } }); mainWindow.on("close", (e) => { if (willQuitApp) { /* the user tried to quit the app */ mainWindow = null; } else { /* the user only tried to close the window */ //if (process.platform != 'linux') { e.preventDefault(); mainWindow.hide(); //mainWindow.minimize(); //} } }); // and load the index.html of the app. const ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36"; mainWindow.webContents.session.setProxy(proxyConfig).then(() => { mainWindow.loadURL( `file://${__dirname}/listen1_chrome_extension/listen1.html`, { userAgent: ua } ); }); setThumbarPause(); // Emitted when the window is closed. mainWindow.on("closed", () => { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. mainWindow = null; }); // define global menu content, also add support for cmd+c and cmd+v shortcuts const template = [ { label: "Application", submenu: [ { label: "Zoom Out", accelerator: "CmdOrCtrl+=", click() { if (windowState.zoomLevel <= 2.5) { windowState.zoomLevel += 0.5; mainWindow.webContents.send( "setZoomLevel", windowState.zoomLevel ); } }, }, { label: "Zoom in", accelerator: "CmdOrCtrl+-", click() { if (windowState.zoomLevel >= -1) { windowState.zoomLevel -= 0.5; mainWindow.webContents.send( "setZoomLevel", windowState.zoomLevel ); } }, }, { label: "Toggle Developer Tools", accelerator: "F12", click() { mainWindow.webContents.toggleDevTools(); }, }, { label: "About Application", selector: "orderFrontStandardAboutPanel:", }, { type: "separator" }, { label: "Close Window", accelerator: "CmdOrCtrl+W", click() { mainWindow.close(); }, }, { label: "Quit", accelerator: "Command+Q", click() { app.quit(); }, }, ], }, { label: "Edit", submenu: [ { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, { type: "separator" }, { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:", }, ], }, ]; mainWindow.setMenu(null); Menu.setApplicationMenu(Menu.buildFromTemplate(template)); initialTray(mainWindow); } const MOBILE_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"; /** * @param {electron.OnBeforeSendHeadersListenerDetails} details */ function hack_referer_header(details) { let replace_referer = true; let replace_origin = true; let add_referer = true; let add_origin = true; let referer_value = ""; let origin_value = ""; let ua_value = ""; if (details.url.includes("://music.163.com/")) { referer_value = "http://music.163.com/"; } if (details.url.includes("://interface3.music.163.com/")) { referer_value = "http://music.163.com/"; } if (details.url.includes("://gist.githubusercontent.com/")) { referer_value = "https://gist.githubusercontent.com/"; } if (details.url.includes(".xiami.com/")) { add_origin = false; referer_value = "https://www.xiami.com/"; } if (details.url.includes("www.xiami.com/api/search/searchSongs")) { const key = /key%22:%22(.*?)%22/.exec(details.url)[1]; add_origin = false; referer_value = `https://www.xiami.com/search?key=${key}`; } if (details.url.includes("c.y.qq.com/")) { referer_value = "https://y.qq.com/"; origin_value = "https://y.qq.com"; } if ( details.url.includes("y.qq.com/") || details.url.includes("qqmusic.qq.com/") || details.url.includes("music.qq.com/") || details.url.includes("imgcache.qq.com/") ) { referer_value = "http://y.qq.com/"; } if (details.url.includes(".kugou.com/")) { referer_value = "https://www.kugou.com/"; ua_value = MOBILE_UA; } if (details.url.includes("m.kugou.com/")) { ua_value = MOBILE_UA; } if (details.url.includes(".kuwo.cn/")) { referer_value = "http://www.kuwo.cn/"; } if ( details.url.includes(".bilibili.com/") || details.url.includes(".bilivideo.com/") ) { referer_value = "https://www.bilibili.com/"; replace_origin = false; add_origin = false; } if (details.url.includes('.bilivideo.cn')) { referer_value = 'https://www.bilibili.com/'; origin_value = 'https://www.bilibili.com/'; add_referer = true; add_origin = true; } if (details.url.includes(".migu.cn")) { referer_value = "http://music.migu.cn/v3/music/player/audio?from=migu"; } if (details.url.includes("m.music.migu.cn")) { referer_value = "https://m.music.migu.cn/"; } if (origin_value == "") { origin_value = referer_value; } let isRefererSet = false; let isOriginSet = false; let isUASet = false; let headers = details.requestHeaders; for (let i = 0, l = headers.length; i < l; ++i) { if ( replace_referer && headers[i].name == "Referer" && referer_value != "" ) { headers[i].value = referer_value; isRefererSet = true; } if (replace_origin && headers[i].name == "Origin" && referer_value != "") { headers[i].value = origin_value; isOriginSet = true; } if (headers[i].name === "User-Agent" && ua_value !== "") { headers[i].value = ua_value; isUASet = true; } } if (add_referer && !isRefererSet && referer_value != "") { headers["Referer"] = referer_value; } if (add_origin && !isOriginSet && referer_value != "") { headers["Origin"] = origin_value; } if (!isUASet && ua_value !== "") { headers["User-Agent"] = ua_value; } details.requestHeaders = headers; } ipcMain.on("currentLyric", (event, arg) => { if (floatingWindow && floatingWindow !== null) { if (typeof arg === "string") { floatingWindow.webContents.send("currentLyric", arg); floatingWindow.webContents.send("currentLyricTrans", ""); } else { floatingWindow.webContents.send("currentLyric", arg.lyric); floatingWindow.webContents.send("currentLyricTrans", arg.tlyric); } } }); ipcMain.on("trackPlayingNow", (event, track) => { if (mainWindow != null) { initialTray(mainWindow, track); } }); ipcMain.on("isPlaying", (event, isPlaying) => { isPlaying ? setThumbbarPlay() : setThumbarPause(); }); ipcMain.on("control", async (event, arg, params) => { switch (arg) { case "enable_global_shortcut": enableGlobalShortcuts(); break; case "disable_global_shortcut": disableGlobalShortcuts(); break; case "enable_lyric_floating_window": createFloatingWindow(params); break; case "disable_lyric_floating_window": floatingWindow?.hide(); break; case "window_min": mainWindow.minimize(); break; case "window_max": windowState.maximized ? mainWindow.unmaximize() : mainWindow.maximize(); windowState.maximized = !windowState.maximized; break; case "window_close": mainWindow.close(); break; case "float_window_accept_mouse_event": floatingWindow.setIgnoreMouseEvents(false); break; case "float_window_ignore_mouse_event": floatingWindow.setIgnoreMouseEvents(true, { forward: true }); break; case "float_window_close": case "float_window_font_small": case "float_window_font_large": case "float_window_background_light": case "float_window_background_dark": case "float_window_font_change_color": mainWindow.webContents.send("lyricWindow", arg); break; case "update_lyric_floating_window_css": await updateFloatingWindow(params); break; case "get_proxy_config": mainWindow.webContents.send("proxyConfig", proxyConfig); break; case "update_proxy_config": await updateProxyConfig(params); break; default: break; } // event.sender.send('asynchronous-reply', 'pong') }); ipcMain.on("openUrl", (event, arg, params) => { const bWindow = new BrowserWindow({ parent: mainWindow, height: 700, resizable: true, width: 985, frame: true, fullscreen: false, maximizable: true, minimizable: true, autoHideMenuBar: true, webPreferences: { // sandbox is necessary for website js to work // thanks to https://github.com/sunzongzheng/music sandbox: true, }, }); bWindow.loadURL(arg); bWindow.setMenu(null); }); ipcMain.on("floatWindowMoving", (e, { mouseX, mouseY }) => { const { x, y } = screen.getCursorScreenPoint(); floatingWindow?.setPosition(x - mouseX, y - mouseY); }); const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { app.on("second-instance", (event, commandLine, workingDirectory) => { // Someone tried to run a second instance, we should focus our window. if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore(); mainWindow.focus(); // When start a new instance, show the main window and active in taskbar. mainWindow.show(); mainWindow.setSkipTaskbar(false); } }); // Create myWindow, load the rest of the app, etc... app.on("ready", () => { createWindow(); remoteMain.initialize(); remoteMain.enable(mainWindow.webContents); }); } // Quit when all windows are closed. app.on("window-all-closed", () => { // On OS X 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(); } }); /* 'activate' is emitted when the user clicks the Dock icon (OS X) */ app.on("activate", () => mainWindow.show()); /* 'before-quit' is emitted when Electron receives * the signal to exit and wants to start closing windows */ app.on("before-quit", () => { if (mainWindow.webContents.isDevToolsOpened()) { mainWindow.webContents.closeDevTools(); } if (floatingWindow) { store.set("floatingWindowBounds", floatingWindow.getBounds()); } store.set("windowState", windowState); store.set("proxyConfig", proxyConfig); willQuitApp = true; }); app.on("will-quit", () => { disableGlobalShortcuts(); }); ================================================ FILE: app/package.json ================================================ { "name": "listen1", "version": "2.33.0", "description": "One for all free music in China", "main": "main.js", "scripts": { "start": "electron main.js" }, "repository": { "type": "git", "url": "git+https://github.com/listen1/listen1_desktop.git" }, "keywords": [ "Electron", "Listen 1", "music player" ], "author": "Listen 1 ", "license": "MIT", "bugs": { "url": "https://github.com/listen1/listen1_desktop/issues" }, "homepage": "https://github.com/listen1/listen1_desktop#readme", "dependencies": { "@electron/remote": "^2.1.2", "chardet": "^1.4.0", "electron-store": "^8.0.1", "electron-updater": "^5.0.1", "music-metadata": "^7.12.3" } } ================================================ FILE: app/preload.js ================================================ const { contextBridge, ipcRenderer } = require("electron"); contextBridge.exposeInMainWorld("api", { ipcRenderer: ipcRenderer, send: (channel, data) => { ipcRenderer.send(channel, data); }, onLyric: (fn) => { // Deliberately strip event as it includes `sender` ipcRenderer.on("currentLyric", (event, ...args) => fn(...args)); }, onTranslLyric: (fn) => { // Deliberately strip event as it includes `sender` ipcRenderer.on("currentLyricTrans", (event, ...args) => fn(...args)); }, }); ================================================ FILE: package.json ================================================ { "name": "listen1", "version": "2.33.0", "description": "One for all free music in China", "main": "app/main.js", "scripts": { "postinstall": "electron-builder install-app-deps", "start": "electron ./app --enable-logging", "dev": "NODE_ENV='development' npm run start", "dist:mac": "CSC_IDENTITY_AUTO_DISCOVERY=false DEBUG=electron-builder electron-builder --mac", "dist": "electron-builder .", "dist:linux": "electron-builder --linux --ia32 --x64", "dist:linux32": "electron-builder --linux --ia32", "dist:linux64": "electron-builder --linux --x64", "dist:linuxArm64": "electron-builder --linux --arm64", "dist:linuxArmv7l": "electron-builder --linux --armv7l", "dist:win": "electron-builder --win" }, "repository": { "type": "git", "url": "git+https://github.com/listen1/listen1_desktop.git" }, "keywords": [ "Electron", "Listen 1" ], "author": "Listen 1 ", "license": "MIT", "bugs": { "url": "https://github.com/listen1/listen1_desktop/issues" }, "homepage": "https://github.com/listen1/listen1_desktop#readme", "build": { "appId": "com.listen1.listen1", "productName": "Listen1", "asar": true, "artifactName": "${name}_${version}_${os}_${arch}.${ext}", "dmg": { "icon": "build/disk.icns", "contents": [ { "x": 192, "y": 344 }, { "x": 448, "y": 344, "type": "link", "path": "/Applications" } ] }, "mac": { "target": [ { "target": "dmg", "arch": [ "x64", "arm64", "universal" ] } ], "category": "public.app-category.music" }, "linux": { "target": [ "tar.gz", "appImage", "deb" ], "category": "Audio" }, "nsis": { "runAfterFinish": false, "deleteAppDataOnUninstall": true, "allowToChangeInstallationDirectory": true, "oneClick": false, "installerLanguages": "zh_CN", "language": 2052, "perMachine": true, "createDesktopShortcut": true }, "win": { "target": [ { "target": "nsis", "arch": [ "x64", "ia32", "arm64" ] }, { "target": "7z", "arch": [ "x64", "ia32", "arm64" ] } ], "icon": "build/icon.ico" } }, "devDependencies": { "electron": "^32.3.2", "electron-builder": "^25.1.8", "prettier": "^2.6.2" } }