feat: 支持解锁配置

This commit is contained in:
imsyy
2025-11-21 17:41:34 +08:00
parent 7721251a98
commit 1b6ebd9c7c
15 changed files with 283 additions and 51 deletions

View File

@@ -32,8 +32,12 @@
## 💬 交流群 ## 💬 交流群
<a href="https://qm.qq.com/cgi-bin/qm/qr?k=2-cVSf1bE0AvAehCib00qFEFdUvPaJ_k&jump_from=webapi&authKey=1NEhib9+GsmsXVo2rCc0IbRaVHeeRXJJ0gbsyKDcIwDdAzYySOubkFCvkV32+7Cw" target="_blank">
![交流群](/screenshots/welcome.png) ![交流群](/screenshots/welcome.png)
</a>
## 👀 Demo ## 👀 Demo
- [SPlayer](https://music.imsyy.top/) - [SPlayer](https://music.imsyy.top/)

1
components.d.ts vendored
View File

@@ -146,6 +146,7 @@ declare module 'vue' {
SongList: typeof import('./src/components/List/SongList.vue')['default'] SongList: typeof import('./src/components/List/SongList.vue')['default']
SongListCard: typeof import('./src/components/Card/SongListCard.vue')['default'] SongListCard: typeof import('./src/components/Card/SongListCard.vue')['default']
SongListMenu: typeof import('./src/components/Menu/SongListMenu.vue')['default'] SongListMenu: typeof import('./src/components/Menu/SongListMenu.vue')['default']
SongUnlockManager: typeof import('./src/components/Modal/SongUnlockManager.vue')['default']
SvgIcon: typeof import('./src/components/Global/SvgIcon.vue')['default'] SvgIcon: typeof import('./src/components/Global/SvgIcon.vue')['default']
TextContainer: typeof import('./src/components/Global/TextContainer.vue')['default'] TextContainer: typeof import('./src/components/Global/TextContainer.vue')['default']
UpdateApp: typeof import('./src/components/Modal/UpdateApp.vue')['default'] UpdateApp: typeof import('./src/components/Modal/UpdateApp.vue')['default']

View File

@@ -49,6 +49,7 @@
"@pixi/filter-color-matrix": "^7.4.3", "@pixi/filter-color-matrix": "^7.4.3",
"@pixi/sprite": "^7.4.3", "@pixi/sprite": "^7.4.3",
"@vueuse/core": "^13.9.0", "@vueuse/core": "^13.9.0",
"@vueuse/integrations": "^14.0.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"axios-retry": "^4.5.0", "axios-retry": "^4.5.0",
"change-case": "^5.4.4", "change-case": "^5.4.4",
@@ -72,6 +73,7 @@
"pinia": "^3.0.4", "pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1", "pinia-plugin-persistedstate": "^4.7.1",
"plyr": "^3.8.3", "plyr": "^3.8.3",
"sortablejs": "^1",
"vue-virt-list": "^1.6.1" "vue-virt-list": "^1.6.1"
}, },
"devDependencies": { "devDependencies": {

91
pnpm-lock.yaml generated
View File

@@ -60,6 +60,9 @@ importers:
'@vueuse/core': '@vueuse/core':
specifier: ^13.9.0 specifier: ^13.9.0
version: 13.9.0(vue@3.5.24(typescript@5.9.3)) version: 13.9.0(vue@3.5.24(typescript@5.9.3))
'@vueuse/integrations':
specifier: ^14.0.0
version: 14.0.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.24(typescript@5.9.3))
axios: axios:
specifier: ^1.13.2 specifier: ^1.13.2
version: 1.13.2 version: 1.13.2
@@ -129,6 +132,9 @@ importers:
plyr: plyr:
specifier: ^3.8.3 specifier: ^3.8.3
version: 3.8.3 version: 3.8.3
sortablejs:
specifier: ^1
version: 1.15.6
vue-virt-list: vue-virt-list:
specifier: ^1.6.1 specifier: ^1.6.1
version: 1.6.1(vue@3.5.24(typescript@5.9.3)) version: 1.6.1(vue@3.5.24(typescript@5.9.3))
@@ -1514,14 +1520,69 @@ packages:
peerDependencies: peerDependencies:
vue: ^3.5.0 vue: ^3.5.0
'@vueuse/core@14.0.0':
resolution: {integrity: sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==}
peerDependencies:
vue: ^3.5.0
'@vueuse/integrations@14.0.0':
resolution: {integrity: sha512-5A0X7q9qyLtM3xyghq5nK/NEESf7cpcZlkQgXTMuW4JWiAMYxc1ImdhhGrk4negFBsq3ejvAlRmLdNrkcTzk1Q==}
peerDependencies:
async-validator: ^4
axios: ^1
change-case: ^5
drauu: ^0.4
focus-trap: ^7
fuse.js: ^7
idb-keyval: ^6
jwt-decode: ^4
nprogress: ^0.2
qrcode: ^1.5
sortablejs: ^1
universal-cookie: ^7 || ^8
vue: ^3.5.0
peerDependenciesMeta:
async-validator:
optional: true
axios:
optional: true
change-case:
optional: true
drauu:
optional: true
focus-trap:
optional: true
fuse.js:
optional: true
idb-keyval:
optional: true
jwt-decode:
optional: true
nprogress:
optional: true
qrcode:
optional: true
sortablejs:
optional: true
universal-cookie:
optional: true
'@vueuse/metadata@13.9.0': '@vueuse/metadata@13.9.0':
resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==} resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
'@vueuse/metadata@14.0.0':
resolution: {integrity: sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==}
'@vueuse/shared@13.9.0': '@vueuse/shared@13.9.0':
resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==} resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==}
peerDependencies: peerDependencies:
vue: ^3.5.0 vue: ^3.5.0
'@vueuse/shared@14.0.0':
resolution: {integrity: sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==}
peerDependencies:
vue: ^3.5.0
'@xmldom/xmldom@0.8.11': '@xmldom/xmldom@0.8.11':
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -3932,6 +3993,9 @@ packages:
resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
sortablejs@1.15.6:
resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -5876,12 +5940,37 @@ snapshots:
'@vueuse/shared': 13.9.0(vue@3.5.24(typescript@5.9.3)) '@vueuse/shared': 13.9.0(vue@3.5.24(typescript@5.9.3))
vue: 3.5.24(typescript@5.9.3) vue: 3.5.24(typescript@5.9.3)
'@vueuse/core@14.0.0(vue@3.5.24(typescript@5.9.3))':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 14.0.0
'@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3))
vue: 3.5.24(typescript@5.9.3)
'@vueuse/integrations@14.0.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.24(typescript@5.9.3))':
dependencies:
'@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.9.3))
'@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.9.3))
vue: 3.5.24(typescript@5.9.3)
optionalDependencies:
async-validator: 4.2.5
axios: 1.13.2
change-case: 5.4.4
qrcode: 1.5.4
sortablejs: 1.15.6
'@vueuse/metadata@13.9.0': {} '@vueuse/metadata@13.9.0': {}
'@vueuse/metadata@14.0.0': {}
'@vueuse/shared@13.9.0(vue@3.5.24(typescript@5.9.3))': '@vueuse/shared@13.9.0(vue@3.5.24(typescript@5.9.3))':
dependencies: dependencies:
vue: 3.5.24(typescript@5.9.3) vue: 3.5.24(typescript@5.9.3)
'@vueuse/shared@14.0.0(vue@3.5.24(typescript@5.9.3))':
dependencies:
vue: 3.5.24(typescript@5.9.3)
'@xmldom/xmldom@0.8.11': {} '@xmldom/xmldom@0.8.11': {}
abbrev@1.1.1: {} abbrev@1.1.1: {}
@@ -8598,6 +8687,8 @@ snapshots:
dependencies: dependencies:
is-plain-obj: 1.1.0 is-plain-obj: 1.1.0
sortablejs@1.15.6: {}
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
source-map-support@0.5.21: source-map-support@0.5.21:

