Merge pull request #236 from algerkong/dev

feat: 优化页面效果 音源解析优化
This commit is contained in:
Alger
2025-05-18 13:12:18 +08:00
committed by GitHub
14 changed files with 129 additions and 83 deletions

View File

@@ -49,7 +49,7 @@
一个第三方音乐播放器、本地服务、桌面歌词、音乐下载、最高音质
## 预览地址
[http://mc.alger.fun/](http://mc.alger.fun/)
[http://music.alger.fun/](http://music.alger.fun/)
## 软件截图
![首页白](./docs/image.png)

View File

@@ -44,7 +44,7 @@ export default {
},
playback: {
quality: '音质设置',
qualityDesc: '选择音乐播放音质VIP',
qualityDesc: '选择音乐播放音质(网易云VIP',
qualityOptions: {
standard: '标准',
higher: '较高',

View File

@@ -152,3 +152,32 @@ export const getBilibiliAudioUrl = async (bvid: string, cid: number): Promise<st
throw error;
}
};
// 根据音乐名称搜索并直接返回音频URL
export const searchAndGetBilibiliAudioUrl = async (
keyword: string
): Promise<string> => {
try {
// 搜索B站视频取第一页第一个结果
const res = await searchBilibili({ keyword, page: 1, pagesize: 1 });
const result = res.data?.data?.result;
if (!result || result.length === 0) {
throw new Error('未找到相关B站视频');
}
const first = result[0];
const bvid = first.bvid;
// 需要获取视频详情以获得cid
const detailRes = await getBilibiliVideoDetail(bvid);
const pages = detailRes.data.pages;
if (!pages || pages.length === 0) {
throw new Error('未找到视频分P信息');
}
const cid = pages[0].cid;
// 获取音频URL
return await getBilibiliAudioUrl(bvid, cid);
} catch (error) {
console.error('根据名称搜索B站音频URL失败:', error);
throw error;
}
}

View File

@@ -126,26 +126,6 @@ export const parseFromGDMusic = async (
}
};
/**
* 获取音质映射
* @param qualitySetting 设置中的音质选项
* @returns 映射到GD音乐台的音质参数
*/
export const getQualityMapping = (qualitySetting: string): string => {
const qualityMap: Record<string, string> = {
standard: '128',
higher: '320',
exhigh: '320',
lossless: '740',
hires: '999',
jyeffect: '999',
sky: '999',
dolby: '999',
jymaster: '999'
};
return qualityMap[qualitySetting] || '320';
};
interface GDMusicUrlResult {
url: string;
br: string;

View File

@@ -5,7 +5,9 @@ import { isElectron } from '@/utils';
import request from '@/utils/request';
import requestMusic from '@/utils/request_music';
import { cloneDeep } from 'lodash';
import { parseFromGDMusic, getQualityMapping } from './gdmusic';
import { parseFromGDMusic } from './gdmusic';
import type { SongResult } from '@/type/music';
import { searchAndGetBilibiliAudioUrl } from './bilibili';
const { addData, getData, deleteData } = musicDB;
@@ -80,7 +82,7 @@ export const getMusicLrc = async (id: number) => {
}
};
export const getParsingMusicUrl = async (id: number, data: any) => {
export const getParsingMusicUrl = async (id: number, data: SongResult) => {
const settingStore = useSettingsStore();
// 如果禁用了音乐解析功能,则直接返回空结果
@@ -98,7 +100,25 @@ export const getParsingMusicUrl = async (id: number, data: any) => {
try {
enabledSources = JSON.parse(savedSource);
console.log(`使用歌曲 ${id} 自定义音源:`, enabledSources);
if(enabledSources.includes('bilibili')){
// 构建搜索关键词,依次判断歌曲名称、歌手名称和专辑名称是否存在
const songName = data?.name || '';
const artistName = Array.isArray(data?.ar) && data.ar.length > 0 && data.ar[0]?.name ? data.ar[0].name : '';
const albumName = data?.al && typeof data.al === 'object' && data.al?.name ? data.al.name : '';
const name = [songName, artistName, albumName].filter(Boolean).join(' ').trim();
console.log('开始搜索bilibili音频', name);
return {
data: {
code: 200,
message: 'success',
data: {
url: await searchAndGetBilibiliAudioUrl(name)
}
}
}
}
} catch (e) {
console.error('e',e)
console.error('解析自定义音源失败, 使用全局设置', e);
enabledSources = settingStore.setData.enabledMusicSources || [];
}
@@ -108,13 +128,11 @@ export const getParsingMusicUrl = async (id: number, data: any) => {
}
// 检查是否选择了GD音乐台解析
if (enabledSources.includes('gdmusic')) {
// 获取音质设置并转换为GD音乐台格式
try {
const quality = getQualityMapping(settingStore.setData.musicQuality || 'higher');
// 调用封装的GD音乐台解析服务
const gdResult = await parseFromGDMusic(id, data, quality);
const gdResult = await parseFromGDMusic(id, data, '999');
if (gdResult) {
return gdResult;
}

View File

@@ -76,7 +76,7 @@ const copyQQ = () => {
};
const toDonateList = () => {
window.open('http://donate.alger.fun', '_blank');
window.open('http://donate.alger.fun/download', '_blank');
};
defineProps({

View File

@@ -4,6 +4,12 @@
<div class="description">
<p>{{ t('donation.description') }}</p>
<p>{{ t('donation.message') }}</p>
<n-button type="primary" @click="toDonateList">
<template #icon>
<i class="ri-cup-line"></i>
</template>
{{ t('donation.toDonateList') }}
</n-button>
</div>
<div class="qrcode-grid">
<div class="qrcode-item">
@@ -16,15 +22,6 @@
<span class="qrcode-label">{{ t('common.alipay') }}</span>
</div>
<div class="donate-button">
<n-button type="primary" @click="toDonateList">
<template #icon>
<i class="ri-cup-line"></i>
</template>
{{ t('donation.toDonateList') }}
</n-button>
</div>
<div class="qrcode-item">
<n-image
:src="wechat"
@@ -66,7 +63,7 @@
<div class="donor-info">
<div class="donor-meta">
<div class="donor-name">{{ donor.name }}</div>
<div class="price-tag">{{ donor.amount }}</div>
<!-- <div class="price-tag">{{ donor.amount }}</div> -->
</div>
<div class="donation-date">{{ donor.date }}</div>
</div>
@@ -172,7 +169,7 @@ const toggleExpand = () => {
};
const toDonateList = () => {
window.open('http://donate.alger.fun', '_blank');
window.open('http://donate.alger.fun/download', '_blank');
};
</script>
@@ -211,13 +208,9 @@ const toDonateList = () => {
@apply rounded-lg p-2.5 transition-all duration-200 hover:shadow-md;
@apply bg-light-100 dark:bg-gray-800/5 backdrop-blur-sm;
@apply border border-gray-200 dark:border-gray-700/10;
@apply flex flex-col justify-between;
@apply flex flex-col;
min-height: 100px;
&.no-message {
@apply justify-between;
}
.card-content {
@apply flex items-start gap-2 mb-2;
}
@@ -327,7 +320,7 @@ const toDonateList = () => {
}
.qrcode-grid {
@apply flex justify-between items-center gap-4;
@apply flex justify-between items-center gap-4 flex-wrap;
.qrcode-item {
@apply flex flex-col items-center gap-2;

View File

@@ -84,12 +84,12 @@ const isReparse = computed(() => {
// 可选音源列表
const musicSourceOptions = ref([
{ label: 'MiGu音乐', value: 'migu' as Platform },
{ label: '酷狗音乐', value: 'kugou' as Platform },
{ label: 'MiGu', value: 'migu' as Platform },
{ label: 'KuGou', value: 'kugou' as Platform },
{ label: 'pyncmd', value: 'pyncmd' as Platform },
{ label: '酷我音乐', value: 'kuwo' as Platform },
{ label: 'Bilibili音乐', value: 'bilibili' as Platform },
{ label: 'GD音乐台', value: 'gdmusic' as Platform }
{ label: 'KuWo', value: 'kuwo' as Platform },
{ label: 'Bilibili', value: 'bilibili' as Platform },
{ label: 'GdMuisc', value: 'gdmusic' as Platform }
]);
// 检查音源是否被选中

View File

@@ -7,7 +7,6 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
<!-- SEO 元数据 -->
<title>网抑云音乐 | AlgerKong AlgerMusicPlayer</title>

View File

@@ -266,7 +266,7 @@ const selectItem = async (key: string) => {
};
const toGithub = () => {
window.open('http://donate.alger.fun', '_blank');
window.open('http://donate.alger.fun/download', '_blank');
};
const updateInfo = ref<UpdateResult>({

View File

@@ -119,6 +119,7 @@ export const getSongUrl = async (
// 如果自定义音源解析失败,继续使用正常的获取流程
console.warn('自定义音源解析失败,使用默认音源');
} catch (error) {
console.error('error',error)
console.error('自定义音源解析出错:', error);
}
}
@@ -548,7 +549,7 @@ export const usePlayerStore = defineStore('player', () => {
const setPlay = async (song: SongResult) => {
try {
// 如果是当前正在播放的音乐,则切换播放/暂停状态
if (playMusic.value.id === song.id) {
if (playMusic.value.id === song.id && playMusic.value.playMusicUrl === song.playMusicUrl) {
if (play.value) {
setPlayMusic(false);
audioService.getCurrentSound()?.pause();

View File

@@ -17,7 +17,7 @@ const baseURL = window.electron
const request = axios.create({
baseURL,
timeout: 5000,
timeout: 15000,
withCredentials: true
});

View File

@@ -135,7 +135,7 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest',
// 使用代理节点
'https://mc.alger.fun/package.json',
'https://music.alger.fun/package.json',
];
if (token) {

View File

@@ -40,7 +40,7 @@
<language-switcher />
</div>
<div class="set-item">
<div class="set-item" v-if="isElectron">
<div>
<div class="set-item-title">{{ t('settings.basic.font') }}</div>
<div class="set-item-content">{{ t('settings.basic.fontDesc') }}</div>
@@ -103,9 +103,9 @@
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-400">{{ setData.animationSpeed }}x</span>
<div class="w-60">
<n-slider
<span class="text-sm text-gray-400" v-if="!isMobile">{{ setData.animationSpeed }}x</span>
<div>
<template v-if="!isMobile"><n-slider
v-model:value="setData.animationSpeed"
:min="0.1"
:max="3"
@@ -117,7 +117,19 @@
}"
:disabled="setData.noAnimate"
class="w-40"
/>
/></template>
<template v-else>
<n-input-number
v-model:value="setData.animationSpeed"
:min="0.1"
:max="3"
:step="0.1"
:placeholder="t('settings.basic.animationSpeedPlaceholder')"
:disabled="setData.noAnimate"
button-placement="both"
style="width: 100px"
/>
</template>
</div>
</div>
</div>
@@ -128,28 +140,42 @@
<div id="playback" ref="playbackRef" class="settings-section">
<div class="settings-section-title">{{ t('settings.sections.playback') }}</div>
<div class="settings-section-content">
<div class="set-item">
<div>
<div class="set-item-title">{{ t('settings.playback.quality') }}</div>
<div class="set-item-content">{{ t('settings.playback.qualityDesc') }}</div>
<div>
<div class="set-item">
<div>
<div class="set-item-title">{{ t('settings.playback.quality') }}</div>
<div class="set-item-content">
{{ t('settings.playback.qualityDesc') }}
</div>
</div>
<n-select
v-model:value="setData.musicQuality"
:options="[
{ label: t('settings.playback.qualityOptions.standard'), value: 'standard' },
{ label: t('settings.playback.qualityOptions.higher'), value: 'higher' },
{ label: t('settings.playback.qualityOptions.exhigh'), value: 'exhigh' },
{ label: t('settings.playback.qualityOptions.lossless'), value: 'lossless' },
{ label: t('settings.playback.qualityOptions.hires'), value: 'hires' },
{ label: t('settings.playback.qualityOptions.jyeffect'), value: 'jyeffect' },
{ label: t('settings.playback.qualityOptions.sky'), value: 'sky' },
{ label: t('settings.playback.qualityOptions.dolby'), value: 'dolby' },
{ label: t('settings.playback.qualityOptions.jymaster'), value: 'jymaster' }
]"
style="width: 160px"
/>
</div>
<!-- 网易云 QQ 音乐 酷我 酷狗 会员购买链接 -->
<div class="p-2 bg-light-100 dark:bg-dark-100 rounded-lg mt-2">
<div>大家还是需要支持正版本软件只做开源探讨</div>
<div class="mt-2">各大音乐会员购买链接</div>
<div class="flex gap-5 flex-wrap">
<a class="text-green-400 hover:text-green-500" href="https://music.163.com/store/vip" target="_blank">网易云音乐会员</a>
<a class="text-green-400 hover:text-green-500" href="https://y.qq.com/portal/vipportal/" target="_blank">QQ音乐会员</a>
<a class="text-green-400 hover:text-green-500" href="https://vip.kugou.com/" target="_blank">酷狗音乐会员</a>
<a class="text-green-400 hover:text-green-500" href="https://vip1.kuwo.cn/" target="_blank">酷我音乐会员</a>
</div>
</div>
<n-select
v-model:value="setData.musicQuality"
:options="[
{ label: t('settings.playback.qualityOptions.standard'), value: 'standard' },
{ label: t('settings.playback.qualityOptions.higher'), value: 'higher' },
{ label: t('settings.playback.qualityOptions.exhigh'), value: 'exhigh' },
{ label: t('settings.playback.qualityOptions.lossless'), value: 'lossless' },
{ label: t('settings.playback.qualityOptions.hires'), value: 'hires' },
{ label: t('settings.playback.qualityOptions.jyeffect'), value: 'jyeffect' },
{ label: t('settings.playback.qualityOptions.sky'), value: 'sky' },
{ label: t('settings.playback.qualityOptions.dolby'), value: 'dolby' },
{ label: t('settings.playback.qualityOptions.jymaster'), value: 'jymaster' }
]"
style="width: 160px"
/>
</div>
<div class="set-item" v-if="isElectron">
<div>
<div class="set-item-title">{{ t('settings.playback.musicSources') }}</div>
@@ -418,7 +444,7 @@
<!-- 捐赠支持 -->
<div id="donation" ref="donationRef" class="settings-section">
<div class="settings-section-title">{{ t('settings.sections.donation') }}</div>
<div class="settings-section-title">{{ t('settings.sectio ns.donation') }}</div>
<div class="settings-section-content">
<div class="set-item">
<div>
@@ -499,7 +525,7 @@ import config from '../../../../package.json';
// 所有平台默认值
const ALL_PLATFORMS: Platform[] = ['migu', 'kugou', 'pyncmd', 'bilibili', 'kuwo'];
const platform = window.electron.ipcRenderer.sendSync('get-platform');
const platform = window.electron ? window.electron.ipcRenderer.sendSync('get-platform') : 'web';
const settingsStore = useSettingsStore();
const userStore = useUserStore();