├── .gitattributes ├── .gitignore ├── app ├── assets │ └── icons │ │ ├── logo.png │ │ └── tray │ │ ├── png │ │ ├── icon.png │ │ └── icon@2x.png │ │ ├── win │ │ └── icon.ico │ │ └── osx │ │ ├── iconTemplate.png │ │ └── iconTemplate@2x.png ├── package-lock.json ├── renderer │ ├── css │ │ └── main.css │ ├── index.html │ └── js │ │ └── preload.js ├── constants.js ├── package.json └── main │ ├── index.js │ └── tray.js ├── .editorconfig ├── LICENSE ├── package.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | npm-debug.log 4 | package-lock.json -------------------------------------------------------------------------------- /app/assets/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/logo.png -------------------------------------------------------------------------------- /app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ongaku-desktop", 3 | "version": "1.0.9", 4 | "lockfileVersion": 1 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/icons/tray/png/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/tray/png/icon.png -------------------------------------------------------------------------------- /app/assets/icons/tray/win/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/tray/win/icon.ico -------------------------------------------------------------------------------- /app/assets/icons/tray/png/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/tray/png/icon@2x.png -------------------------------------------------------------------------------- /app/assets/icons/tray/osx/iconTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/tray/osx/iconTemplate.png -------------------------------------------------------------------------------- /app/assets/icons/tray/osx/iconTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anshumanv/ongaku-desktop/HEAD/app/assets/icons/tray/osx/iconTemplate@2x.png -------------------------------------------------------------------------------- /app/renderer/css/main.css: -------------------------------------------------------------------------------- 1 | html body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | /* CSS for the webview element */ 7 | webview { 8 | display: inline-flex; 9 | width: 100%; 10 | height: 100%; 11 | } 12 | -------------------------------------------------------------------------------- /app/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preferences: [ 3 | { 4 | id: 'op', 5 | name: 'Opening' 6 | }, 7 | { 8 | id: 'ed', 9 | name: 'Ending' 10 | }, 11 | { 12 | id: 'ost', 13 | name: 'OST' 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /app/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ongaku 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ongaku-desktop", 3 | "version": "1.0.10", 4 | "description": "Electron based desktop port of ongaku", 5 | "main": "./main/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/anshumanv/ongaku-desktop.git" 9 | }, 10 | "keywords": [ 11 | "electron", 12 | "anime", 13 | "music", 14 | "music", 15 | "ongaku", 16 | "opening", 17 | "ending", 18 | "ost", 19 | "desktop", 20 | "app" 21 | ], 22 | "author": "Anshuman Verma", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/anshumanv/ongaku-desktop/issues" 26 | }, 27 | "homepage": "https://github.com/anshumanv/ongaku-desktop#readme", 28 | "dependencies": { 29 | "update-electron-app": "1.3.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Anshuman Verma 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ongaku-desktop", 3 | "productName": "ongaku", 4 | "version": "1.0.12", 5 | "description": "Electron based desktop port of ongaku", 6 | "main": "app", 7 | "bin": { 8 | "ongaku": "app/main/index.js" 9 | }, 10 | "scripts": { 11 | "test": "standard app", 12 | "start": "electron app", 13 | "build": "electron-packager app --out=dist --asar --overwrite --all" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/anshumanv/ongaku-desktop.git" 18 | }, 19 | "dependencies": { 20 | "electron-debug": "^1.0.0", 21 | "jquery": "^3.2.1", 22 | "update-electron-app": "1.3.0" 23 | }, 24 | "devDependencies": { 25 | "devtron": "^1.1.0", 26 | "electron": "^1.7.11", 27 | "electron-builder": "^19.27.7", 28 | "electron-packager": "^8.0.0", 29 | "standard": "^3.0.0" 30 | }, 31 | "keywords": [ 32 | "electron", 33 | "anime", 34 | "music", 35 | "ongaku", 36 | "opening", 37 | "ending", 38 | "ost", 39 | "desktop", 40 | "app" 41 | ], 42 | "author": "Anshuman Verma", 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/anshumanv/ongaku-desktop/issues" 46 | }, 47 | "homepage": "https://github.com/anshumanv/ongaku-desktop#readme", 48 | "build": { 49 | "appId": "com.anshumanv.ongaku" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ongaku-desktop [](https://electron.atom.io/) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) [![npm](https://img.shields.io/npm/dt/ongaku-desktop.svg)](https://www.npmjs.com/package/ongaku-desktop) [![npm](https://img.shields.io/npm/v/ongaku-desktop.svg)](https://www.npmjs.com/package/ongaku-desktop) [![SourceForge](https://img.shields.io/sourceforge/dt/ongaku.svg)](https://sourceforge.net/projects/ongaku/) 2 | 3 | > Electron based desktop port of [ongaku](https://ongaku.js.org). 4 | 5 | Play anime music with ease on your desktop. Uses [ongaku](https://github.com/anshumanv/ongaku) web-app under the hood. 6 | 7 | ### Screenshots 8 | 9 | ![sample1](https://user-images.githubusercontent.com/23422478/35113761-a2094da0-fca8-11e7-8f84-f9b95d3d8276.PNG) 10 | ![sample2](https://user-images.githubusercontent.com/23422478/35113789-b9593a56-fca8-11e7-8ea9-200bf347ec4a.PNG) 11 | 12 | 13 | ## Install and Run 14 | 15 | ``` 16 | $ npm install -g ongaku-desktop 17 | $ ongaku 18 | # may need to install electron 19 | # sudo npm install electron -g 20 | ``` 21 | 22 | ## Dev 23 | 24 | ``` 25 | $ git clone --recursive https://github.com/Anshuman-Verma/ongaku-desktop.git 26 | $ cd ongaku-desktop 27 | $ npm install 28 | $ npm start 29 | ``` 30 | 31 | ### Build 32 | 33 | ``` 34 | $ npm run build 35 | ``` 36 | 37 | ### Downloads 38 | 39 | [](https://sourceforge.net/projects/ongaku/) 40 | 41 | ## License 42 | 43 | MIT © [Anshuman Verma](https://twitter.com/Anshumaniac12) 44 | -------------------------------------------------------------------------------- /app/main/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env electron 2 | 3 | const path = require('path') 4 | const electron = require('electron') 5 | const tray = require('./tray.js') 6 | require('update-electron-app')() 7 | 8 | const { 9 | app, 10 | BrowserWindow, 11 | globalShortcut, 12 | ipcMain, 13 | webContents 14 | } = electron 15 | 16 | // Prevent window and tray from being garbage collected 17 | let mainWindow 18 | let trayIcon 19 | let closing 20 | 21 | const mainURL = 'file://' + path.join(__dirname, '../renderer', 'index.html'); 22 | 23 | const APP_ICON = path.join(__dirname, '../assets/icons', 'logo.png'); 24 | 25 | function onTrayToggle(e) { 26 | mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show() 27 | } 28 | 29 | function onTrayClose(e) { 30 | closing = true 31 | mainWindow.close(); 32 | } 33 | 34 | function onClosing (e) { 35 | // The window has asked to be closed 36 | if(!closing) { 37 | mainWindow.hide(); 38 | e.preventDefault(); 39 | } 40 | } 41 | 42 | function onClosed () { 43 | // Dereference the window 44 | // For multiple windows store them in an array 45 | trayIcon = null 46 | mainWindow = null 47 | } 48 | 49 | function createMainWindow () { 50 | const win = new BrowserWindow({ 51 | title: 'ongaku', 52 | icon: APP_ICON, 53 | width: 1280, 54 | height: 720, 55 | minWidth: 800, 56 | minHeight: 480, 57 | autoHideMenuBar: true, 58 | show: false 59 | }) 60 | 61 | // loading the mainURL in our mainWindow 62 | win.loadURL(mainURL) 63 | 64 | // Handle onClose event 65 | win.on('close', onClosing) 66 | win.on('closed', onClosed) 67 | 68 | win.on('focus', () => { 69 | win.webContents.send('focus'); 70 | }) 71 | 72 | // Display the contents only when they are ready-to-show 73 | win.on('ready-to-show', () => { 74 | win.show() 75 | }) 76 | return win 77 | } 78 | 79 | ipcMain.on('focus', () => { 80 | if (mainWindow) { 81 | mainWindow.focus() 82 | } 83 | }) 84 | 85 | app.on('window-all-closed', () => { 86 | if (process.platform !== 'darwin') { 87 | app.quit() 88 | } 89 | }) 90 | 91 | app.on('activate', () => { 92 | if (!mainWindow) { 93 | mainWindow = createMainWindow() 94 | } 95 | }) 96 | 97 | app.on('ready', () => { 98 | closing = false 99 | mainWindow = createMainWindow() 100 | trayIcon = tray.create(onTrayToggle, onTrayClose, mainWindow) 101 | 102 | const page = mainWindow.webContents 103 | 104 | page.on('dom-ready', () => { 105 | mainWindow.show() 106 | }) 107 | }) 108 | -------------------------------------------------------------------------------- /app/main/tray.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { app, Menu, Tray, ipcMain } = require('electron') 3 | const { preferences } = require('../constants') 4 | 5 | function createTray (onToggle, onClose, mainWindow) { 6 | const imageFolder = path.join(__dirname, '../assets/icons/tray') 7 | let trayImage 8 | if (process.platform == 'win32') { 9 | trayImage = path.join(imageFolder, 'win', 'icon.ico') 10 | } else if (process.platform == 'darwin') { 11 | trayImage = path.join(imageFolder, 'osx', 'iconTemplate.png') 12 | } else { 13 | trayImage = path.join(imageFolder, 'png', 'icon.png') 14 | } 15 | 16 | const execWV = code => ( 17 | mainWindow.webContents.executeJavaScript(` 18 | webview = document.querySelector('webview'); 19 | webview.executeJavaScript(\`${code}\`); 20 | `) 21 | ) 22 | 23 | const musicFN = code => execWV(` 24 | mus = document.querySelector('#music'); 25 | ${code} 26 | `) 27 | 28 | const togglePRF = prf => execWV(` 29 | $('input.cb-${prf}').click() 30 | `) 31 | 32 | const contextMenu = [ 33 | { 34 | label: 'Toggle Show/Hide', 35 | click: onToggle 36 | }, 37 | { 38 | label: 'Preferences', 39 | id: 'prf', 40 | enabled: false, 41 | submenu: [] 42 | }, 43 | { 44 | type: 'separator' 45 | }, 46 | { 47 | label: 'Play/Pause', 48 | click() { 49 | musicFN(` 50 | if (mus.paused) { 51 | mus.play(); 52 | } else { 53 | mus.pause(); 54 | } 55 | `) 56 | } 57 | }, 58 | { 59 | label: 'Restart', 60 | click() { 61 | musicFN(` 62 | mus.currentTime = 0; 63 | if (mus.paused) { 64 | mus.play(); 65 | } 66 | `) 67 | } 68 | }, 69 | { 70 | type: 'separator' 71 | }, 72 | { 73 | label: 'Quit', 74 | click: onClose 75 | } 76 | ] 77 | 78 | const trayIcon = new Tray(trayImage) 79 | 80 | trayIcon.on('click', onToggle) 81 | 82 | trayIcon.setTitle('Ongaku') 83 | 84 | trayIcon.setContextMenu(Menu.buildFromTemplate(contextMenu)) 85 | ipcMain.once('loaded', (e, msg) => { 86 | contextMenu.forEach((item, index) => { 87 | if (item.id === 'prf') { 88 | contextMenu[index].enabled = true 89 | 90 | preferences.forEach((prf) => { 91 | contextMenu[index].submenu.push({ 92 | label: prf.name, 93 | id: prf.id, 94 | type: 'checkbox', 95 | checked: true, 96 | click() { 97 | togglePRF(prf.id) 98 | } 99 | }) 100 | 101 | ipcMain.on(prf.id, (e, msg) => { 102 | contextMenu[index].submenu.forEach((item, idx) => { 103 | if (item.id === prf.id) { 104 | contextMenu[index].submenu[idx].checked = msg 105 | } 106 | }) 107 | 108 | trayIcon.setContextMenu(Menu.buildFromTemplate(contextMenu)) 109 | }) 110 | }) 111 | } 112 | }) 113 | 114 | trayIcon.setContextMenu(Menu.buildFromTemplate(contextMenu)) 115 | }) 116 | 117 | trayIcon.setToolTip('Loading...') 118 | ipcMain.on('songName', (e, msg) => { 119 | trayIcon.setToolTip(msg) 120 | }) 121 | 122 | return trayIcon 123 | } 124 | 125 | exports.create = createTray 126 | -------------------------------------------------------------------------------- /app/renderer/js/preload.js: -------------------------------------------------------------------------------- 1 | /* 2 | This script will be loaded before other 3 | scripts run in the webview. See the docs 4 | for more info: 5 | 6 | https://electron.atom.io/docs/api/webview-tag/#preload 7 | */ 8 | const { ipcRenderer } = require('electron'); 9 | const { preferences } = require('../../constants') 10 | 11 | window.onload = () => { 12 | const songs = (window.osts || []).concat(window.openings, window.endings); 13 | 14 | const getSongName = (src) => { 15 | if (!src) { return ''; } 16 | 17 | const len = songs.length; 18 | for (let i = 0; i < len; i++) { 19 | let song = songs[i]; 20 | if (song.link === src) { 21 | return song.name; 22 | } 23 | } 24 | 25 | return ''; 26 | }; 27 | 28 | const notify = (title, body) => { 29 | const options = { body }; 30 | let noti = new Notification(title, options); 31 | noti.addEventListener('click', () => { 32 | ipcRenderer.send('focus', true); 33 | }); 34 | }; 35 | 36 | const updateToolTip = (songName, state) => { 37 | let str 38 | 39 | switch (state) { 40 | case 'playing': 41 | str = 'Now Playing' 42 | break 43 | case 'pause': 44 | str = 'Paused' 45 | break 46 | default: 47 | str = 'Unicorn' // xd 48 | } 49 | 50 | ipcRenderer.send('songName', `${str}: ${songName}`); 51 | } 52 | 53 | let currentSong; 54 | const audio = document.querySelector('audio'); 55 | 56 | // reset current song so play after pause shows notification 57 | audio.addEventListener('pause', () => { 58 | updateToolTip(currentSong, 'pause') 59 | 60 | currentSong = '' 61 | }); 62 | 63 | audio.addEventListener('playing', (e)=> { 64 | let src = e && e.target && e.target.currentSrc; 65 | let songName = getSongName(src); 66 | if (songName && songName !== currentSong) { 67 | currentSong = songName; 68 | 69 | notify('Now playing', currentSong); 70 | updateToolTip(currentSong, 'playing') 71 | } 72 | }); 73 | 74 | // sets song name for the very first time 75 | const trayToolTip = setInterval(() => { 76 | const src = audio.currentSrc 77 | if (src) { 78 | currentSong = getSongName(src) 79 | if (currentSong) { 80 | updateToolTip(currentSong, 'playing') 81 | clearInterval(trayToolTip) 82 | } 83 | } 84 | }, 200) 85 | 86 | ipcRenderer.send('loaded', true) 87 | 88 | // create an observer instance for preferences 89 | const observer = new MutationObserver((mutations) => { 90 | mutations.forEach((mutation) => { 91 | mutation.addedNodes.forEach((node) => { 92 | if (node.classList.contains('popover')) { 93 | const prover = $('div.popover-content') 94 | preferences.forEach((prf) => { 95 | const input = prover.find(`input.cb-${prf.id}`) 96 | input.click(() => { 97 | ipcRenderer.send(prf.id, input.is(':checked')) 98 | }) 99 | }) 100 | } 101 | }) 102 | }) 103 | }) 104 | observer.observe(document.querySelector('body'), { childList: true }) 105 | }; 106 | --------------------------------------------------------------------------------