View File

@@ -1,5 +1,6 @@
import { isElectron } from "@/utils/env"; import { isElectron } from "@/utils/env";
import { songLevelData } from "@/utils/meta"; import { songLevelData } from "@/utils/meta";
import { SongUnlockServer } from "@/utils/songManager";
import request from "@/utils/request"; import request from "@/utils/request";
// 获取歌曲详情 // 获取歌曲详情
@@ -47,16 +48,12 @@ export const songUrl = (
}; };
// 获取解锁歌曲 URL // 获取解锁歌曲 URL
export const unlockSongUrl = ( export const unlockSongUrl = (id: number, keyword: string, server: SongUnlockServer) => {
id: number, const params = server === SongUnlockServer.NETEASE ? { id } : { keyword };
keyword: string,
server: "netease" | "kuwo" | "bodian",
) => {
const params = server === "netease" ? { id } : { keyword };
return request({ return request({
baseURL: "/api/unblock", baseURL: "/api/unblock",
url: `/${server}`, url: `/${server}`,
params, params: { ...params, noCookie: true },
}); });
}; };

View File

@@ -0,0 +1,62 @@
<template>
<div class="song-unlock-manager">
<n-alert title="免责声明" type="info">
本功能仅作为测试使用资源来自网络若侵犯到您的权益请及时联系我们删除
</n-alert>
<div ref="sortableRef" class="sortable-list">
<n-card
v-for="item in settingStore.songUnlockServer"
:key="item.key"
:content-style="{
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '16px',
}"
class="sortable-item"
>
<SvgIcon :depth="3" name="Menu" />
<n-text class="name">{{ item.key }}</n-text>
<n-switch v-model:value="item.enabled" :round="false" />
</n-card>
</div>
</div>
</template>
<script setup lang="ts">
import { useSettingStore } from "@/stores";
import { useSortable } from "@vueuse/integrations/useSortable";
const settingStore = useSettingStore();
const sortableRef = ref<HTMLElement | null>(null);
// 拖拽
useSortable(sortableRef, settingStore.songUnlockServer, {
animation: 150,
handle: ".n-icon",
});
</script>
<style scoped lang="scss">
.sortable-list {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 12px;
.sortable-item {
border-radius: 8px;
.n-icon {
font-size: 16px;
cursor: move;
}
.name {
font-size: 16px;
line-height: normal;
}
.n-switch {
margin-left: auto;
}
}
}
</style>

