mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-27 01:57:24 +08:00
fix: 修改国际化问题 和 音源优先级以及音源解析错误处理
This commit is contained in:
@@ -39,7 +39,9 @@ export default {
|
|||||||
warning: 'Please select a music source',
|
warning: 'Please select a music source',
|
||||||
bilibiliNotSupported: 'Bilibili videos do not support reparsing',
|
bilibiliNotSupported: 'Bilibili videos do not support reparsing',
|
||||||
processing: 'Processing...',
|
processing: 'Processing...',
|
||||||
clear: 'Clear Custom Source'
|
clear: 'Clear Custom Source',
|
||||||
|
customApiFailed: 'Custom API parsing failed, trying built-in sources...',
|
||||||
|
customApiError: 'Custom API request error, trying built-in sources...'
|
||||||
},
|
},
|
||||||
playBar: {
|
playBar: {
|
||||||
expand: 'Expand Lyrics',
|
expand: 'Expand Lyrics',
|
||||||
|
|||||||
+124
-122
@@ -1,123 +1,125 @@
|
|||||||
export default {
|
export default {
|
||||||
nowPlaying: '再生中',
|
nowPlaying: '再生中',
|
||||||
playlist: 'プレイリスト',
|
playlist: 'プレイリスト',
|
||||||
lyrics: '歌詞',
|
lyrics: '歌詞',
|
||||||
previous: '前へ',
|
previous: '前へ',
|
||||||
play: '再生',
|
play: '再生',
|
||||||
pause: '一時停止',
|
pause: '一時停止',
|
||||||
next: '次へ',
|
next: '次へ',
|
||||||
volumeUp: '音量を上げる',
|
volumeUp: '音量を上げる',
|
||||||
volumeDown: '音量を下げる',
|
volumeDown: '音量を下げる',
|
||||||
mute: 'ミュート',
|
mute: 'ミュート',
|
||||||
unmute: 'ミュート解除',
|
unmute: 'ミュート解除',
|
||||||
songNum: '楽曲総数:{num}',
|
songNum: '楽曲総数:{num}',
|
||||||
addCorrection: '{num}秒早める',
|
addCorrection: '{num}秒早める',
|
||||||
subtractCorrection: '{num}秒遅らせる',
|
subtractCorrection: '{num}秒遅らせる',
|
||||||
playFailed: '現在の楽曲の再生に失敗しました。次の曲を再生します',
|
playFailed: '現在の楽曲の再生に失敗しました。次の曲を再生します',
|
||||||
playMode: {
|
playMode: {
|
||||||
sequence: '順次再生',
|
sequence: '順次再生',
|
||||||
loop: 'リピート再生',
|
loop: 'リピート再生',
|
||||||
random: 'ランダム再生'
|
random: 'ランダム再生'
|
||||||
},
|
},
|
||||||
fullscreen: {
|
fullscreen: {
|
||||||
enter: 'フルスクリーン',
|
enter: 'フルスクリーン',
|
||||||
exit: 'フルスクリーン終了'
|
exit: 'フルスクリーン終了'
|
||||||
},
|
},
|
||||||
close: '閉じる',
|
close: '閉じる',
|
||||||
modeHint: {
|
modeHint: {
|
||||||
single: 'リピート再生',
|
single: 'リピート再生',
|
||||||
list: '自動で次の曲を再生'
|
list: '自動で次の曲を再生'
|
||||||
},
|
},
|
||||||
lrc: {
|
lrc: {
|
||||||
noLrc: '歌詞がありません。お楽しみください'
|
noLrc: '歌詞がありません。お楽しみください'
|
||||||
},
|
},
|
||||||
reparse: {
|
reparse: {
|
||||||
title: '解析音源を選択',
|
title: '解析音源を選択',
|
||||||
desc: '音源をクリックして直接解析します。次回この楽曲を再生する際は選択した音源を使用します',
|
desc: '音源をクリックして直接解析します。次回この楽曲を再生する際は選択した音源を使用します',
|
||||||
success: '再解析成功',
|
success: '再解析成功',
|
||||||
failed: '再解析失敗',
|
failed: '再解析失敗',
|
||||||
warning: '音源を選択してください',
|
warning: '音源を選択してください',
|
||||||
bilibiliNotSupported: 'Bilibili動画は再解析をサポートしていません',
|
bilibiliNotSupported: 'Bilibili動画は再解析をサポートしていません',
|
||||||
processing: '解析中...',
|
processing: '解析中...',
|
||||||
clear: 'カスタム音源をクリア'
|
clear: 'カスタム音源をクリア',
|
||||||
},
|
customApiFailed: 'カスタムAPIの解析に失敗しました。内蔵音源を試しています...',
|
||||||
playBar: {
|
customApiError: 'カスタムAPIのリクエストでエラーが発生しました。内蔵音源を試しています...'
|
||||||
expand: '歌詞を展開',
|
},
|
||||||
collapse: '歌詞を折りたたみ',
|
playBar: {
|
||||||
like: 'いいね',
|
expand: '歌詞を展開',
|
||||||
lyric: '歌詞',
|
collapse: '歌詞を折りたたみ',
|
||||||
noSongPlaying: '再生中の楽曲がありません',
|
like: 'いいね',
|
||||||
eq: 'イコライザー',
|
lyric: '歌詞',
|
||||||
playList: 'プレイリスト',
|
noSongPlaying: '再生中の楽曲がありません',
|
||||||
reparse: '再解析',
|
eq: 'イコライザー',
|
||||||
playMode: {
|
playList: 'プレイリスト',
|
||||||
sequence: '順次再生',
|
reparse: '再解析',
|
||||||
loop: 'ループ再生',
|
playMode: {
|
||||||
random: 'ランダム再生'
|
sequence: '順次再生',
|
||||||
},
|
loop: 'ループ再生',
|
||||||
play: '再生開始',
|
random: 'ランダム再生'
|
||||||
pause: '再生一時停止',
|
},
|
||||||
prev: '前の曲',
|
play: '再生開始',
|
||||||
next: '次の曲',
|
pause: '再生一時停止',
|
||||||
volume: '音量',
|
prev: '前の曲',
|
||||||
favorite: '{name}をお気に入りに追加しました',
|
next: '次の曲',
|
||||||
unFavorite: '{name}をお気に入りから削除しました',
|
volume: '音量',
|
||||||
miniPlayBar: 'ミニ再生バー',
|
favorite: '{name}をお気に入りに追加しました',
|
||||||
playbackSpeed: '再生速度',
|
unFavorite: '{name}をお気に入りから削除しました',
|
||||||
advancedControls: 'その他の設定'
|
miniPlayBar: 'ミニ再生バー',
|
||||||
},
|
playbackSpeed: '再生速度',
|
||||||
eq: {
|
advancedControls: 'その他の設定'
|
||||||
title: 'イコライザー',
|
},
|
||||||
reset: 'リセット',
|
eq: {
|
||||||
on: 'オン',
|
title: 'イコライザー',
|
||||||
off: 'オフ',
|
reset: 'リセット',
|
||||||
bass: '低音',
|
on: 'オン',
|
||||||
midrange: '中音',
|
off: 'オフ',
|
||||||
treble: '高音',
|
bass: '低音',
|
||||||
presets: {
|
midrange: '中音',
|
||||||
flat: 'フラット',
|
treble: '高音',
|
||||||
pop: 'ポップ',
|
presets: {
|
||||||
rock: 'ロック',
|
flat: 'フラット',
|
||||||
classical: 'クラシック',
|
pop: 'ポップ',
|
||||||
jazz: 'ジャズ',
|
rock: 'ロック',
|
||||||
electronic: 'エレクトロニック',
|
classical: 'クラシック',
|
||||||
hiphop: 'ヒップホップ',
|
jazz: 'ジャズ',
|
||||||
rb: 'R&B',
|
electronic: 'エレクトロニック',
|
||||||
metal: 'メタル',
|
hiphop: 'ヒップホップ',
|
||||||
vocal: 'ボーカル',
|
rb: 'R&B',
|
||||||
dance: 'ダンス',
|
metal: 'メタル',
|
||||||
acoustic: 'アコースティック',
|
vocal: 'ボーカル',
|
||||||
custom: 'カスタム'
|
dance: 'ダンス',
|
||||||
}
|
acoustic: 'アコースティック',
|
||||||
},
|
custom: 'カスタム'
|
||||||
// タイマー機能関連
|
}
|
||||||
sleepTimer: {
|
},
|
||||||
title: 'スリープタイマー',
|
// タイマー機能関連
|
||||||
cancel: 'タイマーをキャンセル',
|
sleepTimer: {
|
||||||
timeMode: '時間で停止',
|
title: 'スリープタイマー',
|
||||||
songsMode: '楽曲数で停止',
|
cancel: 'タイマーをキャンセル',
|
||||||
playlistEnd: 'プレイリスト終了後に停止',
|
timeMode: '時間で停止',
|
||||||
afterPlaylist: 'プレイリスト終了後に停止',
|
songsMode: '楽曲数で停止',
|
||||||
activeUntilEnd: 'リスト終了まで再生',
|
playlistEnd: 'プレイリスト終了後に停止',
|
||||||
minutes: '分',
|
afterPlaylist: 'プレイリスト終了後に停止',
|
||||||
hours: '時間',
|
activeUntilEnd: 'リスト終了まで再生',
|
||||||
songs: '曲',
|
minutes: '分',
|
||||||
set: '設定',
|
hours: '時間',
|
||||||
timerSetSuccess: '{minutes}分後に停止するよう設定しました',
|
songs: '曲',
|
||||||
songsSetSuccess: '{songs}曲再生後に停止するよう設定しました',
|
set: '設定',
|
||||||
playlistEndSetSuccess: 'プレイリスト終了後に停止するよう設定しました',
|
timerSetSuccess: '{minutes}分後に停止するよう設定しました',
|
||||||
timerCancelled: 'スリープタイマーをキャンセルしました',
|
songsSetSuccess: '{songs}曲再生後に停止するよう設定しました',
|
||||||
timerEnded: 'スリープタイマーが作動しました',
|
playlistEndSetSuccess: 'プレイリスト終了後に停止するよう設定しました',
|
||||||
playbackStopped: '音楽再生を停止しました',
|
timerCancelled: 'スリープタイマーをキャンセルしました',
|
||||||
minutesRemaining: '残り{minutes}分',
|
timerEnded: 'スリープタイマーが作動しました',
|
||||||
songsRemaining: '残り{count}曲'
|
playbackStopped: '音楽再生を停止しました',
|
||||||
},
|
minutesRemaining: '残り{minutes}分',
|
||||||
playList: {
|
songsRemaining: '残り{count}曲'
|
||||||
clearAll: 'プレイリストをクリア',
|
},
|
||||||
alreadyEmpty: 'プレイリストは既に空です',
|
playList: {
|
||||||
cleared: 'プレイリストをクリアしました',
|
clearAll: 'プレイリストをクリア',
|
||||||
empty: 'プレイリストが空です',
|
alreadyEmpty: 'プレイリストは既に空です',
|
||||||
clearConfirmTitle: 'プレイリストをクリア',
|
cleared: 'プレイリストをクリアしました',
|
||||||
clearConfirmContent: 'これによりプレイリスト内のすべての楽曲がクリアされ、現在の再生が停止されます。続行しますか?'
|
empty: 'プレイリストが空です',
|
||||||
}
|
clearConfirmTitle: 'プレイリストをクリア',
|
||||||
|
clearConfirmContent: 'これによりプレイリスト内のすべての楽曲がクリアされ、現在の再生が停止されます。続行しますか?'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
+123
-121
@@ -1,122 +1,124 @@
|
|||||||
export default {
|
export default {
|
||||||
nowPlaying: '현재 재생 중',
|
nowPlaying: '현재 재생 중',
|
||||||
playlist: '재생 목록',
|
playlist: '재생 목록',
|
||||||
lyrics: '가사',
|
lyrics: '가사',
|
||||||
previous: '이전',
|
previous: '이전',
|
||||||
play: '재생',
|
play: '재생',
|
||||||
pause: '일시정지',
|
pause: '일시정지',
|
||||||
next: '다음',
|
next: '다음',
|
||||||
volumeUp: '볼륨 증가',
|
volumeUp: '볼륨 증가',
|
||||||
volumeDown: '볼륨 감소',
|
volumeDown: '볼륨 감소',
|
||||||
mute: '음소거',
|
mute: '음소거',
|
||||||
unmute: '음소거 해제',
|
unmute: '음소거 해제',
|
||||||
songNum: '총 곡 수: {num}',
|
songNum: '총 곡 수: {num}',
|
||||||
addCorrection: '{num}초 앞당기기',
|
addCorrection: '{num}초 앞당기기',
|
||||||
subtractCorrection: '{num}초 지연',
|
subtractCorrection: '{num}초 지연',
|
||||||
playFailed: '현재 곡 재생 실패, 다음 곡 재생',
|
playFailed: '현재 곡 재생 실패, 다음 곡 재생',
|
||||||
playMode: {
|
playMode: {
|
||||||
sequence: '순차 재생',
|
sequence: '순차 재생',
|
||||||
loop: '한 곡 반복',
|
loop: '한 곡 반복',
|
||||||
random: '랜덤 재생'
|
random: '랜덤 재생'
|
||||||
},
|
},
|
||||||
fullscreen: {
|
fullscreen: {
|
||||||
enter: '전체화면',
|
enter: '전체화면',
|
||||||
exit: '전체화면 종료'
|
exit: '전체화면 종료'
|
||||||
},
|
},
|
||||||
close: '닫기',
|
close: '닫기',
|
||||||
modeHint: {
|
modeHint: {
|
||||||
single: '한 곡 반복',
|
single: '한 곡 반복',
|
||||||
list: '자동으로 다음 곡 재생'
|
list: '자동으로 다음 곡 재생'
|
||||||
},
|
},
|
||||||
lrc: {
|
lrc: {
|
||||||
noLrc: '가사가 없습니다. 음악을 감상해주세요'
|
noLrc: '가사가 없습니다. 음악을 감상해주세요'
|
||||||
},
|
},
|
||||||
reparse: {
|
reparse: {
|
||||||
title: '음원 선택',
|
title: '음원 선택',
|
||||||
desc: '음원을 클릭하여 직접 분석하세요. 다음에 이 곡을 재생할 때 선택한 음원을 사용합니다',
|
desc: '음원을 클릭하여 직접 분석하세요. 다음에 이 곡을 재생할 때 선택한 음원을 사용합니다',
|
||||||
success: '재분석 성공',
|
success: '재분석 성공',
|
||||||
failed: '재분석 실패',
|
failed: '재분석 실패',
|
||||||
warning: '음원을 선택해주세요',
|
warning: '음원을 선택해주세요',
|
||||||
bilibiliNotSupported: 'B站 비디오는 재분석을 지원하지 않습니다',
|
bilibiliNotSupported: 'B站 비디오는 재분석을 지원하지 않습니다',
|
||||||
processing: '분석 중...',
|
processing: '분석 중...',
|
||||||
clear: '사용자 정의 음원 지우기'
|
clear: '사용자 정의 음원 지우기',
|
||||||
},
|
customApiFailed: '사용자 정의 API 분석 실패, 기본 음원을 시도합니다...',
|
||||||
playBar: {
|
customApiError: '사용자 정의 API 요청 오류, 기본 음원을 시도합니다...'
|
||||||
expand: '가사 펼치기',
|
},
|
||||||
collapse: '가사 접기',
|
playBar: {
|
||||||
like: '좋아요',
|
expand: '가사 펼치기',
|
||||||
lyric: '가사',
|
collapse: '가사 접기',
|
||||||
noSongPlaying: '재생 중인 곡이 없습니다',
|
like: '좋아요',
|
||||||
eq: '이퀄라이저',
|
lyric: '가사',
|
||||||
playList: '재생 목록',
|
noSongPlaying: '재생 중인 곡이 없습니다',
|
||||||
reparse: '재분석',
|
eq: '이퀄라이저',
|
||||||
playMode: {
|
playList: '재생 목록',
|
||||||
sequence: '순차 재생',
|
reparse: '재분석',
|
||||||
loop: '반복 재생',
|
playMode: {
|
||||||
random: '랜덤 재생'
|
sequence: '순차 재생',
|
||||||
},
|
loop: '반복 재생',
|
||||||
play: '재생 시작',
|
random: '랜덤 재생'
|
||||||
pause: '재생 일시정지',
|
},
|
||||||
prev: '이전 곡',
|
play: '재생 시작',
|
||||||
next: '다음 곡',
|
pause: '재생 일시정지',
|
||||||
volume: '볼륨',
|
prev: '이전 곡',
|
||||||
favorite: '{name} 즐겨찾기 추가됨',
|
next: '다음 곡',
|
||||||
unFavorite: '{name} 즐겨찾기 해제됨',
|
volume: '볼륨',
|
||||||
miniPlayBar: '미니 재생바',
|
favorite: '{name} 즐겨찾기 추가됨',
|
||||||
playbackSpeed: '재생 속도',
|
unFavorite: '{name} 즐겨찾기 해제됨',
|
||||||
advancedControls: '고급 설정'
|
miniPlayBar: '미니 재생바',
|
||||||
},
|
playbackSpeed: '재생 속도',
|
||||||
eq: {
|
advancedControls: '고급 설정'
|
||||||
title: '이퀄라이저',
|
},
|
||||||
reset: '재설정',
|
eq: {
|
||||||
on: '켜기',
|
title: '이퀄라이저',
|
||||||
off: '끄기',
|
reset: '재설정',
|
||||||
bass: '저음',
|
on: '켜기',
|
||||||
midrange: '중음',
|
off: '끄기',
|
||||||
treble: '고음',
|
bass: '저음',
|
||||||
presets: {
|
midrange: '중음',
|
||||||
flat: '플랫',
|
treble: '고음',
|
||||||
pop: '팝',
|
presets: {
|
||||||
rock: '록',
|
flat: '플랫',
|
||||||
classical: '클래식',
|
pop: '팝',
|
||||||
jazz: '재즈',
|
rock: '록',
|
||||||
electronic: '일렉트로닉',
|
classical: '클래식',
|
||||||
hiphop: '힙합',
|
jazz: '재즈',
|
||||||
rb: 'R&B',
|
electronic: '일렉트로닉',
|
||||||
metal: '메탈',
|
hiphop: '힙합',
|
||||||
vocal: '보컬',
|
rb: 'R&B',
|
||||||
dance: '댄스',
|
metal: '메탈',
|
||||||
acoustic: '어쿠스틱',
|
vocal: '보컬',
|
||||||
custom: '사용자 정의'
|
dance: '댄스',
|
||||||
}
|
acoustic: '어쿠스틱',
|
||||||
},
|
custom: '사용자 정의'
|
||||||
sleepTimer: {
|
}
|
||||||
title: '타이머 종료',
|
},
|
||||||
cancel: '타이머 취소',
|
sleepTimer: {
|
||||||
timeMode: '시간으로 종료',
|
title: '타이머 종료',
|
||||||
songsMode: '곡 수로 종료',
|
cancel: '타이머 취소',
|
||||||
playlistEnd: '재생 목록 완료 후 종료',
|
timeMode: '시간으로 종료',
|
||||||
afterPlaylist: '재생 목록 완료 후 종료',
|
songsMode: '곡 수로 종료',
|
||||||
activeUntilEnd: '목록 끝까지 재생',
|
playlistEnd: '재생 목록 완료 후 종료',
|
||||||
minutes: '분',
|
afterPlaylist: '재생 목록 완료 후 종료',
|
||||||
hours: '시간',
|
activeUntilEnd: '목록 끝까지 재생',
|
||||||
songs: '곡',
|
minutes: '분',
|
||||||
set: '설정',
|
hours: '시간',
|
||||||
timerSetSuccess: '{minutes}분 후 종료로 설정됨',
|
songs: '곡',
|
||||||
songsSetSuccess: '{songs}곡 재생 후 종료로 설정됨',
|
set: '설정',
|
||||||
playlistEndSetSuccess: '재생 목록 완료 후 종료로 설정됨',
|
timerSetSuccess: '{minutes}분 후 종료로 설정됨',
|
||||||
timerCancelled: '타이머 종료 취소됨',
|
songsSetSuccess: '{songs}곡 재생 후 종료로 설정됨',
|
||||||
timerEnded: '타이머 종료 실행됨',
|
playlistEndSetSuccess: '재생 목록 완료 후 종료로 설정됨',
|
||||||
playbackStopped: '음악 재생이 중지됨',
|
timerCancelled: '타이머 종료 취소됨',
|
||||||
minutesRemaining: '남은 시간 {minutes}분',
|
timerEnded: '타이머 종료 실행됨',
|
||||||
songsRemaining: '남은 곡 수 {count}곡'
|
playbackStopped: '음악 재생이 중지됨',
|
||||||
},
|
minutesRemaining: '남은 시간 {minutes}분',
|
||||||
playList: {
|
songsRemaining: '남은 곡 수 {count}곡'
|
||||||
clearAll: '재생 목록 비우기',
|
},
|
||||||
alreadyEmpty: '재생 목록이 이미 비어있습니다',
|
playList: {
|
||||||
cleared: '재생 목록이 비워졌습니다',
|
clearAll: '재생 목록 비우기',
|
||||||
empty: '재생 목록이 비어있습니다',
|
alreadyEmpty: '재생 목록이 이미 비어있습니다',
|
||||||
clearConfirmTitle: '재생 목록 비우기',
|
cleared: '재생 목록이 비워졌습니다',
|
||||||
clearConfirmContent: '재생 목록의 모든 곡을 삭제하고 현재 재생을 중지합니다. 계속하시겠습니까?'
|
empty: '재생 목록이 비어있습니다',
|
||||||
}
|
clearConfirmTitle: '재생 목록 비우기',
|
||||||
|
clearConfirmContent: '재생 목록의 모든 곡을 삭제하고 현재 재생을 중지합니다. 계속하시겠습니까?'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -39,7 +39,9 @@ export default {
|
|||||||
warning: '请选择一个音源',
|
warning: '请选择一个音源',
|
||||||
bilibiliNotSupported: 'B站视频不支持重新解析',
|
bilibiliNotSupported: 'B站视频不支持重新解析',
|
||||||
processing: '解析中...',
|
processing: '解析中...',
|
||||||
clear: '清除自定义音源'
|
clear: '清除自定义音源',
|
||||||
|
customApiFailed: '自定义API解析失败,正在尝试使用内置音源...',
|
||||||
|
customApiError: '自定义API请求出错,正在尝试使用内置音源...'
|
||||||
},
|
},
|
||||||
playBar: {
|
playBar: {
|
||||||
expand: '展开歌词',
|
expand: '展开歌词',
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ export default {
|
|||||||
warning: '請選擇一個音源',
|
warning: '請選擇一個音源',
|
||||||
bilibiliNotSupported: 'B站影片不支援重新解析',
|
bilibiliNotSupported: 'B站影片不支援重新解析',
|
||||||
processing: '解析中...',
|
processing: '解析中...',
|
||||||
clear: '清除自訂音源'
|
clear: '清除自訂音源',
|
||||||
|
customApiFailed: '自定義API解析失敗,正在嘗試使用內置音源...',
|
||||||
|
customApiError: '自定義API請求出錯,正在嘗試使用內置音源...'
|
||||||
},
|
},
|
||||||
playBar: {
|
playBar: {
|
||||||
expand: '展開歌詞',
|
expand: '展開歌詞',
|
||||||
|
|||||||
+1
-2
@@ -27,8 +27,7 @@
|
|||||||
"showTopAction": false,
|
"showTopAction": false,
|
||||||
"contentZoomFactor": 1,
|
"contentZoomFactor": 1,
|
||||||
"autoTheme": false,
|
"autoTheme": false,
|
||||||
"manualTheme": "light"
|
|
||||||
"manualTheme": "light",
|
"manualTheme": "light",
|
||||||
"customApiPlugin": "",
|
"customApiPlugin": "",
|
||||||
"customApiPluginName": "",
|
"customApiPluginName": ""
|
||||||
}
|
}
|
||||||
|
|||||||
+115
-59
@@ -9,9 +9,9 @@ import request from '@/utils/request';
|
|||||||
import requestMusic from '@/utils/request_music';
|
import requestMusic from '@/utils/request_music';
|
||||||
|
|
||||||
import { searchAndGetBilibiliAudioUrl } from './bilibili';
|
import { searchAndGetBilibiliAudioUrl } from './bilibili';
|
||||||
|
import type { ParsedMusicResult } from './gdmusic';
|
||||||
import { parseFromGDMusic } from './gdmusic';
|
import { parseFromGDMusic } from './gdmusic';
|
||||||
import { parseFromCustomApi } from './parseFromCustomApi';
|
import { parseFromCustomApi } from './parseFromCustomApi';
|
||||||
import type { ParsedMusicResult } from './gdmusic';
|
|
||||||
|
|
||||||
const { addData, getData, deleteData } = musicDB;
|
const { addData, getData, deleteData } = musicDB;
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ export const getMusicUrl = async (id: number, isDownloaded: boolean = false) =>
|
|||||||
params: {
|
params: {
|
||||||
id,
|
id,
|
||||||
level: settingStore.setData.musicQuality || 'higher',
|
level: settingStore.setData.musicQuality || 'higher',
|
||||||
|
encodeType: settingStore.setData.musicQuality == 'lossless' ? 'aac' : 'flac',
|
||||||
|
// level为lossless时,encodeType=flac时网易云会返回hires音质,encodeType=aac时网易云会返回lossless音质
|
||||||
cookie: `${localStorage.getItem('token')} os=pc;`
|
cookie: `${localStorage.getItem('token')} os=pc;`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -47,7 +49,8 @@ export const getMusicUrl = async (id: number, isDownloaded: boolean = false) =>
|
|||||||
return await request.get('/song/url/v1', {
|
return await request.get('/song/url/v1', {
|
||||||
params: {
|
params: {
|
||||||
id,
|
id,
|
||||||
level: settingStore.setData.musicQuality || 'higher'
|
level: settingStore.setData.musicQuality || 'higher',
|
||||||
|
encodeType: settingStore.setData.musicQuality == 'lossless' ? 'aac' : 'flac'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -116,7 +119,8 @@ const getBilibiliAudio = async (data: SongResult) => {
|
|||||||
* @param data 歌曲数据
|
* @param data 歌曲数据
|
||||||
* @returns 解析结果,失败时返回null
|
* @returns 解析结果,失败时返回null
|
||||||
*/
|
*/
|
||||||
const getGDMusicAudio = async (id: number, data: SongResult): Promise<ParsedMusicResult | null> => { // <-- 在这里明确声明返回类型
|
const getGDMusicAudio = async (id: number, data: SongResult): Promise<ParsedMusicResult | null> => {
|
||||||
|
// <-- 在这里明确声明返回类型
|
||||||
try {
|
try {
|
||||||
const gdResult = await parseFromGDMusic(id, data, '999');
|
const gdResult = await parseFromGDMusic(id, data, '999');
|
||||||
if (gdResult) {
|
if (gdResult) {
|
||||||
@@ -148,74 +152,120 @@ const getUnblockMusicAudio = (id: number, data: SongResult, sources: any[]) => {
|
|||||||
* @returns 解析结果
|
* @returns 解析结果
|
||||||
*/
|
*/
|
||||||
export const getParsingMusicUrl = async (id: number, data: SongResult) => {
|
export const getParsingMusicUrl = async (id: number, data: SongResult) => {
|
||||||
if (isElectron) {
|
try {
|
||||||
const settingStore = useSettingsStore();
|
if (isElectron) {
|
||||||
|
let musicSources: any[] = [];
|
||||||
|
let quality: string = 'higher';
|
||||||
|
try {
|
||||||
|
const settingStore = useSettingsStore();
|
||||||
|
const enableMusicUnblock = settingStore?.setData?.enableMusicUnblock;
|
||||||
|
|
||||||
// 如果禁用了音乐解析功能,则直接返回空结果
|
// 如果禁用了音乐解析功能,则直接返回空结果
|
||||||
if (!settingStore.setData.enableMusicUnblock) {
|
if (!enableMusicUnblock) {
|
||||||
return Promise.resolve({ data: { code: 404, message: '音乐解析功能已禁用' } });
|
return Promise.resolve({ data: { code: 404, message: '音乐解析功能已禁用' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 确定使用的音源列表(自定义或全局)
|
// 1. 确定使用的音源列表(自定义或全局)
|
||||||
const songId = String(id);
|
const songId = String(id);
|
||||||
const savedSourceStr = localStorage.getItem(`song_source_${songId}`);
|
const savedSourceStr = (() => {
|
||||||
let musicSources: any[] = [];
|
try {
|
||||||
|
return localStorage.getItem(`song_source_${songId}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('读取本地存储失败,忽略自定义音源', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
try {
|
if (savedSourceStr) {
|
||||||
if (savedSourceStr) {
|
try {
|
||||||
// 使用自定义音源
|
musicSources = JSON.parse(savedSourceStr);
|
||||||
musicSources = JSON.parse(savedSourceStr);
|
console.log(`使用歌曲 ${id} 自定义音源:`, musicSources);
|
||||||
console.log(`使用歌曲 ${id} 自定义音源:`, musicSources);
|
} catch (e) {
|
||||||
} else {
|
console.error('解析音源设置失败,回退到默认全局设置', e);
|
||||||
// 使用全局音源设置
|
musicSources = settingStore?.setData?.enabledMusicSources || [];
|
||||||
musicSources = settingStore.setData.enabledMusicSources || [];
|
}
|
||||||
console.log(`使用全局音源设置:`, musicSources);
|
} else {
|
||||||
|
// 使用全局音源设置
|
||||||
|
musicSources = settingStore?.setData?.enabledMusicSources || [];
|
||||||
|
console.log(`使用全局音源设置:`, musicSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
quality = settingStore?.setData?.musicQuality || 'higher';
|
||||||
|
} catch (e) {
|
||||||
|
console.error('读取设置失败,使用默认配置', e);
|
||||||
|
musicSources = [];
|
||||||
|
quality = 'higher';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('解析音源设置失败,回退到默认全局设置', e);
|
|
||||||
musicSources = settingStore.setData.enabledMusicSources || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const quality = settingStore.setData.musicQuality || 'higher';
|
// 优先级 1: 自定义 API
|
||||||
|
try {
|
||||||
// 优先级 1: 自定义 API
|
const hasCustom = Array.isArray(musicSources) && musicSources.includes('custom');
|
||||||
if (musicSources.includes('custom') && settingStore.setData.customApiPlugin) {
|
const customEnabled = (() => {
|
||||||
console.log('尝试使用 自定义API 解析...');
|
try {
|
||||||
const customResult = await parseFromCustomApi(id, data, quality);
|
const st = useSettingsStore();
|
||||||
if (customResult) {
|
return Boolean(st?.setData?.customApiPlugin);
|
||||||
return customResult; // 成功则直接返回
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
if (hasCustom && customEnabled) {
|
||||||
|
console.log('尝试使用 自定义API 解析...');
|
||||||
|
const customResult = await parseFromCustomApi(id, data, quality);
|
||||||
|
if (customResult) {
|
||||||
|
return customResult; // 成功则直接返回
|
||||||
|
}
|
||||||
|
console.log('自定义API解析失败,继续尝试其他音源...');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('自定义API解析发生异常,继续尝试其他音源', e);
|
||||||
}
|
}
|
||||||
console.log('自定义API解析失败,继续尝试其他音源...');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优先级 2: Bilibili
|
// 优先级 2: Bilibili
|
||||||
if (musicSources.includes('bilibili')) {
|
try {
|
||||||
console.log('尝试使用 Bilibili 解析...');
|
if (Array.isArray(musicSources) && musicSources.includes('bilibili')) {
|
||||||
const bilibiliResult = await getBilibiliAudio(data);
|
console.log('尝试使用 Bilibili 解析...');
|
||||||
if (bilibiliResult?.data?.data?.url) { // 检查返回的 URL 是否有效
|
const bilibiliResult = await getBilibiliAudio(data);
|
||||||
return bilibiliResult;
|
if (bilibiliResult?.data?.data?.url) {
|
||||||
|
return bilibiliResult;
|
||||||
|
}
|
||||||
|
console.log('Bilibili解析失败,继续尝试其他音源...');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Bilibili解析发生异常,继续尝试其他音源', e);
|
||||||
}
|
}
|
||||||
console.log('Bilibili解析失败,继续尝试其他音源...');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优先级 3: GD 音乐台
|
// 优先级 3: GD 音乐台
|
||||||
if (musicSources.includes('gdmusic')) {
|
try {
|
||||||
console.log('尝试使用 GD音乐台 解析...');
|
if (Array.isArray(musicSources) && musicSources.includes('gdmusic')) {
|
||||||
const gdResult = await getGDMusicAudio(id, data);
|
console.log('尝试使用 GD音乐台 解析...');
|
||||||
if (gdResult) {
|
const gdResult = await getGDMusicAudio(id, data);
|
||||||
return gdResult;
|
if (gdResult) {
|
||||||
|
return gdResult;
|
||||||
|
}
|
||||||
|
console.log('GD音乐台解析失败,继续尝试其他音源...');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('GD音乐台解析发生异常,继续尝试其他音源', e);
|
||||||
}
|
}
|
||||||
console.log('GD音乐台解析失败,继续尝试其他音源...');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优先级 4: UnblockMusic (migu, kugou, pyncmd)
|
// 优先级 4: UnblockMusic (migu, kugou, pyncmd)
|
||||||
const unblockSources = musicSources.filter(
|
try {
|
||||||
source => !['custom', 'bilibili', 'gdmusic'].includes(source)
|
const unblockSources = (Array.isArray(musicSources) ? musicSources : []).filter(
|
||||||
);
|
(source) => !['custom', 'bilibili', 'gdmusic'].includes(source)
|
||||||
if (unblockSources.length > 0) {
|
);
|
||||||
console.log('尝试使用 UnblockMusic 解析:', unblockSources);
|
if (unblockSources.length > 0) {
|
||||||
return getUnblockMusicAudio(id, data, unblockSources);
|
console.log('尝试使用 UnblockMusic 解析:', unblockSources);
|
||||||
|
// 捕获内部可能的异常
|
||||||
|
return await getUnblockMusicAudio(id, data, unblockSources);
|
||||||
|
} else {
|
||||||
|
console.warn('UnblockMusic API 不可用,跳过此解析方式');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('UnblockMusic 解析发生异常,继续后备方案', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('getParsingMusicUrl 执行异常,将使用后备方案:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 后备方案:使用API请求
|
// 后备方案:使用API请求
|
||||||
@@ -228,6 +278,12 @@ export const likeSong = (id: number, like: boolean = true) => {
|
|||||||
return request.get('/like', { params: { id, like } });
|
return request.get('/like', { params: { id, like } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 将每日推荐中的歌曲标记为不感兴趣,并获取一首新歌
|
||||||
|
export const dislikeRecommendedSong = (id: number | string) => {
|
||||||
|
return request.get('/recommend/songs/dislike', {
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
};
|
||||||
// 获取用户喜欢的音乐列表
|
// 获取用户喜欢的音乐列表
|
||||||
export const getLikedList = (uid: number) => {
|
export const getLikedList = (uid: number) => {
|
||||||
return request.get('/likelist', {
|
return request.get('/likelist', {
|
||||||
|
|||||||
@@ -1,106 +1,107 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import {get} from 'lodash';
|
import { get } from 'lodash';
|
||||||
import {useSettingsStore} from '@/store';
|
|
||||||
|
|
||||||
// 从同级目录的 gdmusic.ts 导入类型,确保兼容性
|
import { useSettingsStore } from '@/store';
|
||||||
import type {ParsedMusicResult} from './gdmusic';
|
|
||||||
|
import type { ParsedMusicResult } from './gdmusic';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义自定义API JSON插件的结构
|
* 定义自定义API JSON插件的结构
|
||||||
*/
|
*/
|
||||||
interface CustomApiPlugin {
|
interface CustomApiPlugin {
|
||||||
name: string;
|
name: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
method?: 'GET' | 'POST';
|
method?: 'GET' | 'POST';
|
||||||
params: Record<string, string>;
|
params: Record<string, string>;
|
||||||
qualityMapping?: Record<string, string>;
|
qualityMapping?: Record<string, string>;
|
||||||
responseUrlPath: string;
|
responseUrlPath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从用户导入的自定义API JSON配置中解析音乐URL
|
* 从用户导入的自定义API JSON配置中解析音乐URL
|
||||||
*/
|
*/
|
||||||
export const parseFromCustomApi = async (
|
export const parseFromCustomApi = async (
|
||||||
id: number,
|
id: number,
|
||||||
_songData: any,
|
_songData: any,
|
||||||
quality: string = 'higher',
|
quality: string = 'higher',
|
||||||
timeout: number = 10000
|
timeout: number = 10000
|
||||||
): Promise<ParsedMusicResult | null> => {
|
): Promise<ParsedMusicResult | null> => {
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const pluginString = settingsStore.setData.customApiPlugin;
|
const pluginString = settingsStore.setData.customApiPlugin;
|
||||||
|
|
||||||
if (!pluginString) {
|
if (!pluginString) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let plugin: CustomApiPlugin;
|
||||||
|
try {
|
||||||
|
plugin = JSON.parse(pluginString);
|
||||||
|
if (!plugin.apiUrl || !plugin.params || !plugin.responseUrlPath) {
|
||||||
|
console.error('自定义API:JSON配置文件格式不正确。');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('自定义API:解析JSON配置文件失败。', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`自定义API:正在使用插件 [${plugin.name}] 进行解析...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 准备请求参数,替换占位符
|
||||||
|
const finalParams: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(plugin.params)) {
|
||||||
|
if (value === '{songId}') {
|
||||||
|
finalParams[key] = String(id);
|
||||||
|
} else if (value === '{quality}') {
|
||||||
|
// 使用 qualityMapping (如果存在) 进行音质翻译,否则直接使用原quality
|
||||||
|
finalParams[key] = plugin.qualityMapping?.[quality] ?? quality;
|
||||||
|
} else {
|
||||||
|
// 固定值参数
|
||||||
|
finalParams[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let plugin: CustomApiPlugin;
|
// 2. 判断请求方法,默认为GET
|
||||||
try {
|
const method = plugin.method?.toUpperCase() === 'POST' ? 'POST' : 'GET';
|
||||||
plugin = JSON.parse(pluginString);
|
let response;
|
||||||
if (!plugin.apiUrl || !plugin.params || !plugin.responseUrlPath) {
|
|
||||||
console.error('自定义API:JSON配置文件格式不正确。');
|
// 3. 根据方法发送不同的请求
|
||||||
return null;
|
if (method === 'POST') {
|
||||||
}
|
console.log('自定义API:发送 POST 请求到:', plugin.apiUrl, '参数:', finalParams);
|
||||||
} catch (error) {
|
response = await axios.post(plugin.apiUrl, finalParams, { timeout });
|
||||||
console.error('自定义API:解析JSON配置文件失败。', error);
|
} else {
|
||||||
return null;
|
// 默认为 GET
|
||||||
|
const finalUrl = `${plugin.apiUrl}?${new URLSearchParams(finalParams).toString()}`;
|
||||||
|
console.log('自定义API:发送 GET 请求到:', finalUrl);
|
||||||
|
response = await axios.get(finalUrl, { timeout });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`自定义API:正在使用插件 [${plugin.name}] 进行解析...`);
|
// 4. 使用 lodash.get 安全地从响应数据中提取URL
|
||||||
|
const musicUrl = get(response.data, plugin.responseUrlPath);
|
||||||
|
|
||||||
try {
|
if (musicUrl && typeof musicUrl === 'string') {
|
||||||
// 1. 准备请求参数,替换占位符
|
console.log('自定义API:成功获取URL!');
|
||||||
const finalParams: Record<string, string> = {};
|
// 5. 组装成应用所需的标准格式并返回
|
||||||
for (const [key, value] of Object.entries(plugin.params)) {
|
return {
|
||||||
if (value === '{songId}') {
|
data: {
|
||||||
finalParams[key] = String(id);
|
data: {
|
||||||
} else if (value === '{quality}') {
|
url: musicUrl,
|
||||||
// 使用 qualityMapping (如果存在) 进行音质翻译,否则直接使用原quality
|
br: parseInt(quality) * 1000,
|
||||||
finalParams[key] = plugin.qualityMapping?.[quality] ?? quality;
|
size: 0,
|
||||||
} else {
|
md5: '',
|
||||||
// 固定值参数
|
platform: plugin.name.toLowerCase().replace(/\s/g, ''),
|
||||||
finalParams[key] = value;
|
gain: 0
|
||||||
}
|
},
|
||||||
|
params: { id, type: 'song' }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// 2. 判断请求方法,默认为GET
|
} else {
|
||||||
const method = plugin.method?.toUpperCase() === 'POST' ? 'POST' : 'GET';
|
console.error('自定义API:根据路径未能从响应中找到URL:', plugin.responseUrlPath);
|
||||||
let response;
|
return null;
|
||||||
|
|
||||||
// 3. 根据方法发送不同的请求
|
|
||||||
if (method === 'POST') {
|
|
||||||
console.log('自定义API:发送 POST 请求到:', plugin.apiUrl, '参数:', finalParams);
|
|
||||||
response = await axios.post(plugin.apiUrl, finalParams, { timeout });
|
|
||||||
} else { // 默认为 GET
|
|
||||||
const finalUrl = `${plugin.apiUrl}?${new URLSearchParams(finalParams).toString()}`;
|
|
||||||
console.log('自定义API:发送 GET 请求到:', finalUrl);
|
|
||||||
response = await axios.get(finalUrl, { timeout });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 使用 lodash.get 安全地从响应数据中提取URL
|
|
||||||
const musicUrl = get(response.data, plugin.responseUrlPath);
|
|
||||||
|
|
||||||
if (musicUrl && typeof musicUrl === 'string') {
|
|
||||||
console.log('自定义API:成功获取URL!');
|
|
||||||
// 5. 组装成应用所需的标准格式并返回
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
data: {
|
|
||||||
url: musicUrl,
|
|
||||||
br: parseInt(quality) * 1000,
|
|
||||||
size: 0,
|
|
||||||
md5: '',
|
|
||||||
platform: plugin.name.toLowerCase().replace(/\s/g, ''),
|
|
||||||
gain: 0
|
|
||||||
},
|
|
||||||
params: { id, type: 'song' }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
console.error('自定义API:根据路径未能从响应中找到URL:', plugin.responseUrlPath);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`自定义API [${plugin.name}] 执行失败:`, error);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
} catch (error) {
|
||||||
|
console.error(`自定义API [${plugin.name}] 执行失败:`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-modal
|
<n-modal
|
||||||
v-model:show="visible"
|
v-model:show="visible"
|
||||||
preset="dialog"
|
preset="dialog"
|
||||||
:title="t('settings.playback.musicSources')"
|
:title="t('settings.playback.musicSources')"
|
||||||
:positive-text="t('common.confirm')"
|
:positive-text="t('common.confirm')"
|
||||||
:negative-text="t('common.cancel')"
|
:negative-text="t('common.cancel')"
|
||||||
@positive-click="handleConfirm"
|
@positive-click="handleConfirm"
|
||||||
@negative-click="handleCancel"
|
@negative-click="handleCancel"
|
||||||
>
|
>
|
||||||
<n-space vertical>
|
<n-space vertical>
|
||||||
<p>{{ t('settings.playback.musicSourcesDesc') }}</p>
|
<p>{{ t('settings.playback.musicSourcesDesc') }}</p>
|
||||||
@@ -59,7 +59,6 @@
|
|||||||
<p v-else class="text-sm text-gray-500">尚未导入</p>
|
<p v-else class="text-sm text-gray-500">尚未导入</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</template>
|
</template>
|
||||||
@@ -68,8 +67,8 @@
|
|||||||
import { useMessage } from 'naive-ui';
|
import { useMessage } from 'naive-ui';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useSettingsStore } from '@/store';
|
|
||||||
|
|
||||||
|
import { useSettingsStore } from '@/store';
|
||||||
import { type Platform } from '@/types/music';
|
import { type Platform } from '@/types/music';
|
||||||
|
|
||||||
// 扩展 Platform 类型以包含 'custom'
|
// 扩展 Platform 类型以包含 'custom'
|
||||||
@@ -120,45 +119,48 @@ const importPlugin = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 监听自定义插件内容的变化。如果用户清除了插件,要确保 'custom' 选项被取消勾选
|
// 监听自定义插件内容的变化。如果用户清除了插件,要确保 'custom' 选项被取消勾选
|
||||||
watch(() => settingsStore.setData.customApiPlugin, (newPluginContent) => {
|
watch(
|
||||||
if (!newPluginContent) {
|
() => settingsStore.setData.customApiPlugin,
|
||||||
const index = selectedSources.value.indexOf('custom');
|
(newPluginContent) => {
|
||||||
if (index > -1) {
|
if (!newPluginContent) {
|
||||||
selectedSources.value.splice(index, 1);
|
const index = selectedSources.value.indexOf('custom');
|
||||||
|
if (index > -1) {
|
||||||
|
selectedSources.value.splice(index, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
// 同步外部show属性变化
|
// 同步外部show属性变化
|
||||||
watch(
|
watch(
|
||||||
() => props.show,
|
() => props.show,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
visible.value = newVal;
|
visible.value = newVal;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 同步内部visible变化
|
// 同步内部visible变化
|
||||||
watch(
|
watch(
|
||||||
() => visible.value,
|
() => visible.value,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
emit('update:show', newVal);
|
emit('update:show', newVal);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 同步外部sources属性变化
|
// 同步外部sources属性变化
|
||||||
watch(
|
watch(
|
||||||
() => props.sources,
|
() => props.sources,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
selectedSources.value = [...newVal];
|
selectedSources.value = [...newVal];
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
// 确保至少选择一个音源
|
// 确保至少选择一个音源
|
||||||
const defaultPlatforms = ['migu', 'kugou', 'pyncmd', 'bilibili'];
|
const defaultPlatforms = ['migu', 'kugou', 'pyncmd', 'bilibili'];
|
||||||
const valuesToEmit =
|
const valuesToEmit =
|
||||||
selectedSources.value.length > 0 ? [...new Set(selectedSources.value)] : defaultPlatforms;
|
selectedSources.value.length > 0 ? [...new Set(selectedSources.value)] : defaultPlatforms;
|
||||||
emit('update:sources', valuesToEmit);
|
emit('update:sources', valuesToEmit);
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
};
|
};
|
||||||
@@ -167,4 +169,4 @@ const handleCancel = () => {
|
|||||||
selectedSources.value = [...props.sources];
|
selectedSources.value = [...props.sources];
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -122,7 +122,9 @@ export const getSongUrl = async (
|
|||||||
try {
|
try {
|
||||||
const songSources = JSON.parse(savedSourceStr);
|
const songSources = JSON.parse(savedSourceStr);
|
||||||
useCustomApiForSong = songSources.includes('custom');
|
useCustomApiForSong = songSources.includes('custom');
|
||||||
} catch (e) { /* ignore parsing error */ }
|
} catch (e) {
|
||||||
|
console.error('解析歌曲音源设置失败:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果全局或歌曲专属设置中启用了自定义API,则最优先尝试
|
// 如果全局或歌曲专属设置中启用了自定义API,则最优先尝试
|
||||||
@@ -140,11 +142,11 @@ export const getSongUrl = async (
|
|||||||
} else {
|
} else {
|
||||||
// 自定义API失败,给出提示,然后继续走默认流程
|
// 自定义API失败,给出提示,然后继续走默认流程
|
||||||
console.log('自定义API解析失败,将使用默认降级流程...');
|
console.log('自定义API解析失败,将使用默认降级流程...');
|
||||||
message.warning('自定义API解析失败,正在尝试使用内置音源...'); // 给用户一个提示
|
message.warning(i18n.global.t('player.reparse.customApiFailed')); // 给用户一个提示
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('调用自定义API时发生错误:', error);
|
console.error('调用自定义API时发生错误:', error);
|
||||||
message.error('自定义API请求出错,正在尝试使用内置音源...');
|
message.error(i18n.global.t('player.reparse.customApiError'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 如果自定义API失败或未启用,则执行【原有】的解析流程
|
// 如果自定义API失败或未启用,则执行【原有】的解析流程
|
||||||
|
|||||||
Reference in New Issue
Block a user