mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-26 03:44:57 +08:00
feat: 支持云盘歌曲纠正
This commit is contained in:
@@ -49,5 +49,6 @@ module.exports = {
|
|||||||
$notification: true,
|
$notification: true,
|
||||||
$changeThemeColor: true,
|
$changeThemeColor: true,
|
||||||
$canNotConnect: true,
|
$canNotConnect: true,
|
||||||
|
$refreshCloudList: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: 转到讨论区
|
||||||
|
url: https://github.com/imsyy/SPlayer/discussions
|
||||||
|
about: Issues 用于反馈 Bug, 新的功能建议和提问答疑请到讨论区发起
|
||||||
|
- name: 提问的艺术
|
||||||
|
url: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md
|
||||||
|
about: 默认所有 Issues 发起者均已了解此处的内容
|
||||||
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -8,6 +8,8 @@ export {}
|
|||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
AddPlaylist: typeof import('./src/components/Modal/AddPlaylist.vue')['default']
|
AddPlaylist: typeof import('./src/components/Modal/AddPlaylist.vue')['default']
|
||||||
|
CloudMatch: typeof import('./src/components/Modal/CloudMatch.vue')['default']
|
||||||
|
CloudSongMatch: typeof import('./src/components/Modal/CloudSongMatch.vue')['default']
|
||||||
CommentList: typeof import('./src/components/List/CommentList.vue')['default']
|
CommentList: typeof import('./src/components/List/CommentList.vue')['default']
|
||||||
CountDown: typeof import('./src/components/Player/CountDown.vue')['default']
|
CountDown: typeof import('./src/components/Player/CountDown.vue')['default']
|
||||||
CoverDropdown: typeof import('./src/components/Cover/CoverDropdown.vue')['default']
|
CoverDropdown: typeof import('./src/components/Cover/CoverDropdown.vue')['default']
|
||||||
|
|||||||
@@ -151,80 +151,6 @@ export const setLikeSong = (id, like = true) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取用户云盘数据
|
|
||||||
* @param {number} [limit=30] - 返回数量,默认30
|
|
||||||
* @param {number} [offset=0] - 偏移数量,默认0
|
|
||||||
*/
|
|
||||||
export const getCloud = (limit = 30, offset = 0) => {
|
|
||||||
return axios({
|
|
||||||
method: "GET",
|
|
||||||
url: "/user/cloud",
|
|
||||||
params: {
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
timestamp: new Date().getTime(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户云盘歌曲删除
|
|
||||||
* @param {string} id - 歌曲的id
|
|
||||||
*/
|
|
||||||
export const setCloudDel = (id) => {
|
|
||||||
return axios({
|
|
||||||
method: "GET",
|
|
||||||
url: "/user/cloud/del",
|
|
||||||
params: {
|
|
||||||
id,
|
|
||||||
timestamp: new Date().getTime(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 云盘歌曲信息匹配纠正
|
|
||||||
* @param {string} uid - 用户id
|
|
||||||
* @param {string} sid - 原歌曲id
|
|
||||||
* @param {string} asid - 要匹配的歌曲id
|
|
||||||
*/
|
|
||||||
export const setCloudMatch = (uid, sid, asid) => {
|
|
||||||
return axios({
|
|
||||||
method: "GET",
|
|
||||||
url: "/cloud/match",
|
|
||||||
params: {
|
|
||||||
uid,
|
|
||||||
sid,
|
|
||||||
asid,
|
|
||||||
timestamp: new Date().getTime(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户云盘上传
|
|
||||||
* @param {File} file - 要上传的文件
|
|
||||||
*/
|
|
||||||
export const upCloudSong = (file, onUploadProgress) => {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("songFile", file);
|
|
||||||
return axios({
|
|
||||||
url: "/cloud",
|
|
||||||
method: "POST",
|
|
||||||
hiddenBar: true,
|
|
||||||
params: {
|
|
||||||
timestamp: new Date().getTime(),
|
|
||||||
},
|
|
||||||
data: formData,
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
timeout: 200000,
|
|
||||||
onUploadProgress,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出登录
|
* 退出登录
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<AddPlaylist ref="addPlaylistRef" />
|
<AddPlaylist ref="addPlaylistRef" />
|
||||||
<!-- 下载歌曲 -->
|
<!-- 下载歌曲 -->
|
||||||
<DownloadSong ref="downloadSongRef" />
|
<DownloadSong ref="downloadSongRef" />
|
||||||
|
<!-- 云盘歌曲纠正 -->
|
||||||
|
<CloudSongMatch ref="cloudSongMatchRef" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -27,6 +29,7 @@ import { useRouter } from "vue-router";
|
|||||||
import { addSongToNext } from "@/utils/Player";
|
import { addSongToNext } from "@/utils/Player";
|
||||||
import { setCloudDel } from "@/api/cloud";
|
import { setCloudDel } from "@/api/cloud";
|
||||||
import { addSongToPlayList } from "@/api/playlist";
|
import { addSongToPlayList } from "@/api/playlist";
|
||||||
|
import { copyData } from "@/utils/helper";
|
||||||
import SvgIcon from "@/components/Global/SvgIcon";
|
import SvgIcon from "@/components/Global/SvgIcon";
|
||||||
|
|
||||||
const emit = defineEmits(["playSong"]);
|
const emit = defineEmits(["playSong"]);
|
||||||
@@ -45,6 +48,7 @@ const dropdownOptions = ref(null);
|
|||||||
// 子组件
|
// 子组件
|
||||||
const addPlaylistRef = ref(null);
|
const addPlaylistRef = ref(null);
|
||||||
const downloadSongRef = ref(null);
|
const downloadSongRef = ref(null);
|
||||||
|
const cloudSongMatchRef = ref(null);
|
||||||
|
|
||||||
// 图标渲染
|
// 图标渲染
|
||||||
const renderIcon = (icon, size, translate = 0) => {
|
const renderIcon = (icon, size, translate = 0) => {
|
||||||
@@ -183,6 +187,17 @@ const openDropdown = (e, data, song, index, sourceId) => {
|
|||||||
},
|
},
|
||||||
icon: renderIcon("video"),
|
icon: renderIcon("video"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "copy",
|
||||||
|
label: "复制歌曲 ID",
|
||||||
|
props: {
|
||||||
|
onClick: () => {
|
||||||
|
const songId = song?.id?.toString();
|
||||||
|
copyData(songId);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: renderIcon("content-copy"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "line-cloud",
|
key: "line-cloud",
|
||||||
type: "divider",
|
type: "divider",
|
||||||
@@ -199,6 +214,17 @@ const openDropdown = (e, data, song, index, sourceId) => {
|
|||||||
},
|
},
|
||||||
icon: renderIcon("delete"),
|
icon: renderIcon("delete"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "edit",
|
||||||
|
label: "云盘歌曲纠正",
|
||||||
|
show: isCloud,
|
||||||
|
props: {
|
||||||
|
onClick: () => {
|
||||||
|
cloudSongMatchRef.value?.openCloudSongMatch(song, index);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: renderIcon("edit"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "delete",
|
key: "delete",
|
||||||
label: "从云盘中删除",
|
label: "从云盘中删除",
|
||||||
|
|||||||
248
src/components/Modal/CloudSongMatch.vue
Normal file
248
src/components/Modal/CloudSongMatch.vue
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
<!-- 云盘歌曲纠正 -->
|
||||||
|
<template>
|
||||||
|
<n-modal
|
||||||
|
v-model:show="cloudSongMatchShow"
|
||||||
|
:bordered="false"
|
||||||
|
:on-after-leave="closeCloudSongMatch"
|
||||||
|
title="歌曲纠正"
|
||||||
|
preset="card"
|
||||||
|
>
|
||||||
|
<n-form class="cloud-match" :label-width="80" :model="cloudMatchValue">
|
||||||
|
<n-form-item label="原歌曲 ID" path="asid">
|
||||||
|
<n-input-number v-model:value="cloudMatchValue.sid" :show-button="false" disabled />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="匹配的 ID" path="asid">
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="cloudMatchValue.asid"
|
||||||
|
:show-button="false"
|
||||||
|
placeholder="请输入要匹配的歌曲 ID"
|
||||||
|
/>
|
||||||
|
<n-button
|
||||||
|
:disabled="!cloudMatchValue.asid"
|
||||||
|
style="margin-left: 12px"
|
||||||
|
@click="checkMatchSong(cloudMatchValue.asid)"
|
||||||
|
>
|
||||||
|
检查
|
||||||
|
</n-button>
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<!-- 纠正歌曲数据 -->
|
||||||
|
<Transition name="fade" mode="out-in">
|
||||||
|
<n-card
|
||||||
|
v-if="cloudMatchSongData"
|
||||||
|
:key="cloudMatchSongData"
|
||||||
|
:content-style="{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '16px',
|
||||||
|
height: '100%',
|
||||||
|
}"
|
||||||
|
class="song-detail"
|
||||||
|
>
|
||||||
|
<n-image
|
||||||
|
:src="cloudMatchSongData?.coverSize?.s || cloudMatchSongData?.cover"
|
||||||
|
class="cover-img"
|
||||||
|
preview-disabled
|
||||||
|
@load="
|
||||||
|
(e) => {
|
||||||
|
e.target.style.opacity = 1;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #placeholder>
|
||||||
|
<div class="cover-loading">
|
||||||
|
<img class="loading-img" src="/images/pic/song.jpg?assest" alt="loading-img" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-image>
|
||||||
|
<div class="content">
|
||||||
|
<div class="name">{{ cloudMatchSongData?.name || "未知曲目" }}</div>
|
||||||
|
<div class="artist">
|
||||||
|
<n-icon depth="3" size="20">
|
||||||
|
<SvgIcon icon="account-music" />
|
||||||
|
</n-icon>
|
||||||
|
<div
|
||||||
|
v-if="cloudMatchSongData?.artists && Array.isArray(cloudMatchSongData?.artists)"
|
||||||
|
class="all-ar"
|
||||||
|
>
|
||||||
|
<span v-for="ar in cloudMatchSongData.artists" :key="ar.id" class="ar">
|
||||||
|
{{ ar.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="all-ar">
|
||||||
|
<span class="ar"> {{ cloudMatchSongData?.artists || "未知艺术家" }} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</Transition>
|
||||||
|
<template #footer>
|
||||||
|
<n-space justify="end">
|
||||||
|
<n-button @click="closeCloudSongMatch"> 取消 </n-button>
|
||||||
|
<n-button
|
||||||
|
:disabled="!cloudMatchValue.asid"
|
||||||
|
type="primary"
|
||||||
|
@click="setCloudSongMatchBtn(cloudMatchValue)"
|
||||||
|
>
|
||||||
|
纠正
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { siteData, indexedDBData } from "@/stores";
|
||||||
|
import { setCloudMatch } from "@/api/cloud";
|
||||||
|
import { getSongDetail } from "@/api/song";
|
||||||
|
import formatData from "@/utils/formatData";
|
||||||
|
|
||||||
|
const data = siteData();
|
||||||
|
const indexedDB = indexedDBData();
|
||||||
|
const { userData } = storeToRefs(data);
|
||||||
|
|
||||||
|
// 歌曲信息纠正数据
|
||||||
|
const cloudMatchId = ref(null);
|
||||||
|
const cloudMatchIndex = ref(null);
|
||||||
|
const cloudSongMatchShow = ref(false);
|
||||||
|
const cloudMatchSongData = ref(null);
|
||||||
|
const cloudMatchValue = ref({
|
||||||
|
uid: userData.value?.userId,
|
||||||
|
sid: null,
|
||||||
|
asid: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查纠正歌曲id
|
||||||
|
const checkMatchSong = async (id) => {
|
||||||
|
try {
|
||||||
|
if (!id) return false;
|
||||||
|
const songId = id.toString();
|
||||||
|
const result = await getSongDetail(songId);
|
||||||
|
if (result.code === 200 && result.songs.length > 0) {
|
||||||
|
$message.success("匹配的歌曲检查通过");
|
||||||
|
cloudMatchSongData.value = formatData(result.songs[0], "song")[0];
|
||||||
|
} else {
|
||||||
|
$message.warning("请检查要匹配的歌曲 ID 是否正确");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查纠正歌曲失败:", error);
|
||||||
|
$message.error("检查纠正歌曲失败,请重试");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 歌曲纠正
|
||||||
|
const setCloudSongMatchBtn = async (data) => {
|
||||||
|
if (Number(data.sid) === Number(data.asid)) {
|
||||||
|
return $message.warning("与原歌曲 ID 一致,无需修改");
|
||||||
|
}
|
||||||
|
if (!cloudMatchSongData.value) {
|
||||||
|
return $message.warning("未检测到正确的匹配检查结果");
|
||||||
|
}
|
||||||
|
const result = await setCloudMatch(data.uid, data.sid, data.asid);
|
||||||
|
console.log(result);
|
||||||
|
if (result.code === 200) {
|
||||||
|
// 更改歌曲信息
|
||||||
|
try {
|
||||||
|
cloudMatchSongData.value.pc = undefined;
|
||||||
|
const allCloudSongs = await indexedDB.getfilesDB("userCloudList");
|
||||||
|
allCloudSongs[cloudMatchIndex.value] = JSON.parse(JSON.stringify(cloudMatchSongData.value));
|
||||||
|
await indexedDB.setfilesDB("userCloudList", allCloudSongs.slice());
|
||||||
|
// 刷新列表
|
||||||
|
if (typeof $refreshCloudList !== "undefined") $refreshCloudList();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更改云盘列表时出错:", error);
|
||||||
|
$message.error("更改云盘列表时出错,请刷新后重试");
|
||||||
|
}
|
||||||
|
closeCloudSongMatch();
|
||||||
|
$message.success("歌曲信息纠正成功");
|
||||||
|
} else {
|
||||||
|
$message.error("歌曲信息纠正失败,请重试");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开启歌曲纠正
|
||||||
|
const openCloudSongMatch = (data, index) => {
|
||||||
|
cloudMatchIndex.value = index;
|
||||||
|
cloudMatchValue.value.sid = data.id;
|
||||||
|
cloudSongMatchShow.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭歌曲纠正
|
||||||
|
const closeCloudSongMatch = () => {
|
||||||
|
cloudSongMatchShow.value = false;
|
||||||
|
cloudMatchId.value = null;
|
||||||
|
cloudMatchValue.value.asid = null;
|
||||||
|
cloudMatchSongData.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
openCloudSongMatch,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cloud-match {
|
||||||
|
:deep(.n-input-number) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.song-detail {
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 8px;
|
||||||
|
.cover-img {
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
margin-right: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
box-shadow: 0 0 10px 6px #00000008;
|
||||||
|
transition: opacity 0.1s ease-in-out;
|
||||||
|
:deep(img) {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
.name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.artist {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.n-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.all-ar {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
word-break: break-all;
|
||||||
|
.ar {
|
||||||
|
display: inline-flex;
|
||||||
|
&::after {
|
||||||
|
content: "/";
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
&::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -181,6 +181,10 @@ onBeforeMount(() => {
|
|||||||
getUserCloudDataList();
|
getUserCloudDataList();
|
||||||
getUserCloudData();
|
getUserCloudData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.$refreshCloudList = getUserCloudDataList;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user