View File

@@ -34,7 +34,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { LyricPlayer } from "@applemusic-like-lyrics/vue"; import { LyricPlayer } from "@applemusic-like-lyrics/vue";
import { LyricLine } from "@applemusic-like-lyrics/core"; import { type LyricLine } from "@applemusic-like-lyrics/lyric";
import { useMusicStore, useSettingStore, useStatusStore } from "@/stores"; import { useMusicStore, useSettingStore, useStatusStore } from "@/stores";
import { getLyricLanguage } from "@/utils/format"; import { getLyricLanguage } from "@/utils/format";
import { usePlayer } from "@/utils/player"; import { usePlayer } from "@/utils/player";

View File

@@ -36,6 +36,21 @@
</n-card> </n-card>
</n-collapse-transition> </n-collapse-transition>
</div> </div>
<div class="set-list">
<n-h3 prefix="bar"> 社区与资讯 </n-h3>
<n-flex class="link">
<n-card
v-for="(item, index) in communityData"
:key="index"
class="link-item"
hoverable
@click="openLink(item.url)"
>
<SvgIcon :name="item.icon" :size="26" />
<n-text class="name"> {{ item.name }} </n-text>
</n-card>
</n-flex>
</div>
<div class="set-list"> <div class="set-list">
<n-h3 prefix="bar"> 历史版本 </n-h3> <n-h3 prefix="bar"> 历史版本 </n-h3>
<n-collapse-transition :show="oldVersion?.length > 0"> <n-collapse-transition :show="oldVersion?.length > 0">
@@ -59,21 +74,6 @@
</n-collapse> </n-collapse>
</n-collapse-transition> </n-collapse-transition>
</div> </div>
<div class="set-list">
<n-h3 prefix="bar"> 社区与资讯 </n-h3>
<n-flex class="link">
<n-card
v-for="(item, index) in communityData"
:key="index"
class="link-item"
hoverable
@click="openLink(item.url)"
>
<SvgIcon :name="item.icon" :size="26" />
<n-text class="name"> {{ item.name }} </n-text>
</n-card>
</n-flex>
</div>
</div> </div>
</template> </template>
@@ -89,6 +89,11 @@ const statusStore = useStatusStore();
// 社区数据 // 社区数据
const communityData = [ const communityData = [
{
name: "加入交流群",
url: "https://qm.qq.com/cgi-bin/qm/qr?k=2-cVSf1bE0AvAehCib00qFEFdUvPaJ_k&jump_from=webapi&authKey=1NEhib9+GsmsXVo2rCc0IbRaVHeeRXJJ0gbsyKDcIwDdAzYySOubkFCvkV32+7Cw",
icon: "QQ",
},
{ {
name: "GitHub", name: "GitHub",
url: packageJson.github, url: packageJson.github,

View File

@@ -251,9 +251,7 @@
<div class="label"> <div class="label">
<n-text class="name"> <n-text class="name">
启用在线 TTML 歌词 启用在线 TTML 歌词
<n-tag type="warning" size="small" round style="display: inline; vertical-align: middle" <n-tag type="warning" size="small" round> Beta </n-tag>
>Beta</n-tag
>
</n-text> </n-text>
<n-text class="tip" :depth="3"> <n-text class="tip" :depth="3">
是否从 AMLL TTML DB 获取歌词如有TTML 是否从 AMLL TTML DB 获取歌词如有TTML

View File

@@ -66,13 +66,6 @@
</div> </div>
<n-switch v-model:value="settingStore.playSongDemo" class="set" :round="false" /> <n-switch v-model:value="settingStore.playSongDemo" class="set" :round="false" />
</n-card> </n-card>
<n-card v-if="isElectron" class="set-item">
<div class="label">
<n-text class="name">音乐解锁</n-text>
<n-text class="tip" :depth="3">在无法正常播放时进行替换可能会与原曲不符</n-text>
</div>
<n-switch v-model:value="settingStore.useSongUnlock" class="set" :round="false" />
</n-card>
<n-card v-if="isElectron" class="set-item"> <n-card v-if="isElectron" class="set-item">
<div class="label"> <div class="label">
<n-text class="name">音频输出设备</n-text> <n-text class="name">音频输出设备</n-text>
@@ -87,6 +80,35 @@
/> />
</n-card> </n-card>
</div> </div>
<div v-if="isElectron" class="set-list">
<n-h3 prefix="bar">
音乐解锁
<n-tag type="warning" size="small" round>Beta</n-tag>
</n-h3>
<n-card class="set-item">
<div class="label">
<n-text class="name">音乐解锁</n-text>
<n-text class="tip" :depth="3"> 在无法正常播放时进行替换可能会与原曲不符 </n-text>
</div>
<n-switch v-model:value="settingStore.useSongUnlock" class="set" :round="false" />
</n-card>
<!-- 音源配置 -->
<n-card class="set-item">
<div class="label">
<n-text class="name">音源配置</n-text>
<n-text class="tip" :depth="3"> 配置歌曲解锁的音源顺序或是否启用 </n-text>
</div>
<n-button
:disabled="!settingStore.useSongUnlock"
type="primary"
strong
secondary
@click="openSongUnlockManager"
>
配置
</n-button>
</n-card>
</div>
<div class="set-list"> <div class="set-list">
<n-h3 prefix="bar"> 播放器 </n-h3> <n-h3 prefix="bar"> 播放器 </n-h3>
<n-card class="set-item"> <n-card class="set-item">
@@ -228,6 +250,7 @@ import { renderOption } from "@/utils/helper";
import { isElectron } from "@/utils/env"; import { isElectron } from "@/utils/env";
import { uniqBy } from "lodash"; import { uniqBy } from "lodash";
import { usePlayer } from "@/utils/player"; import { usePlayer } from "@/utils/player";
import { openSongUnlockManager } from "@/utils/modal";
const player = usePlayer(); const player = usePlayer();
const settingStore = useSettingStore(); const settingStore = useSettingStore();

View File

@@ -1,5 +1,6 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { keywords, regexes } from "@/assets/data/exclude"; import { keywords, regexes } from "@/assets/data/exclude";
import { SongUnlockServer } from "@/utils/songManager";
export interface SettingState { export interface SettingState {
/** 明暗模式 */ /** 明暗模式 */
@@ -99,6 +100,8 @@ export interface SettingState {
songVolumeFadeTime: number; songVolumeFadeTime: number;
/** 是否使用解灰 */ /** 是否使用解灰 */
useSongUnlock: boolean; useSongUnlock: boolean;
/** 歌曲解锁音源 */
songUnlockServer: { key: SongUnlockServer; enabled: boolean }[];
/** 显示倒计时 */ /** 显示倒计时 */
countDownShow: boolean; countDownShow: boolean;
/** 显示歌词条 */ /** 显示歌词条 */
@@ -201,6 +204,11 @@ export const useSettingStore = defineStore("setting", {
songVolumeFade: true, songVolumeFade: true,
songVolumeFadeTime: 300, songVolumeFadeTime: 300,
useSongUnlock: true, useSongUnlock: true,
songUnlockServer: [
{ key: SongUnlockServer.BODIAN, enabled: true },
{ key: SongUnlockServer.GEQUBAO, enabled: true },
{ key: SongUnlockServer.NETEASE, enabled: true },
],
countDownShow: true, countDownShow: true,
barLyricShow: true, barLyricShow: true,
playerType: "cover", playerType: "cover",

View File

@@ -3,7 +3,6 @@
padding: 0; padding: 0;
user-select: none; user-select: none;
box-sizing: border-box; box-sizing: border-box;
-webkit-user-drag: none;
::after { ::after {
box-sizing: border-box; box-sizing: border-box;
} }
@@ -18,6 +17,12 @@ body {
overflow: hidden; overflow: hidden;
} }
img,
video,
audio {
-webkit-user-drag: none;
}
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@@ -20,6 +20,7 @@ import ExcludeLyrics from "@/components/Modal/ExcludeLyrics.vue";
import ChangeRate from "@/components/Modal/ChangeRate.vue"; import ChangeRate from "@/components/Modal/ChangeRate.vue";
import AutoClose from "@/components/Modal/AutoClose.vue"; import AutoClose from "@/components/Modal/AutoClose.vue";
import Equalizer from "@/components/Modal/Equalizer.vue"; import Equalizer from "@/components/Modal/Equalizer.vue";
import SongUnlockManager from "@/components/Modal/SongUnlockManager.vue";
import { NScrollbar } from "naive-ui"; import { NScrollbar } from "naive-ui";
// 用户协议 // 用户协议
@@ -319,3 +320,17 @@ export const openDescModal = (content: string, title: string = "歌单简介") =
}, },
}); });
}; };
/** 打开音源管理弹窗 */
export const openSongUnlockManager = () => {
window.$modal.create({
preset: "card",
transformOrigin: "center",
autoFocus: false,
style: { width: "500px" },
title: "音源管理",
content: () => {
return h(SongUnlockManager);
},
});
};

