YesPlayMusic/src/electron/ipcMain.js
2022-10-05 16:21:18 +08:00

334 lines
9.4 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 UNM from '@unblockneteasemusic/rust-napi';
import { registerGlobalShortcut } from '@/electron/globalShortcut';
import cloneDeep from 'lodash/cloneDeep';
import shortcuts from '@/utils/shortcuts';
import { createMenu } from './menu';
import { isCreateTray, isMac } from '@/utils/platform';
import { resolve } from 'path';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
const clc = require('cli-color');
const log = text => {
console.log(`${clc.blueBright('[ipcMain.js]')} ${text}`);
};
const exitAsk = (e, win) => {
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, win) => {
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);
});
};
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 base64 data from bilivideo.
*
* @param {string} url The URL to fetch.
* @returns {Promise<string>} The file base64 data.
*/
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 encodedData;
}
/**
* Parse the source string (`a, b`) to source list `['a', 'b']`.
*
* @param {import("@unblockneteasemusic/rust-napi").Executor} executor
* @param {string} sourceString The source string.
* @returns {string[]} The source list.
*/
function parseSourceStringToList(executor, sourceString) {
const availableSource = executor.list();
return sourceString
.split(',')
.map(s => s.trim().toLowerCase())
.filter(s => {
const isAvailable = availableSource.includes(s);
if (!isAvailable) {
log(`This source is not one of the supported source: ${s}`);
}
return isAvailable;
});
}
export function initIpcMain(win, store, trayEventEmitter) {
// WIP: Do not enable logging as it has some issues in non-blocking I/O environment.
// UNM.enableLogging(UNM.LoggingType.ConsoleEnv);
const unmExecutor = new UNM.Executor();
ipcMain.handle(
'unblock-music',
/**
*
* @param {*} _
* @param {string | null} sourceListString
* @param {Record<string, any>} ncmTrack
* @param {UNM.Context} context
*/
async (_, sourceListString, ncmTrack, context) => {
// Formt the track input
// FIXME: Figure out the structure of Track
const song = {
id: ncmTrack.id && ncmTrack.id.toString(),
name: ncmTrack.name,
duration: ncmTrack.dt,
album: ncmTrack.al && {
id: ncmTrack.al.id && ncmTrack.al.id.toString(),
name: ncmTrack.al.name,
},
artists: ncmTrack.ar
? ncmTrack.ar.map(({ id, name }) => ({
id: id && id.toString(),
name,
}))
: [],
};
const sourceList =
typeof sourceListString === 'string'
? parseSourceStringToList(unmExecutor, sourceListString)
: ['ytdl', 'bilibili', 'pyncm', 'kugou'];
log(`[UNM] using source: ${sourceList.join(', ')}`);
log(`[UNM] using configuration: ${JSON.stringify(context)}`);
try {
// TODO: tell users to install yt-dlp.
const matchedAudio = await unmExecutor.search(
sourceList,
song,
context
);
const retrievedSong = await unmExecutor.retrieve(matchedAudio, context);
// bilibili's audio file needs some special treatment
if (retrievedSong.url.includes('bilivideo.com')) {
retrievedSong.url = await getBiliVideoFile(retrievedSong.url);
}
log(`respond with retrieve song…`);
log(JSON.stringify(matchedAudio));
return retrievedSong;
} catch (err) {
const errorMessage = err instanceof Error ? `${err.message}` : `${err}`;
log(`UnblockNeteaseMusic failed: ${errorMessage}`);
return null;
}
}
);
ipcMain.on('close', e => {
if (isMac) {
win.hide();
exitAsk(e, win);
} 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, win);
}
}
});
ipcMain.on('minimize', () => {
win.minimize();
});
ipcMain.on('maximizeOrUnmaximize', () => {
win.isMaximized() ? win.unmaximize() : win.maximize();
});
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);
});
ipcMain.on('saveLyric', (event, { name, lyric }) => {
let lyricsDirPath = resolve(process.env.HOME, '.lyrics');
if (!existsSync(lyricsDirPath)) mkdirSync(lyricsDirPath);
if (!existsSync(resolve(lyricsDirPath, name + '.lrc'))) {
writeFileSync(resolve(lyricsDirPath, name + '.lrc'), lyric);
}
win.webContents.send('saveLyricFinished');
});
if (isCreateTray) {
ipcMain.on('updateTrayTooltip', (_, title) => {
trayEventEmitter.emit('updateTooltip', title);
});
ipcMain.on('updateTrayPlayState', (_, isPlaying) => {
trayEventEmitter.emit('updatePlayState', isPlaying);
});
ipcMain.on('updateTrayLikeState', (_, isLiked) => {
trayEventEmitter.emit('updateLikeState', isLiked);
});
}
}