新增蓝牙歌词支持(#615)

This commit is contained in:
lyswhut 2024-11-02 20:50:56 +08:00
parent 81ae3cb48d
commit ccebcd2fdc
27 changed files with 332 additions and 103 deletions

View File

@ -7,8 +7,10 @@ import android.content.IntentFilter;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -20,12 +22,14 @@ public class Lyric extends LyricPlayer {
LyricEvent lyricEvent = null; LyricEvent lyricEvent = null;
ReactApplicationContext reactAppContext; ReactApplicationContext reactAppContext;
boolean isShowLyric = false; boolean isRunPlayer = false;
// String lastText = "LX Music ^-^"; // String lastText = "LX Music ^-^";
int lastLine = 0; int lastLine = 0;
List lines = new ArrayList(); List lines = new ArrayList();
boolean isShowTranslation; boolean isShowTranslation;
boolean isShowRoma; boolean isShowRoma;
boolean isShowLyricView = false;
boolean isSendLyricTextEvent = false;
String lyricText = ""; String lyricText = "";
String translationText = ""; String translationText = "";
String romaLyricText = ""; String romaLyricText = "";
@ -36,6 +40,7 @@ public class Lyric extends LyricPlayer {
this.isShowRoma = isShowRoma; this.isShowRoma = isShowRoma;
this.playbackRate = playbackRate; this.playbackRate = playbackRate;
registerScreenBroadcastReceiver(); registerScreenBroadcastReceiver();
// checkA2DPConnection(reactContext);
} }
private void registerScreenBroadcastReceiver() { private void registerScreenBroadcastReceiver() {
@ -65,8 +70,40 @@ public class Lyric extends LyricPlayer {
reactAppContext.registerReceiver(screenOnOffReceiver, theFilter); reactAppContext.registerReceiver(screenOnOffReceiver, theFilter);
} }
// private void checkA2DPConnection(Context context) {
// BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
// bluetoothAdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
// @Override
// public void onServiceConnected(int profile, BluetoothProfile proxy) {
// if (profile == BluetoothProfile.A2DP) {
// List<BluetoothDevice> connectedDevices = proxy.getConnectedDevices();
// if (!connectedDevices.isEmpty()) {
// System.out.println("已连接的 A2DP 媒体设备:");
// for (BluetoothDevice device : connectedDevices) {
// System.out.println("设备名称: " + "地址: " + device.getAddress());
// }
// } else {
// System.out.println("没有连接的 A2DP 媒体设备");
// }
// }
// bluetoothAdapter.closeProfileProxy(profile, proxy);
// }
// @Override
// public void onServiceDisconnected(int profile) {
// // 服务断开时的处理
// System.out.println("蓝牙服务断开时的处理");
// }
// }, BluetoothProfile.A2DP);
// } else {
// System.out.println("蓝牙未开启或设备不支持蓝牙");
// }
// }
private void handleScreenOff() { private void handleScreenOff() {
if (!isShowLyric) return; if (!isRunPlayer || !isShowLyricView) return;
setTempPause(true); setTempPause(true);
if (lyricView != null) { if (lyricView != null) {
@ -77,30 +114,59 @@ public class Lyric extends LyricPlayer {
} }
private void handleScreenOn() { private void handleScreenOn() {
if (!isShowLyric) return; if (!isRunPlayer || !isShowLyricView) return;
if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent); if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent);
lyricView.runOnUiThread(() -> { lyricView.runOnUiThread(() -> {
lyricView.showLyricView(); lyricView.showLyricView();
setViewLyric(lastLine); handleGetCurrentLyric(lastLine);
setTempPause(false); setTempPause(false);
}); });
} }
private void setViewLyric(int lineNum) { private void pausePlayer() {
if (!isRunPlayer || isShowLyricView || isSendLyricTextEvent) return;
isRunPlayer = false;
this.pause();
}
private void setCurrentLyric(String lyric, ArrayList<String> extendedLyrics) {
if (isShowLyricView && lyricView != null) {
lyricView.setLyric(lyric, extendedLyrics);
}
if (isSendLyricTextEvent) {
WritableMap params = Arguments.createMap();
params.putString("text", lyric);
params.putArray("extendedLyrics", Arguments.makeNativeArray(extendedLyrics));
lyricEvent.sendEvent(lyricEvent.LYRIC_Line_PLAY, params);
}
}
private void handleGetCurrentLyric(int lineNum) {
lastLine = lineNum; lastLine = lineNum;
if (lyricView == null) return;
if (lineNum >= 0 && lineNum < lines.size()) { if (lineNum >= 0 && lineNum < lines.size()) {
HashMap line = (HashMap) lines.get(lineNum); HashMap line = (HashMap) lines.get(lineNum);
if (line != null) { if (line != null) {
lyricView.setLyric((String) line.get("text"), (ArrayList<String>) line.get("extendedLyrics")); setCurrentLyric((String) line.get("text"), (ArrayList<String>) line.get("extendedLyrics"));
return; return;
} }
} }
lyricView.setLyric("", new ArrayList<>(0)); setCurrentLyric("", new ArrayList<>(0));
} }
public void showLyric(Bundle options, Promise promise) { public void setSendLyricTextEvent(boolean isSend) {
if (isSendLyricTextEvent == isSend) return;
isSendLyricTextEvent = isSend;
if (isSend) {
if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext);
isRunPlayer = true;
} else {
pausePlayer();
}
}
public void showDesktopLyric(Bundle options, Promise promise) {
if (isShowLyricView) return;
if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext); if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext);
isShowLyricView = true;
if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent); if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent);
try { try {
lyricView.showLyricView(options); lyricView.showLyricView(options);
@ -109,24 +175,26 @@ public class Lyric extends LyricPlayer {
Log.e("Lyric", e.getMessage()); Log.e("Lyric", e.getMessage());
return; return;
} }
isRunPlayer = true;
isShowLyric = true;
promise.resolve(null); promise.resolve(null);
} }
public void hideLyric() { public void hideDesktopLyric() {
this.pause(); if (!isShowLyricView) return;
isShowLyricView = false;
pausePlayer();
if (lyricView != null) { if (lyricView != null) {
lyricView.destroy(); lyricView.destroy();
lyricView = null;
} }
isShowLyric = false;
} }
private void refreshLyric() { private void refreshLyric() {
if (!isRunPlayer) return;
ArrayList<String> extendedLyrics = new ArrayList<>(2); ArrayList<String> extendedLyrics = new ArrayList<>(2);
if (isShowTranslation && !"".equals(translationText)) extendedLyrics.add(translationText); if (isShowTranslation && !"".equals(translationText)) extendedLyrics.add(translationText);
if (isShowRoma && !"".equals(romaLyricText)) extendedLyrics.add(romaLyricText); if (isShowRoma && !"".equals(romaLyricText)) extendedLyrics.add(romaLyricText);
if (lyricView != null) super.setLyric(lyricText, extendedLyrics); super.setLyric(lyricText, extendedLyrics);
} }
public void setLyric(String lyric, String translation, String romaLyric) { public void setLyric(String lyric, String translation, String romaLyric) {
@ -139,7 +207,7 @@ public class Lyric extends LyricPlayer {
@Override @Override
public void onSetLyric(List lines) { public void onSetLyric(List lines) {
this.lines = lines; this.lines = lines;
setViewLyric(-1); handleGetCurrentLyric(-1);
// for (int i = 0; i < lines.size(); i++) { // for (int i = 0; i < lines.size(); i++) {
// HashMap line = (HashMap) lines.get(i); // HashMap line = (HashMap) lines.get(i);
// Log.d("Lyric", "onSetLyric: " +(String) line.get("text") + " " + line.get("extendedLyrics")); // Log.d("Lyric", "onSetLyric: " +(String) line.get("text") + " " + line.get("extendedLyrics"));
@ -148,14 +216,14 @@ public class Lyric extends LyricPlayer {
@Override @Override
public void onPlay(int lineNum) { public void onPlay(int lineNum) {
setViewLyric(lineNum); handleGetCurrentLyric(lineNum);
// Log.d("Lyric", lineNum + " " + text + " " + (String) line.get("translation")); // Log.d("Lyric", lineNum + " " + text + " " + (String) line.get("translation"));
} }
public void pauseLyric() { public void pauseLyric() {
pause(); pause();
if (!isShowLyric) return; if (!isRunPlayer) return;
if (lyricView != null) lyricView.setLyric("", new ArrayList<>(0)); handleGetCurrentLyric(-1);
} }
public void lockLyric() { public void lockLyric() {
@ -199,14 +267,22 @@ public class Lyric extends LyricPlayer {
} }
public void setPlayedColor(String unplayColor, String playedColor, String shadowColor) { public void setPlayedColor(String unplayColor, String playedColor, String shadowColor) {
if (lyricView == null) return;
lyricView.setColor(unplayColor, playedColor, shadowColor); lyricView.setColor(unplayColor, playedColor, shadowColor);
} }
public void setAlpha(float alpha) { lyricView.setAlpha(alpha); } public void setAlpha(float alpha) {
if (lyricView == null) return;
lyricView.setAlpha(alpha);
}
public void setTextSize(float size) { lyricView.setTextSize(size); } public void setTextSize(float size) {
if (lyricView == null) return;
lyricView.setTextSize(size);
}
public void setLyricTextPosition(String positionX, String positionY) { public void setLyricTextPosition(String positionX, String positionY) {
if (lyricView == null) return;
lyricView.setLyricTextPosition(positionX, positionY); lyricView.setLyricTextPosition(positionX, positionY);
} }
} }

View File

@ -1,7 +1,5 @@
package cn.toside.music.mobile.lyric; package cn.toside.music.mobile.lyric;
import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
@ -10,12 +8,13 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
public class LyricEvent { public class LyricEvent {
final String SET_VIEW_POSITION = "set-position"; final String SET_VIEW_POSITION = "set-position";
final String LYRIC_Line_PLAY = "lyric-line-play";
private final ReactApplicationContext reactContext; private final ReactApplicationContext reactContext;
LyricEvent(ReactApplicationContext reactContext) { this.reactContext = reactContext; } LyricEvent(ReactApplicationContext reactContext) { this.reactContext = reactContext; }
public void sendEvent(String eventName, @Nullable WritableMap params) { public void sendEvent(String eventName, @Nullable WritableMap params) {
Log.d("Lyric", "senEvent: " + eventName); // Log.d("Lyric", "senEvent: " + eventName);
reactContext reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params); .emit(eventName, params);

View File

@ -66,14 +66,21 @@ public class LyricModule extends ReactContextBaseJavaModule {
} }
@ReactMethod @ReactMethod
public void showLyric(ReadableMap data, Promise promise) { public void showDesktopLyric(ReadableMap data, Promise promise) {
if (lyric == null) lyric = new Lyric(reactContext, isShowTranslation, isShowRoma, playbackRate); if (lyric == null) lyric = new Lyric(reactContext, isShowTranslation, isShowRoma, playbackRate);
lyric.showLyric(Arguments.toBundle(data), promise); lyric.showDesktopLyric(Arguments.toBundle(data), promise);
} }
@ReactMethod @ReactMethod
public void hideLyric(Promise promise) { public void hideDesktopLyric(Promise promise) {
if (lyric != null) lyric.hideLyric(); if (lyric != null) lyric.hideDesktopLyric();
promise.resolve(null);
}
@ReactMethod
public void setSendLyricTextEvent(boolean isSend, Promise promise) {
if (lyric == null) lyric = new Lyric(reactContext, isShowTranslation, isShowRoma, playbackRate);
lyric.setSendLyricTextEvent(isSend);
promise.resolve(null); promise.resolve(null);
} }

12
package-lock.json generated
View File

@ -29,7 +29,7 @@
"react-native-pager-view": "6.3.0", "react-native-pager-view": "6.3.0",
"react-native-quick-base64": "^2.1.2", "react-native-quick-base64": "^2.1.2",
"react-native-quick-md5": "^3.0.6", "react-native-quick-md5": "^3.0.6",
"react-native-track-player": "github:lyswhut/react-native-track-player#409bfef00c16eb08f2aea35dfaf0b2fc40be0434", "react-native-track-player": "github:lyswhut/react-native-track-player#930681ab40fdb50f3d0eedff6ecd29b1666fd3ff",
"react-native-vector-icons": "^10.2.0" "react-native-vector-icons": "^10.2.0"
}, },
"devDependencies": { "devDependencies": {
@ -10873,8 +10873,8 @@
}, },
"node_modules/react-native-track-player": { "node_modules/react-native-track-player": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "git+ssh://git@github.com/lyswhut/react-native-track-player.git#409bfef00c16eb08f2aea35dfaf0b2fc40be0434", "resolved": "git+ssh://git@github.com/lyswhut/react-native-track-player.git#930681ab40fdb50f3d0eedff6ecd29b1666fd3ff",
"integrity": "sha512-GyFA7xRWX6eU8/0u/J/g9/RLc4h/NnnmXHNReTLwi3/lLpP+IcAZxrlRiuPdlHelr7y/bxA7pMZEntPXrRRxVw==", "integrity": "sha512-i268ePGmDQvImXWi7mRttFnyA3ReIoMY12/40g4qj1OQgkDScDi/00O0QCYfL3pHCQc36pfi40GWNLM4i02Ziw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.6", "react": ">=16.8.6",
@ -21482,9 +21482,9 @@
} }
}, },
"react-native-track-player": { "react-native-track-player": {
"version": "git+ssh://git@github.com/lyswhut/react-native-track-player.git#409bfef00c16eb08f2aea35dfaf0b2fc40be0434", "version": "git+ssh://git@github.com/lyswhut/react-native-track-player.git#930681ab40fdb50f3d0eedff6ecd29b1666fd3ff",
"integrity": "sha512-GyFA7xRWX6eU8/0u/J/g9/RLc4h/NnnmXHNReTLwi3/lLpP+IcAZxrlRiuPdlHelr7y/bxA7pMZEntPXrRRxVw==", "integrity": "sha512-i268ePGmDQvImXWi7mRttFnyA3ReIoMY12/40g4qj1OQgkDScDi/00O0QCYfL3pHCQc36pfi40GWNLM4i02Ziw==",
"from": "react-native-track-player@github:lyswhut/react-native-track-player#409bfef00c16eb08f2aea35dfaf0b2fc40be0434", "from": "react-native-track-player@github:lyswhut/react-native-track-player#930681ab40fdb50f3d0eedff6ecd29b1666fd3ff",
"requires": {} "requires": {}
}, },
"react-native-vector-icons": { "react-native-vector-icons": {

View File

@ -64,7 +64,7 @@
"react-native-pager-view": "6.3.0", "react-native-pager-view": "6.3.0",
"react-native-quick-base64": "^2.1.2", "react-native-quick-base64": "^2.1.2",
"react-native-quick-md5": "^3.0.6", "react-native-quick-md5": "^3.0.6",
"react-native-track-player": "github:lyswhut/react-native-track-player#409bfef00c16eb08f2aea35dfaf0b2fc40be0434", "react-native-track-player": "github:lyswhut/react-native-track-player#930681ab40fdb50f3d0eedff6ecd29b1666fd3ff",
"react-native-vector-icons": "^10.2.0" "react-native-vector-icons": "^10.2.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,3 +1,7 @@
### 新增
- 新增蓝牙歌词支持,可以去 设置-播放设置-显示蓝牙歌词 启用(#615
### 优化 ### 优化
- 首次使用的提示窗口可以点击背景或者返回键关闭(#577 - 首次使用的提示窗口可以点击背景或者返回键关闭(#577

View File

@ -73,6 +73,7 @@ export const storageDataPrefix = {
theme: '@theme', theme: '@theme',
cheatTip: '@cheat_tip', cheatTip: '@cheat_tip',
remoteLyricTip: '@remote_lyric_tip',
dislikeList: '@dislike_list', dislikeList: '@dislike_list',

View File

@ -31,6 +31,7 @@ const defaultSetting: LX.AppSetting = {
'player.isShowLyricRoma': false, 'player.isShowLyricRoma': false,
'player.isShowNotificationImage': true, 'player.isShowNotificationImage': true,
'player.isS2t': false, 'player.isS2t': false,
'player.isShowBluetoothLyric': false,
// 'playDetail.isZoomActiveLrc': false, // 'playDetail.isZoomActiveLrc': false,
// 'playDetail.isShowLyricProgressSetting': false, // 'playDetail.isShowLyricProgressSetting': false,

View File

@ -14,7 +14,7 @@ import { saveData } from '@/plugins/storage'
import { throttle } from '@/utils/common' import { throttle } from '@/utils/common'
import { getSelectedManagedFolder, saveFontSize, saveViewPrevState, setSelectedManagedFolder } from '@/utils/data' import { getSelectedManagedFolder, saveFontSize, saveViewPrevState, setSelectedManagedFolder } from '@/utils/data'
import { showPactModal as handleShowPactModal } from '@/navigation' import { showPactModal as handleShowPactModal } from '@/navigation'
import { hideLyric } from '@/utils/nativeModules/lyricDesktop' import { hideDesktopLyricView } from '@/utils/nativeModules/lyricDesktop'
import { getPersistedUriList, selectManagedFolder } from '@/utils/fs' import { getPersistedUriList, selectManagedFolder } from '@/utils/fs'
@ -57,7 +57,7 @@ export const exitApp = (reason: string) => {
void Promise.all([ void Promise.all([
hideDesktopLyric(), hideDesktopLyric(),
destroyPlayer(), destroyPlayer(),
hideLyric(), hideDesktopLyricView(),
]).finally(() => { ]).finally(() => {
isDestroying = false isDestroying = false
utilExitApp() utilExitApp()

View File

@ -1,6 +1,7 @@
import { import {
hideLyric, hideDesktopLyricView,
showLyric, showDesktopLyricView,
setSendLyricTextEvent,
setLyric, setLyric,
play, play,
pause, pause,
@ -25,10 +26,13 @@ import settingState from '@/store/setting/state'
import playerState from '@/store/player/state' import playerState from '@/store/player/state'
import { tranditionalize } from '@/utils/simplify-chinese-main' import { tranditionalize } from '@/utils/simplify-chinese-main'
import { getPosition } from '@/plugins/player' import { getPosition } from '@/plugins/player'
export {
onLyricLinePlay,
} from '@/utils/nativeModules/lyricDesktop'
export const showDesktopLyric = async() => { export const showDesktopLyric = async() => {
const setting = settingState.setting const setting = settingState.setting
await showLyric({ await showDesktopLyricView({
isShowToggleAnima: setting['desktopLyric.showToggleAnima'], isShowToggleAnima: setting['desktopLyric.showToggleAnima'],
isSingleLine: setting['desktopLyric.isSingleLine'], isSingleLine: setting['desktopLyric.isSingleLine'],
isLock: setting['desktopLyric.isLock'], isLock: setting['desktopLyric.isLock'],
@ -60,7 +64,7 @@ export const showDesktopLyric = async() => {
} }
export const hideDesktopLyric = async() => { export const hideDesktopLyric = async() => {
return hideLyric() return hideDesktopLyricView()
} }
export const playDesktopLyric = play export const playDesktopLyric = play
@ -91,3 +95,21 @@ export const openDesktopLyricOverlayPermissionActivity = openOverlayPermissionAc
export const onDesktopLyricPositionChange = onPositionChange export const onDesktopLyricPositionChange = onPositionChange
export const showRemoteLyric = async(isSend: boolean) => {
await setSendLyricTextEvent(isSend)
if (isSend) {
let lrc = playerState.musicInfo.lrc ?? ''
let tlrc = playerState.musicInfo.tlrc ?? ''
let rlrc = playerState.musicInfo.rlrc ?? ''
if (settingState.setting['player.isS2t']) {
lrc = tranditionalize(lrc)
tlrc = tranditionalize(tlrc)
}
await setLyric(lrc, tlrc, rlrc)
if (playerState.isPlay && !global.lx.gettingUrlId) {
void getPosition().then(position => {
void play(position * 1000)
})
}
}
}

View File

@ -8,10 +8,10 @@ import initLyric from './lyric'
export default async(setting: LX.AppSetting) => { export default async(setting: LX.AppSetting) => {
await initPlayer(setting) await initPlayer(setting)
await initLyric(setting)
await initPlayInfo(setting) await initPlayInfo(setting)
initPlayStatus() initPlayStatus()
initPlayerEvent() initPlayerEvent()
initWatchList() initWatchList()
initPlayProgress() initPlayProgress()
await initLyric(setting)
} }

View File

@ -1,24 +1,51 @@
import { init as initLyricPlayer, toggleTranslation, toggleRoma, play, pause, stop, setLyric, setPlaybackRate } from '@/core/lyric' import { init as initLyricPlayer, toggleTranslation, toggleRoma, play, pause, stop, setLyric, setPlaybackRate } from '@/core/lyric'
import { updateSetting } from '@/core/common' import { updateSetting } from '@/core/common'
import { onDesktopLyricPositionChange, showDesktopLyric } from '@/core/desktopLyric' import { onDesktopLyricPositionChange, showDesktopLyric, onLyricLinePlay, showRemoteLyric } from '@/core/desktopLyric'
import playerState from '@/store/player/state'
import { updateNowPlayingTitles } from '@/plugins/player/utils'
import { setLastLyric } from '@/core/player/playInfo'
import { state } from '@/plugins/player/playList'
const updateRemoteLyric = async(lrc?: string) => {
setLastLyric(lrc)
if (lrc == null) {
void updateNowPlayingTitles((state.prevDuration || 0) * 1000, playerState.musicInfo.name, playerState.musicInfo.singer ?? '', playerState.musicInfo.album ?? '')
} else {
void updateNowPlayingTitles((state.prevDuration || 0) * 1000, lrc, `${playerState.musicInfo.name}${playerState.musicInfo.singer ? ` - ${playerState.musicInfo.singer}` : ''}`, playerState.musicInfo.album ?? '')
}
}
export default async(setting: LX.AppSetting) => { export default async(setting: LX.AppSetting) => {
await initLyricPlayer() await initLyricPlayer()
void setPlaybackRate(setting['player.playbackRate']) await Promise.all([
void toggleTranslation(setting['player.isShowLyricTranslation']) setPlaybackRate(setting['player.playbackRate']),
void toggleRoma(setting['player.isShowLyricRoma']) toggleTranslation(setting['player.isShowLyricTranslation']),
toggleRoma(setting['player.isShowLyricRoma']),
])
if (setting['desktopLyric.enable']) { if (setting['desktopLyric.enable']) {
showDesktopLyric().catch(() => { showDesktopLyric().catch(() => {
updateSetting({ 'desktopLyric.enable': false }) updateSetting({ 'desktopLyric.enable': false })
}) })
} }
if (setting['player.isShowBluetoothLyric']) {
showRemoteLyric(true).catch(() => {
updateSetting({ 'player.isShowBluetoothLyric': false })
})
}
onDesktopLyricPositionChange(position => { onDesktopLyricPositionChange(position => {
updateSetting({ updateSetting({
'desktopLyric.position.x': position.x, 'desktopLyric.position.x': position.x,
'desktopLyric.position.y': position.y, 'desktopLyric.position.y': position.y,
}) })
}) })
onLyricLinePlay(({ text, extendedLyrics }) => {
if (!text && !state.isPlaying) {
void updateRemoteLyric()
} else {
void updateRemoteLyric(text)
}
})
global.app_event.on('play', play) global.app_event.on('play', play)

View File

@ -19,7 +19,7 @@ export default () => {
const setButtons = () => { const setButtons = () => {
// setPlayerAction(buttons) // setPlayerAction(buttons)
if (!playerState.playMusicInfo.musicInfo) return if (!playerState.playMusicInfo.musicInfo) return
void updateMetaData(playerState.musicInfo, playerState.isPlay) void updateMetaData(playerState.musicInfo, playerState.isPlay, playerState.lastLyric)
} }
// const updateCollectStatus = async() => { // const updateCollectStatus = async() => {
// // let status = !!playMusicInfo.musicInfo && await checkListExistMusic(LIST_ID_LOVE, playerState.playMusicInfo.musicInfo.id) // // let status = !!playMusicInfo.musicInfo && await checkListExistMusic(LIST_ID_LOVE, playerState.playMusicInfo.musicInfo.id)

View File

@ -40,7 +40,7 @@ export default async(setting: LX.AppSetting) => {
const updatePic = () => { const updatePic = () => {
if (!settingState.setting['player.isShowNotificationImage']) return if (!settingState.setting['player.isShowNotificationImage']) return
if (playerState.playMusicInfo.musicInfo && playerState.musicInfo.pic) { if (playerState.playMusicInfo.musicInfo && playerState.musicInfo.pic) {
delayUpdateMusicInfo(playerState.musicInfo) delayUpdateMusicInfo(playerState.musicInfo, playerState.lastLyric)
} }
} }

View File

@ -14,6 +14,10 @@ export const setLoadErrorPicUrl = (url: string) => {
playerActions.setLoadErrorPicUrl(url) playerActions.setLoadErrorPicUrl(url)
} }
export const setLastLyric = (lrc?: string) => {
playerActions.setLastLyric(lrc)
}
export const setPlayListId = (listId: string | null) => { export const setPlayListId = (listId: string | null) => {
playerActions.setPlayListId(listId) playerActions.setPlayListId(listId)
} }

View File

@ -346,6 +346,7 @@
"setting_play_play_quality": "Prioritize playback sound quality (if supported)", "setting_play_play_quality": "Prioritize playback sound quality (if supported)",
"setting_play_s2t": "Convert the played lyrics to Traditional Chinese", "setting_play_s2t": "Convert the played lyrics to Traditional Chinese",
"setting_play_save_play_time": "Remember playback progress", "setting_play_save_play_time": "Remember playback progress",
"setting_play_show_bluetooth_lyric": "Show bluetooth lyrics",
"setting_play_show_notification_image": "Show song picture in notification bar", "setting_play_show_notification_image": "Show song picture in notification bar",
"setting_play_show_roma": "Show lyrics roman (if available)", "setting_play_show_roma": "Show lyrics roman (if available)",
"setting_play_show_translation": "Show lyrics translation (if available)", "setting_play_show_translation": "Show lyrics translation (if available)",

View File

@ -346,6 +346,7 @@
"setting_play_play_quality": "优先播放的音质(如果支持)", "setting_play_play_quality": "优先播放的音质(如果支持)",
"setting_play_s2t": "将播放的歌词转繁体", "setting_play_s2t": "将播放的歌词转繁体",
"setting_play_save_play_time": "记住播放进度", "setting_play_save_play_time": "记住播放进度",
"setting_play_show_bluetooth_lyric": "显示蓝牙歌词",
"setting_play_show_notification_image": "在通知栏显示歌曲图片", "setting_play_show_notification_image": "在通知栏显示歌曲图片",
"setting_play_show_roma": "显示歌词罗马音(如果可用)", "setting_play_show_roma": "显示歌词罗马音(如果可用)",
"setting_play_show_translation": "显示歌词翻译(如果可用)", "setting_play_show_translation": "显示歌词翻译(如果可用)",

View File

@ -10,8 +10,10 @@ const list: LX.Player.Track[] = []
const defaultUserAgent = 'Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Mobile Safari/537.36' const defaultUserAgent = 'Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Mobile Safari/537.36'
const httpRxp = /^(https?:\/\/.+|\/.+)/ const httpRxp = /^(https?:\/\/.+|\/.+)/
let isPlaying = false export const state = {
let prevDuration = -1 isPlaying: false,
prevDuration: -1,
}
const formatMusicInfo = (musicInfo: LX.Player.PlayMusic) => { const formatMusicInfo = (musicInfo: LX.Player.PlayMusic) => {
return 'progress' in musicInfo ? { return 'progress' in musicInfo ? {
@ -106,22 +108,21 @@ export const getCurrentTrack = async() => {
return list[currentTrackIndex] return list[currentTrackIndex]
} }
export const updateMetaData = async(musicInfo: LX.Player.MusicInfo, isPlay: boolean, force = false) => { export const updateMetaData = async(musicInfo: LX.Player.MusicInfo, isPlay: boolean, lyric?: string, force = false) => {
if (!force && isPlay == isPlaying) { if (!force && isPlay == state.isPlaying) {
const duration = await TrackPlayer.getDuration() const duration = await TrackPlayer.getDuration()
// console.log('currentIsPlaying', prevDuration, duration) if (state.prevDuration != duration) {
if (prevDuration != duration) { state.prevDuration = duration
prevDuration = duration
const trackInfo = await getCurrentTrack() const trackInfo = await getCurrentTrack()
if (trackInfo && musicInfo) { if (trackInfo && musicInfo) {
delayUpdateMusicInfo(musicInfo) delayUpdateMusicInfo(musicInfo, lyric)
} }
} }
} else { } else {
const [duration, trackInfo] = await Promise.all([TrackPlayer.getDuration(), getCurrentTrack()]) const [duration, trackInfo] = await Promise.all([TrackPlayer.getDuration(), getCurrentTrack()])
prevDuration = duration state.prevDuration = duration
if (trackInfo && musicInfo) { if (trackInfo && musicInfo) {
delayUpdateMusicInfo(musicInfo) delayUpdateMusicInfo(musicInfo, lyric)
} }
} }
} }
@ -175,7 +176,8 @@ export const playMusic = (musicInfo: LX.Player.PlayMusic, url: string, time: num
// let musicId = null // let musicId = null
// let duration = 0 // let duration = 0
let prevArtwork: string | undefined let prevArtwork: string | undefined
const updateMetaInfo = async(mInfo: LX.Player.MusicInfo) => { const updateMetaInfo = async(mInfo: LX.Player.MusicInfo, lyric?: string) => {
console.log('updateMetaInfo', lyric)
const isShowNotificationImage = settingState.setting['player.isShowNotificationImage'] const isShowNotificationImage = settingState.setting['player.isShowNotificationImage']
// const mInfo = formatMusicInfo(musicInfo) // const mInfo = formatMusicInfo(musicInfo)
// console.log('+++++updateMusicPic+++++', track.artwork, track.duration) // console.log('+++++updateMusicPic+++++', track.artwork, track.duration)
@ -189,16 +191,25 @@ const updateMetaInfo = async(mInfo: LX.Player.MusicInfo) => {
// duration = global.playInfo.duration || 0 // duration = global.playInfo.duration || 0
// } // }
// console.log('+++++updateMetaInfo+++++', mInfo.name) // console.log('+++++updateMetaInfo+++++', mInfo.name)
isPlaying = await TrackPlayer.getState() == State.Playing state.isPlaying = await TrackPlayer.getState() == State.Playing
let artwork = isShowNotificationImage ? mInfo.pic ?? prevArtwork : undefined let artwork = isShowNotificationImage ? mInfo.pic ?? prevArtwork : undefined
if (mInfo.pic) prevArtwork = mInfo.pic if (mInfo.pic) prevArtwork = mInfo.pic
let name: string
let singer: string
if (!state.isPlaying || lyric == null) {
name = mInfo.name ?? 'Unknow'
singer = mInfo.singer ?? 'Unknow'
} else {
name = lyric
singer = `${mInfo.name}${mInfo.singer ? ` - ${mInfo.singer}` : ''}`
}
await TrackPlayer.updateNowPlayingMetadata({ await TrackPlayer.updateNowPlayingMetadata({
title: mInfo.name ?? 'Unknow', title: name,
artist: mInfo.singer ?? 'Unknow', artist: singer,
album: mInfo.album ?? undefined, album: mInfo.album ?? undefined,
artwork, artwork,
duration: prevDuration || 0, duration: state.prevDuration || 0,
}, isPlaying) }, state.isPlaying)
} }
@ -206,12 +217,13 @@ const updateMetaInfo = async(mInfo: LX.Player.MusicInfo) => {
const debounceUpdateMetaInfoTools = { const debounceUpdateMetaInfoTools = {
updateMetaPromise: Promise.resolve(), updateMetaPromise: Promise.resolve(),
musicInfo: null as LX.Player.MusicInfo | null, musicInfo: null as LX.Player.MusicInfo | null,
debounce(fn: (musicInfo: LX.Player.MusicInfo) => void | Promise<void>) { debounce(fn: (musicInfo: LX.Player.MusicInfo, lyric?: string) => void | Promise<void>) {
// let delayTimer = null // let delayTimer = null
let isDelayRun = false let isDelayRun = false
let timer: number | null = null let timer: number | null = null
let _musicInfo: LX.Player.MusicInfo | null = null let _musicInfo: LX.Player.MusicInfo | null = null
return (musicInfo: LX.Player.MusicInfo) => { let _lyric: string | undefined
return (musicInfo: LX.Player.MusicInfo, lyric?: string) => {
// console.log('debounceUpdateMetaInfoTools', musicInfo) // console.log('debounceUpdateMetaInfoTools', musicInfo)
if (timer) { if (timer) {
BackgroundTimer.clearTimeout(timer) BackgroundTimer.clearTimeout(timer)
@ -223,31 +235,34 @@ const debounceUpdateMetaInfoTools = {
// } // }
if (isDelayRun) { if (isDelayRun) {
_musicInfo = musicInfo _musicInfo = musicInfo
_lyric = lyric
timer = BackgroundTimer.setTimeout(() => { timer = BackgroundTimer.setTimeout(() => {
timer = null timer = null
let musicInfo = _musicInfo let musicInfo = _musicInfo
let lyric = _lyric
_musicInfo = null _musicInfo = null
_lyric = undefined
if (!musicInfo) return if (!musicInfo) return
// isDelayRun = false // isDelayRun = false
void fn(musicInfo) void fn(musicInfo, lyric)
}, 1000) }, 500)
} else { } else {
isDelayRun = true isDelayRun = true
void fn(musicInfo) void fn(musicInfo, lyric)
BackgroundTimer.setTimeout(() => { BackgroundTimer.setTimeout(() => {
// delayTimer = null // delayTimer = null
isDelayRun = false isDelayRun = false
}, 1000) }, 500)
} }
} }
}, },
init() { init() {
return this.debounce(async(musicInfo: LX.Player.MusicInfo) => { return this.debounce(async(musicInfo: LX.Player.MusicInfo, lyric?: string) => {
this.musicInfo = musicInfo this.musicInfo = musicInfo
return this.updateMetaPromise.then(() => { return this.updateMetaPromise.then(() => {
// console.log('run') // console.log('run')
if (this.musicInfo?.id === musicInfo.id) { if (this.musicInfo?.id === musicInfo.id) {
this.updateMetaPromise = updateMetaInfo(musicInfo) this.updateMetaPromise = updateMetaInfo(musicInfo, lyric)
} }
}) })
}) })

View File

@ -166,6 +166,10 @@ export const setPause = async() => TrackPlayer.pause()
export const setCurrentTime = async(time: number) => TrackPlayer.seekTo(time) export const setCurrentTime = async(time: number) => TrackPlayer.seekTo(time)
export const setVolume = async(num: number) => TrackPlayer.setVolume(num) export const setVolume = async(num: number) => TrackPlayer.setVolume(num)
export const setPlaybackRate = async(num: number) => TrackPlayer.setRate(num) export const setPlaybackRate = async(num: number) => TrackPlayer.setRate(num)
export const updateNowPlayingTitles = async(duration: number, title: string, artist: string, album: string) => {
console.log('set playing titles', duration, title, artist, album)
return TrackPlayer.updateNowPlayingTitles(duration, title, artist, album)
}
export const resetPlay = async() => Promise.all([setPause(), setCurrentTime(0)]) export const resetPlay = async() => Promise.all([setPause(), setCurrentTime(0)])

View File

@ -0,0 +1,44 @@
import { updateSetting } from '@/core/common'
import { useI18n } from '@/lang'
import { createStyle, remoteLyricTip } from '@/utils/tools'
import { memo } from 'react'
import { View } from 'react-native'
import { useSettingValue } from '@/store/setting/hook'
import CheckBoxItem from '../../components/CheckBoxItem'
import { showRemoteLyric } from '@/core/desktopLyric'
import { setLastLyric } from '@/core/player/playInfo'
import { updateNowPlayingTitles } from '@/plugins/player/utils'
import playerState from '@/store/player/state'
import { state } from '@/plugins/player/playList'
export default memo(() => {
const t = useI18n()
const isShowBluetoothLyric = useSettingValue('player.isShowBluetoothLyric')
const setShowBluetoothLyric = async(isShowBluetoothLyric: boolean) => {
if (isShowBluetoothLyric) {
await remoteLyricTip()
}
updateSetting({ 'player.isShowBluetoothLyric': isShowBluetoothLyric })
void showRemoteLyric(isShowBluetoothLyric)
if (!isShowBluetoothLyric) {
setLastLyric()
void updateNowPlayingTitles((state.prevDuration || 0) * 1000, playerState.musicInfo.name, playerState.musicInfo.singer ?? '', playerState.musicInfo.album ?? '')
}
}
return (
<View style={styles.content}>
<CheckBoxItem check={isShowBluetoothLyric} onChange={setShowBluetoothLyric} label={t('setting_play_show_bluetooth_lyric')} />
</View>
)
})
const styles = createStyle({
content: {
marginTop: 5,
},
})

View File

@ -6,6 +6,7 @@ import PlayHighQuality from './PlayHighQuality'
import IsHandleAudioFocus from './IsHandleAudioFocus' import IsHandleAudioFocus from './IsHandleAudioFocus'
import IsEnableAudioOffload from './IsEnableAudioOffload' import IsEnableAudioOffload from './IsEnableAudioOffload'
import IsAutoCleanPlayedList from './IsAutoCleanPlayedList' import IsAutoCleanPlayedList from './IsAutoCleanPlayedList'
import IsShowBluetoothLyric from './IsShowBluetoothLyric'
import IsShowNotificationImage from './IsShowNotificationImage' import IsShowNotificationImage from './IsShowNotificationImage'
import IsShowLyricTranslation from './IsShowLyricTranslation' import IsShowLyricTranslation from './IsShowLyricTranslation'
import IsShowLyricRoma from './IsShowLyricRoma' import IsShowLyricRoma from './IsShowLyricRoma'
@ -23,6 +24,7 @@ export default memo(() => {
<IsAutoCleanPlayedList /> <IsAutoCleanPlayedList />
<IsHandleAudioFocus /> <IsHandleAudioFocus />
<IsEnableAudioOffload /> <IsEnableAudioOffload />
<IsShowBluetoothLyric />
<IsShowNotificationImage /> <IsShowNotificationImage />
<IsShowLyricTranslation /> <IsShowLyricTranslation />
<IsShowLyricRoma /> <IsShowLyricRoma />

View File

@ -37,7 +37,7 @@ export default () => {
value = Math.trunc(value) value = Math.trunc(value)
const rate = value / 100 const rate = value / 100
void setLyricPlaybackRate(rate) void setLyricPlaybackRate(rate)
void updateMetaData(playerState.musicInfo, playerState.isPlay, true) // 更新通知栏的播放速率 void updateMetaData(playerState.musicInfo, playerState.isPlay, playerState.lastLyric, true) // 更新通知栏的播放速率
if (playbackRate == value) return if (playbackRate == value) return
updateSetting({ 'player.playbackRate': rate }) updateSetting({ 'player.playbackRate': rate })
} }
@ -45,7 +45,7 @@ export default () => {
if (settingState.setting['player.playbackRate'] == 1) return if (settingState.setting['player.playbackRate'] == 1) return
setSliderSize(100) setSliderSize(100)
void setPlaybackRate(1).then(() => { void setPlaybackRate(1).then(() => {
void updateMetaData(playerState.musicInfo, playerState.isPlay, true) // 更新通知栏的播放速率 void updateMetaData(playerState.musicInfo, playerState.isPlay, playerState.lastLyric, true) // 更新通知栏的播放速率
void setLyricPlaybackRate(1) void setLyricPlaybackRate(1)
}) })
updateSetting({ 'player.playbackRate': 1 }) updateSetting({ 'player.playbackRate': 1 })

View File

@ -107,4 +107,7 @@ export default {
setLoadErrorPicUrl(url: string) { setLoadErrorPicUrl(url: string) {
state.loadErrorPicUrl = url state.loadErrorPicUrl = url
}, },
setLastLyric(lrc?: string) {
state.lastLyric = lrc
},
} }

View File

@ -34,6 +34,8 @@ export interface InitState {
nowPlayTimeStr: string nowPlayTimeStr: string
maxPlayTimeStr: string maxPlayTimeStr: string
} }
lastLyric: string | undefined
} }
const state: InitState = { const state: InitState = {
@ -77,6 +79,8 @@ const state: InitState = {
nowPlayTimeStr: '00:00', nowPlayTimeStr: '00:00',
maxPlayTimeStr: '00:00', maxPlayTimeStr: '00:00',
}, },
lastLyric: undefined,
} }

View File

@ -186,6 +186,11 @@ declare global {
*/ */
'player.isS2t': boolean 'player.isS2t': boolean
/**
*
*/
'player.isShowBluetoothLyric': boolean
/** /**
* - * -
*/ */

View File

@ -2,8 +2,6 @@ import { NativeModules, NativeEventEmitter } from 'react-native'
const { LyricModule } = NativeModules const { LyricModule } = NativeModules
let isShowLyric = false
// export const themes = [ // export const themes = [
// { id: 'green', value: '#07c556' }, // { id: 'green', value: '#07c556' },
// { id: 'yellow', value: '#fffa12' }, // { id: 'yellow', value: '#fffa12' },
@ -34,11 +32,19 @@ let isShowLyric = false
const getAlpha = (num: number) => num / 100 const getAlpha = (num: number) => num / 100
const getTextSize = (num: number) => num / 10 const getTextSize = (num: number) => num / 10
/**
*
* @param isShow
* @returns
*/
export const setSendLyricTextEvent = async(isSend: boolean) => {
return LyricModule.setSendLyricTextEvent(isSend)
}
/** /**
* show lyric * show lyric
*/ */
export const showLyric = async({ export const showDesktopLyricView = async({
isShowToggleAnima, isShowToggleAnima,
isSingleLine, isSingleLine,
width, width,
@ -69,8 +75,7 @@ export const showLyric = async({
textPositionX: LX.AppSetting['desktopLyric.textPosition.x'] textPositionX: LX.AppSetting['desktopLyric.textPosition.x']
textPositionY: LX.AppSetting['desktopLyric.textPosition.y'] textPositionY: LX.AppSetting['desktopLyric.textPosition.y']
}): Promise<void> => { }): Promise<void> => {
if (isShowLyric) return Promise.resolve() return LyricModule.showDesktopLyric({
return LyricModule.showLyric({
isSingleLine, isSingleLine,
isShowToggleAnima, isShowToggleAnima,
isLock, isLock,
@ -85,19 +90,14 @@ export const showLyric = async({
textY: textPositionY.toUpperCase(), textY: textPositionY.toUpperCase(),
width, width,
maxLineNum, maxLineNum,
}).then(() => {
isShowLyric = true
}) })
} }
/** /**
* hide lyric * hide lyric
*/ */
export const hideLyric = async(): Promise<void> => { export const hideDesktopLyricView = async(): Promise<void> => {
if (!isShowLyric) return Promise.resolve() return LyricModule.hideDesktopLyric()
return LyricModule.hideLyric().then(() => {
isShowLyric = false
})
} }
@ -107,7 +107,6 @@ export const hideLyric = async(): Promise<void> => {
* @returns {Promise} Promise * @returns {Promise} Promise
*/ */
export const play = async(time: number): Promise<void> => { export const play = async(time: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.play(time) return LyricModule.play(time)
} }
@ -115,7 +114,6 @@ export const play = async(time: number): Promise<void> => {
* pause lyric * pause lyric
*/ */
export const pause = async(): Promise<void> => { export const pause = async(): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.pause() return LyricModule.pause()
} }
@ -126,12 +124,10 @@ export const pause = async(): Promise<void> => {
* @param romalrc lyric translation * @param romalrc lyric translation
*/ */
export const setLyric = async(lyric: string, translation: string, romalrc: string): Promise<void> => { export const setLyric = async(lyric: string, translation: string, romalrc: string): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setLyric(lyric, translation || '', romalrc || '') return LyricModule.setLyric(lyric, translation || '', romalrc || '')
} }
export const setPlaybackRate = async(rate: number): Promise<void> => { export const setPlaybackRate = async(rate: number): Promise<void> => {
// if (!isShowLyric) return Promise.resolve()
return LyricModule.setPlaybackRate(rate) return LyricModule.setPlaybackRate(rate)
} }
@ -140,7 +136,6 @@ export const setPlaybackRate = async(rate: number): Promise<void> => {
* @param isShowTranslation is show translation * @param isShowTranslation is show translation
*/ */
export const toggleTranslation = async(isShowTranslation: boolean): Promise<void> => { export const toggleTranslation = async(isShowTranslation: boolean): Promise<void> => {
// if (!isShowLyric) return Promise.resolve()
return LyricModule.toggleTranslation(isShowTranslation) return LyricModule.toggleTranslation(isShowTranslation)
} }
@ -149,7 +144,6 @@ export const toggleTranslation = async(isShowTranslation: boolean): Promise<void
* @param isShowRoma is show roma lyric * @param isShowRoma is show roma lyric
*/ */
export const toggleRoma = async(isShowRoma: boolean): Promise<void> => { export const toggleRoma = async(isShowRoma: boolean): Promise<void> => {
// if (!isShowLyric) return Promise.resolve()
return LyricModule.toggleRoma(isShowRoma) return LyricModule.toggleRoma(isShowRoma)
} }
@ -158,7 +152,6 @@ export const toggleRoma = async(isShowRoma: boolean): Promise<void> => {
* @param isLock is lock lyric window * @param isLock is lock lyric window
*/ */
export const toggleLock = async(isLock: boolean): Promise<void> => { export const toggleLock = async(isLock: boolean): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.toggleLock(isLock) return LyricModule.toggleLock(isLock)
} }
@ -169,7 +162,6 @@ export const toggleLock = async(isLock: boolean): Promise<void> => {
* @param shadowColor * @param shadowColor
*/ */
export const setColor = async(unplayColor: string, playedColor: string, shadowColor: string): Promise<void> => { export const setColor = async(unplayColor: string, playedColor: string, shadowColor: string): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setColor(unplayColor, playedColor, shadowColor) return LyricModule.setColor(unplayColor, playedColor, shadowColor)
} }
@ -178,7 +170,6 @@ export const setColor = async(unplayColor: string, playedColor: string, shadowCo
* @param alpha text alpha * @param alpha text alpha
*/ */
export const setAlpha = async(alpha: number): Promise<void> => { export const setAlpha = async(alpha: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setAlpha(getAlpha(alpha)) return LyricModule.setAlpha(getAlpha(alpha))
} }
@ -187,42 +178,34 @@ export const setAlpha = async(alpha: number): Promise<void> => {
* @param size text size * @param size text size
*/ */
export const setTextSize = async(size: number): Promise<void> => { export const setTextSize = async(size: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setTextSize(getTextSize(size)) return LyricModule.setTextSize(getTextSize(size))
} }
export const setShowToggleAnima = async(isShowToggleAnima: boolean): Promise<void> => { export const setShowToggleAnima = async(isShowToggleAnima: boolean): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setShowToggleAnima(isShowToggleAnima) return LyricModule.setShowToggleAnima(isShowToggleAnima)
} }
export const setSingleLine = async(isSingleLine: boolean): Promise<void> => { export const setSingleLine = async(isSingleLine: boolean): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setSingleLine(isSingleLine) return LyricModule.setSingleLine(isSingleLine)
} }
export const setPosition = async(x: number, y: number): Promise<void> => { export const setPosition = async(x: number, y: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setPosition(x, y) return LyricModule.setPosition(x, y)
} }
export const setMaxLineNum = async(maxLineNum: number): Promise<void> => { export const setMaxLineNum = async(maxLineNum: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setMaxLineNum(maxLineNum) return LyricModule.setMaxLineNum(maxLineNum)
} }
export const setWidth = async(width: number): Promise<void> => { export const setWidth = async(width: number): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setWidth(width) return LyricModule.setWidth(width)
} }
// export const fixViewPosition = async(): Promise<void> => { // export const fixViewPosition = async(): Promise<void> => {
// if (!isShowLyric) return Promise.resolve()
// return LyricModule.fixViewPosition() // return LyricModule.fixViewPosition()
// } // }
export const setLyricTextPosition = async(textX: LX.AppSetting['desktopLyric.textPosition.x'], textY: LX.AppSetting['desktopLyric.textPosition.y']): Promise<void> => { export const setLyricTextPosition = async(textX: LX.AppSetting['desktopLyric.textPosition.x'], textY: LX.AppSetting['desktopLyric.textPosition.y']): Promise<void> => {
if (!isShowLyric) return Promise.resolve()
return LyricModule.setLyricTextPosition(textX.toUpperCase(), textY.toUpperCase()) return LyricModule.setLyricTextPosition(textX.toUpperCase(), textY.toUpperCase())
} }
@ -246,3 +229,15 @@ export const onPositionChange = (handler: (position: { x: number, y: number }) =
} }
} }
export const onLyricLinePlay = (handler: (lineInfo: { text: string, extendedLyrics: string[] }) => void): () => void => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const eventEmitter = new NativeEventEmitter(LyricModule)
const eventListener = eventEmitter.addListener('lyric-line-play', event => {
handler(event as { text: string, extendedLyrics: string[] })
})
return () => {
eventListener.remove()
}
}

View File

@ -559,3 +559,17 @@ export const cheatTip = async() => {
void saveData(storageDataPrefix.cheatTip, true) void saveData(storageDataPrefix.cheatTip, true)
}) })
} }
export const remoteLyricTip = async() => {
const isRead = await getData<boolean>(storageDataPrefix.remoteLyricTip)
if (isRead) return
return tipDialog({
title: '有点温馨的提示',
message: '若你将本功能用于汽车,请记住这个:\n道路千万条安全第一条\n道路千万条安全第一条\n道路千万条安全第一条',
btnText: '我知道了 (Close)',
bgClose: true,
}).then(() => {
void saveData(storageDataPrefix.remoteLyricTip, true)
})
}