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

View File

@@ -1,6 +1,6 @@
{ {
"name": "AlgerMusicPlayer", "name": "AlgerMusicPlayer",
"version": "3.2.0", "version": "3.3.0",
"description": "Alger Music Player", "description": "Alger Music Player",
"author": "Alger <algerkc@qq.com>", "author": "Alger <algerkc@qq.com>",
"main": "./out/main/index.js", "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) => { ipcMain.handle('unblock-music', async (_, id, data) => {
return unblockMusic(id); return unblockMusic(id, data);
}); });
import server from 'netease-cloud-music-api-alger/server'; import server from 'netease-cloud-music-api-alger/server';

View File

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

View File

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

View File

@@ -12,7 +12,8 @@ declare global {
dragStart: (data: string) => void; dragStart: (data: string) => void;
miniTray: () => void; miniTray: () => void;
restart: () => 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', 'setMenus',
homeRouter.filter((item) => item.meta.isMobile) homeRouter.filter((item) => item.meta.isMobile)
); );
console.log(
'qqq ',
homeRouter.filter((item) => item.meta.isMobile)
);
} }
}); });
</script> </script>

View File

@@ -1,11 +1,33 @@
import { ILyric } from '@/type/lyric'; import { ILyric } from '@/type/lyric';
import { IPlayMusicUrl } from '@/type/music';
import { isElectron } from '@/utils'; import { isElectron } from '@/utils';
import request from '@/utils/request'; import request from '@/utils/request';
import requestMusic from '@/utils/request_music'; 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 // 根据音乐Id获取音乐播放URl
export const getMusicUrl = (id: number) => { export const getMusicUrl = async (id: number) => {
return request.get<IPlayMusicUrl>('/song/url', { params: { id } }); 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 } }); return request.get<ILyric>('/lyric', { params: { id } });
}; };
export const getParsingMusicUrl = (id: number) => { export const getParsingMusicUrl = (id: number, data: any) => {
if (isElectron) { if (isElectron) {
return window.api.unblockMusic(id); return window.api.unblockMusic(id, data);
} }
return requestMusic.get<any>('/music', { params: { id } }); 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" :step="0.1"
@update:value="handleProgressChange" @update:value="handleProgressChange"
> >
<template #rail>
<div class="progress-rail">
<div class="progress-buffer" :style="{ width: `${bufferedProgress}%` }"></div>
</div>
</template>
</n-slider> </n-slider>
</div> </div>
@@ -643,10 +638,44 @@ const isMobile = computed(() => store.state.isMobile);
.custom-slider { .custom-slider {
:deep(.n-slider) { :deep(.n-slider) {
--n-rail-height: 4px; --n-rail-height: 4px;
--n-rail-color: rgba(255, 255, 255, 0.2); --n-rail-color: theme('colors.gray.200');
--n-fill-color: #10b981; --n-rail-color-dark: theme('colors.gray.700');
--n-fill-color: theme('colors.green.500');
--n-handle-size: 12px; --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>
<div class="ml-4"> <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 class="recommend-singer-item-info-name text-el">{{ item.name }}</div>
</div> </div>
</div> </div>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,6 +48,10 @@
<i class="iconfont ri-login-box-line"></i> <i class="iconfont ri-login-box-line"></i>
<span>去登录</span> <span>去登录</span>
</div> </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')"> <div class="menu-item" @click="selectItem('set')">
<i class="iconfont ri-settings-3-line"></i> <i class="iconfont ri-settings-3-line"></i>
@@ -195,8 +199,7 @@ const selectItem = async (key: string) => {
switch (key) { switch (key) {
case 'logout': case 'logout':
logout().then(() => { logout().then(() => {
store.state.user = null; store.commit('logout');
localStorage.clear();
router.push('/login'); router.push('/login');
}); });
break; break;
@@ -237,7 +240,7 @@ const checkForUpdates = async () => {
const toGithubRelease = () => { const toGithubRelease = () => {
if (updateInfo.value.hasUpdate) { if (updateInfo.value.hasUpdate) {
window.open(updateInfo.value.releaseInfo?.html_url || 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank'); store.commit('setShowUpdateModal', true)
} else { } else {
window.open('https://github.com/algerkong/AlgerMusicPlayer/releases', '_blank'); 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 type { SongResult } from '@/type/music';
import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme'; import { applyTheme, getCurrentTheme, ThemeType } from '@/utils/theme';
import { isElectron } from '@/utils'; import { isElectron } from '@/utils';
import { likeSong, getLikedList } from '@/api/music';
import setData from '@/../main/set.json'
// 默认设置 // 默认设置
const defaultSettings = { const defaultSettings = setData;
isProxy: false,
noAnimate: false,
animationSpeed: 1,
author: 'Alger',
authorUrl: 'https://github.com/algerkong'
};
function getLocalStorageItem<T>(key: string, defaultValue: T): T { function getLocalStorageItem<T>(key: string, defaultValue: T): T {
const item = localStorage.getItem(key); const item = localStorage.getItem(key);
@@ -38,6 +34,7 @@ interface State {
playMode: number; playMode: number;
theme: ThemeType; theme: ThemeType;
musicFull: boolean; musicFull: boolean;
showUpdateModal: boolean;
} }
const state: State = { const state: State = {
@@ -57,7 +54,8 @@ const state: State = {
favoriteList: getLocalStorageItem('favoriteList', []), favoriteList: getLocalStorageItem('favoriteList', []),
playMode: getLocalStorageItem('playMode', 0), playMode: getLocalStorageItem('playMode', 0),
theme: getCurrentTheme(), theme: getCurrentTheme(),
musicFull: false musicFull: false,
showUpdateModal: false
}; };
const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook(); const { handlePlayMusic, nextPlay, prevPlay } = useMusicListHook();
@@ -100,15 +98,25 @@ const mutations = {
localStorage.setItem('appSettings', JSON.stringify(setData)); localStorage.setItem('appSettings', JSON.stringify(setData));
} }
}, },
addToFavorite(state: State, songId: number) { async addToFavorite(state: State, songId: number) {
if (!state.favoriteList.includes(songId)) { try {
state.favoriteList = [songId, ...state.favoriteList]; state.user && localStorage.getItem('token') && await likeSong(songId, true);
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList)); 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) { async removeFromFavorite(state: State, songId: number) {
state.favoriteList = state.favoriteList.filter((id) => id !== songId); try {
localStorage.setItem('favoriteList', JSON.stringify(state.favoriteList)); 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) { togglePlayMode(state: State) {
state.playMode = (state.playMode + 1) % 3; state.playMode = (state.playMode + 1) % 3;
@@ -117,13 +125,20 @@ const mutations = {
toggleTheme(state: State) { toggleTheme(state: State) {
state.theme = state.theme === 'dark' ? 'light' : 'dark'; state.theme = state.theme === 'dark' ? 'light' : 'dark';
applyTheme(state.theme); applyTheme(state.theme);
},
setShowUpdateModal(state, value) {
state.showUpdateModal = value
},
logout(state: State) {
state.user = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
} }
}; };
const actions = { const actions = {
initializeSettings({ commit }: { commit: any }) { initializeSettings({ commit }: { commit: any }) {
if (isElectron) { if (isElectron) {
// const setData = (window as any).electron.ipcRenderer.getStoreValue('set');
const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set'); const setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
commit('setSetData', setData || defaultSettings); commit('setSetData', setData || defaultSettings);
} else { } else {
@@ -140,6 +155,24 @@ const actions = {
}, },
initializeTheme({ state }: { state: State }) { initializeTheme({ state }: { state: State }) {
applyTheme(state.theme); 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(); 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 = '') => { export const getImgUrl = (url: string | undefined, size: string = '') => {
const bdUrl = 'https://image.baidu.com/search/down?url=';
const imgUrl = `${url}?param=${size}`; const imgUrl = `${url}?param=${size}`;
if (!getIsMc()) { return imgUrl;
return imgUrl;
}
return `${bdUrl}${encodeURIComponent(imgUrl)}`;
}; };
export const isMobile = computed(() => { export const isMobile = computed(() => {

View File

@@ -1,8 +1,14 @@
import axios, { InternalAxiosRequestConfig } from 'axios'; import axios, { InternalAxiosRequestConfig } from 'axios';
import { isElectron } from '.'; import { isElectron } from '.';
import store from '@/store';
import { createDiscreteApi } from 'naive-ui'
const { notification } = createDiscreteApi(
['notification']
)
let setData: any = null; let setData: any = null;
const getSetData = ()=>{ const getSetData = ()=>{
if (window.electron) { if (window.electron) {
setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set'); setData = window.electron.ipcRenderer.sendSync('get-store-value', 'set');
@@ -44,7 +50,9 @@ request.interceptors.request.use(
}; };
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (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; return response;
}, },
async (error) => { async (error) => {
console.log('error',error)
const config = error.config as CustomAxiosRequestConfig; const config = error.config as CustomAxiosRequestConfig;
// 如果没有配置,直接返回错误 // 如果没有配置,直接返回错误
@@ -79,6 +88,30 @@ request.interceptors.response.use(
return Promise.reject(error); 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) { if (config.retryCount !== undefined && config.retryCount < MAX_RETRIES) {
config.retryCount++; config.retryCount++;

View File

@@ -1,7 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import config from '../../../package.json'; import config from '../../../package.json';
import { useDateFormat } from '@vueuse/core'; import { useDateFormat } from '@vueuse/core';
interface GithubReleaseInfo { interface GithubReleaseInfo {
tag_name: string; tag_name: string;
body: string; body: string;
@@ -36,20 +35,41 @@ export const getLatestReleaseInfo = async (): Promise<GithubReleaseInfo | null>
try { try {
const token = import.meta.env.VITE_GITHUB_TOKEN; const token = import.meta.env.VITE_GITHUB_TOKEN;
const headers = {}; const headers = {};
if (token) {
headers['Authorization'] = `token ${token}`; const apiUrls = [
} // 原始地址
const response = await axios.get(
'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest', 'https://api.github.com/repos/algerkong/AlgerMusicPlayer/releases/latest',
{
headers // 使用 ghproxy.com 代理
} 'https://www.ghproxy.cn/https://raw.githubusercontent.com/algerkong/AlgerMusicPlayer/dev_electron/package.json',
);
// 使用 gitee 镜像(如果有的话)
if (response.data) { // 'https://gitee.com/api/v5/repos/[用户名]/AlgerMusicPlayer/releases/latest'
return response.data; ];
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) { } catch (error) {
console.error('获取 GitHub Release 信息失败:', error); console.error('获取 GitHub Release 信息失败:', error);
return null; 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; 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> => { export const checkUpdate = async (currentVersion: string = config.version): Promise<UpdateResult | null> => {
try { try {
const releaseInfo = await getLatestReleaseInfo(); const releaseInfo = await getLatestReleaseInfo();
console.log('releaseInfo',releaseInfo)
if (!releaseInfo) { if (!releaseInfo) {
return null; return null;
} }
@@ -77,6 +98,8 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
if (latestVersion === currentVersion) { if (latestVersion === currentVersion) {
return null; return null;
} }
console.log('latestVersion',latestVersion)
console.log('currentVersion',currentVersion)
return { return {
hasUpdate: true, hasUpdate: true,
@@ -84,7 +107,7 @@ export const checkUpdate = async (currentVersion: string = config.version): Prom
currentVersion, currentVersion,
releaseInfo: { releaseInfo: {
tag_name: latestVersion, 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, html_url: releaseInfo.html_url,
assets: releaseInfo.assets.map(asset => ({ assets: releaseInfo.assets.map(asset => ({
browser_download_url: asset.browser_download_url, browser_download_url: asset.browser_download_url,

View File

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

View File

@@ -112,6 +112,27 @@
<n-button size="small" type="primary" @click="openAuthor"><i class="ri-github-line"></i>前往github</n-button> <n-button size="small" type="primary" @click="openAuthor"><i class="ri-github-line"></i>前往github</n-button>
</div> </div>
</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 class="set-item" v-if="isElectron">
<div> <div>
<div class="set-item-title">关闭行为</div> <div class="set-item-title">关闭行为</div>
@@ -129,6 +150,7 @@
style="width: 160px" style="width: 160px"
/> />
</div> </div>
<div class="set-item" v-if="isElectron"> <div class="set-item" v-if="isElectron">
<div> <div>
<div class="set-item-title">重启</div> <div class="set-item-title">重启</div>
@@ -168,6 +190,7 @@
/> />
</div> </div>
</div> </div>
</div> </div>
<PlayBottom/> <PlayBottom/>
<n-modal <n-modal
@@ -247,6 +270,10 @@ const setData = computed(() => {
port: 7890 port: 7890
}; };
} }
// 确保音质设置存在
if (!data.musicQuality) {
data.musicQuality = 'higher';
}
return data; return data;
}); });
@@ -290,7 +317,7 @@ const checkForUpdates = async (isClick = false) => {
}; };
const openReleasePage = () => { const openReleasePage = () => {
window.open(updateInfo.value.releaseInfo?.html_url || 'https://github.com/algerkong/AlgerMusicPlayer/releases/latest', '_blank'); store.commit('setShowUpdateModal', true)
}; };
const selectDownloadPath = async () => { const selectDownloadPath = async () => {

View File

@@ -89,7 +89,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue'; import { computed, ref, watch, onBeforeUnmount } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
@@ -112,42 +112,58 @@ const userDetail = ref<IUserDetail>();
const playList = ref<any[]>([]); const playList = ref<any[]>([]);
const recordList = ref(); const recordList = ref();
const infoLoading = ref(false); 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); const user = computed(() => store.state.user);
onBeforeUnmount(() => {
mounted.value = false;
});
const loadPage = async () => { const loadPage = async () => {
if (!user.value) { if (!mounted.value || !user.value) return;
router.push('/login');
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'); router.push('/login');
} else { } else {
loadPage(); loadPage();
} }
}); }, { immediate: true });
const isShowList = ref(false);
const list = ref<Playlist>();
const listLoading = ref(false);
// 展示歌单 // 展示歌单
const showPlaylist = async (id: number, name: string) => { const showPlaylist = async (id: number, name: string) => {
isShowList.value = true; isShowList.value = true;