增加音质切换功能
This commit is contained in:
parent
4defaa103e
commit
61128e50fc
@ -77,6 +77,8 @@ export const DOWNLOAD_STATUS = {
|
||||
|
||||
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 = {
|
||||
helloMsg: 'Hello~::^-^::~v3~',
|
||||
idPrefix: 'OjppZDo6',
|
||||
|
||||
@ -24,7 +24,8 @@ const defaultSetting: LX.AppSetting = {
|
||||
|
||||
'player.startupAutoPlay': false,
|
||||
'player.togglePlayMethod': 'listLoop',
|
||||
'player.highQuality': false,
|
||||
'player.firstPlayQuality': '128k',
|
||||
'player.autoLowerQualityOnError': false,
|
||||
'player.isShowTaskProgess': true,
|
||||
'player.volume': 1,
|
||||
'player.isMute': false,
|
||||
|
||||
9
src/common/types/app_setting.d.ts
vendored
9
src/common/types/app_setting.d.ts
vendored
@ -89,9 +89,14 @@ declare global {
|
||||
'player.togglePlayMethod': 'listLoop' | 'random' | 'list' | 'singleLoop' | 'none'
|
||||
|
||||
/**
|
||||
* 是否优先播放320k音质
|
||||
* 优先播放音质
|
||||
*/
|
||||
'player.highQuality': boolean
|
||||
'player.firstPlayQuality': LX.Quality
|
||||
|
||||
/**
|
||||
* 获取当前音质错误时自动降低音质
|
||||
*/
|
||||
'player.autoLowerQualityOnError': boolean
|
||||
|
||||
/**
|
||||
* 是否显示任务栏进度条
|
||||
|
||||
@ -488,6 +488,9 @@
|
||||
"setting__play_lyric_roma": "Show lyrics roman",
|
||||
"setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese",
|
||||
"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_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",
|
||||
|
||||
@ -487,6 +487,9 @@
|
||||
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
|
||||
"setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文",
|
||||
"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_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
|
||||
"setting__play_mediaDevice_title": "选择声音输出的媒体设备",
|
||||
|
||||
@ -488,6 +488,9 @@
|
||||
"setting__play_lyric_roma": "顯示歌詞羅馬音(如果可用)",
|
||||
"setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文",
|
||||
"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_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲",
|
||||
"setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備",
|
||||
|
||||
@ -51,11 +51,11 @@ export const getMusicUrl = async({ musicInfo, quality, isRefresh, allowToggleSou
|
||||
|
||||
// // return Promise.reject(new Error('该歌曲没有可播放的音频'))
|
||||
// }
|
||||
const targetQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo)
|
||||
const targetQuality = quality ?? getPlayQuality(musicInfo)
|
||||
const cachedUrl = await getStoreMusicUrl(musicInfo, targetQuality)
|
||||
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)
|
||||
void saveMusicUrl(musicInfo, targetQuality, url)
|
||||
return url
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
import { langS2T, toNewMusicInfo, toOldMusicInfo } from '@renderer/utils'
|
||||
import { requestMsg } from '@renderer/utils/message'
|
||||
import { QUALITYS } from '@common/constants'
|
||||
|
||||
|
||||
const getOtherSourcePromises = new Map()
|
||||
@ -138,16 +139,33 @@ export const getCachedLyricInfo = async(musicInfo: LX.Music.MusicInfo): Promise<
|
||||
return null
|
||||
}
|
||||
|
||||
export const getPlayQuality = (highQuality: boolean, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
|
||||
let type: LX.Quality = '128k'
|
||||
let list = qualityList.value[musicInfo.source]
|
||||
if (highQuality && musicInfo.meta._qualitys['320k'] && list && list.includes('320k')) type = '320k'
|
||||
return type
|
||||
const sliceQualityList = (startQuality: LX.Quality, skipNow?: boolean) => {
|
||||
let startNum = QUALITYS.indexOf(startQuality)
|
||||
if (skipNow) startNum = QUALITYS.indexOf(startQuality) + 1
|
||||
|
||||
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[]
|
||||
quality?: LX.Quality
|
||||
rawQuality?: LX.Quality
|
||||
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
|
||||
isRefresh: boolean
|
||||
retryedSource?: LX.OnlineSource[]
|
||||
@ -164,14 +182,23 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
|
||||
if (retryedSource.includes(musicInfo.source)) continue
|
||||
retryedSource.push(musicInfo.source)
|
||||
if (!assertApiSupport(musicInfo.source)) continue
|
||||
itemQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo)
|
||||
itemQuality = quality ?? getPlayQuality(musicInfo)
|
||||
if (!musicInfo.meta._qualitys[itemQuality]) continue
|
||||
|
||||
console.log('try toggle to: ', musicInfo.source, musicInfo.name, musicInfo.singer, musicInfo.interval)
|
||||
onToggleSource(musicInfo)
|
||||
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)
|
||||
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) => {
|
||||
if (err.message == requestMsg.tooManyRequests) throw err
|
||||
console.log(err)
|
||||
return getOnlineOtherSourceMusicUrl({ musicInfos, quality, onToggleSource, isRefresh, retryedSource })
|
||||
return getOnlineOtherSourceMusicUrl({ musicInfos, quality, onToggleSource, isRefresh, retryedSource, rawQuality })
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在线音乐URL
|
||||
*/
|
||||
export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource }: {
|
||||
export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource, rawQuality }: {
|
||||
musicInfo: LX.Music.MusicInfoOnline
|
||||
quality?: LX.Quality
|
||||
rawQuality?: LX.Quality
|
||||
isRefresh: boolean
|
||||
allowToggleSource: boolean
|
||||
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
|
||||
@ -209,8 +237,9 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
|
||||
quality: LX.Quality
|
||||
isFromCache: boolean
|
||||
}> => {
|
||||
// console.log(musicInfo.source)
|
||||
const targetQuality = quality ?? getPlayQuality(appSetting['player.highQuality'], musicInfo)
|
||||
const targetQuality = quality ?? getPlayQuality(musicInfo)
|
||||
rawQuality = rawQuality ?? targetQuality
|
||||
console.log(targetQuality, rawQuality)
|
||||
|
||||
let reqPromise
|
||||
try {
|
||||
@ -218,12 +247,29 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
|
||||
} catch (err: any) {
|
||||
reqPromise = Promise.reject(err)
|
||||
}
|
||||
|
||||
return reqPromise.then(({ url, type }: { url: string, type: LX.Quality }) => {
|
||||
return { musicInfo, url, quality: type, isFromCache: false }
|
||||
}).catch(async(err: any) => {
|
||||
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
|
||||
onToggleSource()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||
return await getOtherSource(musicInfo).then(otherSource => {
|
||||
console.log('find otherSource', otherSource)
|
||||
@ -231,7 +277,8 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
|
||||
return getOnlineOtherSourceMusicUrl({
|
||||
musicInfos: [...otherSource],
|
||||
onToggleSource,
|
||||
quality,
|
||||
quality: rawQuality ?? quality,
|
||||
rawQuality: rawQuality ?? quality,
|
||||
isRefresh,
|
||||
retryedSource: [musicInfo.source],
|
||||
})
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
import { getMusicInfosByList } from './musicInfo'
|
||||
import { createHttpFetch } from './util'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取歌手信息
|
||||
* @param {*} id
|
||||
*/
|
||||
getInfo(id) {
|
||||
if (id == 0) throw new Error('歌手不存在') // kg源某些歌曲在歌手没被kg收录时返回的歌手id为0
|
||||
return createHttpFetch(`http://mobiles.kugou.com/api/v5/singer/info?singerid=${id}`).then(body => {
|
||||
if (!body) throw new Error('get singer info faild.')
|
||||
|
||||
return {
|
||||
source: 'kg',
|
||||
id: body.singerid,
|
||||
info: {
|
||||
name: body.singername,
|
||||
desc: body.intro,
|
||||
avatar: body.imgurl.replace('{size}', 480),
|
||||
gender: body.grade === 1 ? 'man' : 'woman',
|
||||
},
|
||||
count: {
|
||||
music: body.songcount,
|
||||
album: body.albumcount,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取歌手专辑列表
|
||||
* @param {*} id
|
||||
* @param {*} page
|
||||
* @param {*} limit
|
||||
*/
|
||||
getAlbumList(id, page = 1, limit = 10) {
|
||||
if (id == 0) throw new Error('歌手不存在')
|
||||
return createHttpFetch(`http://mobiles.kugou.com/api/v5/singer/album?singerid=${id}&page=${page}&pagesize=${limit}`).then(body => {
|
||||
if (!body.info) throw new Error('get singer album list faild.')
|
||||
|
||||
const list = this.filterAlbumList(body.info)
|
||||
return {
|
||||
source: 'kg',
|
||||
list,
|
||||
limit,
|
||||
page,
|
||||
total: body.total,
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取歌手歌曲列表
|
||||
* @param {*} id
|
||||
* @param {*} page
|
||||
* @param {*} limit
|
||||
*/
|
||||
async getSongList(id, page = 1, limit = 100) {
|
||||
if (id == 0) throw new Error('歌手不存在')
|
||||
const body = await createHttpFetch(`http://mobiles.kugou.com/api/v5/singer/song?singerid=${id}&page=${page}&pagesize=${limit}`)
|
||||
if (!body.info) throw new Error('get singer song list faild.')
|
||||
|
||||
const list = await getMusicInfosByList(body.info)
|
||||
return {
|
||||
source: 'kg',
|
||||
list,
|
||||
limit,
|
||||
page,
|
||||
total: body.total,
|
||||
}
|
||||
},
|
||||
filterAlbumList(raw) {
|
||||
return raw.map(item => {
|
||||
return {
|
||||
id: item.albumid,
|
||||
count: item.songcount,
|
||||
info: {
|
||||
name: item.albumname,
|
||||
author: item.singername,
|
||||
img: item.replaceAll('{size}', '480'),
|
||||
desc: item.intro,
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
@ -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})")
|
||||
.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})")
|
||||
.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
|
||||
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
|
||||
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')")
|
||||
h3#play_mediaDevice {{ $t('setting__play_mediaDevice') }}
|
||||
div
|
||||
@ -33,7 +39,7 @@ import { hasInitedAdvancedAudioFeatures } from '@renderer/plugins/player'
|
||||
import { dialog } from '@renderer/plugins/Dialog'
|
||||
import { useI18n } from '@renderer/plugins/i18n'
|
||||
import { appSetting, updateSetting } from '@renderer/store/setting'
|
||||
|
||||
import { QUALITYS_SUPPORT } from '@common/constants'
|
||||
|
||||
export default {
|
||||
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 {
|
||||
appSetting,
|
||||
updateSetting,
|
||||
mediaDevices,
|
||||
mediaDeviceId,
|
||||
handleMediaDeviceIdChnage,
|
||||
playQualityList,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user