mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
🐞 fix: 优化播放处理
This commit is contained in:
@@ -30,6 +30,10 @@
|
|||||||
|
|
||||||
- 欢迎各位大佬 `Star` 😍
|
- 欢迎各位大佬 `Star` 😍
|
||||||
|
|
||||||
|
## 💬 交流群
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 👀 Demo
|
## 👀 Demo
|
||||||
|
|
||||||
- [SPlayer](https://music.imsyy.top/)
|
- [SPlayer](https://music.imsyy.top/)
|
||||||
|
|||||||
BIN
screenshots/welcome.png
Normal file
BIN
screenshots/welcome.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -105,59 +105,51 @@ class LyricManager {
|
|||||||
const isStale = () => this.activeLyricReq !== req || musicStore.playSong?.id !== id;
|
const isStale = () => this.activeLyricReq !== req || musicStore.playSong?.id !== id;
|
||||||
// 处理 TTML 歌词
|
// 处理 TTML 歌词
|
||||||
const adoptTTML = async () => {
|
const adoptTTML = async () => {
|
||||||
try {
|
if (!settingStore.enableTTMLLyric) return;
|
||||||
if (!settingStore.enableTTMLLyric) return;
|
const ttmlContent = await songLyricTTML(id);
|
||||||
const ttmlContent = await songLyricTTML(id);
|
if (isStale()) return;
|
||||||
if (isStale()) return;
|
if (!ttmlContent || typeof ttmlContent !== "string") return;
|
||||||
if (!ttmlContent || typeof ttmlContent !== "string") return;
|
const parsed = parseTTML(ttmlContent);
|
||||||
const parsed = parseTTML(ttmlContent);
|
const lines = parsed?.lines || [];
|
||||||
const lines = parsed?.lines || [];
|
if (!lines.length) return;
|
||||||
if (!lines.length) return;
|
result.yrcData = lines;
|
||||||
result.yrcData = lines;
|
ttmlAdopted = true;
|
||||||
ttmlAdopted = true;
|
|
||||||
} catch (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// 处理 LRC 歌词
|
// 处理 LRC 歌词
|
||||||
const adoptLRC = async () => {
|
const adoptLRC = async () => {
|
||||||
try {
|
const data = await songLyric(id);
|
||||||
const data = await songLyric(id);
|
if (isStale()) return;
|
||||||
if (isStale()) return;
|
if (!data || data.code !== 200) return;
|
||||||
if (!data || data.code !== 200) return;
|
let lrcLines: LyricLine[] = [];
|
||||||
let lrcLines: LyricLine[] = [];
|
let yrcLines: LyricLine[] = [];
|
||||||
let yrcLines: LyricLine[] = [];
|
// 普通歌词
|
||||||
// 普通歌词
|
if (data?.lrc?.lyric) {
|
||||||
if (data?.lrc?.lyric) {
|
lrcLines = parseLrc(data.lrc.lyric) || [];
|
||||||
lrcLines = parseLrc(data.lrc.lyric) || [];
|
// 普通歌词翻译
|
||||||
// 普通歌词翻译
|
if (data?.tlyric?.lyric)
|
||||||
if (data?.tlyric?.lyric)
|
lrcLines = this.alignLyrics(lrcLines, parseLrc(data.tlyric.lyric), "translatedLyric");
|
||||||
lrcLines = this.alignLyrics(lrcLines, parseLrc(data.tlyric.lyric), "translatedLyric");
|
// 普通歌词音译
|
||||||
// 普通歌词音译
|
if (data?.romalrc?.lyric)
|
||||||
if (data?.romalrc?.lyric)
|
lrcLines = this.alignLyrics(lrcLines, parseLrc(data.romalrc.lyric), "romanLyric");
|
||||||
lrcLines = this.alignLyrics(lrcLines, parseLrc(data.romalrc.lyric), "romanLyric");
|
|
||||||
}
|
|
||||||
// 逐字歌词
|
|
||||||
if (data?.yrc?.lyric) {
|
|
||||||
yrcLines = parseYrc(data.yrc.lyric) || [];
|
|
||||||
// 逐字歌词翻译
|
|
||||||
if (data?.ytlrc?.lyric)
|
|
||||||
yrcLines = this.alignLyrics(yrcLines, parseLrc(data.ytlrc.lyric), "translatedLyric");
|
|
||||||
// 逐字歌词音译
|
|
||||||
if (data?.yromalrc?.lyric)
|
|
||||||
yrcLines = this.alignLyrics(yrcLines, parseLrc(data.yromalrc.lyric), "romanLyric");
|
|
||||||
}
|
|
||||||
if (lrcLines.length) result.lrcData = lrcLines;
|
|
||||||
// 如果没有 TTML,则采用 网易云 YRC
|
|
||||||
if (!result.yrcData.length && yrcLines.length) {
|
|
||||||
result.yrcData = yrcLines;
|
|
||||||
}
|
|
||||||
// 先返回一次,避免 TTML 请求过慢
|
|
||||||
const lyricData = this.handleLyricExclude(result);
|
|
||||||
this.setFinalLyric(lyricData, req);
|
|
||||||
} catch (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
|
// 逐字歌词
|
||||||
|
if (data?.yrc?.lyric) {
|
||||||
|
yrcLines = parseYrc(data.yrc.lyric) || [];
|
||||||
|
// 逐字歌词翻译
|
||||||
|
if (data?.ytlrc?.lyric)
|
||||||
|
yrcLines = this.alignLyrics(yrcLines, parseLrc(data.ytlrc.lyric), "translatedLyric");
|
||||||
|
// 逐字歌词音译
|
||||||
|
if (data?.yromalrc?.lyric)
|
||||||
|
yrcLines = this.alignLyrics(yrcLines, parseLrc(data.yromalrc.lyric), "romanLyric");
|
||||||
|
}
|
||||||
|
if (lrcLines.length) result.lrcData = lrcLines;
|
||||||
|
// 如果没有 TTML,则采用 网易云 YRC
|
||||||
|
if (!result.yrcData.length && yrcLines.length) {
|
||||||
|
result.yrcData = yrcLines;
|
||||||
|
}
|
||||||
|
// 先返回一次,避免 TTML 请求过慢
|
||||||
|
const lyricData = this.handleLyricExclude(result);
|
||||||
|
this.setFinalLyric(lyricData, req);
|
||||||
};
|
};
|
||||||
// 设置 TTML
|
// 设置 TTML
|
||||||
await Promise.allSettled([adoptTTML(), adoptLRC()]);
|
await Promise.allSettled([adoptTTML(), adoptLRC()]);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { MessageReactive } from "naive-ui";
|
|||||||
import { Howl, Howler } from "howler";
|
import { Howl, Howler } from "howler";
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores";
|
import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores";
|
||||||
|
import { useIntervalFn } from "@vueuse/core";
|
||||||
import { calculateProgress } from "./time";
|
import { calculateProgress } from "./time";
|
||||||
import { shuffleArray, runIdle } from "./helper";
|
import { shuffleArray, runIdle } from "./helper";
|
||||||
import { heartRateList } from "@/api/playlist";
|
import { heartRateList } from "@/api/playlist";
|
||||||
@@ -23,8 +24,11 @@ import audioContextManager from "@/utils/player-utils/context";
|
|||||||
import lyricManager from "./lyricManager";
|
import lyricManager from "./lyricManager";
|
||||||
import blob from "./blob";
|
import blob from "./blob";
|
||||||
|
|
||||||
// 播放器核心
|
/**
|
||||||
// Howler.js
|
* 播放器核心
|
||||||
|
* Howler.js 音频库
|
||||||
|
*/
|
||||||
|
let _player: Player | null = null;
|
||||||
|
|
||||||
/* *允许播放格式 */
|
/* *允许播放格式 */
|
||||||
const allowPlayFormat = ["mp3", "flac", "webm", "ogg", "wav"];
|
const allowPlayFormat = ["mp3", "flac", "webm", "ogg", "wav"];
|
||||||
@@ -33,78 +37,12 @@ class Player {
|
|||||||
/** 播放器 */
|
/** 播放器 */
|
||||||
private player: Howl;
|
private player: Howl;
|
||||||
/** 定时器 */
|
/** 定时器 */
|
||||||
private playerInterval: ReturnType<typeof setInterval> | undefined;
|
private readonly playerInterval = useIntervalFn(
|
||||||
/** 自动关闭定时器 */
|
() => {
|
||||||
private autoCloseInterval: ReturnType<typeof setInterval> | undefined;
|
if (!this.player?.playing()) return;
|
||||||
/** 频谱数据 */
|
const musicStore = useMusicStore();
|
||||||
private audioContext: AudioContext | null = null;
|
const statusStore = useStatusStore();
|
||||||
private analyser: AnalyserNode | null = null;
|
const settingStore = useSettingStore();
|
||||||
private dataArray: Uint8Array<ArrayBuffer> | null = null;
|
|
||||||
/** 其他数据 */
|
|
||||||
private message: MessageReactive | null = null;
|
|
||||||
/** 预载下一首歌曲播放地址缓存(仅存 URL,不创建 Howl) */
|
|
||||||
private nextPrefetch: { id: number; url: string | null; ublock: boolean } | null = null;
|
|
||||||
/** 并发控制:当前播放会话与初始化/切曲状态 */
|
|
||||||
private playSessionId: number = 0;
|
|
||||||
/** 是否正在切换歌曲 */
|
|
||||||
private switching: boolean = false;
|
|
||||||
/** 当前曲目重试信息(按歌曲维度计数) */
|
|
||||||
private retryInfo: { songId: number; count: number } = { songId: 0, count: 0 };
|
|
||||||
constructor() {
|
|
||||||
// 创建播放器实例
|
|
||||||
this.player = new Howl({ src: [""], format: allowPlayFormat, autoplay: false });
|
|
||||||
// 初始化媒体会话
|
|
||||||
this.initMediaSession();
|
|
||||||
// 挂载全局
|
|
||||||
window.$player = this;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 新建会话并返回会话 id
|
|
||||||
*/
|
|
||||||
private newSession(): number {
|
|
||||||
this.playSessionId += 1;
|
|
||||||
return this.playSessionId;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 检查传入会话是否过期
|
|
||||||
*/
|
|
||||||
private isStale(sessionId: number): boolean {
|
|
||||||
return sessionId !== this.playSessionId;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 重置底层播放器与定时器(幂等)
|
|
||||||
*/
|
|
||||||
private resetPlayerCore() {
|
|
||||||
try {
|
|
||||||
// 仅卸载当前播放器实例
|
|
||||||
if (this.player) {
|
|
||||||
this.player.stop();
|
|
||||||
this.player.off();
|
|
||||||
this.player.unload();
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
this.cleanupAllTimers();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 处理播放状态
|
|
||||||
*/
|
|
||||||
private handlePlayStatus() {
|
|
||||||
const musicStore = useMusicStore();
|
|
||||||
const statusStore = useStatusStore();
|
|
||||||
const settingStore = useSettingStore();
|
|
||||||
const currentSessionId = this.playSessionId;
|
|
||||||
// 清理定时器
|
|
||||||
clearInterval(this.playerInterval);
|
|
||||||
// 更新播放状态
|
|
||||||
this.playerInterval = setInterval(() => {
|
|
||||||
// 检查会话是否过期
|
|
||||||
if (currentSessionId !== this.playSessionId) {
|
|
||||||
clearInterval(this.playerInterval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.player.playing()) return;
|
|
||||||
const currentTime = this.getSeek();
|
const currentTime = this.getSeek();
|
||||||
const duration = this.getDuration();
|
const duration = this.getDuration();
|
||||||
// 计算进度条距离
|
// 计算进度条距离
|
||||||
@@ -130,7 +68,47 @@ class Player {
|
|||||||
window.electron.ipcRenderer.send("set-bar", progress);
|
window.electron.ipcRenderer.send("set-bar", progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 250);
|
},
|
||||||
|
250,
|
||||||
|
{ immediate: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 自动关闭定时器 */
|
||||||
|
private autoCloseInterval: ReturnType<typeof setInterval> | undefined;
|
||||||
|
/** 频谱数据 */
|
||||||
|
private audioContext: AudioContext | null = null;
|
||||||
|
private analyser: AnalyserNode | null = null;
|
||||||
|
private dataArray: Uint8Array<ArrayBuffer> | null = null;
|
||||||
|
/** 其他数据 */
|
||||||
|
private message: MessageReactive | null = null;
|
||||||
|
/** 预载下一首歌曲播放地址缓存(仅存 URL,不创建 Howl) */
|
||||||
|
private nextPrefetch: { id: number; url: string | null; ublock: boolean } | null = null;
|
||||||
|
/** 当前曲目重试信息(按歌曲维度计数) */
|
||||||
|
private retryInfo: { songId: number; count: number } = { songId: 0, count: 0 };
|
||||||
|
constructor() {
|
||||||
|
// 创建播放器实例
|
||||||
|
this.player = new Howl({ src: [""], format: allowPlayFormat, autoplay: false });
|
||||||
|
// 初始化媒体会话
|
||||||
|
this.initMediaSession();
|
||||||
|
// 挂载全局
|
||||||
|
window.$player = this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 重置底层播放器与定时器(幂等)
|
||||||
|
*/
|
||||||
|
private resetPlayerCore() {
|
||||||
|
try {
|
||||||
|
// 仅卸载当前播放器实例
|
||||||
|
if (this.player) {
|
||||||
|
this.player.stop();
|
||||||
|
this.player.off();
|
||||||
|
this.player.unload();
|
||||||
|
}
|
||||||
|
Howler.unload();
|
||||||
|
} catch {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
this.cleanupAllTimers();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 预载下一首歌曲的播放地址(优先官方,失败则并发尝试解灰)
|
* 预载下一首歌曲的播放地址(优先官方,失败则并发尝试解灰)
|
||||||
@@ -215,19 +193,10 @@ class Player {
|
|||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
// 播放信息
|
// 播放信息
|
||||||
const { id, path, type } = musicStore.playSong;
|
const { id, path, type } = musicStore.playSong;
|
||||||
const currentSessionId = this.playSessionId;
|
|
||||||
// 检查会话是否过期
|
|
||||||
if (currentSessionId !== this.playSessionId) {
|
|
||||||
console.log("🚫 Session expired, skipping player creation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 统一重置底层播放器
|
// 统一重置底层播放器
|
||||||
this.resetPlayerCore();
|
this.resetPlayerCore();
|
||||||
// 二次检查会话
|
|
||||||
if (currentSessionId !== this.playSessionId) {
|
|
||||||
console.log("🚫 Session expired after cleanup, aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 创建播放器
|
// 创建播放器
|
||||||
this.player = new Howl({
|
this.player = new Howl({
|
||||||
src,
|
src,
|
||||||
@@ -250,8 +219,7 @@ class Player {
|
|||||||
// else resetSongLyric();
|
// else resetSongLyric();
|
||||||
// 获取歌词数据
|
// 获取歌词数据
|
||||||
lyricManager.handleLyric(id, path);
|
lyricManager.handleLyric(id, path);
|
||||||
// 定时获取状态
|
|
||||||
if (!this.playerInterval) this.handlePlayStatus();
|
|
||||||
// 新增播放历史
|
// 新增播放历史
|
||||||
if (type !== "radio") dataStore.setHistory(musicStore.playSong);
|
if (type !== "radio") dataStore.setHistory(musicStore.playSong);
|
||||||
// 获取歌曲封面主色
|
// 获取歌曲封面主色
|
||||||
@@ -279,10 +247,8 @@ class Player {
|
|||||||
const playSongData = getPlaySongData();
|
const playSongData = getPlaySongData();
|
||||||
// 获取配置
|
// 获取配置
|
||||||
const { seek } = options;
|
const { seek } = options;
|
||||||
const currentSessionId = this.playSessionId;
|
|
||||||
// 初次加载
|
// 初次加载
|
||||||
this.player.once("load", () => {
|
this.player.once("load", () => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
|
||||||
// 允许跨域
|
// 允许跨域
|
||||||
if (settingStore.showSpectrums) {
|
if (settingStore.showSpectrums) {
|
||||||
const audioDom = this.getAudioDom();
|
const audioDom = this.getAudioDom();
|
||||||
@@ -325,8 +291,8 @@ class Player {
|
|||||||
});
|
});
|
||||||
// 播放
|
// 播放
|
||||||
this.player.on("play", () => {
|
this.player.on("play", () => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
|
||||||
window.document.title = getPlayerInfo() || "SPlayer";
|
window.document.title = getPlayerInfo() || "SPlayer";
|
||||||
|
this.playerInterval.resume();
|
||||||
// 重置重试计数
|
// 重置重试计数
|
||||||
try {
|
try {
|
||||||
const current = getPlaySongData();
|
const current = getPlaySongData();
|
||||||
@@ -344,8 +310,8 @@ class Player {
|
|||||||
});
|
});
|
||||||
// 暂停
|
// 暂停
|
||||||
this.player.on("pause", () => {
|
this.player.on("pause", () => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
|
||||||
if (!isElectron) window.document.title = "SPlayer";
|
if (!isElectron) window.document.title = "SPlayer";
|
||||||
|
this.playerInterval.pause();
|
||||||
// ipc
|
// ipc
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
window.electron.ipcRenderer.send("play-status-change", false);
|
window.electron.ipcRenderer.send("play-status-change", false);
|
||||||
@@ -354,7 +320,7 @@ class Player {
|
|||||||
});
|
});
|
||||||
// 结束
|
// 结束
|
||||||
this.player.on("end", () => {
|
this.player.on("end", () => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
this.playerInterval.pause();
|
||||||
// statusStore.playStatus = false;
|
// statusStore.playStatus = false;
|
||||||
console.log("⏹️ song end:", playSongData);
|
console.log("⏹️ song end:", playSongData);
|
||||||
|
|
||||||
@@ -374,13 +340,11 @@ class Player {
|
|||||||
});
|
});
|
||||||
// 错误
|
// 错误
|
||||||
this.player.on("loaderror", (sourceid, err: unknown) => {
|
this.player.on("loaderror", (sourceid, err: unknown) => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
|
||||||
const code = typeof err === "number" ? err : undefined;
|
const code = typeof err === "number" ? err : undefined;
|
||||||
this.handlePlaybackError(code);
|
this.handlePlaybackError(code);
|
||||||
console.error("❌ song error:", sourceid, playSongData, err);
|
console.error("❌ song error:", sourceid, playSongData, err);
|
||||||
});
|
});
|
||||||
this.player.on("playerror", (sourceid, err: unknown) => {
|
this.player.on("playerror", (sourceid, err: unknown) => {
|
||||||
if (currentSessionId !== this.playSessionId) return;
|
|
||||||
const code = typeof err === "number" ? err : undefined;
|
const code = typeof err === "number" ? err : undefined;
|
||||||
this.handlePlaybackError(code);
|
this.handlePlaybackError(code);
|
||||||
console.error("❌ song play error:", sourceid, playSongData, err);
|
console.error("❌ song play error:", sourceid, playSongData, err);
|
||||||
@@ -507,7 +471,6 @@ class Player {
|
|||||||
}
|
}
|
||||||
// 超过次数:切到下一首或清空
|
// 超过次数:切到下一首或清空
|
||||||
this.retryInfo.count = 0;
|
this.retryInfo.count = 0;
|
||||||
this.switching = false;
|
|
||||||
if (dataStore.playList.length > 1) {
|
if (dataStore.playList.length > 1) {
|
||||||
window.$message.error("当前歌曲播放失败,已跳至下一首");
|
window.$message.error("当前歌曲播放失败,已跳至下一首");
|
||||||
await this.nextOrPrev("next");
|
await this.nextOrPrev("next");
|
||||||
@@ -589,7 +552,6 @@ class Player {
|
|||||||
const musicStore = useMusicStore();
|
const musicStore = useMusicStore();
|
||||||
const statusStore = useStatusStore();
|
const statusStore = useStatusStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
const sessionId = this.newSession();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取播放数据
|
// 获取播放数据
|
||||||
@@ -609,7 +571,6 @@ class Player {
|
|||||||
|
|
||||||
// 本地歌曲
|
// 本地歌曲
|
||||||
if (path) {
|
if (path) {
|
||||||
if (this.isStale(sessionId)) return;
|
|
||||||
try {
|
try {
|
||||||
await this.createPlayer(`file://${path}`, autoPlay, seek);
|
await this.createPlayer(`file://${path}`, autoPlay, seek);
|
||||||
await this.parseLocalMusicInfo(path);
|
await this.parseLocalMusicInfo(path);
|
||||||
@@ -658,20 +619,18 @@ class Player {
|
|||||||
|
|
||||||
if (!playerUrl) {
|
if (!playerUrl) {
|
||||||
window.$message.error("该歌曲暂无音源,跳至下一首");
|
window.$message.error("该歌曲暂无音源,跳至下一首");
|
||||||
this.switching = false;
|
|
||||||
await this.nextOrPrev("next");
|
await this.nextOrPrev("next");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ 获取歌曲地址出错:", err);
|
console.error("❌ 获取歌曲地址出错:", err);
|
||||||
window.$message.error("获取歌曲地址失败,跳至下一首");
|
window.$message.error("获取歌曲地址失败,跳至下一首");
|
||||||
this.switching = false;
|
|
||||||
await this.nextOrPrev("next");
|
await this.nextOrPrev("next");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有有效 URL 才创建播放器
|
// 有有效 URL 才创建播放器
|
||||||
if (playerUrl && !this.isStale(sessionId)) {
|
if (playerUrl) {
|
||||||
try {
|
try {
|
||||||
await this.createPlayer(playerUrl, autoPlay, seek);
|
await this.createPlayer(playerUrl, autoPlay, seek);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -682,10 +641,7 @@ class Player {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ 初始化音乐播放器出错:", err);
|
console.error("❌ 初始化音乐播放器出错:", err);
|
||||||
window.$message.error("播放遇到错误,尝试下一首");
|
window.$message.error("播放遇到错误,尝试下一首");
|
||||||
this.switching = false;
|
|
||||||
await this.nextOrPrev("next");
|
await this.nextOrPrev("next");
|
||||||
} finally {
|
|
||||||
this.switching = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -758,12 +714,6 @@ class Player {
|
|||||||
const dataStore = useDataStore();
|
const dataStore = useDataStore();
|
||||||
const musicStore = useMusicStore();
|
const musicStore = useMusicStore();
|
||||||
try {
|
try {
|
||||||
if (this.switching) {
|
|
||||||
console.log("🔄 Already switching, ignoring request");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.switching = true;
|
|
||||||
|
|
||||||
// 立即更新UI状态,防止用户重复点击
|
// 立即更新UI状态,防止用户重复点击
|
||||||
statusStore.playLoading = true;
|
statusStore.playLoading = true;
|
||||||
statusStore.playStatus = false;
|
statusStore.playStatus = false;
|
||||||
@@ -818,16 +768,12 @@ class Player {
|
|||||||
// 重置播放进度(切换歌曲时必须重置)
|
// 重置播放进度(切换歌曲时必须重置)
|
||||||
statusStore.currentTime = 0;
|
statusStore.currentTime = 0;
|
||||||
statusStore.progress = 0;
|
statusStore.progress = 0;
|
||||||
// 暂停当前播放
|
|
||||||
await this.pause(false);
|
|
||||||
// 初始化播放器(不传入seek参数,确保从头开始播放)
|
// 初始化播放器(不传入seek参数,确保从头开始播放)
|
||||||
await this.initPlayer(play, 0);
|
await this.initPlayer(play, 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in nextOrPrev:", error);
|
console.error("Error in nextOrPrev:", error);
|
||||||
statusStore.playLoading = false;
|
statusStore.playLoading = false;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
|
||||||
this.switching = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1046,7 +992,6 @@ class Player {
|
|||||||
// 查找索引(在处理后的列表中查找)
|
// 查找索引(在处理后的列表中查找)
|
||||||
statusStore.playIndex = processedData.findIndex((item) => item.id === song.id);
|
statusStore.playIndex = processedData.findIndex((item) => item.id === song.id);
|
||||||
// 播放
|
// 播放
|
||||||
await this.pause(false);
|
|
||||||
await this.initPlayer();
|
await this.initPlayer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1055,7 +1000,6 @@ class Player {
|
|||||||
? Math.floor(Math.random() * processedData.length)
|
? Math.floor(Math.random() * processedData.length)
|
||||||
: 0;
|
: 0;
|
||||||
// 播放
|
// 播放
|
||||||
await this.pause(false);
|
|
||||||
await this.initPlayer();
|
await this.initPlayer();
|
||||||
}
|
}
|
||||||
// 更改播放歌单
|
// 更改播放歌单
|
||||||
@@ -1095,11 +1039,6 @@ class Player {
|
|||||||
const dataStore = useDataStore();
|
const dataStore = useDataStore();
|
||||||
const statusStore = useStatusStore();
|
const statusStore = useStatusStore();
|
||||||
try {
|
try {
|
||||||
if (this.switching) {
|
|
||||||
console.log("🔄 Already switching, ignoring request");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.switching = true;
|
|
||||||
// 立即更新UI状态,防止用户重复点击
|
// 立即更新UI状态,防止用户重复点击
|
||||||
statusStore.playLoading = true;
|
statusStore.playLoading = true;
|
||||||
statusStore.playStatus = false;
|
statusStore.playStatus = false;
|
||||||
@@ -1118,8 +1057,6 @@ class Player {
|
|||||||
statusStore.currentTime = 0;
|
statusStore.currentTime = 0;
|
||||||
statusStore.progress = 0;
|
statusStore.progress = 0;
|
||||||
statusStore.lyricIndex = -1;
|
statusStore.lyricIndex = -1;
|
||||||
// 暂停当前播放
|
|
||||||
await this.pause(false);
|
|
||||||
// 清理定时器,防止旧定时器继续运行
|
// 清理定时器,防止旧定时器继续运行
|
||||||
this.cleanupAllTimers();
|
this.cleanupAllTimers();
|
||||||
// 清理并播放(不传入seek参数,确保从头开始播放)
|
// 清理并播放(不传入seek参数,确保从头开始播放)
|
||||||
@@ -1128,8 +1065,6 @@ class Player {
|
|||||||
console.error("Error in togglePlayIndex:", error);
|
console.error("Error in togglePlayIndex:", error);
|
||||||
statusStore.playLoading = false;
|
statusStore.playLoading = false;
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
|
||||||
this.switching = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1469,9 +1404,8 @@ class Player {
|
|||||||
*/
|
*/
|
||||||
private cleanupAllTimers() {
|
private cleanupAllTimers() {
|
||||||
// 清理播放状态定时器
|
// 清理播放状态定时器
|
||||||
if (this.playerInterval) {
|
if (this.playerInterval.isActive.value) {
|
||||||
clearInterval(this.playerInterval);
|
this.playerInterval.pause();
|
||||||
this.playerInterval = undefined;
|
|
||||||
}
|
}
|
||||||
// 清理自动关闭定时器
|
// 清理自动关闭定时器
|
||||||
if (this.autoCloseInterval) {
|
if (this.autoCloseInterval) {
|
||||||
@@ -1495,3 +1429,12 @@ class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default new Player();
|
export default new Player();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取播放器实例
|
||||||
|
* @returns Player
|
||||||
|
*/
|
||||||
|
export const usePlayer = (): Player => {
|
||||||
|
if (!_player) _player = new Player();
|
||||||
|
return _player;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user