mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-06 16:40:50 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddb814da10 | ||
|
|
e266ea8ef8 | ||
|
|
a894954641 | ||
|
|
f640ab9969 | ||
|
|
9eb17fd978 | ||
|
|
020aca7384 | ||
|
|
fcc47dc0ff | ||
|
|
17ce268da6 | ||
|
|
43c64b1b43 | ||
|
|
11ced6b418 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -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)
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
3
src/preload/index.d.ts
vendored
3
src/preload/index.d.ts
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,6 @@ onMounted(() => {
|
||||
'setMenus',
|
||||
homeRouter.filter((item) => item.meta.isMobile)
|
||||
);
|
||||
console.log(
|
||||
'qqq ',
|
||||
homeRouter.filter((item) => item.meta.isMobile)
|
||||
);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -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');
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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('获取音乐下载地址失败');
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user