├── .prettierrc ├── public ├── robots.txt ├── icon.png ├── favicon.ico ├── img │ └── icons │ │ ├── icon.ico │ │ ├── 16x16.png │ │ ├── 24x24.png │ │ ├── 32x32.png │ │ ├── 48x48.png │ │ ├── 64x64.png │ │ ├── icon.icns │ │ ├── 1024x1024.png │ │ ├── 128x128.png │ │ ├── 256x256.png │ │ └── 512x512.png └── index.html ├── .browserslistrc ├── src ├── shims-node.d.ts ├── electron │ ├── fs-promises-shim.js │ ├── noop-devtools.js │ ├── noop-electron-devtools-installer.js │ ├── proxy.ts │ ├── notification.ts │ ├── globalShortcut.ts │ ├── store.ts │ └── menu.ts ├── assets │ ├── logo.png │ └── logo.svg ├── media │ ├── yamusic.png │ ├── yaradio.png │ ├── yamusic_64x64.png │ ├── yaradio_64x64.png │ └── yaradio-yamusic.png ├── shims-vue.d.ts ├── shims-vuetify.d.ts ├── shims-tsx.d.ts ├── store │ └── index.ts ├── main.ts ├── router │ └── index.ts ├── plugins │ └── vuetify.ts ├── registerServiceWorker.ts ├── components │ └── Home.vue ├── views │ ├── YandexMusic.vue │ └── YandexRadio.vue ├── background.ts └── App.vue ├── types └── global.d.ts ├── .github ├── example.gif ├── img │ ├── card.png │ ├── qiwi.png │ └── yoom.png ├── FUNDING.yml └── workflows │ └── release.yml ├── babel.config.js ├── user-data └── config.json ├── todo.md ├── .gitignore ├── .travis.yml ├── .eslintrc.js ├── tsconfig.json ├── LICENSE ├── package.json ├── readme.md ├── CHANGELOG.md └── vue.config.js /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf" 3 | } 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /src/shims-node.d.ts: -------------------------------------------------------------------------------- 1 | declare const __static: string; 2 | -------------------------------------------------------------------------------- /src/electron/fs-promises-shim.js: -------------------------------------------------------------------------------- 1 | module.exports = require("fs").promises; 2 | -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/icon.png -------------------------------------------------------------------------------- /types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | let __static: string; 3 | } 4 | 5 | export {} 6 | -------------------------------------------------------------------------------- /.github/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/.github/example.gif -------------------------------------------------------------------------------- /.github/img/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/.github/img/card.png -------------------------------------------------------------------------------- /.github/img/qiwi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/.github/img/qiwi.png -------------------------------------------------------------------------------- /.github/img/yoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/.github/img/yoom.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/media/yamusic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/media/yamusic.png -------------------------------------------------------------------------------- /src/media/yaradio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/media/yaradio.png -------------------------------------------------------------------------------- /public/img/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/icon.ico -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /public/img/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/16x16.png -------------------------------------------------------------------------------- /public/img/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/24x24.png -------------------------------------------------------------------------------- /public/img/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/32x32.png -------------------------------------------------------------------------------- /public/img/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/48x48.png -------------------------------------------------------------------------------- /public/img/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/64x64.png -------------------------------------------------------------------------------- /public/img/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/icon.icns -------------------------------------------------------------------------------- /src/media/yamusic_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/media/yamusic_64x64.png -------------------------------------------------------------------------------- /src/media/yaradio_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/media/yaradio_64x64.png -------------------------------------------------------------------------------- /public/img/icons/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/1024x1024.png -------------------------------------------------------------------------------- /public/img/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/128x128.png -------------------------------------------------------------------------------- /public/img/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/256x256.png -------------------------------------------------------------------------------- /public/img/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/public/img/icons/512x512.png -------------------------------------------------------------------------------- /src/media/yaradio-yamusic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dedpnd/yaradio-yamusic/HEAD/src/media/yaradio-yamusic.png -------------------------------------------------------------------------------- /src/shims-vuetify.d.ts: -------------------------------------------------------------------------------- 1 | declare module "vuetify/lib/framework" { 2 | import Vuetify from "vuetify"; 3 | export default Vuetify; 4 | } 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ["https://www.tinkoff.ru/sl/6XuoF9Bz5bk", "qiwi.com/n/CHATU497", "https://yoomoney.ru/fundraise/vp4PcgKpSS0.221130"] 3 | -------------------------------------------------------------------------------- /src/electron/noop-devtools.js: -------------------------------------------------------------------------------- 1 | // Stub used to bypass vue-cli-plugin-electron-builder's devtools installer 2 | export default function installVueDevtools() { 3 | return Promise.resolve(); 4 | } 5 | -------------------------------------------------------------------------------- /src/electron/noop-electron-devtools-installer.js: -------------------------------------------------------------------------------- 1 | // Stub to avoid loading electron-devtools-installer on old Electron 2 | const installExtension = async () => Promise.resolve(); 3 | export const VUEJS_DEVTOOLS = "noop-devtools"; 4 | export default installExtension; 5 | -------------------------------------------------------------------------------- /user-data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "notifications": true, 4 | "proxy": { 5 | "protocol": null, 6 | "url": null 7 | }, 8 | "gs": { 9 | "play": true, 10 | "nextTrack": true, 11 | "prevTrack": true, 12 | "mute": true, 13 | "exit": true 14 | } 15 | }, 16 | "lastApp": null, 17 | "lastWindowState": null, 18 | "quit?": false 19 | } -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | Vue.use(Vuex); 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | loading: false, 9 | }, 10 | mutations: { 11 | loadingSetFalse: (state) => (state.loading = false), 12 | loadingSetTrue: (state) => (state.loading = true), 13 | }, 14 | actions: {}, 15 | modules: {}, 16 | }); 17 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import "@mdi/font/css/materialdesignicons.css"; 3 | import App from "./App.vue"; 4 | import "./registerServiceWorker"; 5 | import router from "./router"; 6 | import store from "./store"; 7 | import vuetify from "./plugins/vuetify"; 8 | 9 | Vue.config.productionTip = false; 10 | 11 | new Vue({ 12 | router, 13 | store, 14 | vuetify, 15 | render: (h) => h(App), 16 | }).$mount("#app"); 17 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | - vs (auto fix on save) has problem 2 | - new window without MenuBar 3 | - icon for yandex music 4 | - ExtensionLoadWarning: Warnings loading extension at \extensions\nhdogjmejiglipccpnnnanhbledajbpd: Unrecognized manifest key 'browser_action'. Unrecognized manifest key 'update_url'. Permission 'contextMenus' is unknown or URL pattern is malformed. Cannot load extension with file or directory name _metadata. Filenames starting with "_" are reserved for use by the system. 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # GitHub 2 | gh-token.env 3 | 4 | # Notification [dev] 5 | 100x100.jpeg 6 | 7 | # Desktop Services Store 8 | .DS_Store 9 | 10 | # Node modules 11 | /node_modules 12 | 13 | # local env files 14 | .env.local 15 | .env.*.local 16 | 17 | # Log files 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | pnpm-debug.log* 22 | 23 | # Editor directories and files 24 | .idea 25 | .vscode 26 | *.suo 27 | *.ntvs* 28 | *.njsproj 29 | *.sln 30 | *.sw? 31 | 32 | #Electron-builder output 33 | /dist 34 | /dist_electron -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | Artboard 46 2 | -------------------------------------------------------------------------------- /src/electron/proxy.ts: -------------------------------------------------------------------------------- 1 | import { ipcMain, session } from "electron"; 2 | import store from "./store"; 3 | 4 | async function SetProxy() { 5 | const _SettingsProxy = store.get("settings").proxy; 6 | if (_SettingsProxy.protocol && _SettingsProxy.url) { 7 | await session.fromPartition("persist:webviewsession").setProxy({ 8 | proxyRules: `${_SettingsProxy.protocol}://${_SettingsProxy.url}`, 9 | }); 10 | } 11 | } 12 | 13 | ipcMain.on("SetProxy", async (event) => { 14 | await SetProxy(); 15 | event.returnValue = null; 16 | }); 17 | 18 | export default SetProxy; 19 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Router, { RouteConfig } from "vue-router"; 3 | 4 | Vue.use(Router); 5 | 6 | const routes: Array = [ 7 | { 8 | path: "/", 9 | redirect: "/music", 10 | }, 11 | { 12 | path: "/music", 13 | name: "YaMusic", 14 | component: () => import("../views/YandexMusic.vue"), 15 | }, 16 | { 17 | path: "*", 18 | redirect: "/music", 19 | }, 20 | ]; 21 | 22 | const router = new Router({ 23 | mode: "history", 24 | base: process.env.BASE_URL, 25 | routes, 26 | }); 27 | 28 | export default router; 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: osx 4 | osx_image: xcode10.2 5 | language: node_js 6 | node_js: "10" 7 | env: 8 | - ELECTRON_CACHE=$HOME/.cache/electron 9 | - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | - $HOME/.cache/electron 15 | - $HOME/.cache/electron-builder 16 | 17 | script: 18 | - | 19 | npm run electron:build -- -p always 20 | before_cache: 21 | - rm -rf $HOME/.cache/electron-builder/wine 22 | 23 | branches: 24 | except: 25 | - "master" 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "@vue/eslint-config-typescript/recommended", 10 | "@vue/eslint-config-prettier", 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 2020, 14 | }, 15 | rules: { 16 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 17 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 18 | "@typescript-eslint/no-explicit-any": 0, 19 | "vue/multi-word-component-names": 0, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ release ] 6 | 7 | jobs: 8 | build: 9 | runs-on: macos-latest 10 | 11 | env: 12 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 13 | 14 | strategy: 15 | matrix: 16 | node-version: [14.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm i 25 | - run: npm run electron:build -- -p always 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuetify from "vuetify"; 3 | import "vuetify/dist/vuetify.min.css"; 4 | import { mdiCog, mdiDelete, mdiCashMultiple, mdiRefresh } from "@mdi/js"; 5 | 6 | Vue.use(Vuetify); 7 | 8 | export default new Vuetify({ 9 | icons: { 10 | iconfont: "mdiSvg", 11 | values: { 12 | settings: mdiCog, 13 | delete: mdiDelete, 14 | cash: mdiCashMultiple, 15 | refresh: mdiRefresh, 16 | }, 17 | }, 18 | theme: { 19 | themes: { 20 | light: { 21 | primary: "#ee44aa", 22 | secondary: "#424242", 23 | accent: "#82B1FF", 24 | error: "#FF5252", 25 | info: "#2196F3", 26 | success: "#4CAF50", 27 | warning: "#FFC107", 28 | }, 29 | }, 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env", 16 | "vuetify" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx", 36 | "./types/*.d.ts", 37 | "./src/shims-node.d.ts" 38 | ], 39 | "exclude": [ 40 | "node_modules" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from "register-service-worker"; 4 | 5 | if (process.env.NODE_ENV === "production") { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | "App is being served from cache by a service worker.\n" + 10 | "For more details, visit https://goo.gl/AFskqB" 11 | ); 12 | }, 13 | registered() { 14 | console.log("Service worker has been registered."); 15 | }, 16 | cached() { 17 | console.log("Content has been cached for offline use."); 18 | }, 19 | updatefound() { 20 | console.log("New content is downloading."); 21 | }, 22 | updated() { 23 | console.log("New content is available; please refresh."); 24 | }, 25 | offline() { 26 | console.log( 27 | "No internet connection found. App is running in offline mode." 28 | ); 29 | }, 30 | error(error) { 31 | console.error("Error during service worker registration:", error); 32 | }, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 dedpnd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/electron/notification.ts: -------------------------------------------------------------------------------- 1 | import { ipcMain, Notification } from "electron"; 2 | import * as path from "path"; 3 | import fetch from "electron-fetch"; 4 | import fs from "fs"; 5 | import store from "./store"; 6 | 7 | async function invoke(title: string, msg: string, img: string) { 8 | let imgFile = path.join(__dirname, "media", "yaradio_64x64.png"); 9 | 10 | try { 11 | if (img) { 12 | const pathToFile = path.join(__dirname, "../", "100x100.jpeg"); 13 | const resImg = await fetch("http://" + img.replace("%%", "100x100")); 14 | await fs.promises.writeFile(pathToFile, await resImg.buffer(), { 15 | encoding: "binary", 16 | }); 17 | imgFile = pathToFile; 18 | } 19 | 20 | new Notification({ 21 | title: title || "YaMusic", 22 | body: msg || "-", 23 | icon: imgFile, 24 | silent: true, 25 | }).show(); 26 | } catch (error) { 27 | console.log("Notifier has error: ", error); 28 | } 29 | } 30 | 31 | ipcMain.on("setTrackInfo", async (event, data) => { 32 | const _SettingsNotifications = store.get("settings").notifications; 33 | if (data && _SettingsNotifications) { 34 | await invoke( 35 | data.title, 36 | data.artists.map((e: any) => e.title).join(", "), 37 | data.cover 38 | ); 39 | } 40 | 41 | event.returnValue = null; 42 | }); 43 | 44 | export default async (win: any) => { 45 | win.send("getTrackInfo"); 46 | }; 47 | -------------------------------------------------------------------------------- /src/electron/globalShortcut.ts: -------------------------------------------------------------------------------- 1 | import { app, globalShortcut, IpcRenderer, BrowserWindow, App } from "electron"; 2 | import store from "./store"; 3 | 4 | function shortcutTpl(win: IpcRenderer & BrowserWindow) { 5 | return [ 6 | { 7 | accelerator: "MediaPlayPause", 8 | enabled: store.get("settings.gs.play"), 9 | func: () => { 10 | win.send("play"); 11 | }, 12 | }, 13 | { 14 | accelerator: "MediaNextTrack", 15 | enabled: store.get("settings.gs.nextTrack"), 16 | func: () => { 17 | win.send("next"); 18 | }, 19 | }, 20 | { 21 | accelerator: "MediaPreviousTrack", 22 | enabled: store.get("settings.gs.prevTrack"), 23 | func: () => { 24 | win.send("prev"); 25 | }, 26 | }, 27 | { 28 | accelerator: "VolumeMute", 29 | enabled: store.get("settings.gs.mute"), 30 | func: () => { 31 | win.send("mute"); 32 | }, 33 | }, 34 | { 35 | accelerator: "CmdOrCtrl+Q", 36 | enabled: store.get("settings.gs.exit"), 37 | func: () => { 38 | app.quit(); 39 | }, 40 | }, 41 | ]; 42 | } 43 | 44 | export default (win: BrowserWindow, app: App) => { 45 | const tplShortcut = shortcutTpl(win as IpcRenderer & BrowserWindow); 46 | 47 | tplShortcut.forEach((e) => { 48 | if (e.enabled) { 49 | globalShortcut.register(e.accelerator, e.func); 50 | } 51 | }); 52 | 53 | app.on("will-quit", () => { 54 | // Unregister all shortcuts. 55 | globalShortcut.unregisterAll(); 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /src/electron/store.ts: -------------------------------------------------------------------------------- 1 | import Store from "electron-store"; 2 | import { 3 | Rectangle, 4 | app as electronApp, 5 | remote as electronRemote, 6 | } from "electron"; 7 | import * as path from "path"; 8 | 9 | interface StoreType { 10 | settings: { 11 | notifications: boolean; 12 | proxy: { 13 | protocol: string | null; 14 | url: string | null; 15 | }; 16 | gs: { 17 | play: boolean; 18 | nextTrack: boolean; 19 | prevTrack: boolean; 20 | mute: boolean; 21 | exit: boolean; 22 | }; 23 | }; 24 | lastApp: string | null; 25 | lastWindowState: Rectangle | null; 26 | "quit?": boolean; 27 | } 28 | 29 | const appInstance = electronApp || (electronRemote && electronRemote.app); 30 | 31 | const store = new Store({ 32 | cwd: appInstance ? undefined : path.join(process.cwd(), "user-data"), 33 | defaults: { 34 | settings: { 35 | notifications: true, 36 | proxy: { 37 | protocol: null, 38 | url: null, 39 | }, 40 | gs: { 41 | play: true, 42 | nextTrack: true, 43 | prevTrack: true, 44 | mute: true, 45 | exit: true, 46 | }, 47 | }, 48 | lastApp: null, 49 | lastWindowState: null, 50 | "quit?": false, 51 | }, 52 | }); 53 | 54 | store.set("quit?", false); 55 | 56 | // #HotFix 1.0.1 57 | if (!store.get("settings.gs")) { 58 | store.set("settings.gs", { 59 | play: true, 60 | nextTrack: true, 61 | prevTrack: true, 62 | mute: true, 63 | exit: true, 64 | }); 65 | } 66 | 67 | export default store as Store; 68 | -------------------------------------------------------------------------------- /src/electron/menu.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { IpcRenderer, BrowserWindow, Menu, Tray, App } from "electron"; 3 | 4 | const iconPath = path.join(__static, "img/icons", "16x16.png"); 5 | let appIcon = null; 6 | 7 | function toggleWindowVisibility(win: BrowserWindow) { 8 | if (win.isVisible()) { 9 | win.hide(); 10 | } else { 11 | win.show(); 12 | } 13 | } 14 | 15 | function getCtxTpl(win: IpcRenderer & BrowserWindow, app: App): any { 16 | return [ 17 | { 18 | label: "Play | Pause", 19 | click: () => win.send("play"), 20 | }, 21 | { 22 | label: "Next Track", 23 | click: () => win.send("next"), 24 | }, 25 | { 26 | type: "separator", 27 | }, 28 | { 29 | label: "Like", 30 | click: () => win.send("like"), 31 | }, 32 | { 33 | label: "Dislike", 34 | click: () => win.send("dislike"), 35 | }, 36 | { 37 | type: "separator", 38 | }, 39 | { 40 | label: "Show/Hide App", 41 | click: function () { 42 | toggleWindowVisibility(win); 43 | }, 44 | }, 45 | { 46 | label: "Quit", 47 | click: function () { 48 | app.quit(); 49 | }, 50 | }, 51 | ]; 52 | } 53 | 54 | export default (win: BrowserWindow, app: App) => { 55 | const ctxTpl = getCtxTpl(win as IpcRenderer & BrowserWindow, app); 56 | const ctxMenu = Menu.buildFromTemplate(ctxTpl); 57 | 58 | appIcon = new Tray(iconPath); 59 | appIcon.setContextMenu(ctxMenu); 60 | 61 | appIcon.addListener("click", (e) => { 62 | e.preventDefault(); 63 | toggleWindowVisibility(win); 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yaradio-yamusic", 3 | "productName": "YaMusic.app", 4 | "version": "1.0.6", 5 | "description": "Yandex Radio and Yandex Music desktop application, two in one :)", 6 | "scripts": { 7 | "serve": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=1 SASS_SILENCE_DEPRECATIONS=all vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint", 10 | "electron:build": "vue-cli-service electron:build -mwl", 11 | "electron:build:win": "vue-cli-service electron:build --win", 12 | "electron:serve": "cross-env BROWSERSLIST_IGNORE_OLD_DATA=1 SASS_SILENCE_DEPRECATIONS=all vue-cli-service electron:serve", 13 | "electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten", 14 | "postinstall": "electron-builder install-app-deps", 15 | "postuninstall": "electron-builder install-app-deps", 16 | "gen:changelog": "docker run -it --rm --env-file gh-token.env -v docker:/usr/local/src/your-app ferrarimarco/github-changelog-generator -u dedpnd -p yaradio-yamusic" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/dedpnd/yaradio-yamusic.git" 21 | }, 22 | "author": "dedpnd ", 23 | "license": "MIT", 24 | "keyword": [ 25 | "electron", 26 | "yandex music app", 27 | "yandex radio app", 28 | "electron music player", 29 | "yamusic", 30 | "yaradio", 31 | "Яндекс Музыка", 32 | "Яндекс Радио" 33 | ], 34 | "bugs": { 35 | "url": "https://github.com/dedpnd/yaradio-yamusic/issues" 36 | }, 37 | "homepage": "https://github.com/dedpnd/yaradio-yamusic#readme", 38 | "dependencies": { 39 | "@mdi/js": "^7.4.47", 40 | "@mdi/font": "^7.4.47", 41 | "core-js": "^3.37.0", 42 | "electron-fetch": "^1.7.2", 43 | "electron-store": "^8.1.0", 44 | "electron-updater": "^6.3.9", 45 | "register-service-worker": "^1.7.2", 46 | "roboto-fontface": "^0.10.0", 47 | "vue": "2.7.16", 48 | "vue-router": "^3.5.4", 49 | "vuex": "^3.6.2", 50 | "vuetify": "^2.7.1" 51 | }, 52 | "devDependencies": { 53 | "@types/electron-devtools-installer": "^2.2.3", 54 | "@typescript-eslint/eslint-plugin": "2.34.0", 55 | "@typescript-eslint/parser": "2.34.0", 56 | "@vue/cli-plugin-babel": "~4.5.15", 57 | "@vue/cli-plugin-eslint": "~4.5.15", 58 | "@vue/cli-plugin-pwa": "~4.5.15", 59 | "@vue/cli-plugin-router": "~4.5.15", 60 | "@vue/cli-plugin-typescript": "~4.5.15", 61 | "@vue/cli-plugin-vuex": "~4.5.15", 62 | "@vue/cli-service": "~4.5.15", 63 | "@vue/eslint-config-prettier": "6.0.0", 64 | "@vue/eslint-config-typescript": "5.1.0", 65 | "cache-loader": "^4.1.0", 66 | "cross-env": "^7.0.3", 67 | "crypto-browserify": "^3.12.1", 68 | "electron": "^27.3.11", 69 | "electron-devtools-installer": "^3.2.0", 70 | "electron-icon-builder": "^2.0.1", 71 | "eslint": "6.8.0", 72 | "eslint-plugin-prettier": "3.4.1", 73 | "eslint-plugin-vue": "6.2.2", 74 | "os-browserify": "^0.3.0", 75 | "path-browserify": "^1.0.1", 76 | "prettier": "2.8.8", 77 | "sass": "^1.72.0", 78 | "sass-loader": "10.4.1", 79 | "stream-browserify": "^3.0.0", 80 | "ts-loader": "^8.4.0", 81 | "typescript": "^4.5.5", 82 | "vue-cli-plugin-electron-builder": "~2.1.1", 83 | "vue-template-compiler": "2.7.16", 84 | "vuetify-loader": "^1.9.2" 85 | }, 86 | "overrides": { 87 | "vue-cli-plugin-electron-builder": { 88 | "electron-builder": "^23.1.0" 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Yandex Radio and Yandex Music 2 | 3 | > Desktop app for [Yandex Radio](https://radio.yandex.ru/) and [Yandex Music](https://music.yandex.ru/). 4 | > Skips ads when is possible! 5 | > Ability to work through a proxy. 6 | > Thank you for choosing my app :relaxed: 7 | ----------------------------------------- 8 | > Неофициальное декстопное приложение [«Яндекс Радио»](https://radio.yandex.ru/) и [«Яндекс Музыка»](https://music.yandex.ru/) для всех платформ. 9 | > Работает через любой прокси. 10 | > Управление через медиа-кнопки. 11 | > Уведомления при смене трека. 12 | > По возможности блокирует рекламу. 13 | > Спасибо, что выбрали мое приложение :relaxed: 14 | 15 |   16 | ![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/dedpnd/yaradio-yamusic.svg) 17 | ![GitHub last commit](https://img.shields.io/github/last-commit/dedpnd/yaradio-yamusic.svg) 18 | ![GitHub issues](https://img.shields.io/github/issues/dedpnd/yaradio-yamusic.svg) 19 | ![GitHub package.json version](https://img.shields.io/github/package-json/v/dedpnd/yaradio-yamusic.svg) 20 | ![GitHub Release Date](https://img.shields.io/github/release-date/dedpnd/yaradio-yamusic.svg) 21 | ![GitHub All Releases](https://img.shields.io/github/downloads/dedpnd/yaradio-yamusic/total.svg) 22 | ![GitHub package.json dependency version (prod)](https://img.shields.io/github/package-json/dependency-version/dedpnd/yaradio-yamusic/electron.svg) 23 | 24 |