View File

@@ -84,23 +84,34 @@ export const getUnlockSongUrl = async (songData: SongType): Promise<string | nul
const artist = Array.isArray(songData.artists) ? songData.artists[0].name : songData.artists; const artist = Array.isArray(songData.artists) ? songData.artists[0].name : songData.artists;
const keyWord = songData.name + "-" + artist; const keyWord = songData.name + "-" + artist;
if (!songId || !keyWord) return null; if (!songId || !keyWord) return null;
// 获取音源列表
const servers: any[] = [ const settingStore = useSettingStore();
"bodian", const servers = settingStore.songUnlockServer
"netease", .filter((server) => server.enabled)
]; .map((server) => server.key);
if (servers.length === 0) return null;
// 尝试解锁 // 并发请求
const promises = servers.map(server => unlockSongUrl(songId, keyWord, server)); const promises = servers.map((server) =>
const results = await Promise.allSettled(promises); unlockSongUrl(songId, keyWord, server)
// 解析结果 .then((result) => ({
for (const result of results) { server,
if ( result,
result.status === "fulfilled" && success: result.code === 200 && !!result.url,
result.value.code === 200 && }))
result.value.url .catch((err) => {
) { console.error(`Unlock failed with server ${server}:`, err);
return result.value.url; return { server, result: null, success: false };
}),
);
// 按优先级顺序处理结果
for (const p of promises) {
try {
const item = await p;
if (item.success && item.result) {
return item.result.url;
}
} catch {
continue;
} }
} }
return null; return null;

View File

@@ -1,3 +1,13 @@
/**
* 歌曲解锁服务器
*/
export enum SongUnlockServer {
NETEASE = "netease",
BODIAN = "bodian",
// KUWO = "kuwo",
GEQUBAO = "gequbao",
}
class SongManager {} class SongManager {}
export default new SongManager(); export default new SongManager();