增加音质切换功能

This commit is contained in:
Folltoshe 2023-06-27 21:18:13 +08:00
parent a597249744
commit edf82686c5
9 changed files with 114 additions and 21 deletions

View File

@ -77,6 +77,8 @@ export const DOWNLOAD_STATUS = {
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const
export const QUALITYS_SUPPORT = ['128k', '320k', 'flac', 'flac24bit'] as const
export const SYNC_CODE = { export const SYNC_CODE = {
helloMsg: 'Hello~::^-^::~v3~', helloMsg: 'Hello~::^-^::~v3~',
idPrefix: 'OjppZDo6', idPrefix: 'OjppZDo6',

View File

@ -24,7 +24,8 @@ const defaultSetting: LX.AppSetting = {
'player.startupAutoPlay': false, 'player.startupAutoPlay': false,
'player.togglePlayMethod': 'listLoop', 'player.togglePlayMethod': 'listLoop',
'player.highQuality': false, 'player.firstPlayQuality': '128k',
'player.autoLowerQualityOnError': false,
'player.isShowTaskProgess': true, 'player.isShowTaskProgess': true,
'player.volume': 1, 'player.volume': 1,
'player.isMute': false, 'player.isMute': false,

View File

@ -89,9 +89,14 @@ declare global {
'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none' 'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none'
/** /**
* 320k音 *
*/ */
'player.highQuality': boolean 'player.firstPlayQuality': LX.Quality
/**
*
*/
'player.autoLowerQualityOnError': boolean
/** /**
* *

View File

@ -488,6 +488,9 @@
"setting__play_lyric_roma": "Show lyrics roman", "setting__play_lyric_roma": "Show lyrics roman",
"setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese", "setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese",
"setting__play_lyric_transition": "Show lyrics translation", "setting__play_lyric_transition": "Show lyrics translation",
"setting__play_quality_auto_lower_quality_on_error": "Automatically lower the sound quality when getting the current sound quality error",
"setting__play_quality_first": "Play sound quality first",
"setting__play_quality_first_title": "Select priority playback sound quality",
"setting__play_mediaDevice": "Audio output", "setting__play_mediaDevice": "Audio output",
"setting__play_mediaDevice_remove_stop_play": "Pause the song when the current sound output device is changed", "setting__play_mediaDevice_remove_stop_play": "Pause the song when the current sound output device is changed",
"setting__play_mediaDevice_title": "Select a media device for audio output", "setting__play_mediaDevice_title": "Select a media device for audio output",

View File

@ -487,6 +487,9 @@
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)", "setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
"setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文", "setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文",
"setting__play_lyric_transition": "显示歌词翻译(如果可用)", "setting__play_lyric_transition": "显示歌词翻译(如果可用)",
"setting__play_quality_auto_lower_quality_on_error": "获取当前音质错误时自动降低音质",
"setting__play_quality_first": "优先播放音质",
"setting__play_quality_first_title": "选择优先播放音质",
"setting__play_mediaDevice": "音频输出", "setting__play_mediaDevice": "音频输出",
"setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲", "setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
"setting__play_mediaDevice_title": "选择声音输出的媒体设备", "setting__play_mediaDevice_title": "选择声音输出的媒体设备",

View File

@ -488,6 +488,9 @@
"setting__play_lyric_roma": "顯示歌詞羅馬音(如果可用)", "setting__play_lyric_roma": "顯示歌詞羅馬音(如果可用)",
"setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文", "setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文",
"setting__play_lyric_transition": "顯示歌詞翻譯(如果可用)", "setting__play_lyric_transition": "顯示歌詞翻譯(如果可用)",
"setting__play_quality_auto_lower_quality_on_error": "獲取當前音質錯誤時自動降低音質",
"setting__play_quality_first": "優先播放音質",
"setting__play_quality_first_title": "選擇優先播放音質",
"setting__play_mediaDevice": "音頻輸出", "setting__play_mediaDevice": "音頻輸出",
"setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲", "setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲",
"setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備", "setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備",

View File

@ -51,11 +51,11 @@ export const getMusicUrl = async({ musicInfo, quality, isRefresh, allowToggleSou
// // return Promise.reject(new Error('该歌曲没有可播放的音频')) // // return Promise.reject(new Error('该歌曲没有可播放的音频'))
// } // }
const targetQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo) const targetQuality = quality ?? getPlayQuality(musicInfo)
const cachedUrl = await getStoreMusicUrl(musicInfo, targetQuality) const cachedUrl = await getStoreMusicUrl(musicInfo, targetQuality)
if (cachedUrl && !isRefresh) return cachedUrl if (cachedUrl && !isRefresh) return cachedUrl
return await handleGetOnlineMusicUrl({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource }).then(({ url, quality: targetQuality, musicInfo: targetMusicInfo, isFromCache }) => { return await handleGetOnlineMusicUrl({ musicInfo, quality: targetQuality, onToggleSource, isRefresh, allowToggleSource }).then(({ url, quality: targetQuality, musicInfo: targetMusicInfo, isFromCache }) => {
if (targetMusicInfo.id != musicInfo.id && !isFromCache) void saveMusicUrl(targetMusicInfo, targetQuality, url) if (targetMusicInfo.id != musicInfo.id && !isFromCache) void saveMusicUrl(targetMusicInfo, targetQuality, url)
void saveMusicUrl(musicInfo, targetQuality, url) void saveMusicUrl(musicInfo, targetQuality, url)
return url return url

View File

@ -10,6 +10,7 @@ import {
import { appSetting } from '@renderer/store/setting' import { appSetting } from '@renderer/store/setting'
import { langS2T, toNewMusicInfo, toOldMusicInfo } from '@renderer/utils' import { langS2T, toNewMusicInfo, toOldMusicInfo } from '@renderer/utils'
import { requestMsg } from '@renderer/utils/message' import { requestMsg } from '@renderer/utils/message'
import { QUALITYS } from '@common/constants'
const getOtherSourcePromises = new Map() const getOtherSourcePromises = new Map()
@ -138,16 +139,33 @@ export const getCachedLyricInfo = async(musicInfo: LX.Music.MusicInfo): Promise<
return null return null
} }
export const getPlayQuality = (highQuality: boolean, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => { const sliceQualityList = (startQuality: LX.Quality, skipNow?: boolean) => {
let type: LX.Quality = '128k' let startNum = QUALITYS.indexOf(startQuality)
let list = qualityList.value[musicInfo.source] if (skipNow) startNum = QUALITYS.indexOf(startQuality) + 1
if (highQuality && musicInfo.meta._qualitys['320k'] && list && list.includes('320k')) type = '320k'
return type const list = QUALITYS.slice(startNum)
return list
} }
export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggleSource, isRefresh, retryedSource = [] }: { export const getPlayQuality = (musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
let list = qualityList.value[musicInfo.source]
let firstPlayQuality: LX.Quality = appSetting['player.firstPlayQuality']
if (!list) return firstPlayQuality
if (!list.includes(firstPlayQuality)) firstPlayQuality = list[list.length - 1]
const rangeType = sliceQualityList(firstPlayQuality)
for (const quality of rangeType) {
if (musicInfo.meta._qualitys[quality]) return quality
}
return '128k'
}
export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggleSource, isRefresh, retryedSource = [], rawQuality = '128k' }: {
musicInfos: LX.Music.MusicInfoOnline[] musicInfos: LX.Music.MusicInfoOnline[]
quality?: LX.Quality quality?: LX.Quality
rawQuality?: LX.Quality
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
isRefresh: boolean isRefresh: boolean
retryedSource?: LX.OnlineSource[] retryedSource?: LX.OnlineSource[]
@ -164,14 +182,23 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
if (retryedSource.includes(musicInfo.source)) continue if (retryedSource.includes(musicInfo.source)) continue
retryedSource.push(musicInfo.source) retryedSource.push(musicInfo.source)
if (!assertApiSupport(musicInfo.source)) continue if (!assertApiSupport(musicInfo.source)) continue
itemQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo) itemQuality = quality ?? getPlayQuality(musicInfo)
if (!musicInfo.meta._qualitys[itemQuality]) continue if (!musicInfo.meta._qualitys[itemQuality]) continue
console.log('try toggle to: ', musicInfo.source, musicInfo.name, musicInfo.singer, musicInfo.interval) console.log('try toggle to: ', musicInfo.source, musicInfo.name, musicInfo.singer, musicInfo.interval)
onToggleSource(musicInfo) onToggleSource(musicInfo)
break break
} }
if (!musicInfo || !itemQuality) throw new Error(window.i18n.t('toggle_source_failed'))
if (!musicInfo || !itemQuality) {
const rangeType = sliceQualityList(rawQuality, true)
if (appSetting['player.autoLowerQualityOnError'] && rawQuality != '128k' && rangeType.length > 0) {
for (const type of rangeType) {
if (musicInfo.meta._qualitys[type]) return getOnlineOtherSourceMusicUrl({ musicInfos, quality: type, onToggleSource, isRefresh, retryedSource, rawQuality })
}
}
throw new Error(window.i18n.t('toggle_source_failed'))
}
const cachedUrl = await getStoreMusicUrl(musicInfo, itemQuality) const cachedUrl = await getStoreMusicUrl(musicInfo, itemQuality)
if (cachedUrl && !isRefresh) return { url: cachedUrl, musicInfo, quality: itemQuality, isFromCache: true } if (cachedUrl && !isRefresh) return { url: cachedUrl, musicInfo, quality: itemQuality, isFromCache: true }
@ -190,16 +217,17 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
}).catch((err: any) => { }).catch((err: any) => {
if (err.message == requestMsg.tooManyRequests) throw err if (err.message == requestMsg.tooManyRequests) throw err
console.log(err) console.log(err)
return getOnlineOtherSourceMusicUrl({ musicInfos, quality, onToggleSource, isRefresh, retryedSource }) return getOnlineOtherSourceMusicUrl({ musicInfos, quality, onToggleSource, isRefresh, retryedSource, rawQuality })
}) })
} }
/** /**
* 线URL * 线URL
*/ */
export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource }: { export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource, rawQuality }: {
musicInfo: LX.Music.MusicInfoOnline musicInfo: LX.Music.MusicInfoOnline
quality?: LX.Quality quality?: LX.Quality
rawQuality?: LX.Quality
isRefresh: boolean isRefresh: boolean
allowToggleSource: boolean allowToggleSource: boolean
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
@ -209,8 +237,9 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
quality: LX.Quality quality: LX.Quality
isFromCache: boolean isFromCache: boolean
}> => { }> => {
// console.log(musicInfo.source) const targetQuality = quality ?? getPlayQuality(musicInfo)
const targetQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo) rawQuality = rawQuality ?? targetQuality
console.log(targetQuality, rawQuality)
let reqPromise let reqPromise
try { try {
@ -218,12 +247,29 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
} catch (err: any) { } catch (err: any) {
reqPromise = Promise.reject(err) reqPromise = Promise.reject(err)
} }
return reqPromise.then(({ url, type }: { url: string, type: LX.Quality }) => { return reqPromise.then(({ url, type }: { url: string, type: LX.Quality }) => {
return { musicInfo, url, quality: type, isFromCache: false } return { musicInfo, url, quality: type, isFromCache: false }
}).catch(async(err: any) => { }).catch(async(err: any) => {
console.log(err) console.log(err)
const rangeType = sliceQualityList(targetQuality, true)
if (targetQuality != '128k' && rangeType.length > 0 && appSetting['player.autoLowerQualityOnError']) {
for (const type of rangeType) {
if (musicInfo.meta._qualitys[type]) return handleGetOnlineMusicUrl({
musicInfo,
quality: type,
onToggleSource,
isRefresh,
allowToggleSource,
rawQuality
})
}
}
if (!allowToggleSource || err.message == requestMsg.tooManyRequests) throw err if (!allowToggleSource || err.message == requestMsg.tooManyRequests) throw err
onToggleSource() onToggleSource()
// eslint-disable-next-line @typescript-eslint/promise-function-async // eslint-disable-next-line @typescript-eslint/promise-function-async
return await getOtherSource(musicInfo).then(otherSource => { return await getOtherSource(musicInfo).then(otherSource => {
console.log('find otherSource', otherSource) console.log('find otherSource', otherSource)
@ -231,7 +277,8 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
return getOnlineOtherSourceMusicUrl({ return getOnlineOtherSourceMusicUrl({
musicInfos: [...otherSource], musicInfos: [...otherSource],
onToggleSource, onToggleSource,
quality, quality: rawQuality ?? quality,
rawQuality: rawQuality ?? quality,
isRefresh, isRefresh,
retryedSource: [musicInfo.source], retryedSource: [musicInfo.source],
}) })

View File

@ -15,12 +15,18 @@ dd
base-checkbox(id="setting_player_lyric_s2t" :model-value="appSetting['player.isS2t']" :label="$t('setting__play_lyric_s2t')" @update:model-value="updateSetting({'player.isS2t': $event})") base-checkbox(id="setting_player_lyric_s2t" :model-value="appSetting['player.isS2t']" :label="$t('setting__play_lyric_s2t')" @update:model-value="updateSetting({'player.isS2t': $event})")
.gap-top .gap-top
base-checkbox(id="setting_player_lyric_play_lxlrc" :model-value="appSetting['player.isPlayLxlrc']" :label="$t('setting__play_lyric_lxlrc')" @update:model-value="updateSetting({'player.isPlayLxlrc': $event})") base-checkbox(id="setting_player_lyric_play_lxlrc" :model-value="appSetting['player.isPlayLxlrc']" :label="$t('setting__play_lyric_lxlrc')" @update:model-value="updateSetting({'player.isPlayLxlrc': $event})")
.gap-top
base-checkbox(id="setting_player_highQuality" :model-value="appSetting['player.highQuality']" :label="$t('setting__play_quality')" @update:model-value="updateSetting({'player.highQuality': $event})")
.gap-top .gap-top
base-checkbox(id="setting_player_showTaskProgess" :model-value="appSetting['player.isShowTaskProgess']" :label="$t('setting__play_task_bar')" @update:model-value="updateSetting({'player.isShowTaskProgess': $event})") base-checkbox(id="setting_player_showTaskProgess" :model-value="appSetting['player.isShowTaskProgess']" :label="$t('setting__play_task_bar')" @update:model-value="updateSetting({'player.isShowTaskProgess': $event})")
.gap-top .gap-top
base-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" :model-value="appSetting['player.isMediaDeviceRemovedStopPlay']" :label="$t('setting__play_mediaDevice_remove_stop_play')" @update:model-value="updateSetting({'player.isMediaDeviceRemovedStopPlay': $event})") base-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" :model-value="appSetting['player.isMediaDeviceRemovedStopPlay']" :label="$t('setting__play_mediaDevice_remove_stop_play')" @update:model-value="updateSetting({'player.isMediaDeviceRemovedStopPlay': $event})")
.gap-top
base-checkbox(id="setting__play_quality_auto_lower_quality_on_error" :model-value="appSetting['player.autoLowerQualityOnError']" :label="$t('setting__play_quality_auto_lower_quality_on_error')" @update:model-value="updateSetting({'player.autoLowerQualityOnError': $event})")
dd(:aria-label="$t('setting__play_quality_first_title')")
h3#play_firstPlayQuality {{ $t('setting__play_quality_first') }}
div
base-selection.gap-left(:list="playQualityList" item-key="qualityId" item-name="label" :model-value="appSetting['player.firstPlayQuality']" @update:model-value="updateSetting({'player.firstPlayQuality': $event})")
dd(:aria-label="$t('setting__play_mediaDevice_title')") dd(:aria-label="$t('setting__play_mediaDevice_title')")
h3#play_mediaDevice {{ $t('setting__play_mediaDevice') }} h3#play_mediaDevice {{ $t('setting__play_mediaDevice') }}
div div
@ -33,7 +39,7 @@ import { hasInitedAdvancedAudioFeatures } from '@renderer/plugins/player'
import { dialog } from '@renderer/plugins/Dialog' import { dialog } from '@renderer/plugins/Dialog'
import { useI18n } from '@renderer/plugins/i18n' import { useI18n } from '@renderer/plugins/i18n'
import { appSetting, updateSetting } from '@renderer/store/setting' import { appSetting, updateSetting } from '@renderer/store/setting'
import { QUALITYS_SUPPORT } from '@common/constants'
export default { export default {
name: 'SettingPlay', name: 'SettingPlay',
@ -83,12 +89,35 @@ export default {
}) })
const getQualityTypeName = (quality) => {
switch (quality) {
case 'flac24bit':
return t('download__lossless') + ' FLAC Hires'
case 'flac':
case 'ape':
case 'wav':
return t('download__lossless') + ' ' + quality.toUpperCase()
case '320k':
return t('download__high_quality') + ' ' + quality.toUpperCase()
case '192k':
case '128k':
return t('download__normal') + ' ' + quality.toUpperCase()
}
}
const playQualityList = ref([])
QUALITYS_SUPPORT.forEach(item => {
playQualityList.value.push({ qualityId: item, label: getQualityTypeName(item) })
})
return { return {
appSetting, appSetting,
updateSetting, updateSetting,
mediaDevices, mediaDevices,
mediaDeviceId, mediaDeviceId,
handleMediaDeviceIdChnage, handleMediaDeviceIdChnage,
playQualityList,
} }
}, },
} }