Skip to content

Commit

Permalink
feat: switch to UNM (Rust) (qier222#1536)
Browse files Browse the repository at this point in the history
* refactor: use unm-rust-napi

* ci(build): install UNM dependencies for certain platforms

* feat: add the ability to configure UNM

* feat: add the UNM configuration in settings page

* refactor(jsconfig): jsx -> preserve

* fix(ci/build): use bash to get unm version

* chore(deps): upgrade UNM to 0.3.0-pre.0

* refactor(electron/ipcMain): update default sources

* fix(views/settings): remove duplicated config entry

* feat(settings): allow configuring QQ cookie

We also removed some duplicate entries in views/settings.vue.

* chore(deps): UNM -> 0.3.0-pre.1

* refactor: remove unused old UNM

* fix(utils/player): do not include rust-napi in client code

As we only imported the constant, I just expand it as the integer.

Co-authored-by: qier222 <[email protected]>
  • Loading branch information
pan93412 and qier222 authored Apr 27, 2022
1 parent e1f7618 commit 93ae57a
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 208 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: Release

env:
YARN_INSTALL_NOPT: yarn add --ignore-platform --ignore-optional

on:
push:
branches:
Expand Down Expand Up @@ -42,6 +45,39 @@ jobs:
with:
snapcraft_token: ${{ secrets.snapcraft_token }}

- id: get_unm_version
name: Get the installed UNM version
run: |
yarn --ignore-optional
unm_version=$(node -e "console.log(require('./node_modules/@unblockneteasemusic/rust-napi/package.json').version)")
echo "::set-output name=unmver::${unm_version}"
shell: bash

- name: Install UNM dependencies for Windows
if: runner.os == 'Windows'
run: |
${{ env.YARN_INSTALL_NOPT }} \
@unblockneteasemusic/rust-napi-win32-x64-msvc@${{steps.get_unm_version.outputs.unmver}}
shell: bash

- name: Install UNM dependencies for macOS
if: runner.os == 'macOS'
run: |
${{ env.YARN_INSTALL_NOPT }} \
@unblockneteasemusic/rust-napi-darwin-x64@${{steps.get_unm_version.outputs.unmver}} \
@unblockneteasemusic/rust-napi-darwin-arm64@${{steps.get_unm_version.outputs.unmver}} \
dmg-license
shell: bash

- name: Install UNM dependencies for Linux
if: runner.os == 'Linux'
run: |
${{ env.YARN_INSTALL_NOPT }} \
@unblockneteasemusic/rust-napi-linux-x64-gnu@${{steps.get_unm_version.outputs.unmver}} \
@unblockneteasemusic/rust-napi-linux-arm64-gnu@${{steps.get_unm_version.outputs.unmver}} \
@unblockneteasemusic/rust-napi-linux-arm-gnueabihf@${{steps.get_unm_version.outputs.unmver}}
shell: bash

- name: Build/release Electron app
uses: samuelmeuli/[email protected]
env:
Expand Down
3 changes: 2 additions & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
},
"target": "ES6",
"module": "commonjs",
"allowSyntheticDefaultImports": true
"allowSyntheticDefaultImports": true,
"jsx": "preserve"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"main": "background.js",
"dependencies": {
"@unblockneteasemusic/server": "v0.27.0-rc.6",
"@unblockneteasemusic/rust-napi": "^0.3.0-pre.1",
"NeteaseCloudMusicApi": "^4.5.2",
"axios": "^0.26.1",
"change-case": "^4.1.2",
Expand Down
110 changes: 73 additions & 37 deletions src/electron/ipcMain.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { app, dialog, globalShortcut, ipcMain } from 'electron';
import match from '@unblockneteasemusic/server';
import UNM from '@unblockneteasemusic/rust-napi';
import { registerGlobalShortcut } from '@/electron/globalShortcut';
import cloneDeep from 'lodash/cloneDeep';
import shortcuts from '@/utils/shortcuts';
Expand Down Expand Up @@ -112,55 +112,91 @@ async function getBiliVideoFile(url) {
/**
* 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(sourceString) {
return sourceString.split(',').map(s => s.trim());
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) {
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);
});
// 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();

const sourceList =
typeof source === 'string' ? parseSourceStringToList(source) : null;
log(`[UNM] using source: ${sourceList || '<default>'}`);
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,
}))
: [],
};

try {
const matchedAudio = await Promise.race([
const sourceList =
typeof sourceListString === 'string'
? parseSourceStringToList(unmExecutor, sourceListString)
: ['migu', '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.
// we passed "null" to source, to let UNM choose the default source.
match(track.id, sourceList, track),
timeoutPromise,
]);
const matchedAudio = await unmExecutor.search(
sourceList,
song,
context
);
const retrievedSong = await unmExecutor.retrieve(matchedAudio, context);

if (!matchedAudio || !matchedAudio.url) {
throw new Error('no such a song found');
}
// bilibili's audio file needs some special treatment
if (retrievedSong.url.includes('bilivideo.com')) {
retrievedSong.url = await getBiliVideoFile(retrievedSong.url);
}

// bilibili's audio file needs some special treatment
if (matchedAudio.url.includes('bilivideo.com')) {
matchedAudio.url = await getBiliVideoFile(matchedAudio.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;
}

return matchedAudio;
} catch (err) {
const errorMessage = err instanceof Error ? `${err.message}` : `${err}`;
log(`UnblockNeteaseMusic failed: ${errorMessage}`);
return null;
}
});
);

ipcMain.on('close', e => {
if (isMac) {
Expand Down
51 changes: 45 additions & 6 deletions src/utils/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,22 +370,61 @@ export default class {
}
async _getAudioSourceFromUnblockMusic(track) {
console.debug(`[debug][Player.js] _getAudioSourceFromUnblockMusic`);

if (
process.env.IS_ELECTRON !== true ||
store.state.settings.enableUnblockNeteaseMusic === false
) {
return null;
}
const source = await ipcRenderer.invoke(

/**
*
* @param {string=} searchMode
* @returns {import("@unblockneteasemusic/rust-napi").SearchMode}
*/
const determineSearchMode = searchMode => {
/**
* FastFirst = 0
* OrderFirst = 1
*/
switch (searchMode) {
case 'fast-first':
return 0;
case 'order-first':
return 1;
default:
return 0;
}
};

/** @type {import("@unblockneteasemusic/rust-napi").RetrievedSongInfo | null} */
const retrieveSongInfo = await ipcRenderer.invoke(
'unblock-music',
store.state.settings.unmSource,
track,
store.state.settings.unmSource
/** @type {import("@unblockneteasemusic/rust-napi").Context} */ ({
enableFlac: store.state.settings.unmEnableFlac || null,
proxyUri: store.state.settings.unmProxyUri || null,
searchMode: determineSearchMode(store.state.settings.unmSearchMode),
config: {
'joox:cookie': store.state.settings.unmJooxCookie || null,
'qq:cookie': store.state.settings.unmQQCookie || null,
'ytdl:exe': store.state.settings.unmYtDlExe || null,
},
})
);
if (store.state.settings.automaticallyCacheSongs && source?.url) {
// TODO: 将unblockMusic字样换成真正的来源(比如酷我咪咕等)
cacheTrackSource(track, source.url, 128000, 'unblockMusic');

if (store.state.settings.automaticallyCacheSongs && retrieveSongInfo?.url) {
cacheTrackSource(
track,
retrieveSongInfo.url,
128000,
`unm:${retrieveSongInfo.source}`
);
}
return source?.url;

return retrieveSongInfo?.url;
}
_getAudioSource(track) {
return this._getAudioSourceFromCache(String(track.id))
Expand Down
Loading

0 comments on commit 93ae57a

Please sign in to comment.