25 | 26 |

27 | 28 | --- 29 | 30 | ## Support me! 31 |

32 | 33 |                   34 | 35 |                   36 | 37 |

38 | 39 |   40 | > Thank you all for your support! You motivate me to continue working on the application and I read all your messages :) 41 | ----------------------------------------- 42 | > Спасибо всем за поддержку! Вы мотивируете меня заниматься приложением дальше и я читаю все ваши сообщения :) 43 | 44 | ## Installation 45 | Choose a package that is suitable for your OS and install. 46 | 47 | ## Download 48 | | Windows | Linux | Mac | 49 | | ------------ | ------------ | ------------ | 50 | | [Download - (**exe**)](https://github.com/dedpnd/yaradio-yamusic/releases/latest/download/YaMusic.app-Setup-1.0.6.exe) | [Download - (**AppImage**)](https://github.com/dedpnd/yaradio-yamusic/releases/latest/download/YaMusic.app-1.0.6.AppImage) | [Download - (**dmg**)](https://github.com/dedpnd/yaradio-yamusic/releases/latest/download/YaMusic.app-1.0.6.dmg) | 51 | | | [Download - (**deb**)](https://github.com/dedpnd/yaradio-yamusic/releases/latest/download/yaradio-yamusic_1.0.6_amd64.deb) | | 52 | 53 | 54 | 55 | --- 56 | 57 | ## Usage 58 | > Start app in dev mode 59 | ``` 60 | npm run electron:serve 61 | ``` 62 | > Build app 63 | ``` 64 | npm run electron:build 65 | ``` 66 | --- 67 | 68 | ## Documentation 69 | Learn more about supporting [platforms](https://www.electron.build/) 70 | 71 | --- 72 | 73 | ## Related 74 | - [yaradio](https://github.com/maxvipon/yaradio) - Yandex.Radio Unofficial Desktop app 75 | 76 | --- 77 | 78 | ## License 79 | 80 | ![GitHub](https://img.shields.io/github/license/dedpnd/yaradio-yamusic.svg) 81 | 82 | - **[MIT license](http://opensource.org/licenses/mit-license.php)** 83 | - Copyright 2019-2021 © dedpnd. 84 | -------------------------------------------------------------------------------- /src/views/YandexMusic.vue: -------------------------------------------------------------------------------- 1 | // webpreferences="nativeWindowOpen=yes" 2 | 3 | 14 | 15 | 134 | -------------------------------------------------------------------------------- /src/views/YandexRadio.vue: -------------------------------------------------------------------------------- 1 | // webpreferences="nativeWindowOpen=yes" 2 | 3 | 14 | 15 | 134 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v1.0.3](https://github.com/dedpnd/yaradio-yamusic/tree/v1.0.3) (2021-05-04) 4 | 5 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v1.0.2...v1.0.3) 6 | 7 | **Implemented enhancements:** 8 | 9 | - Please add "go to home page" action on "Radio" and "Music" buttons [\#28](https://github.com/dedpnd/yaradio-yamusic/issues/28) 10 | 11 | **Fixed bugs:** 12 | 13 | - Application blocks side panel [\#43](https://github.com/dedpnd/yaradio-yamusic/issues/43) 14 | - Some office shortcuts keys does not works when YaMusic running [\#42](https://github.com/dedpnd/yaradio-yamusic/issues/42) 15 | - Unable to use bluetooth actions such as prev/next/start/stop functions [\#37](https://github.com/dedpnd/yaradio-yamusic/issues/37) 16 | - wrong icon path for deb package [\#32](https://github.com/dedpnd/yaradio-yamusic/issues/32) 17 | 18 | ## [v1.0.2](https://github.com/dedpnd/yaradio-yamusic/tree/v1.0.2) (2021-02-24) 19 | 20 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v1.0.1...v1.0.2) 21 | 22 | **Implemented enhancements:** 23 | 24 | - Custom global HotKeys [\#29](https://github.com/dedpnd/yaradio-yamusic/issues/29) 25 | 26 | ## [v1.0.1](https://github.com/dedpnd/yaradio-yamusic/tree/v1.0.1) (2021-02-16) 27 | 28 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v1.0.0...v1.0.1) 29 | 30 | **Fixed bugs:** 31 | 32 | - issue with macOS 10.15.6 [\#23](https://github.com/dedpnd/yaradio-yamusic/issues/23) 33 | - Using the back/forward mouse click stops the song that is currently playing [\#14](https://github.com/dedpnd/yaradio-yamusic/issues/14) 34 | 35 | **Closed issues:** 36 | 37 | - Ctrl+Q hotkey is stopping app [\#30](https://github.com/dedpnd/yaradio-yamusic/issues/30) 38 | 39 | ## [v1.0.0](https://github.com/dedpnd/yaradio-yamusic/tree/v1.0.0) (2021-01-10) 40 | 41 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.4.0...v1.0.0) 42 | 43 | ## [v.0.4.0](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.4.0) (2020-04-23) 44 | 45 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.6...v.0.4.0) 46 | 47 | **Fixed bugs:** 48 | 49 | - Application not running in mac os mojave 10.14.6 in v.0.3.6 [\#10](https://github.com/dedpnd/yaradio-yamusic/issues/10) 50 | - Cannot open on macOs 10.14.6 [\#5](https://github.com/dedpnd/yaradio-yamusic/issues/5) 51 | 52 | **Closed issues:** 53 | 54 | - Yandex blocked the app? [\#13](https://github.com/dedpnd/yaradio-yamusic/issues/13) 55 | - Setting to specify where to show next song popup [\#8](https://github.com/dedpnd/yaradio-yamusic/issues/8) 56 | 57 | ## [v.0.3.6](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.6) (2019-09-16) 58 | 59 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.5...v.0.3.6) 60 | 61 | **Implemented enhancements:** 62 | 63 | - Next song notification title [\#3](https://github.com/dedpnd/yaradio-yamusic/issues/3) 64 | 65 | **Fixed bugs:** 66 | 67 | - Cant hide to tray in latest release \(v0.3.5\) [\#7](https://github.com/dedpnd/yaradio-yamusic/issues/7) 68 | 69 | **Merged pull requests:** 70 | 71 | - Show/hide tray menu item + hide to tray on close window on linux [\#9](https://github.com/dedpnd/yaradio-yamusic/pull/9) ([kuntashov](https://github.com/kuntashov)) 72 | 73 | ## [v.0.3.5](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.5) (2019-05-30) 74 | 75 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.4...v.0.3.5) 76 | 77 | **Implemented enhancements:** 78 | 79 | - Nofifications on song change [\#2](https://github.com/dedpnd/yaradio-yamusic/issues/2) 80 | 81 | ## [v.0.3.4](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.4) (2019-03-14) 82 | 83 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.3...v.0.3.4) 84 | 85 | ## [v.0.3.3](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.3) (2019-03-13) 86 | 87 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.2...v.0.3.3) 88 | 89 | **Implemented enhancements:** 90 | 91 | - How can I set global shortcuts to play/pause and like/dislike buttons? [\#1](https://github.com/dedpnd/yaradio-yamusic/issues/1) 92 | 93 | ## [v.0.3.2](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.2) (2018-08-22) 94 | 95 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/v.0.3.0...v.0.3.2) 96 | 97 | ## [v.0.3.0](https://github.com/dedpnd/yaradio-yamusic/tree/v.0.3.0) (2018-06-29) 98 | 99 | [Full Changelog](https://github.com/dedpnd/yaradio-yamusic/compare/a70521a02def6ae2848b7e5db75f1669ad216cbe...v.0.3.0) 100 | 101 | 102 | 103 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 104 | -------------------------------------------------------------------------------- /src/background.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as path from "path"; 4 | import { app, protocol, session, BrowserWindow } from "electron"; 5 | import { createProtocol } from "vue-cli-plugin-electron-builder/lib"; 6 | import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer"; 7 | import { autoUpdater } from "electron-updater"; 8 | 9 | import store from "./electron/store"; 10 | import ctxMenu from "./electron/menu"; 11 | import globalShortcut from "./electron/globalShortcut"; 12 | import notification from "./electron/notification"; 13 | import proxy from "./electron/proxy"; 14 | 15 | const isDevelopment = process.env.NODE_ENV !== "production"; 16 | const _defaultHeight = 620; 17 | const _defaultWidth = 820; 18 | 19 | protocol.registerSchemesAsPrivileged([ 20 | { scheme: "app", privileges: { secure: true, standard: true } }, 21 | ]); 22 | 23 | let win: BrowserWindow | null = null; 24 | const appRunning = app.requestSingleInstanceLock(); 25 | 26 | if (!appRunning) { 27 | app.quit(); 28 | } 29 | 30 | app.on("second-instance", () => { 31 | if (win) { 32 | if (win.isMinimized() || !win.isVisible()) { 33 | win.restore(); 34 | } 35 | win.focus(); 36 | } 37 | }); 38 | 39 | async function createWindow() { 40 | const lastWindowState = store.get("lastWindowState"); 41 | 42 | win = new BrowserWindow({ 43 | title: "YaMusic", 44 | x: lastWindowState ? lastWindowState.x : 100, 45 | y: lastWindowState ? lastWindowState.y : 100, 46 | height: lastWindowState ? lastWindowState.height : _defaultHeight, 47 | width: lastWindowState ? lastWindowState.width : _defaultWidth, 48 | icon: path.join(__static, "icon.png"), 49 | minHeight: _defaultHeight, 50 | minWidth: _defaultWidth, 51 | autoHideMenuBar: true, 52 | backgroundColor: "#ECEFF1", 53 | webPreferences: { 54 | webviewTag: true, 55 | zoomFactor: 1.0, 56 | enableRemoteModule: true, 57 | nodeIntegration: true, 58 | contextIsolation: false, 59 | }, 60 | }); 61 | 62 | if (process.env.WEBPACK_DEV_SERVER_URL) { 63 | await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string); 64 | if (!process.env.IS_TEST) win.webContents.openDevTools(); 65 | } else { 66 | createProtocol("app"); 67 | win.loadURL("app://./index.html"); 68 | autoUpdater.checkForUpdatesAndNotify(); 69 | } 70 | 71 | win.on("close", (e) => { 72 | if (!store.get("quit?")) { 73 | e.preventDefault(); 74 | } 75 | 76 | if (win) { 77 | switch (process.platform) { 78 | case "win32": 79 | case "linux": 80 | win.hide(); 81 | break; 82 | case "darwin": 83 | app.hide(); 84 | break; 85 | default: 86 | } 87 | } 88 | }); 89 | 90 | return win; 91 | } 92 | 93 | app.on("window-all-closed", () => { 94 | if (process.platform !== "darwin") { 95 | app.quit(); 96 | } 97 | }); 98 | 99 | app.on("activate", () => { 100 | if (BrowserWindow.getAllWindows().length === 0) createWindow(); 101 | }); 102 | 103 | app.on("ready", async () => { 104 | if ( 105 | isDevelopment && 106 | !process.env.IS_TEST && 107 | process.env.VUE_DEVTOOLS === "1" 108 | ) { 109 | try { 110 | await installExtension(VUEJS_DEVTOOLS); 111 | } catch (e) { 112 | console.error("Vue Devtools failed to install:", e.toString()); 113 | } 114 | } 115 | 116 | const win = await createWindow(); 117 | 118 | ctxMenu(win, app); 119 | globalShortcut(win, app); 120 | await proxy(); 121 | 122 | session 123 | .fromPartition("persist:webviewsession") 124 | .webRequest.onBeforeRequest( 125 | { urls: ["*://*/*"] }, 126 | (details: any, callback) => { 127 | if ( 128 | details.url.includes("awaps.yandex.net") || 129 | details.url.includes("vh-bsvideo-converted") || 130 | details.url.includes("get-video-an") || 131 | details.url.includes("an.yandex.ru/vmap") || 132 | details.url.includes("yandex.ru/an") || 133 | details.url.includes("mc.yandex.ru/watch") || 134 | details.url.includes("strm.yandex.ru") || 135 | details.url.includes("mc.yandex.ru/clmap") || 136 | details.url.includes("yandex.ru/ads") 137 | ) { 138 | return { cancel: true }; 139 | } 140 | if (details.url.includes("start?__t")) { 141 | notification(win); 142 | } 143 | 144 | callback({}); 145 | } 146 | ); 147 | }); 148 | 149 | app.on("before-quit", () => { 150 | store.set("quit?", true); 151 | if (win) { 152 | if (!win.isFullScreen()) { 153 | store.set("lastWindowState", win.getBounds()); 154 | } 155 | 156 | store.set("lastApp", win.getTitle()); 157 | } 158 | }); 159 | 160 | if (isDevelopment) { 161 | if (process.platform === "win32") { 162 | process.on("message", (data) => { 163 | if (data === "graceful-exit") { 164 | app.quit(); 165 | } 166 | }); 167 | } else { 168 | process.on("SIGTERM", () => { 169 | app.quit(); 170 | }); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier, @typescript-eslint/no-var-requires */ 2 | const path = require("path"); 3 | const webpack = require("webpack"); 4 | 5 | // Disable fork-ts-checker from @vue/cli-plugin-typescript (causes Ajv errors on modern Node) 6 | process.env.VUE_CLI_TEST = "true"; 7 | 8 | module.exports = { 9 | transpileDependencies: ["vuetify"], 10 | chainWebpack: config => { 11 | // Use a supported hash algorithm on Node 17+ to avoid OpenSSL errors 12 | config.output.hashFunction("sha256"); 13 | config.resolve.alias.set("fs/promises", path.resolve(__dirname, "src/electron/fs-promises-shim.js")); 14 | config.resolve.alias.set("vue-cli-plugin-electron-builder/lib/installVueDevtools", path.resolve(__dirname, "src/electron/noop-devtools.js")); 15 | config.resolve.alias.set("electron-devtools-installer", path.resolve(__dirname, "src/electron/noop-electron-devtools-installer.js")); 16 | // Vue CLI/webpack 4 doesn't understand Vuetify 3 export maps; point to real files 17 | // Disable fork-ts-checker (spawns a child process that can fail with EPERM on some Windows setups) 18 | config.plugins.delete("fork-ts-checker"); 19 | config 20 | .plugin("replace-devtools") 21 | .use(webpack.NormalModuleReplacementPlugin, [ 22 | /vue-cli-plugin-electron-builder[\\/]lib[\\/]installVueDevtools/, 23 | path.resolve(__dirname, "src/electron/noop-devtools.js") 24 | ]); 25 | // config.plugin("copy").tap(args => { 26 | // args.push([{ 27 | // from: path.join(__dirname, "src/media"), 28 | // to: path.join(__dirname, "dist_electron"), 29 | // toType: "dir" 30 | // }]); //add 31 | // return args; 32 | // }); 33 | }, 34 | css: { 35 | loaderOptions: { 36 | sass: { 37 | sassOptions: { 38 | quietDeps: true, 39 | silenceDeprecations: ["legacy-js-api", "import", "global-builtin", "slash-div"] 40 | } 41 | }, 42 | scss: { 43 | sassOptions: { 44 | quietDeps: true, 45 | silenceDeprecations: ["legacy-js-api", "import", "global-builtin", "slash-div"] 46 | } 47 | } 48 | } 49 | }, 50 | pluginOptions: { 51 | typescript: { 52 | forkTsChecker: false 53 | }, 54 | electronBuilder: { 55 | nodeIntegration: true, 56 | mainProcessFile: "src/background.ts", 57 | chainWebpackMainProcess: config => { 58 | config.output.hashFunction("sha256"); 59 | config.resolve.alias.set("vue-cli-plugin-electron-builder/lib/installVueDevtools", path.resolve(__dirname, "src/electron/noop-devtools.js")); 60 | config.resolve.alias.set("fs/promises", path.resolve(__dirname, "src/electron/fs-promises-shim.js")); 61 | config.resolve.alias.set("electron-devtools-installer", path.resolve(__dirname, "src/electron/noop-electron-devtools-installer.js")); 62 | config.plugins.delete("fork-ts-checker"); 63 | config.resolve.extensions 64 | .merge([".ts", ".tsx", ".js", ".json"]); 65 | config 66 | .plugin("replace-devtools-main") 67 | .use(webpack.NormalModuleReplacementPlugin, [ 68 | /vue-cli-plugin-electron-builder[\\/]lib[\\/]installVueDevtools/, 69 | path.resolve(__dirname, "src/electron/noop-devtools.js") 70 | ]); 71 | }, 72 | chainWebpackRendererProcess: config => { 73 | config.output.hashFunction("sha256"); 74 | config.resolve.alias.set("vue-cli-plugin-electron-builder/lib/installVueDevtools", path.resolve(__dirname, "src/electron/noop-devtools.js")); 75 | config.resolve.alias.set("fs/promises", path.resolve(__dirname, "src/electron/fs-promises-shim.js")); 76 | config.resolve.alias.set("electron-devtools-installer", path.resolve(__dirname, "src/electron/noop-electron-devtools-installer.js")); 77 | config.plugins.delete("fork-ts-checker"); 78 | config.resolve.extensions.merge([".ts", ".tsx", ".js", ".json"]); 79 | config.externals({ 80 | electron: "commonjs2 electron", 81 | "electron-store": "commonjs2 electron-store", 82 | conf: "commonjs2 conf" 83 | }); 84 | config 85 | .plugin("replace-devtools-renderer") 86 | .use(webpack.NormalModuleReplacementPlugin, [ 87 | /vue-cli-plugin-electron-builder[\\/]lib[\\/]installVueDevtools/, 88 | path.resolve(__dirname, "src/electron/noop-devtools.js") 89 | ]); 90 | }, 91 | builderOptions: { 92 | "publish": ['github'], 93 | "productName": "YaMusic.app", 94 | "appId": "com.github.dedpnd.yamusic", 95 | "win": { 96 | "target": [ 97 | "nsis" 98 | ], 99 | "icon": "./build/icons/icon.ico" 100 | }, 101 | "mac": { 102 | "category": "public.app-category.music", 103 | "target": [ 104 | "dmg" 105 | ], 106 | "icon": "./build/icons/icon.icns" 107 | }, 108 | "linux": { 109 | "category": "Audio", 110 | "target": [ 111 | "AppImage", 112 | "deb" 113 | ], 114 | "icon": "./build/icons/256x256.png" 115 | } 116 | } 117 | }, 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 158 | 159 | 214 | 215 | 309 | --------------------------------------------------------------------------------