Compare commits

..

10 Commits

Author SHA1 Message Date
alger
ddb814da10 feat: v3.3.0 2025-01-06 22:33:13 +08:00
alger
e266ea8ef8 🐞 fix: 修复类型校验问题 2025-01-06 22:24:37 +08:00
alger
a894954641 🐞 fix: 修复类型校验问题 2025-01-06 22:15:25 +08:00
alger
f640ab9969 feat: v3.3.0 2025-01-06 22:10:20 +08:00
alger
9eb17fd978 feat: 优化登录失效 2025-01-06 22:03:50 +08:00
alger
020aca7384 feat: 添加音质选择 优化灰色歌曲解析 2025-01-06 20:54:42 +08:00
alger
fcc47dc0ff feat: 添加退出登录 2025-01-05 15:58:48 +08:00
alger
17ce268da6 feat: 修复未登录 收藏问题 2025-01-05 15:01:55 +08:00
alger
43c64b1b43 feat: 收藏功能改为接口对接 2025-01-04 16:58:08 +08:00
alger
11ced6b418 feat: 优化更新检查 下载 功能 2025-01-04 16:13:37 +08:00
22 changed files with 347 additions and 143 deletions

View File

@@ -1,13 +1,11 @@
# 更新日志
## [v3.2.0] - 2024-01-04
## [v3.3.0] - 2024-01-04
### ✨ 新功能
- 添加代理功能和 realIP配置功能 解决内网访问问题 和 国外访问问题 (d7e94a3)
- 关闭应用的提示修改,可存储配置最小化或关闭 (46f8067)
- 解决检查更新请求失败问题 (cdb9524)
### 🐞 问题修复
- 修复歌词页面与底栏冲突问题(#26) (1dc7d0c)
- 修复搜索歌曲列表页面显示错误问题(#33) (1dc7d0c)
- 修复搜索类型切换没有重新加载搜索的问题(#25) (ba64631)
- 添加音质选择功能,优化灰色歌曲解析 (020aca7)
- 收藏功能改为接口对接 与 网易云同步 (43c64b1)
- 添加退出登录 (fcc47dc)
- 优化登录失效处理 (9eb17fd)
- 修复未登录状态下的收藏问题 (17ce268)
- 优化更新检查和下载功能 (11ced6b)

View File

@@ -1,6 +1,6 @@
{
"name": "AlgerMusicPlayer",
"version": "3.2.0",
"version": "3.3.0",
"description": "Alger Music Player",
"author": "Alger <algerkc@qq.com>",
"main": "./out/main/index.js",

View File

@@ -12,8 +12,8 @@ if (!fs.existsSync(path.resolve(os.tmpdir(), 'anonymous_token'))) {
}
// 处理解锁音乐请求
ipcMain.handle('unblock-music', async (_, id) => {
return unblockMusic(id);
ipcMain.handle('unblock-music', async (_, id, data) => {
return unblockMusic(id, data);
});
import server from 'netease-cloud-music-api-alger/server';

View File

@@ -6,12 +6,13 @@
"host": "127.0.0.1",
"port": 7890
},
"enableRealIP":false,
"realIP":"",
"enableRealIP": false,
"realIP": "",
"noAnimate": false,
"animationSpeed": 1,
"author": "Alger",
"authorUrl": "https://github.com/algerkong",
"musicApiPort": 30488,
"closeAction": "ask"
"closeAction": "ask",
"musicQuality": "higher"
}

View File

@@ -1,8 +1,8 @@
import match from '@unblockneteasemusic/server';
const unblockMusic = async (id: any): Promise<any> => {
const unblockMusic = async (id: any, songData: any): Promise<any> => {
return new Promise((resolve, reject) => {
match(parseInt(id, 10), ['qq', 'migu', 'kugou', 'joox'])
match(parseInt(id, 10), ['qq', 'migu', 'kugou', 'joox'], songData)
.then((data) => {
resolve({
data: {

View File

@@ -12,7 +12,8 @@ declare global {
dragStart: (data: string) => void;
miniTray: () => void;
restart: () => void;
unblockMusic: (id: number) => Promise<any>;
unblockMusic: (id: number, data: any) => Promise<any>;
};
$message:any
}
}

View File

@@ -32,10 +32,6 @@ onMounted(() => {
'setMenus',
homeRouter.filter((item) => item.meta.isMobile)
);
console.log(
'qqq ',
homeRouter.filter((item) => item.meta.isMobile)
);
}
});
</script>

View File

@@ -1,11 +1,33 @@
import { ILyric } from '@/type/lyric';
import { IPlayMusicUrl } from '@/type/music';
import { isElectron } from '@/utils';
import request from '@/utils/request';
import requestMusic from '@/utils/request_music';
import store from '@/store';
// 获取音乐音质详情
export const getMusicQualityDetail = (id: number) => {
return request.get('/song/music/detail', { params: { id } });
};
// 根据音乐Id获取音乐播放URl
export const getMusicUrl = (id: number) => {
return request.get<IPlayMusicUrl>('/song/url', { params: { id } });
export const getMusicUrl = async (id: number) => {
const res = await request.get('/song/download/url/v1', {
params: {
id,
level: store.state.setData.musicQuality || 'higher'
}
});
if (res.data.data.url) {
return {data:{ data:[{url:res.data.data.url}]}};
}
return await request.get('/song/url/v1', {
params: {
id,
level: store.state.setData.musicQuality || 'higher'
}
});
};
// 获取歌曲详情
@@ -18,9 +40,19 @@ export const getMusicLrc = (id: number) => {
return request.get<ILyric>('/lyric', { params: { id } });
};
export const getParsingMusicUrl = (id: number) => {
export const getParsingMusicUrl = (id: number, data: any) => {
if (isElectron) {
return window.api.unblockMusic(id);
return window.api.unblockMusic(id, data);
}
return requestMusic.get<any>('/music', { params: { id } });
};
// 收藏歌曲
export const likeSong = (id: number, like: boolean = true) => {
return request.get('/like', { params: { id, like } });
};
// 获取用户喜欢的音乐列表
export const getLikedList = () => {
return request.get('/likelist');
};

View File

@@ -38,11 +38,6 @@
:step="0.1"
@update:value="handleProgressChange"
>
<template #rail>
<div class="progress-rail">
<div class="progress-buffer" :style="{ width: `${bufferedProgress}%` }"></div>
</div>
</template>
</n-slider>
</div>
@@ -643,10 +638,44 @@ const isMobile = computed(() => store.state.isMobile);
.custom-slider {
:deep(.n-slider) {
--n-rail-height: 4px;
--n-rail-color: rgba(255, 255, 255, 0.2);
--n-fill-color: #10b981;
--n-rail-color: theme('colors.gray.200');
--n-rail-color-dark: theme('colors.gray.700');
--n-fill-color: theme('colors.green.500');
--n-handle-size: 12px;
--n-handle-color: #10b981;
--n-handle-color: theme('colors.green.500');
&.n-slider--vertical {
height: 100%;
.n-slider-rail {
width: 4px;
}
&:hover {
.n-slider-rail {
width: 6px;
}
.n-slider-handle {
width: 14px;
height: 14px;
}
}
}
.n-slider-rail {
@apply overflow-hidden transition-all duration-200;
@apply bg-gray-500 dark:bg-dark-300 bg-opacity-10 !important;
}
.n-slider-handle {
@apply transition-all duration-200;
opacity: 0;
}
&:hover .n-slider-handle {
opacity: 1;
}
}
}

View File

@@ -53,7 +53,6 @@
</div>
<div class="ml-4">
<div class="recommend-singer-item-info-name text-el">{{ item.name }}</div>
<div class="recommend-singer-item-info-name text-el">{{ item.name }}</div>
</div>
</div>
</div>

View File

@@ -81,6 +81,7 @@ import type { SongResult } from '@/type/music';
import { getImgUrl, isElectron } from '@/utils';
import { getImageBackground } from '@/utils/linearColor';
import { getSongUrl } from '@/hooks/MusicListHook';
import { cloneDeep } from 'lodash';
const props = withDefaults(
defineProps<{
@@ -148,7 +149,7 @@ const downloadMusic = async () => {
isDownloading.value = true;
const loadingMessage = message.loading('正在下载中...', { duration: 0 });
const url = await getSongUrl(props.item.id);
const url = await getSongUrl(props.item.id, cloneDeep(props.item));
if (!url) {
loadingMessage.destroy();
message.error('获取音乐下载地址失败');

View File

@@ -44,10 +44,11 @@
</template>
<script setup lang="ts">
import { onMounted, ref, computed } from 'vue';
import { onMounted, ref, computed, watch } from 'vue';
import { marked } from 'marked';
import { checkUpdate, UpdateResult } from '@/utils/update';
import config from '../../../../package.json';
import { useStore } from 'vuex';
// 配置 marked
marked.setOptions({
@@ -64,6 +65,25 @@ const updateInfo = ref<UpdateResult>({
releaseInfo: null
});
const store = useStore()
// 添加计算属性
const showUpdateModalState = computed({
get: () => store.state.showUpdateModal,
set: (val) => store.commit('setShowUpdateModal', val)
})
// 替换原来的 watch
watch(showUpdateModalState, (newVal) => {
if (newVal) {
showModal.value = true
}
})
watch(() => showModal.value, (newVal) => {
showUpdateModalState.value = newVal
})
// 解析 Markdown
const parsedReleaseNotes = computed(() => {
if (!updateInfo.value.releaseInfo?.body) return '';
@@ -96,12 +116,30 @@ const checkForUpdates = async () => {
}
};
const handleUpdate = async () => {
const assets = updateInfo.value.releaseInfo?.assets || [];
const platform = window.electron.process.platform;
const arch = window.electron.ipcRenderer.sendSync('get-arch');
console.log(arch);
console.log(platform);
console.log('arch',arch)
console.log('platform',platform)
const version = updateInfo.value.latestVersion
const downUrls = {
win32: {
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win.exe`,
x64: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-x64.exe`,
ia32: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-win-ia32.exe`,
},
darwin: {
all: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}AlgerMusicPlayer-${version}-mac-universal.dmg`,
},
linux: {
AppImage: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.AppImage`,
deb: `https://github.com/algerkong/AlgerMusicPlayer/releases/download/v${version}/AlgerMusicPlayer-${version}-linux-x64.deb`,
}
}
let downloadUrl = '';
@@ -111,30 +149,29 @@ const handleUpdate = async () => {
const macAsset = assets.find(asset =>
asset.name.includes('mac')
);
downloadUrl = macAsset?.browser_download_url || '';
downloadUrl = macAsset?.browser_download_url || downUrls.darwin.all || '';
} else if (platform === 'win32') {
// Windows
const winAsset = assets.find(asset =>
asset.name.includes('win') &&
(arch === 'x64' ? asset.name.includes('x64') : asset.name.includes('ia32'))
);
downloadUrl = winAsset?.browser_download_url || '';
downloadUrl = winAsset?.browser_download_url || downUrls.win32[arch] || downUrls.win32.all || '';
} else if (platform === 'linux') {
// Linux
const linuxAsset = assets.find(asset =>
(asset.name.endsWith('.AppImage') || asset.name.endsWith('.deb')) &&
asset.name.includes('x64')
);
downloadUrl = linuxAsset?.browser_download_url || '';
downloadUrl = linuxAsset?.browser_download_url || downUrls.linux[arch] || '';
}
if (downloadUrl) {
window.open(`https://ghproxy.cn/${downloadUrl}`, '_blank');
window.open(`https://www.ghproxy.cn/${downloadUrl}`, '_blank');
} else {
// 如果没有找到对应的安装包,跳转到 release 页面
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
}
closeModal();
};
onMounted(() => {

View File

@@ -4,31 +4,31 @@ import { getMusicLrc, getMusicUrl, getParsingMusicUrl } from '@/api/music';
import { useMusicHistory } from '@/hooks/MusicHistoryHook';
import { audioService } from '@/services/audioService';
import type { ILyric, ILyricText, SongResult } from '@/type/music';
import { getImgUrl, getMusicProxyUrl } from '@/utils';
import { getImgUrl } from '@/utils';
import { getImageLinearBackground } from '@/utils/linearColor';
import { cloneDeep } from 'lodash';
const musicHistory = useMusicHistory();
// 获取歌曲url
export const getSongUrl = async (id: number) => {
export const getSongUrl = async (id: number, songData: any) => {
const { data } = await getMusicUrl(id);
let url = '';
try {
if (data.data[0].freeTrialInfo || !data.data[0].url) {
const res = await getParsingMusicUrl(id);
console.log('res', res);
const res = await getParsingMusicUrl(id, songData);
url = res.data.data.url;
}
} catch (error) {
console.error('error', error);
}
url = url || data.data[0].url;
return getMusicProxyUrl(url);
return url;
};
const getSongDetail = async (playMusic: SongResult) => {
playMusic.playLoading = true;
const playMusicUrl = await getSongUrl(playMusic.id);
const playMusicUrl = await getSongUrl(playMusic.id, cloneDeep(playMusic));
const { backgroundColor, primaryColor } =
playMusic.backgroundColor && playMusic.primaryColor
? playMusic

View File

@@ -28,7 +28,7 @@
<play-bar v-if="isPlay" :style="isMobile && store.state.musicFull ? 'bottom: 0;' : ''" />
</div>
<install-app-modal v-if="!isElectron"></install-app-modal>
<update-modal />
<update-modal v-if="isElectron"/>
</div>
</template>

View File

@@ -48,6 +48,10 @@
<i class="iconfont ri-login-box-line"></i>
<span>去登录</span>
</div>
<div v-if="store.state.user" class="menu-item" @click="selectItem('logout')">
<i class="iconfont ri-logout-box-r-line"></i>
<span>退出登录</span>
</div>
<!-- 切换主题 -->
<div class="menu-item" @click="selectItem('set')">
<i class="iconfont ri-settings-3-line"></i>
@@ -195,8 +199,7 @@ const selectItem = async (key: string) => {
switch (key) {
case 'logout':
logout().then(() => {
store.state.user = null;
localStorage.clear();
store.commit('logout');
router.push('/login');
});
break;
@@ -237,7 +240,7 @@ const checkForUpdates = async () => {
const toGithubRelease = () => {
if (updateInfo.value.hasUpdate) {
window.open(updateInfo.value.releaseInfo?.html_url || 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
store.commit('setShowUpdateModal', true)
} else {
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases', '_blank');
}

View File

@@ -5,15 +5,11 @@ import homeRouter from '@/router/home';
import type { SongResult } from '@/type/music';
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
import { isElectron } from '@/utils';
import { likeSong, getLikedList } from '@/api/music';
import setData from '@/../main/set.json'
// 默认设置
const defaultSettings = {
isProxy: false,
noAnimate: false,
animationSpeed: 1,
author: 'Alger',
authorUrl: 'https://github.com/algerkong'
};
const defaultSettings = setData;
function getLocalStorageItem<T>(key: string, defaultValue: T): T {
const item = localStorage.getItem(key);
@@ -38,6 +34,7 @@ interface State {
playMode: number;
theme: ThemeType;
musicFull: boolean;
showUpdateModal: boolean;
}
const state: State = {
@@ -57,7 +54,8 @@ const state: State = {
favoriteList: getLocalStorageItem('favoriteList', []),
playMode: getLocalStorageItem('playMode', 0),
theme: getCurrentTheme(),
musicFull: false
musicFull: false,
showUpdateModal: false
};
const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook();
@@ -100,15 +98,25 @@ const mutations = {
localStorage.setItem('appSettings', JSON.stringify(setData));
}
},
addToFavorite(state: State, songId: number) {
if (!state.favoriteList.includes(songId)) {
state.favoriteList = [songId, ...state.favoriteList];
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
async addToFavorite(state: State, songId: number) {
try {
state.user && localStorage.getItem('token') && await likeSong(songId, true);
if (!state.favoriteList.includes(songId)) {
state.favoriteList = [songId, ...state.favoriteList];
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
}
} catch (error) {
console.error('收藏歌曲失败:', error);
}
},
removeFromFavorite(state: State, songId: number) {
state.favoriteList = state.favoriteList.filter((id) => id !== songId);
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
async removeFromFavorite(state: State, songId: number) {
try {
state.user && localStorage.getItem('token') && await likeSong(songId, false);
state.favoriteList = state.favoriteList.filter((id) => id !== songId);
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
} catch (error) {
console.error('取消收藏歌曲失败:', error);
}
},
togglePlayMode(state: State) {
state.playMode = (state.playMode + 1) % 3;
@@ -117,13 +125,20 @@ const mutations = {
toggleTheme(state: State) {
state.theme = state.theme === 'dark' ? 'light' : 'dark';
applyTheme(state.theme);
},
setShowUpdateModal(state, value) {
state.showUpdateModal = value
},
logout(state: State) {
state.user = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
}
};
const actions = {
initializeSettings({ commit }: { commit: any }) {
if (isElectron) {
// const setData = (window as any).electron.ipcRenderer.getStoreValue('set');
const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
commit('setSetData', setData || defaultSettings);
} else {
@@ -140,6 +155,24 @@ const actions = {
},
initializeTheme({ state }: { state: State }) {
applyTheme(state.theme);
},
async initializeFavoriteList({ state }: { state: State }) {
try {
if(state.user && localStorage.getItem('token')){
const res = await getLikedList();
if (res.data?.ids) {
state.favoriteList = res.data.ids.reverse();
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList));
}
}else{
const localFavoriteList = localStorage.getItem('favoriteList');
if (localFavoriteList) {
state.favoriteList = JSON.parse(localFavoriteList);
}
}
} catch (error) {
console.error('获取收藏列表失败:', error);
}
}
};

View File

@@ -54,34 +54,9 @@ export const formatNumber = (num: string | number) => {
return num.toString();
};
const windowData = window as any;
export const getIsMc = () => {
if (!windowData.electron) {
return false;
}
const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
if (setData.isProxy) {
return true;
}
return false;
};
const ProxyUrl = import.meta.env.VITE_API_PROXY;
export const getMusicProxyUrl = (url: string) => {
if (!getIsMc()) {
return url;
}
const PUrl = url.split('').join('+');
return `${ProxyUrl}/mc?url=${PUrl}`;
};
export const getImgUrl = (url: string | undefined, size: string = '') => {
const bdUrl = 'https://image.baidu.com/search/down?url=';
const imgUrl = `${url}?param=${size}`;
if (!getIsMc()) {
return imgUrl;
}
return `${bdUrl}${encodeURIComponent(imgUrl)}`;
return imgUrl;
};
export const isMobile = computed(() => {

View File

@@ -1,8 +1,14 @@
import axios, { InternalAxiosRequestConfig } from 'axios';
import { isElectron } from '.';
import store from '@/store';
import { createDiscreteApi } from 'naive-ui'
const { notification } = createDiscreteApi(
['notification']
)
let setData: any = null;
const getSetData = ()=>{
if (window.electron) {
setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
@@ -44,7 +50,9 @@ request.interceptors.request.use(
};
const token = localStorage.getItem('token');
if (token) {
config.params.cookie = token;
config.params.cookie = token + ' os=pc;';
}else{
config.params.cookie = 'os=pc;';
}
}
@@ -72,6 +80,7 @@ request.interceptors.response.use(
return response;
},
async (error) => {
console.log('error',error)
const config = error.config as CustomAxiosRequestConfig;
// 如果没有配置,直接返回错误
@@ -79,6 +88,30 @@ request.interceptors.response.use(
return Promise.reject(error);
}
// 处理 301 状态码
if (error.response?.status === 301) {
// 使用 store mutation 清除用户信息
store.commit('logout');
// 如果还可以重试,则重新发起请求
if (config.retryCount === undefined || config.retryCount < MAX_RETRIES) {
config.retryCount = (config.retryCount || 1) + 1;
console.log(`301 状态码,清除登录信息后重试第 ${config.retryCount}`);
notification.error({
content: '登录状态失效,请重新登录',
meta: '请重新登录',
duration: 2500,
keepAliveOnHover: true
})
// 延迟重试
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
// 重新发起请求
return request(config);
}
}
// 检查是否还可以重试
if (config.retryCount !== undefined && config.retryCount < MAX_RETRIES) {
config.retryCount++;

View File

@@ -1,7 +1,6 @@
import axios from 'axios';
import config from '../../../package.json';
import { useDateFormat } from '@vueuse/core';
interface GithubReleaseInfo {
tag_name: string;
body: string;
@@ -36,20 +35,41 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
try {
const token = import.meta.env.VITE_GITHUB_TOKEN;
const headers = {};
if (token) {
headers['Authorization'] = `token ${token}`;
}
const response = await axios.get(
const apiUrls = [
// 原始地址
'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest',
{
headers
}
);
if (response.data) {
return response.data;
// 使用 ghproxy.com 代理
'https://www.ghproxy.cn/https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/package.json',
// 使用 gitee 镜像(如果有的话)
// 'https://gitee.com/api/v5/repos/[用户名]/AlgerMusicPlayer/releases/latest'
];
if (token) {
headers['Authorization'] = `token ${token}`;
}
return null;
for (const url of apiUrls) {
try {
const response = await axios.get(url, { headers });
if (url.includes('package.json')) {
// 如果是 package.json直接读取版本号
return {
tag_name: response.data.version,
body:(await axios.get('https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/CHANGELOG.md')).data,
html_url: 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest',
assets: []
} as unknown as GithubReleaseInfo;
}
return response.data;
} catch (err) {
console.warn(`尝试访问 ${url} 失败:`, err);
continue;
}
}
throw new Error('所有 API 地址均无法访问');
} catch (error) {
console.error('获取 GitHub Release 信息失败:', error);
return null;
@@ -59,7 +79,7 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
/**
* 格式化时间
*/
const formatDate = (dateStr: string): string => {
export const formatDate = (dateStr: string): string => {
return useDateFormat(new Date(dateStr), 'YYYY-MM-DD HH:mm').value;
};
@@ -69,6 +89,7 @@ const formatDate = (dateStr: string): string => {
export const checkUpdate = async (currentVersion: string = config.version): Promise<UpdateResult | null> => {
try {
const releaseInfo = await getLatestReleaseInfo();
console.log('releaseInfo',releaseInfo)
if (!releaseInfo) {
return null;
}
@@ -77,6 +98,8 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
if (latestVersion === currentVersion) {
return null;
}
console.log('latestVersion',latestVersion)
console.log('currentVersion',currentVersion)
return {
hasUpdate: true,
@@ -84,7 +107,7 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
currentVersion,
releaseInfo: {
tag_name: latestVersion,
body: `## 更新内容\n\n- 版本: ${latestVersion}\n- 发布时间: ${formatDate(releaseInfo.published_at)}\n\n${releaseInfo.body}`,
body: `## 更新内容\n\n- 版本: ${latestVersion}\n${releaseInfo.body}`,
html_url: releaseInfo.html_url,
assets: releaseInfo.assets.map(asset => ({
browser_download_url: asset.browser_download_url,

View File

@@ -54,7 +54,7 @@ const scrollbarRef = ref();
// 无限滚动相关
const pageSize = 16;
const currentPage = ref(1);
const props = defineProps({
isComponent: {
type: Boolean,
@@ -64,10 +64,9 @@ const props = defineProps({
// 获取当前页的收藏歌曲ID
const getCurrentPageIds = () => {
const reversedList = [...favoriteList.value];
const startIndex = (currentPage.value - 1) * pageSize;
const endIndex = startIndex + pageSize;
return reversedList.slice(startIndex, endIndex);
return favoriteList.value.slice(startIndex, endIndex);
};
// 获取收藏歌曲详情
@@ -120,6 +119,7 @@ const handleScroll = (e: any) => {
};
onMounted(() => {
store.dispatch('initializeFavoriteList');
getFavoriteSongs();
});

View File

@@ -112,6 +112,27 @@
<n-button size="small" type="primary" @click="openAuthor"><i class="ri-github-line"></i>前往github</n-button>
</div>
</div>
<div class="set-item">
<div>
<div class="set-item-title">音质设置</div>
<div class="set-item-content">选择音乐播放音质VIP</div>
</div>
<n-select
v-model:value="setData.musicQuality"
:options="[
{ label: '标准', value: 'standard' },
{ label: '较高', value: 'higher' },
{ label: '极高', value: 'exhigh' },
{ label: '无损', value: 'lossless' },
{ label: 'Hi-Res', value: 'hires' },
{ label: '高清环绕声', value: 'jyeffect' },
{ label: '沉浸环绕声', value: 'sky' },
{ label: '杜比全景声', value: 'dolby' },
{ label: '超清母带', value: 'jymaster' }
]"
style="width: 160px"
/>
</div>
<div class="set-item" v-if="isElectron">
<div>
<div class="set-item-title">关闭行为</div>
@@ -129,6 +150,7 @@
style="width: 160px"
/>
</div>
<div class="set-item" v-if="isElectron">
<div>
<div class="set-item-title">重启</div>
@@ -168,6 +190,7 @@
/>
</div>
</div>
</div>
<PlayBottom/>
<n-modal
@@ -247,6 +270,10 @@ const setData = computed(() => {
port: 7890
};
}
// 确保音质设置存在
if (!data.musicQuality) {
data.musicQuality = 'higher';
}
return data;
});
@@ -290,7 +317,7 @@ const checkForUpdates = async (isClick = false) => {
};
const openReleasePage = () => {
window.open(updateInfo.value.releaseInfo?.html_url || 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank');
store.commit('setShowUpdateModal', true)
};
const selectDownloadPath = async () => {

View File

@@ -89,7 +89,7 @@
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { computed, ref, watch, onBeforeUnmount } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
@@ -112,42 +112,58 @@ const userDetail = ref<IUserDetail>();
const playList = ref<any[]>([]);
const recordList = ref();
const infoLoading = ref(false);
const mounted = ref(true);
const isShowList = ref(false);
const list = ref<Playlist>();
const listLoading = ref(false);
const user = computed(() => store.state.user);
onBeforeUnmount(() => {
mounted.value = false;
});
const loadPage = async () => {
if (!user.value) {
router.push('/login');
return;
if (!mounted.value || !user.value) return;
try {
infoLoading.value = true;
const { data: userData } = await getUserDetail(user.value.userId);
if (!mounted.value) return;
userDetail.value = userData;
const { data: playlistData } = await getUserPlaylist(user.value.userId);
if (!mounted.value) return;
playList.value = playlistData.playlist;
const { data: recordData } = await getUserRecord(user.value.userId);
if (!mounted.value) return;
recordList.value = recordData.allData.map((item: any) => ({
...item,
...item.song,
picUrl: item.song.al.picUrl
}));
} catch (error) {
console.error('加载用户页面失败:', error);
} finally {
if (mounted.value) {
infoLoading.value = false;
}
}
infoLoading.value = true;
const { data: userData } = await getUserDetail(user.value.userId);
userDetail.value = userData;
const { data: playlistData } = await getUserPlaylist(user.value.userId);
playList.value = playlistData.playlist;
const { data: recordData } = await getUserRecord(user.value.userId);
recordList.value = recordData.allData.map((item: any) => ({
...item,
...item.song,
picUrl: item.song.al.picUrl
}));
infoLoading.value = false;
};
onActivated(() => {
if (!user.value) {
// 监听用户状态变化
watch(() => store.state.user, (newUser) => {
if (!mounted.value) return;
if (!newUser) {
router.push('/login');
} else {
loadPage();
}
});
}, { immediate: true });
const isShowList = ref(false);
const list = ref<Playlist>();
const listLoading = ref(false);
// 展示歌单
const showPlaylist = async (id: number, name: string) => {
isShowList.value = true;