YesPlayMusic/src/electron/ipcMain.js
pan93412 83b78bab34
feat: allow customizing UNM's sources (#1134)
* feat(ipcMain/unm): allow passing customized source

* feat(utils/Player): pass settings.unmSource

According to c280221a44608777a69038306a6ea8e92953fb9a,
we can let users customize their desired source now.

* feat(views/settings): allow configuring sources

We haven't supported specifying the environment variable in YPM yet.
2021-12-28 00:49:31 +08:00

276 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string>} 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 || '<default>'}`);
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);
});
};
}