import { app, dialog, globalShortcut, ipcMain } from 'electron'; import match from '@unblockneteasemusic/server'; import { registerGlobalShortcut } from '@/electron/globalShortcut'; import cloneDeep from 'lodash/cloneDeep'; import shortcuts from '@/utils/shortcuts'; import { createMenu } from './menu'; const clc = require('cli-color'); const log = text => { console.log(`${clc.blueBright('[ipcMain.js]')} ${text}`); }; const client = require('discord-rich-presence')('818936529484906596'); /** * Make data a Buffer. * * @param {?} data The data to convert. * @returns {import("buffer").Buffer} The converted data. */ function toBuffer(data) { if (data instanceof Buffer) { return data; } else { return Buffer.from(data); } } /** * Get the file URI from bilivideo. * * @param {string} url The URL to fetch. * @returns {Promise} The file URI. */ async function getBiliVideoFile(url) { const axios = await import('axios').then(m => m.default); const response = await axios.get(url, { headers: { Referer: 'https://www.bilibili.com/', 'User-Agent': 'okhttp/3.4.1', }, responseType: 'arraybuffer', }); const buffer = toBuffer(response.data); const encodedData = buffer.toString('base64'); return `data:application/octet-stream;base64,${encodedData}`; } /** * Parse the source string (`a, b`) to source list `['a', 'b']`. * * @param {string} sourceString The source string. * @returns {string[]} The source list. */ function parseSourceStringToList(sourceString) { return sourceString.split(',').map(s => s.trim()); } export function initIpcMain(win, store) { ipcMain.handle('unblock-music', async (_, track, source) => { // 兼容 unblockneteasemusic 所使用的 api 字段 track.alias = track.alia || []; track.duration = track.dt || 0; track.album = track.al || []; track.artists = track.ar || []; const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject('timeout'); }, 5000); }); const sourceList = typeof source === 'string' ? parseSourceStringToList(source) : null; log(`[UNM] using source: ${sourceList || ''}`); try { const matchedAudio = await Promise.race([ // TODO: tell users to install yt-dlp. // we passed "null" to source, to let UNM choose the default source. match(track.id, sourceList, track), timeoutPromise, ]); if (!matchedAudio || !matchedAudio.url) { throw new Error('no such a song found'); } // bilibili's audio file needs some special treatment if (matchedAudio.url.includes('bilivideo.com')) { matchedAudio.url = await getBiliVideoFile(matchedAudio.url); } return matchedAudio; } catch (err) { const errorMessage = err instanceof Error ? `${err.message}` : `${err}`; log(`UnblockNeteaseMusic failed: ${errorMessage}`); return null; } }); ipcMain.on('close', e => { if (process.platform === 'darwin') { win.hide(); exitAsk(e); } else { let closeOpt = store.get('settings.closeAppOption'); if (closeOpt === 'exit') { win = null; //app.quit(); app.exit(); //exit()直接关闭客户端,不会执行quit(); } else if (closeOpt === 'minimizeToTray') { e.preventDefault(); win.hide(); } else { exitAskWithoutMac(e); } } }); ipcMain.on('minimize', () => { win.minimize(); }); ipcMain.on('maximizeOrUnmaximize', () => { const isMaximized = win.isMaximized(); isMaximized ? win.unmaximize() : win.maximize(); win.webContents.send('isMaximized', isMaximized); }); ipcMain.on('settings', (event, options) => { store.set('settings', options); if (options.enableGlobalShortcut) { registerGlobalShortcut(win, store); } else { log('unregister global shortcut'); globalShortcut.unregisterAll(); } }); ipcMain.on('playDiscordPresence', (event, track) => { client.updatePresence({ details: track.name + ' - ' + track.ar.map(ar => ar.name).join(','), state: track.al.name, endTimestamp: Date.now() + track.dt, largeImageKey: 'logo', largeImageText: 'Listening ' + track.name, smallImageKey: 'play', smallImageText: 'Playing', instance: true, }); }); ipcMain.on('pauseDiscordPresence', (event, track) => { client.updatePresence({ details: track.name + ' - ' + track.ar.map(ar => ar.name).join(','), state: track.al.name, largeImageKey: 'logo', largeImageText: 'YesPlayMusic', smallImageKey: 'pause', smallImageText: 'Pause', instance: true, }); }); ipcMain.on('setProxy', (event, config) => { const proxyRules = `${config.protocol}://${config.server}:${config.port}`; store.set('proxy', proxyRules); win.webContents.session.setProxy( { proxyRules, }, () => { log('finished setProxy'); } ); }); ipcMain.on('removeProxy', (event, arg) => { log('removeProxy'); win.webContents.session.setProxy({}); store.set('proxy', ''); }); ipcMain.on('switchGlobalShortcutStatusTemporary', (e, status) => { log('switchGlobalShortcutStatusTemporary'); if (status === 'disable') { globalShortcut.unregisterAll(); } else { registerGlobalShortcut(win, store); } }); ipcMain.on('updateShortcut', (e, { id, type, shortcut }) => { log('updateShortcut'); let shortcuts = store.get('settings.shortcuts'); let newShortcut = shortcuts.find(s => s.id === id); newShortcut[type] = shortcut; store.set('settings.shortcuts', shortcuts); createMenu(win, store); globalShortcut.unregisterAll(); registerGlobalShortcut(win, store); }); ipcMain.on('restoreDefaultShortcuts', () => { log('restoreDefaultShortcuts'); store.set('settings.shortcuts', cloneDeep(shortcuts)); createMenu(win, store); globalShortcut.unregisterAll(); registerGlobalShortcut(win, store); }); const exitAsk = e => { e.preventDefault(); //阻止默认行为 dialog .showMessageBox({ type: 'info', title: 'Information', cancelId: 2, defaultId: 0, message: '确定要关闭吗?', buttons: ['最小化', '直接退出'], }) .then(result => { if (result.response == 0) { e.preventDefault(); //阻止默认行为 win.minimize(); //调用 最小化实例方法 } else if (result.response == 1) { win = null; //app.quit(); app.exit(); //exit()直接关闭客户端,不会执行quit(); } }) .catch(err => { log(err); }); }; const exitAskWithoutMac = e => { e.preventDefault(); //阻止默认行为 dialog .showMessageBox({ type: 'info', title: 'Information', cancelId: 2, defaultId: 0, message: '确定要关闭吗?', buttons: ['最小化到托盘', '直接退出'], checkboxLabel: '记住我的选择', }) .then(result => { if (result.checkboxChecked && result.response !== 2) { win.webContents.send( 'rememberCloseAppOption', result.response === 0 ? 'minimizeToTray' : 'exit' ); } if (result.response === 0) { e.preventDefault(); //阻止默认行为 win.hide(); //调用 最小化实例方法 } else if (result.response === 1) { win = null; //app.quit(); app.exit(); //exit()直接关闭客户端,不会执行quit(); } }) .catch(err => { log(err); }); }; }