feat: 支持云盘歌曲纠正

This commit is contained in:
imsyy
2023-12-07 13:34:51 +08:00
parent b65369a8a6
commit 6d5fa15098
7 changed files with 289 additions and 74 deletions

View File

@@ -49,5 +49,6 @@ module.exports = {
$notification: true,
$changeThemeColor: true,
$canNotConnect: true,
$refreshCloudList: true,
},
};

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View 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
View File

@@ -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']

View File

@@ -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,
});
};
/**
* 退出登录
*/

View File

@@ -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: "从云盘中删除",

View 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>

View File

@@ -181,6 +181,10 @@ onBeforeMount(() => {
getUserCloudDataList();
getUserCloudData();
});
onMounted(() => {
window.$refreshCloudList = getUserCloudDataList;
});
</script>
<style lang="scss" scoped>