From 22462e4d0e002a393246c423ef57103ba084697a Mon Sep 17 00:00:00 2001 From: Core <64542347+coredev-uk@users.noreply.github.com> Date: Tue, 28 Dec 2021 20:50:31 +0000 Subject: [PATCH] initial --- index.js | 66 +------ src/main/base/store.js | 48 +++++ src/main/base/win.js | 257 +++++++++++++++++++++++++ src/main/cider-base.js | 275 +++------------------------ src/main/{ => plugins}/discordrpc.js | 0 src/main/{ => plugins}/lastfm.js | 2 +- src/main/{ => plugins}/mpris.js | 0 7 files changed, 344 insertions(+), 304 deletions(-) create mode 100644 src/main/base/store.js create mode 100644 src/main/base/win.js rename src/main/{ => plugins}/discordrpc.js (100%) rename src/main/{ => plugins}/lastfm.js (98%) rename src/main/{ => plugins}/mpris.js (100%) diff --git a/index.js b/index.js index 6688ecfc..61dfc939 100644 --- a/index.js +++ b/index.js @@ -7,49 +7,12 @@ const { app } = require('electron'), const ElectronSentry = require("@sentry/electron"); ElectronSentry.init({ dsn: "https://68c422bfaaf44dea880b86aad5a820d2@o954055.ingest.sentry.io/6112214" }); -const configSchema = { - "general": { - "close_behavior": 0, // 0 = close, 1 = minimize, 2 = minimize to tray - "startup_behavior": 0, // 0 = nothing, 1 = open on startup - "discord_rpc": 1, // 0 = disabled, 1 = enabled as Cider, 2 = enabled as Apple Music - "discordClearActivityOnPause" : 0, // 0 = disabled, 1 = enabled - "volume": 1 - }, - "audio": { - "quality": "extreme", - "seamless_audio": true - }, - "visual": { - "theme": "", - "scrollbars": 0, // 0 = show on hover, 2 = always hide, 3 = always show - "refresh_rate": 0, - "animated_artwork": "always", // 0 = always, 1 = limited, 2 = never - "animated_artwork_qualityLevel": 1, - "hw_acceleration": "default", // default, webgpu, disabled - "window_transparency": "default" - }, - "lyrics": { - "enable_mxm": false, - "mxm_karaoke" : false, - "mxm_language": "en", - "enable_yt": false, - }, - "lastfm": { - "enabled": false, - "scrobble_after": 30, - "auth_token": "", - "enabledRemoveFeaturingArtists" : true, - "NowPlaying": "true" - } -} - // Enable WebGPU and list adapters (EXPERIMENTAL.) // Note: THIS HAS TO BE BEFORE ANYTHING GETS INITIALIZED. -const Store = require("electron-store"); -app.cfg = new Store({ - defaults: configSchema, -}); +// const {Init} = require("./src/main/cider-base"); +// Init() +CiderBase.Init() switch (app.cfg.get("visual.hw_acceleration")) { default: @@ -66,17 +29,6 @@ switch (app.cfg.get("visual.hw_acceleration")) { break; } -// Creating the Application Window and Calling all the Functions -function CreateWindow() { - if (app.isQuiting) { app.quit(); return; } - - /** CIDER **/ - const ciderwin = require("./src/main/cider-base") - app.win = ciderwin - app.win.Start() - /** CIDER **/ -} - if (process.platform === "linux") { app.commandLine.appendSwitch('disable-features', 'MediaSessionService'); } @@ -99,7 +51,8 @@ app.on('ready', () => { console.info('[Cider] Running in development mode.') require('vue-devtools').install() } - CreateWindow() + const {Start} = require('./src/main/cider-base') + Start() }); app.on('before-quit', () => { @@ -163,20 +116,21 @@ app.on('second-instance', (_e, argv) => { if (argv.includes("--force-quit")) { console.warn('[InstanceHandler][SecondInstanceHandler] Force Quit found. Quitting App.'); - // app.isQuiting = true + app.isQuiting = true app.quit() - } else if (CiderBase.win && true) { // If a Second Instance has Been Started + } else if (app.win && !app.cfg.get('advanced.allowMultipleInstances')) { // If a Second Instance has Been Started console.warn('[InstanceHandler][SecondInstanceHandler] Showing window.'); app.win.show() app.win.focus() } }) -if (!app.requestSingleInstanceLock() && true) { +if (!app.requestSingleInstanceLock() && !app.cfg.get('advanced.allowMultipleInstances')) { console.warn("[InstanceHandler] Existing Instance is Blocking Second Instance."); app.quit(); - // app.isQuiting = true + app.isQuiting = true } + \ No newline at end of file diff --git a/src/main/base/store.js b/src/main/base/store.js new file mode 100644 index 00000000..1f6d3d93 --- /dev/null +++ b/src/main/base/store.js @@ -0,0 +1,48 @@ +const Store = require("electron-store"), + {app} = require("electron"); + +module.exports = { + + defaults: { + "general": { + "close_behavior": 0, // 0 = close, 1 = minimize, 2 = minimize to tray + "startup_behavior": 0, // 0 = nothing, 1 = open on startup + "discord_rpc": 1, // 0 = disabled, 1 = enabled as Cider, 2 = enabled as Apple Music + "discordClearActivityOnPause" : 0, // 0 = disabled, 1 = enabled + "volume": 1 + }, + "audio": { + "quality": "extreme", + "seamless_audio": true + }, + "visual": { + "theme": "", + "scrollbars": 0, // 0 = show on hover, 2 = always hide, 3 = always show + "refresh_rate": 0, + "animated_artwork": "always", // 0 = always, 1 = limited, 2 = never + "animated_artwork_qualityLevel": 1, + "hw_acceleration": "default", // default, webgpu, disabled + "window_transparency": "default" + }, + "lyrics": { + "enable_mxm": false, + "mxm_karaoke" : false, + "mxm_language": "en", + "enable_yt": false, + }, + "lastfm": { + "enabled": false, + "scrobble_after": 30, + "auth_token": "", + "enabledRemoveFeaturingArtists" : true, + "NowPlaying": "true" + } + }, + + init() { + app.cfg = new Store({ + defaults: this.defaults, + }); + } + +} \ No newline at end of file diff --git a/src/main/base/win.js b/src/main/base/win.js new file mode 100644 index 00000000..44d3fa6f --- /dev/null +++ b/src/main/base/win.js @@ -0,0 +1,257 @@ +const {join} = require("path"), + {ipcMain, app, shell, screen} = require("electron"), + express = require("express"), + path = require("path"), + getPort = require("get-port"), + yt = require("youtube-search-without-api-key"), + os = require("os"); + +module.exports = { + + browserWindow: {}, + + clientPort: await getPort({port: 9000}), + + EnvironmentVariables: { + "env": { + platform: os.platform(), + dev: app.isPackaged + } + }, + + //------------------------------------------------------------------------------- + // Public Methods + //------------------------------------------------------------------------------- + + /** + * Creates the BrowserWindow for the application. + * @return {object} Window + */ + createBrowserWindow() { + const windowStateKeeper = require("electron-window-state"), + BrowserWindow = require((process.platform === "win32") ? "electron-acrylic-window" : "electron").BrowserWindow; + + const windowState = windowStateKeeper({ + defaultWidth: 1024, + defaultHeight: 600 + }); + + this.browserWindow = new BrowserWindow({ + icon: join(__dirname, `../../../resources/icons/icon.ico`), + width: windowState.width, + height: windowState.height, + x: windowState.x, + y: windowState.y, + minWidth: 844, + minHeight: 410, + frame: false, + title: "Cider", + vibrancy: 'dark', + transparent: process.platform === "darwin", + hasShadow: false, + webPreferences: { + webviewTag: true, + plugins: true, + nodeIntegration: true, + nodeIntegrationInWorker: false, + webSecurity: false, + allowRunningInsecureContent: true, + enableRemoteModule: true, + sandbox: true, + nativeWindowOpen: true, + contextIsolation: false, + preload: join(__dirname, '../../preload/cider-preload.js') + } + }) + + this.initializeWebServer(); + this.initializeSession(); + this.initializeHandlers(); + + windowState.manage(this.browserWindow); + this.browserWindow.webContents.setZoomFactor(screen.getPrimaryDisplay().scaleFactor) + + return this.browserWindow + }, + + /** + * Initializes the BrowserWindow handlers for the application. + */ + initializeHandlers() { + const self = this; + + this.browserWindow.on('closed', () => { + this.browserWindow = null; + }); + + if (process.platform === "win32") { + let WND_STATE = { + MINIMIZED: 0, + NORMAL: 1, + MAXIMIZED: 2, + FULL_SCREEN: 3 + } + let wndState = WND_STATE.NORMAL + + self.browserWindow.on("resize", (_event) => { + const isMaximized = self.browserWindow.isMaximized() + const isMinimized = self.browserWindow.isMinimized() + const isFullScreen = self.browserWindow.isFullScreen() + const state = wndState; + if (isMinimized && state !== WND_STATE.MINIMIZED) { + wndState = WND_STATE.MINIMIZED + } else if (isFullScreen && state !== WND_STATE.FULL_SCREEN) { + wndState = WND_STATE.FULL_SCREEN + } else if (isMaximized && state !== WND_STATE.MAXIMIZED) { + wndState = WND_STATE.MAXIMIZED + self.browserWindow.webContents.executeJavaScript(`app.chrome.maximized = true`) + } else if (state !== WND_STATE.NORMAL) { + wndState = WND_STATE.NORMAL + self.browserWindow.webContents.executeJavaScript(`app.chrome.maximized = false`) + } + }) + } + + // Set window Handler + this.browserWindow.webContents.setWindowOpenHandler(({url}) => { + if (url.includes("apple") || url.includes("localhost")) { + return {action: "allow"} + } + shell.openExternal(url).catch(() => { + }) + return { + action: 'deny' + } + }) + + //------------------------------------------------------------------------------- + // Renderer IPC Listeners + //------------------------------------------------------------------------------- + + ipcMain.on("cider-platform", (event) => { + event.returnValue = process.platform + }) + + ipcMain.on("get-gpu-mode", (event) => { + event.returnValue = process.platform + }) + + ipcMain.on("is-dev", (event) => { + event.returnValue = !app.isPackaged + }) + + // IPC stuff (listeners) + ipcMain.on('close', () => { // listen for close event + self.browserWindow.close(); + }) + + ipcMain.handle('getYTLyrics', async (event, track, artist) => { + const u = track + " " + artist + " official video"; + const videos = await yt.search(u); + return videos + }) + + ipcMain.handle('getStoreValue', (event, key, defaultValue) => { + return (defaultValue ? app.cfg.get(key, true) : app.cfg.get(key)); + }); + + ipcMain.handle('setStoreValue', (event, key, value) => { + app.cfg.set(key, value); + }); + + ipcMain.on('getStore', (event) => { + event.returnValue = app.cfg.store + }) + + ipcMain.on('setStore', (event, store) => { + app.cfg.store = store + }) + + ipcMain.on('maximize', () => { // listen for maximize event + if (self.browserWindow.isMaximized()) { + self.browserWindow.unmaximize() + } else { + self.browserWindow.maximize() + } + }) + + ipcMain.on('minimize', () => { // listen for minimize event + self.browserWindow.minimize(); + }) + + // Set scale + ipcMain.on('setScreenScale', (event, scale) => { + self.browserWindow.webContents.setZoomFactor(parseFloat(scale)) + }) + }, + + /** + * Starts the webserver + */ + initializeWebServer() { + const self = this; + const webapp = express(), + webRemotePath = path.join(__dirname, '../../renderer/'); + + webapp.set("views", path.join(webRemotePath, "views")); + webapp.set("view engine", "ejs"); + + webapp.use(function (req, res, next) { + // if not localhost + if (req.headers.host.includes("localhost") && req.headers["user-agent"].includes("Cider")) { + next(); + } + }); + + webapp.use(express.static(webRemotePath)); + webapp.get('/', function (req, res) { + //res.sendFile(path.join(webRemotePath, 'index_old.html')); + res.render("main", self.EnvironmentVariables) + }); + webapp.listen(this.clientPort, function () { + console.log(`Cider client port: ${self.clientPort}`); + }); + }, + + /** + * Initializes the application session. + */ + initializeSession() { + const self = this; + + // intercept "https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js" and redirect to local file "./apple-hls.js" instead + this.browserWindow.webContents.session.webRequest.onBeforeRequest( + { + urls: ["https://*/*.js"] + }, + (details, callback) => { + if (details.url.includes("hls.js")) { + callback({ + redirectURL: `http://localhost:${self.clientPort}/apple-hls.js` + }) + } else { + callback({ + cancel: false + }) + } + } + ) + + this.browserWindow.webContents.session.webRequest.onBeforeSendHeaders(async (details, callback) => { + if (details.url === "https://buy.itunes.apple.com/account/web/info") { + details.requestHeaders['sec-fetch-site'] = 'same-site'; + details.requestHeaders['DNT'] = '1'; + let itspod = await this.browserWindow.webContents.executeJavaScript(`window.localStorage.getItem("music.ampwebplay.itspod")`) + if (itspod != null) + details.requestHeaders['Cookie'] = `itspod=${itspod}` + } + callback({requestHeaders: details.requestHeaders}) + }) + + let location = `http://localhost:${this.clientPort}/` + this.browserWindow.loadURL(location).catch(err => { + console.log(err) + }) + } + +} \ No newline at end of file diff --git a/src/main/cider-base.js b/src/main/cider-base.js index 52786201..abb14e59 100644 --- a/src/main/cider-base.js +++ b/src/main/cider-base.js @@ -6,243 +6,50 @@ const path = require("path"); const windowStateKeeper = require("electron-window-state"); const os = require('os'); const yt = require('youtube-search-without-api-key'); -const discord = require('./discordrpc'); -const lastfm = require('./lastfm'); -const mpris = require('./mpris'); +const discord = require('./plugins/discordrpc'); +const lastfm = require('./plugins/lastfm'); +const mpris = require('./plugins/mpris'); // Analytics for debugging. const ElectronSentry = require("@sentry/electron"); ElectronSentry.init({dsn: "https://68c422bfaaf44dea880b86aad5a820d2@o954055.ingest.sentry.io/6112214"}); -const CiderBase = { - win: null, - async Start() { - this.clientPort = await getPort({port: 9000}); - this.win = this.CreateBrowserWindow() - }, - clientPort: 0, - CreateBrowserWindow() { - // Set default window sizes - const mainWindowState = windowStateKeeper({ - defaultWidth: 1024, - defaultHeight: 600 - }); +module.exports = { - let win = null - const options = { - icon: join(__dirname, `../../resources/icons/icon.ico`), - width: mainWindowState.width, - height: mainWindowState.height, - x: mainWindowState.x, - y: mainWindowState.y, - minWidth: 844, - minHeight: 410, - frame: false, - title: "Cider", - vibrancy: 'dark', - // transparent: true, - hasShadow: false, - webPreferences: { - webviewTag: true, - plugins: true, - nodeIntegration: true, - nodeIntegrationInWorker: false, - webSecurity: false, - allowRunningInsecureContent: true, - enableRemoteModule: true, - sandbox: true, - nativeWindowOpen: true, - contextIsolation: false, - preload: join(__dirname, '../preload/cider-preload.js') - } - } + //------------------------------------------------------------------------------- + // Public Methods + //------------------------------------------------------------------------------- - CiderBase.InitWebServer() - - // Create the BrowserWindow - if (process.platform === "darwin" || process.platform === "linux") { - win = new BrowserWindow(options) - } else { - const {BrowserWindow} = require("electron-acrylic-window"); - win = new BrowserWindow(options) - } - - // intercept "https://js-cdn.music.apple.com/hls.js/2.141.0/hls.js/hls.js" and redirect to local file "./apple-hls.js" instead - win.webContents.session.webRequest.onBeforeRequest( - { - urls: ["https://*/*.js"] - }, - (details, callback) => { - if (details.url.includes("hls.js")) { - callback({ - redirectURL: `http://localhost:${CiderBase.clientPort}/apple-hls.js` - }) - } else { - callback({ - cancel: false - }) - } - } - ) - - win.webContents.session.webRequest.onBeforeSendHeaders(async (details, callback) => { - if (details.url === "https://buy.itunes.apple.com/account/web/info") { - details.requestHeaders['sec-fetch-site'] = 'same-site'; - details.requestHeaders['DNT'] = '1'; - let itspod = await win.webContents.executeJavaScript(`window.localStorage.getItem("music.ampwebplay.itspod")`) - if (itspod != null) - details.requestHeaders['Cookie'] = `itspod=${itspod}` - } - callback({requestHeaders: details.requestHeaders}) - }) - - let location = `http://localhost:${CiderBase.clientPort}/` - win.loadURL(location) - win.on("closed", () => { - win = null - }) - - // Register listeners on Window to track size and position of the Window. - mainWindowState.manage(win); - - // IPC stuff (senders) - ipcMain.on("cider-platform", (event) => { - event.returnValue = process.platform - }) - - ipcMain.on("get-gpu-mode", (event) => { - event.returnValue = process.platform - }) - - ipcMain.on("is-dev", (event) => { - event.returnValue = !app.isPackaged - }) - - // IPC stuff (listeners) - ipcMain.on('close', () => { // listen for close event - win.close(); - }) - - ipcMain.handle('getYTLyrics', async (event, track, artist) => { - var u = track + " " + artist + " official video"; - const videos = await yt.search(u); - return videos - }) - - ipcMain.handle('getStoreValue', (event, key, defaultValue) => { - return (defaultValue ? app.cfg.get(key, true) : app.cfg.get(key)); - }); - - ipcMain.handle('setStoreValue', (event, key, value) => { - app.cfg.set(key, value); - }); - - ipcMain.on('getStore', (event) => { - event.returnValue = app.cfg.store - }) - - ipcMain.on('setStore', (event, store) => { - app.cfg.store = store - }) - - ipcMain.on('maximize', () => { // listen for maximize event - if (win.isMaximized()) { - win.unmaximize() - } else { - win.maximize() - } - }) - - ipcMain.on('minimize', () => { // listen for minimize event - win.minimize(); - }) - - if (process.platform === "win32") { - let WND_STATE = { - MINIMIZED: 0, - NORMAL: 1, - MAXIMIZED: 2, - FULL_SCREEN: 3 - } - let wndState = WND_STATE.NORMAL - - win.on("resize", (_event) => { - const isMaximized = win.isMaximized() - const isMinimized = win.isMinimized() - const isFullScreen = win.isFullScreen() - const state = wndState; - if (isMinimized && state !== WND_STATE.MINIMIZED) { - wndState = WND_STATE.MINIMIZED - } else if (isFullScreen && state !== WND_STATE.FULL_SCREEN) { - wndState = WND_STATE.FULL_SCREEN - } else if (isMaximized && state !== WND_STATE.MAXIMIZED) { - wndState = WND_STATE.MAXIMIZED - win.webContents.executeJavaScript(`app.chrome.maximized = true`) - } else if (state !== WND_STATE.NORMAL) { - wndState = WND_STATE.NORMAL - win.webContents.executeJavaScript(`app.chrome.maximized = false`) - } - }) - } - - // Set window Handler - win.webContents.setWindowOpenHandler(({url}) => { - if (url.includes("apple") || url.includes("localhost")) { - return {action: "allow"} - } - shell.openExternal(url).catch(() => { - }) - return { - action: 'deny' - } - }) - - // Set scale - ipcMain.on('setScreenScale', (event, scale) => { - win.webContents.setZoomFactor(parseFloat(scale)) - }) - - win.webContents.setZoomFactor(screen.getPrimaryDisplay().scaleFactor) - - mpris.connect(win) - - lastfm.authenticate() - // Discord - discord.connect((app.cfg.get("general.discord_rpc") == 1) ? '911790844204437504' : '886578863147192350'); - ipcMain.on('playbackStateDidChange', (_event, a) => { - app.media = a; - discord.updateActivity(a) - mpris.updateState(a) - lastfm.scrobbleSong(a) - lastfm.updateNowPlayingSong(a) - }); - ipcMain.on('nowPlayingItemDidChange', (_event, a) => { - app.media = a; - discord.updateActivity(a) - mpris.updateAttributes(a) - lastfm.scrobbleSong(a) - lastfm.updateNowPlayingSong(a) - }); - - return win + /** + * Starts the application (called on on-ready). - Starts BrowserWindow and WebServer + */ + Start() { + const {createBrowserWindow} = require("./base/win"); + app.win = createBrowserWindow() }, - EnvironmentVariables: { - "env": { - platform: os.platform(), - dev: app.isPackaged - } + /** + * Initializes the main application (run before on-ready) + */ + Init() { + // Initialize the config. + const {init} = require("./base/store"); + init() }, - LinkHandler: (startArgs) => { + + /** + * Handles all links being opened in the application. + */ + LinkHandler(startArgs) { if (!startArgs) return; - console.log("lfmtoken",String(startArgs)) + console.log("lfmtoken", String(startArgs)) if (String(startArgs).includes('auth')) { let authURI = String(startArgs).split('/auth/')[1] if (authURI.startsWith('lastfm')) { // If we wanted more auth options const authKey = authURI.split('lastfm?token=')[1]; app.cfg.set('lastfm.enabled', true); app.cfg.set('lastfm.auth_token', authKey); - CiderBase.win.webContents.send('LastfmAuthenticated', authKey); + app.win.webContents.send('LastfmAuthenticated', authKey); lastfm.authenticate() } } else { @@ -258,30 +65,4 @@ const CiderBase = { } }, - - async InitWebServer() { - const webapp = express(); - const webRemotePath = path.join(__dirname, '../renderer/'); - webapp.set("views", path.join(webRemotePath, "views")); - webapp.set("view engine", "ejs"); - - webapp.use(function (req, res, next) { - // if not localhost - if (req.headers.host.includes("localhost") && req.headers["user-agent"].includes("Cider")) { - next(); - } - }); - - webapp.use(express.static(webRemotePath)); - webapp.get('/', function (req, res) { - //res.sendFile(path.join(webRemotePath, 'index_old.html')); - res.render("main", CiderBase.EnvironmentVariables) - }); - webapp.listen(CiderBase.clientPort, function () { - console.log(`Cider client port: ${CiderBase.clientPort}`); - }); - }, - -} - -module.exports = CiderBase; +} \ No newline at end of file diff --git a/src/main/discordrpc.js b/src/main/plugins/discordrpc.js similarity index 100% rename from src/main/discordrpc.js rename to src/main/plugins/discordrpc.js diff --git a/src/main/lastfm.js b/src/main/plugins/lastfm.js similarity index 98% rename from src/main/lastfm.js rename to src/main/plugins/lastfm.js index 74dfa5b2..541cde82 100644 --- a/src/main/lastfm.js +++ b/src/main/plugins/lastfm.js @@ -2,7 +2,7 @@ const {app, Notification} = require('electron'), fs = require('fs'), {resolve} = require('path'), sessionPath = resolve(app.getPath('userData'), 'session.json'), - apiCredentials = require('../../resources/lfmApiCredentials.json'), + apiCredentials = require('../../../resources/lfmApiCredentials.json'), LastfmAPI = require('lastfmapi'); const lfm = { diff --git a/src/main/mpris.js b/src/main/plugins/mpris.js similarity index 100% rename from src/main/mpris.js rename to src/main/plugins/mpris.js