Skip to content

Commit 41ece1d

Browse files
committed
🐞 fix: 修复播放错误判断
1 parent 029b51e commit 41ece1d

5 files changed

Lines changed: 102 additions & 14 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@
7373
"pinia": "^3.0.4",
7474
"pinia-plugin-persistedstate": "^4.7.1",
7575
"plyr": "^3.8.3",
76-
"sortablejs": "^1.15.6"
76+
"sortablejs": "^1.15.6",
77+
"vue-virtual-scroller": "2.0.0-beta.8"
7778
},
7879
"devDependencies": {
7980
"@electron-toolkit/preload": "^3.0.1",

pnpm-lock.yaml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/stores/setting.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { keywords, regexes } from "@/assets/data/exclude";
33
import { SongUnlockServer } from "@/utils/songManager";
44
import type { SongLevelType } from "@/types/main";
55
import { defaultAMLLDbServer } from "@/utils/meta";
6-
import {
7-
CURRENT_SETTING_SCHEMA_VERSION,
8-
settingMigrations,
9-
} from "./migrations/settingMigrations";
6+
import { CURRENT_SETTING_SCHEMA_VERSION, settingMigrations } from "./migrations/settingMigrations";
107

118
export interface SettingState {
129
/** Schema 版本号(可选,用于数据迁移) */
@@ -251,7 +248,7 @@ export interface SettingState {
251248

252249
export const useSettingStore = defineStore("setting", {
253250
state: (): SettingState => ({
254-
schemaVersion: CURRENT_SETTING_SCHEMA_VERSION,
251+
schemaVersion: 0,
255252
themeMode: "auto",
256253
themeColorType: "default",
257254
themeCustomColor: "#fe7971",

src/utils/audioManager.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,21 @@ class AudioManager {
300300

301301
events.forEach((event) => {
302302
this.audioElement!.addEventListener(event, (e) => {
303-
const listeners = this.eventListeners.get(event);
304-
if (listeners) {
305-
listeners.forEach((cb) => cb(e));
303+
// 传递错误码
304+
if (event === "error" && this.audioElement) {
305+
const errCode = this.getErrorCode();
306+
const customEvent = new CustomEvent("error", {
307+
detail: { originalEvent: e, errorCode: errCode },
308+
});
309+
const listeners = this.eventListeners.get(event);
310+
if (listeners) {
311+
listeners.forEach((cb) => cb(customEvent));
312+
}
313+
} else {
314+
const listeners = this.eventListeners.get(event);
315+
if (listeners) {
316+
listeners.forEach((cb) => cb(e));
317+
}
306318
}
307319
});
308320
});
@@ -390,6 +402,32 @@ class AudioManager {
390402
public get src() {
391403
return this.audioElement?.src || "";
392404
}
405+
406+
/**
407+
* 获取音频错误码
408+
* @returns 错误码
409+
*/
410+
private getErrorCode(): number {
411+
if (!this.audioElement?.error) return 0;
412+
413+
// 参考 HTML Audio Element 错误码
414+
// MEDIA_ERR_ABORTED (1): 用户中止了加载
415+
// MEDIA_ERR_NETWORK (2): 网络错误或资源过期
416+
// MEDIA_ERR_DECODE (3): 解码错误
417+
// MEDIA_ERR_SRC_NOT_SUPPORTED (4): 不支持的格式
418+
switch (this.audioElement.error.code) {
419+
case MediaError.MEDIA_ERR_ABORTED:
420+
return 1;
421+
case MediaError.MEDIA_ERR_NETWORK:
422+
return 2; // 网络错误或资源过期
423+
case MediaError.MEDIA_ERR_DECODE:
424+
return 3;
425+
case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
426+
return 4;
427+
default:
428+
return 0;
429+
}
430+
}
393431
}
394432

395433
export default new AudioManager();

src/utils/player.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ class Player {
113113
const errorCallback = (e: Event) => {
114114
const playSongData = songManager.getPlaySongData();
115115
console.error("❌ song error:", playSongData, e);
116-
this.handlePlaybackError();
116+
// 提取错误码
117+
let errCode: number | undefined;
118+
if ("detail" in e && e.detail) {
119+
errCode = (e.detail as { errorCode?: number }).errorCode;
120+
}
121+
this.handlePlaybackError(errCode);
117122
};
118123
audioManager.on("error", errorCallback);
119124
this.eventCallbacks.set("error", errorCallback);
@@ -301,19 +306,28 @@ class Player {
301306
const dataStore = useDataStore();
302307
const playSongData = songManager.getPlaySongData();
303308
const currentSongId = playSongData?.type === "radio" ? playSongData.dj?.id : playSongData?.id;
309+
// 保存当前播放进度,用于恢复
310+
const currentSeek = this.getSeek();
304311
// 初始化/切换曲目时重置计数
305312
if (!this.retryInfo.songId || this.retryInfo.songId !== Number(currentSongId || 0)) {
306313
this.retryInfo = { songId: Number(currentSongId || 0), count: 0 };
307314
}
308315
this.retryInfo.count += 1;
309-
// 错误码 2:资源过期或临时网络错误
316+
// 2:资源过期或临时网络错误(通常是长时间暂停导致URL过期)
310317
if (errCode === 2 && this.retryInfo.count <= 2) {
311-
await this.initPlayer(true, this.getSeek());
318+
console.log("🔄 检测到资源过期,重新获取播放地址并从原位置继续:", currentSeek);
319+
await this.initPlayer(true, currentSeek);
312320
return;
313321
}
314-
// 其它错误:最多 3 次
322+
// 其它错误:最多 3 次,首次重试从原位置开始
315323
if (this.retryInfo.count <= 3) {
316-
await this.initPlayer(true, 0);
324+
const seekPosition = this.retryInfo.count === 1 ? currentSeek : 0;
325+
console.log("🔄 播放出错,尝试重试:", { count: this.retryInfo.count, seekPosition });
326+
// 只在第一次重试时显示提示,避免过于频繁
327+
if (this.retryInfo.count === 1) {
328+
window.$message.info("播放出现问题,正在尝试恢复...");
329+
}
330+
await this.initPlayer(true, seekPosition);
317331
return;
318332
}
319333
// 超过次数:切到下一首或清空

0 commit comments

Comments
 (0)