Merge pull request #1818 from Revincx/revincx-pr

This commit is contained in:
pan93412 2023-08-26 11:17:42 +08:00 committed by GitHub
commit fd40a29180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 144 additions and 8 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -31,7 +31,8 @@ import { EventEmitter } from 'events';
import express from 'express'; import express from 'express';
import expressProxy from 'express-http-proxy'; import expressProxy from 'express-http-proxy';
import Store from 'electron-store'; import Store from 'electron-store';
import { createMpris } from '@/electron/mpris'; import { createMpris, createDbus } from '@/electron/mpris';
import { spawn } from 'child_process';
const clc = require('cli-color'); const clc = require('cli-color');
const log = text => { const log = text => {
console.log(`${clc.blueBright('[background.js]')} ${text}`); console.log(`${clc.blueBright('[background.js]')} ${text}`);
@ -420,6 +421,21 @@ class Background {
registerGlobalShortcut(this.window, this.store); registerGlobalShortcut(this.window, this.store);
} }
// try to start osdlyrics process on start
if (this.store.get('settings.enableOsdlyricsSupport')) {
await createDbus(this.window);
log('try to start osdlyrics process');
const osdlyricsProcess = spawn('osdlyrics');
osdlyricsProcess.on('error', err => {
log(`failed to start osdlyrics: ${err.message}`);
});
osdlyricsProcess.on('exit', (code, signal) => {
log(`osdlyrics process exited with code ${code}, signal ${signal}`);
});
}
// create mpris // create mpris
if (isCreateMpris) { if (isCreateMpris) {
createMpris(this.window); createMpris(this.window);

View File

@ -1,3 +1,4 @@
import dbus from 'dbus-next';
import { ipcMain, app } from 'electron'; import { ipcMain, app } from 'electron';
export function createMpris(window) { export function createMpris(window) {
@ -28,6 +29,8 @@ export function createMpris(window) {
}); });
ipcMain.on('metadata', (e, metadata) => { ipcMain.on('metadata', (e, metadata) => {
// 更新 Mpris 状态前将位置设为0, 否则 OSDLyrics 获取到的进度是上首音乐切换时的进度
player.getPosition = () => 0;
player.metadata = { player.metadata = {
'mpris:trackid': player.objectPath('track/' + metadata.trackId), 'mpris:trackid': player.objectPath('track/' + metadata.trackId),
'mpris:artUrl': metadata.artwork[0].src, 'mpris:artUrl': metadata.artwork[0].src,
@ -41,6 +44,11 @@ export function createMpris(window) {
ipcMain.on('playerCurrentTrackTime', (e, position) => { ipcMain.on('playerCurrentTrackTime', (e, position) => {
player.getPosition = () => position * 1000 * 1000; player.getPosition = () => position * 1000 * 1000;
player.seeked(position * 1000 * 1000);
});
ipcMain.on('seeked', (e, position) => {
player.seeked(position * 1000 * 1000);
}); });
ipcMain.on('switchRepeatMode', (e, mode) => { ipcMain.on('switchRepeatMode', (e, mode) => {
@ -61,3 +69,26 @@ export function createMpris(window) {
player.shuffle = shuffle; player.shuffle = shuffle;
}); });
} }
export async function createDbus(window) {
const bus = dbus.sessionBus();
const Variant = dbus.Variant;
const osdService = await bus.getProxyObject(
'org.osdlyrics.Daemon',
'/org/osdlyrics/Lyrics'
);
const osdInterface = osdService.getInterface('org.osdlyrics.Lyrics');
ipcMain.on('sendLyrics', async (e, { track, lyrics }) => {
const metadata = {
title: new Variant('s', track.name),
artist: new Variant('s', track.ar.map(ar => ar.name).join(', ')),
};
await osdInterface.SetLyricContent(metadata, Buffer.from(lyrics));
window.webContents.send('saveLyricFinished');
});
}

View File

@ -1,6 +1,6 @@
/* global __static */ /* global __static */
import path from 'path'; import path from 'path';
import { app, nativeImage, Tray, Menu } from 'electron'; import { app, nativeImage, Tray, Menu, nativeTheme } from 'electron';
import { isLinux } from '@/utils/platform'; import { isLinux } from '@/utils/platform';
function createMenuTemplate(win) { function createMenuTemplate(win) {
@ -197,8 +197,11 @@ class YPMTrayWindowsImpl {
} }
export function createTray(win, eventEmitter) { export function createTray(win, eventEmitter) {
// 感觉图标颜色应该不属于界面主题范畴,只需要跟随系统主题
let iconTheme = nativeTheme.shouldUseDarkColors ? 'light' : 'dark';
let icon = nativeImage let icon = nativeImage
.createFromPath(path.join(__static, 'img/icons/menu@88.png')) .createFromPath(path.join(__static, `img/icons/menu-${iconTheme}@88.png`))
.resize({ .resize({
height: 20, height: 20,
width: 20, width: 20,

View File

@ -193,6 +193,13 @@ export default {
exit: 'Exit', exit: 'Exit',
minimizeToTray: 'Minimize to tray', minimizeToTray: 'Minimize to tray',
}, },
enableOsdlyricsSupport: {
title: 'desktop lyrics support',
desc1:
'Only takes effect under Linux. After enabled, it downloads the lyrics file to the local, and tries to launch OSDLyrics at startup.',
desc2:
'Please ensure that you have installed OSDLyrics before turning on this.',
},
unm: { unm: {
enable: 'Enable', enable: 'Enable',
audioSource: { audioSource: {

View File

@ -194,6 +194,12 @@ export default {
exit: '退出', exit: '退出',
minimizeToTray: '最小化到托盘', minimizeToTray: '最小化到托盘',
}, },
enableOsdlyricsSupport: {
title: '桌面歌词支持',
desc1:
'仅 Linux 下生效。启用后会将歌词文件下载到本地,并在开启播放器时尝试拉起 OSDLyrics。',
desc2: '请在开启之前确保您已经正确安装了 OSDLyrics。',
},
unm: { unm: {
enable: '启用', enable: '启用',
audioSource: { audioSource: {

View File

@ -191,6 +191,12 @@ export default {
exit: '退出', exit: '退出',
minimizeToTray: '最小化到工作列角落', minimizeToTray: '最小化到工作列角落',
}, },
enableOsdlyricsSupport: {
title: '桌面歌詞支援',
desc1:
'只在 Linux 環境下生效。啟用後會將歌詞檔案下載至本機位置,並在開啟播放器時嘗試連帶啟動 OSDLyrics。',
desc2: '請在開啟之前確保您已經正確安裝了 OSDLyrics。',
},
unm: { unm: {
enable: '啟用', enable: '啟用',
audioSource: { audioSource: {

View File

@ -23,6 +23,7 @@ let localStorage = {
nyancatStyle: false, nyancatStyle: false,
showLyricsTranslation: true, showLyricsTranslation: true,
lyricsBackground: true, lyricsBackground: true,
enableOsdlyricsSupport: false,
closeAppOption: 'ask', closeAppOption: 'ask',
enableDiscordRichPresence: false, enableDiscordRichPresence: false,
enableGlobalShortcut: true, enableGlobalShortcut: true,

View File

@ -3,7 +3,7 @@ import { getArtist } from '@/api/artist';
import { trackScrobble, trackUpdateNowPlaying } from '@/api/lastfm'; import { trackScrobble, trackUpdateNowPlaying } from '@/api/lastfm';
import { fmTrash, personalFM } from '@/api/others'; import { fmTrash, personalFM } from '@/api/others';
import { getPlaylistDetail, intelligencePlaylist } from '@/api/playlist'; import { getPlaylistDetail, intelligencePlaylist } from '@/api/playlist';
import { getMP3, getTrackDetail, scrobble } from '@/api/track'; import { getLyric, getMP3, getTrackDetail, scrobble } from '@/api/track';
import store from '@/store'; import store from '@/store';
import { isAccountLoggedIn } from '@/utils/auth'; import { isAccountLoggedIn } from '@/utils/auth';
import { cacheTrackSource, getTrackSource } from '@/utils/db'; import { cacheTrackSource, getTrackSource } from '@/utils/db';
@ -201,6 +201,9 @@ export default class {
set progress(value) { set progress(value) {
if (this._howler) { if (this._howler) {
this._howler.seek(value); this._howler.seek(value);
if (isCreateMpris) {
ipcRenderer?.send('seeked', this._howler.seek());
}
} }
} }
get isCurrentTrackLiked() { get isCurrentTrackLiked() {
@ -622,9 +625,30 @@ export default class {
navigator.mediaSession.metadata = new window.MediaMetadata(metadata); navigator.mediaSession.metadata = new window.MediaMetadata(metadata);
if (isCreateMpris) { if (isCreateMpris) {
ipcRenderer?.send('metadata', metadata); this._updateMprisState(track, metadata);
} }
} }
// OSDLyrics 会检测 Mpris 状态并寻找对应歌词文件,所以要在更新 Mpris 状态之前保证歌词下载完成
async _updateMprisState(track, metadata) {
if (!store.state.settings.enableOsdlyricsSupport) {
return ipcRenderer?.send('metadata', metadata);
}
let lyricContent = await getLyric(track.id);
if (!lyricContent.lrc || !lyricContent.lrc.lyric) {
return ipcRenderer?.send('metadata', metadata);
}
ipcRenderer.send('sendLyrics', {
track,
lyrics: lyricContent.lrc.lyric,
});
ipcRenderer.on('saveLyricFinished', () => {
ipcRenderer?.send('metadata', metadata);
});
}
_updateMediaSessionPositionState() { _updateMediaSessionPositionState() {
if ('mediaSession' in navigator === false) { if ('mediaSession' in navigator === false) {
return; return;
@ -822,11 +846,14 @@ export default class {
this.play(); this.play();
} }
} }
seek(time = null) { seek(time = null, sendMpris = true) {
if (isCreateMpris && sendMpris && time) {
ipcRenderer?.send('seeked', time);
}
if (time !== null) { if (time !== null) {
this._howler?.seek(time); this._howler?.seek(time);
if (this._playing) if (this._playing)
this._playDiscordPresence(this._currentTrack, this.seek()); this._playDiscordPresence(this._currentTrack, this.seek(null, false));
} }
return this._howler === null ? 0 : this._howler.seek(); return this._howler === null ? 0 : this._howler.seek();
} }

View File

@ -566,7 +566,7 @@ export default {
}, },
setLyricsInterval() { setLyricsInterval() {
this.lyricsInterval = setInterval(() => { this.lyricsInterval = setInterval(() => {
const progress = this.player.seek() ?? 0; const progress = this.player.seek(null, false) ?? 0;
let oldHighlightLyricIndex = this.highlightLyricIndex; let oldHighlightLyricIndex = this.highlightLyricIndex;
this.highlightLyricIndex = this.lyric.findIndex((l, index) => { this.highlightLyricIndex = this.lyric.findIndex((l, index) => {
const nextLyric = this.lyric[index + 1]; const nextLyric = this.lyric[index + 1];

View File

@ -251,6 +251,33 @@
</select> </select>
</div> </div>
</div> </div>
<div v-if="isElectron && isLinux" class="item">
<div class="left">
<div class="title">
{{ $t('settings.unm.enable') }}
<a target="_blank" href="https://github.com/osdlyrics/osdlyrics"
>OSDLyrics</a
>
{{ $t('settings.enableOsdlyricsSupport.title') }}
</div>
<div class="description">
{{ $t('settings.enableOsdlyricsSupport.desc1') }}
<br />
{{ $t('settings.enableOsdlyricsSupport.desc2') }}
</div>
</div>
<div class="right">
<div class="toggle">
<input
id="enable-osdlyrics-support"
v-model="enableOsdlyricsSupport"
type="checkbox"
name="enable-osdlyrics-support"
/>
<label for="enable-osdlyrics-support"></label>
</div>
</div>
</div>
<section v-if="isElectron" class="unm-configuration"> <section v-if="isElectron" class="unm-configuration">
<h3>UnblockNeteaseMusic</h3> <h3>UnblockNeteaseMusic</h3>
@ -975,6 +1002,17 @@ export default {
}); });
}, },
}, },
enableOsdlyricsSupport: {
get() {
return this.settings.enableOsdlyricsSupport;
},
set(value) {
this.$store.commit('updateSettings', {
key: 'enableOsdlyricsSupport',
value,
});
},
},
closeAppOption: { closeAppOption: {
get() { get() {
return this.settings.closeAppOption; return this.settings.closeAppOption;
@ -1477,6 +1515,7 @@ h3 {
select { select {
min-width: 192px; min-width: 192px;
max-width: 600px;
font-weight: 600; font-weight: 600;
border: none; border: none;
padding: 8px 12px 8px 12px; padding: 8px 12px 8px 12px;