mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
feat: 支持云盘歌曲纠正
This commit is contained in:
@@ -49,5 +49,6 @@ module.exports = {
|
||||
$notification: true,
|
||||
$changeThemeColor: 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' {
|
||||
export interface GlobalComponents {
|
||||
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']
|
||||
CountDown: typeof import('./src/components/Player/CountDown.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" />
|
||||
<!-- 下载歌曲 -->
|
||||
<DownloadSong ref="downloadSongRef" />
|
||||
<!-- 云盘歌曲纠正 -->
|
||||
<CloudSongMatch ref="cloudSongMatchRef" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -27,6 +29,7 @@ import { useRouter } from "vue-router";
|
||||
import { addSongToNext } from "@/utils/Player";
|
||||
import { setCloudDel } from "@/api/cloud";
|
||||
import { addSongToPlayList } from "@/api/playlist";
|
||||
import { copyData } from "@/utils/helper";
|
||||
import SvgIcon from "@/components/Global/SvgIcon";
|
||||
|
||||
const emit = defineEmits(["playSong"]);
|
||||
@@ -45,6 +48,7 @@ const dropdownOptions = ref(null);
|
||||
// 子组件
|
||||
const addPlaylistRef = ref(null);
|
||||
const downloadSongRef = ref(null);
|
||||
const cloudSongMatchRef = ref(null);
|
||||
|
||||
// 图标渲染
|
||||
const renderIcon = (icon, size, translate = 0) => {
|
||||
@@ -183,6 +187,17 @@ const openDropdown = (e, data, song, index, sourceId) => {
|
||||
},
|
||||
icon: renderIcon("video"),
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
label: "复制歌曲 ID",
|
||||
props: {
|
||||
onClick: () => {
|
||||
const songId = song?.id?.toString();
|
||||
copyData(songId);
|
||||
},
|
||||
},
|
||||
icon: renderIcon("content-copy"),
|
||||
},
|
||||
{
|
||||
key: "line-cloud",
|
||||
type: "divider",
|
||||
@@ -199,6 +214,17 @@ const openDropdown = (e, data, song, index, sourceId) => {
|
||||
},
|
||||
icon: renderIcon("delete"),
|
||||
},
|
||||
{
|
||||
key: "edit",
|
||||
label: "云盘歌曲纠正",
|
||||
show: isCloud,
|
||||
props: {
|
||||
onClick: () => {
|
||||
cloudSongMatchRef.value?.openCloudSongMatch(song, index);
|
||||
},
|
||||
},
|
||||
icon: renderIcon("edit"),
|
||||
},
|
||||
{
|
||||
key: "delete",
|
||||
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();
|
||||
getUserCloudData();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
window.$refreshCloudList = getUserCloudDataList;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in New Issue
Block a user