From 7dcbc87436148d911f5651af4c0c022a5e10673d Mon Sep 17 00:00:00 2001 From: Megghy Date: Mon, 20 Nov 2023 14:18:43 +0800 Subject: [PATCH] add song request from web --- src/api/api-models.ts | 2 + src/components/SongList.vue | 20 ++++- src/views/ViewerLayout.vue | 4 +- src/views/manage/SongListManageView.vue | 17 ++++- src/views/obs/SongRequestOBS.vue | 30 +++++++- src/views/open_live/MusicRequest.vue | 76 ++++++++++++------- src/views/view/SongListView.vue | 58 +++++++++++++- .../DefaultSongListTemplate.vue | 58 +++++++++++++- .../SimpleSongListTemplate.vue | 16 ++-- 9 files changed, 232 insertions(+), 49 deletions(-) diff --git a/src/api/api-models.ts b/src/api/api-models.ts index 1930a4e..0e14689 100644 --- a/src/api/api-models.ts +++ b/src/api/api-models.ts @@ -41,6 +41,7 @@ export interface AccountInfo extends UserInfo { eventFetcherOnline: boolean eventFetcherStatus: string + canRequestSong: boolean nextSendEmailTime?: number } @@ -116,6 +117,7 @@ export interface SongsInfo { tags?: string[] createTime: number updateTime: number + paidSong: boolean } export enum SongLanguage { Chinese, // 中文 diff --git a/src/components/SongList.vue b/src/components/SongList.vue index 08a991a..e7cf340 100644 --- a/src/components/SongList.vue +++ b/src/components/SongList.vue @@ -12,6 +12,7 @@ import { NAvatar, NButton, NCard, + NCheckbox, NCollapseTransition, NDataTable, NDivider, @@ -31,9 +32,9 @@ import { NTooltip, useMessage, } from 'naive-ui' -import { onMounted, h, ref, watch, computed, reactive } from 'vue' +import { onMounted, h, ref, watch, computed, reactive, VNodeChild } from 'vue' import APlayer from 'vue3-aplayer' -import { NotepadEdit20Filled, Delete24Filled, Play24Filled, SquareArrowForward24Filled } from '@vicons/fluent' +import { NotepadEdit20Filled, Delete24Filled, Play24Filled, SquareArrowForward24Filled, Info24Filled } from '@vicons/fluent' import NeteaseIcon from '@/svgs/netease.svg' import FiveSingIcon from '@/svgs/fivesing.svg' @@ -41,6 +42,7 @@ const props = defineProps<{ songs: SongsInfo[] canEdit?: boolean isSelf: boolean + extraButtom?: (song: SongsInfo) => VNodeChild[] }>() watch( () => props.songs, @@ -212,7 +214,7 @@ function createColumns(): DataTableColumns { title: '操作', key: 'manage', disabled: () => !props.canEdit, - width: props.isSelf ? 170 : 100, + width: 170, render(data) { return h( NSpace, @@ -289,6 +291,7 @@ function createColumns(): DataTableColumns { }), ] : null, + props.extraButtom?.(data), ] ) }, @@ -528,6 +531,17 @@ onMounted(() => { + + + 是否付费歌曲 + + + 用于区分是否可以从网页进行点歌 + + + diff --git a/src/views/ViewerLayout.vue b/src/views/ViewerLayout.vue index 97be43c..79984a8 100644 --- a/src/views/ViewerLayout.vue +++ b/src/views/ViewerLayout.vue @@ -219,7 +219,9 @@ onMounted(async () => { - +
+ +
diff --git a/src/views/manage/SongListManageView.vue b/src/views/manage/SongListManageView.vue index 54269d9..1da633b 100644 --- a/src/views/manage/SongListManageView.vue +++ b/src/views/manage/SongListManageView.vue @@ -4,13 +4,16 @@ import { SongFrom, SongLanguage, SongsInfo } from '@/api/api-models' import { QueryGetAPI, QueryPostAPI } from '@/api/query' import SongList from '@/components/SongList.vue' import { FETCH_API, SONG_API_URL } from '@/data/constants' +import { Info24Filled } from '@vicons/fluent' import { FormInst, FormRules, NButton, + NCheckbox, NDivider, NForm, NFormItem, + NIcon, NInput, NModal, NPagination, @@ -21,6 +24,7 @@ import { NTable, NTabs, NTag, + NTooltip, NTransfer, useMessage, } from 'naive-ui' @@ -218,7 +222,7 @@ async function getNeteaseSongList() { if (data.code == 200) { neteaseSongs.value = data.data neteaseSongsOptions.value = data.data.map((s) => ({ - label: `${s.name} - ${s.author.join('/')}`, + label: `${s.name} - ${!s.author ? '未知' : s.author.join('/')}`, value: s.key, disabled: songs.value.findIndex((exist) => exist.id == s.id) > -1, })) @@ -363,6 +367,17 @@ onMounted(async () => { + + + 是否付费歌曲 + + + 用于区分是否可以从网页进行点歌 + + + 添加 diff --git a/src/views/obs/SongRequestOBS.vue b/src/views/obs/SongRequestOBS.vue index eb32485..9baa4ea 100644 --- a/src/views/obs/SongRequestOBS.vue +++ b/src/views/obs/SongRequestOBS.vue @@ -69,7 +69,9 @@ const allowGuardTypes = computed(() => { async function update() { const r = await get() if (r) { - songs.value = r.songs + songs.value = r.songs.sort((a, b) => { + return b.createAt - a.createAt + }) settings.value = r.setting } } @@ -90,7 +92,7 @@ onUnmounted(() => {

已有 {{ activeSongs.length ?? 0 }} 首

-
+
@@ -211,9 +213,16 @@ onUnmounted(() => { /* 添加无限旋转动画 */ animation: rotate 20s linear infinite; } +/* 网页点歌 */ +.song-request-singing-container[from='3'] .song-request-singing-avatar { + display: none; +} .song-request-singing-song-name { font-size: large; font-weight: bold; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 80%; } .song-request-singing-name { font-size: 12px; @@ -236,12 +245,16 @@ onUnmounted(() => { padding: 10px; height: 100%; border-radius: 10px; + overflow-x: hidden; } .marquee { justify-items: left; } .song-request-list-item { display: flex; + width: 100%; + align-self: flex-start; + position: relative; align-items: center; justify-content: left; gap: 10px; @@ -249,13 +262,17 @@ onUnmounted(() => { .song-request-list-item-song-name { font-size: 18px; font-weight: bold; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 80%; } /* 手动添加 */ .song-request-list-item[from='0'] .song-request-list-item-name { font-style: italic; font-weight: bold; - color: #c6e4d9; + color: #d2d8d6; font-size: 12px; } .song-request-list-item[from='0'] .song-request-list-item-avatar { @@ -265,10 +282,15 @@ onUnmounted(() => { /* 弹幕点歌 */ .song-request-list-item[from='1'] { } + .song-request-list-item-name { font-style: italic; font-size: 12px; color: rgba(204, 204, 204, 0.993); + text-overflow: ellipsis; + white-space: nowrap; + + margin-left: auto; } .song-request-list-item-level { text-align: center; diff --git a/src/views/open_live/MusicRequest.vue b/src/views/open_live/MusicRequest.vue index d65c798..95e5987 100644 --- a/src/views/open_live/MusicRequest.vue +++ b/src/views/open_live/MusicRequest.vue @@ -85,6 +85,7 @@ const message = useMessage() const notice = useNotification() const isWarnMessageAutoClose = useStorage('SongRequest.Settings.WarnMessageAutoClose', false) +const volumn = useStorage('Settings.Volumn', 0.5) const isLoading = ref(false) const showOBSModal = ref(false) @@ -114,12 +115,24 @@ const localActiveSongs = useStorage('SongRequest.ActiveSongs', [] as SongRequest const originSongs = ref(await getAllSong()) const songs = computed(() => { return originSongs.value.filter((s) => { - return ( - (filterSongName.value == '' || filterSongNameContains.value - ? s.songName.toLowerCase().includes(filterSongName.value.toLowerCase()) - : s.songName.toLowerCase() == filterSongName.value.toLowerCase()) && - (filterName.value == '' || filterNameContains.value ? s.user?.name.toLowerCase().includes(filterName.value.toLowerCase()) : s.user?.name.toLowerCase() == filterName.value.toLowerCase()) - ) + if (filterName.value) { + if (filterNameContains.value) { + if (!s.user?.name.toLowerCase().includes(filterName.value.toLowerCase())) { + return false + } + } else if (s.user?.name.toLowerCase() !== filterName.value.toLowerCase()) { + return false + } + } else if (filterSongName.value) { + if (filterSongNameContains.value) { + if (!s.songName.toLowerCase().includes(filterSongName.value.toLowerCase())) { + return false + } + } else if (s.songName.toLowerCase() !== filterSongName.value.toLowerCase()) { + return false + } + } + return true }) }) const activeSongs = computed(() => { @@ -172,27 +185,6 @@ async function getAllSong() { return localActiveSongs.value } } -async function getActiveSong() { - if (accountInfo.value) { - try { - const data = await QueryGetAPI(SONG_REQUEST_API_URL + 'get-active', { - id: accountInfo.value.id, - }) - if (data.code == 200) { - console.log('[OPEN-LIVE-Song-Request] 已获取点歌队列') - return data.data - } else { - message.error('无法获取点歌队列: ' + data.message) - return [] - } - } catch (err) { - console.error(err) - } - return [] - } else { - return localActiveSongs.value - } -} async function addSong(danmaku: EventModel) { console.log(`[OPEN-LIVE-Song-Request] 收到 [${danmaku.name}] 的点歌${danmaku.type == EventDataTypes.SC ? 'SC' : '弹幕'}: ${danmaku.msg}`) if (accountInfo.value) { @@ -656,8 +648,32 @@ function GetGuardColor(level: number | null | undefined): string { } return '' } +async function updateActive() { + if (!accountInfo.value) return + try { + const data = await QueryGetAPI(SONG_REQUEST_API_URL + 'get-active', { + id: accountInfo.value?.id, + }) + if (data.code == 200) { + data.data.forEach((item) => { + const song = originSongs.value.find((s) => s.id == item.id) + if (song) { + if (song.status != item.status) song.status = item.status + } else { + originSongs.value.unshift(item) + } + }) + } else { + message.error('无法获取点歌队列: ' + data.message) + return [] + } + } catch (err) { + console.error(err) + } +} let timer: any +let updateActiveTimer: any const updateKey = ref(0) onMounted(() => { if (accountInfo.value) { @@ -668,11 +684,15 @@ onMounted(() => { timer = setInterval(() => { updateKey.value++ }, 1000) + updateActiveTimer = setInterval(() => { + updateActive() + }, 3000) }) onUnmounted(() => { props.client.off('danmaku', onGetDanmaku) props.client.off('sc', onGetSC) clearInterval(timer) + clearInterval(updateActiveTimer) }) @@ -771,7 +791,7 @@ onUnmounted(() => { - + diff --git a/src/views/view/songListTemplate/SimpleSongListTemplate.vue b/src/views/view/songListTemplate/SimpleSongListTemplate.vue index 38b781e..342af0c 100644 --- a/src/views/view/songListTemplate/SimpleSongListTemplate.vue +++ b/src/views/view/songListTemplate/SimpleSongListTemplate.vue @@ -1,11 +1,17 @@ \ No newline at end of file +