From f7d3a2128a9b6237fee7880e848467b260022d32 Mon Sep 17 00:00:00 2001 From: Megghy Date: Sun, 3 Dec 2023 15:14:02 +0800 Subject: [PATCH] add live --- package.json | 2 + src/api/api-models.ts | 27 ++ src/components/DanmakuContainer.vue | 549 ++++++++++++++++++++++++++ src/components/DanmakuItem.vue | 169 ++++++++ src/components/LiveInfoContainer.vue | 202 ++++++++++ src/components/SimpleVirtualList.vue | 84 ++++ src/data/DanmakuExport.ts | 117 ++++++ src/data/constants.ts | 1 + src/router/index.ts | 18 + src/views/AboutView.vue | 1 + src/views/IndexView.vue | 5 + src/views/ManageLayout.vue | 17 +- src/views/manage/LiveDetailManage.vue | 62 +++ src/views/manage/LiveManager.vue | 68 ++++ src/views/open_live/MusicRequest.vue | 1 + yarn.lock | 12 + 16 files changed, 1334 insertions(+), 1 deletion(-) create mode 100644 src/components/DanmakuContainer.vue create mode 100644 src/components/DanmakuItem.vue create mode 100644 src/components/LiveInfoContainer.vue create mode 100644 src/components/SimpleVirtualList.vue create mode 100644 src/data/DanmakuExport.ts create mode 100644 src/views/manage/LiveDetailManage.vue create mode 100644 src/views/manage/LiveManager.vue diff --git a/package.json b/package.json index fba9dea..41936dc 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "core-js": "^3.8.3", "date-fns": "^2.30.0", "echarts": "^5.4.3", + "fast-xml-parser": "^4.3.2", "file-saver": "^2.0.5", "grapheme-splitter": "^1.0.4", "html2canvas": "^1.4.1", @@ -35,6 +36,7 @@ "vue-turnstile": "^1.0.0", "vue3-aplayer": "^1.7.3", "vue3-marquee": "^4.1.0", + "vueuc": "^0.4.51", "vuex": "^4.0.0", "worker-timers": "^7.0.78" }, diff --git a/src/api/api-models.ts b/src/api/api-models.ts index 598897d..44f612b 100644 --- a/src/api/api-models.ts +++ b/src/api/api-models.ts @@ -410,3 +410,30 @@ export interface ResponseQueueModel { finishAt?: number | null isInLocal?: boolean } +export interface ResponseLiveInfoModel { + liveId: string + isFinish: boolean + parentArea: string + area: string + coverUrl: string + danmakusCount: number + startAt: number + stopAt: number | null + title: string + totalIncome: number + totalIncomeWithGuard: number + likeCount: number + paymentCount: number + interactionCount: number +} +export interface DanmakuModel { + id: string + uId: number + uName: string + type: EventDataTypes // Assuming EventDataTypes is an enum or type available in your TypeScript codebase + time: number + msg: string | null + price: number | null + isEmoji: boolean + num: number +} diff --git a/src/components/DanmakuContainer.vue b/src/components/DanmakuContainer.vue new file mode 100644 index 0000000..252cef5 --- /dev/null +++ b/src/components/DanmakuContainer.vue @@ -0,0 +1,549 @@ + + + + + + diff --git a/src/components/DanmakuItem.vue b/src/components/DanmakuItem.vue new file mode 100644 index 0000000..cdb238b --- /dev/null +++ b/src/components/DanmakuItem.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/src/components/LiveInfoContainer.vue b/src/components/LiveInfoContainer.vue new file mode 100644 index 0000000..1717194 --- /dev/null +++ b/src/components/LiveInfoContainer.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/src/components/SimpleVirtualList.vue b/src/components/SimpleVirtualList.vue new file mode 100644 index 0000000..6c156ae --- /dev/null +++ b/src/components/SimpleVirtualList.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/data/DanmakuExport.ts b/src/data/DanmakuExport.ts new file mode 100644 index 0000000..8aff3f8 --- /dev/null +++ b/src/data/DanmakuExport.ts @@ -0,0 +1,117 @@ +import { AccountInfo, DanmakuModel, EventDataTypes, ResponseLiveInfoModel } from '@/api/api-models' +import { XMLBuilder } from 'fast-xml-parser' +import { List } from 'linqts' + +const builder = new XMLBuilder({ + attributeNamePrefix: '@', + ignoreAttributes: false, + processEntities: false, + format: true, +}) + +export function GetString(account: AccountInfo | undefined, live: ResponseLiveInfoModel, danmakus: DanmakuModel[], type: 'json' | 'xml' | 'csv') { + const tempDanmakus = new List(danmakus) + .Select((d) => { + return { + uId: d.uId, + uName: d.uName, + sendDate: d.time, + type: d.type, + message: d.msg, + price: d.price, + } + }) + .ToArray() + const obj = { + live: live, + danmakus: tempDanmakus, + } + switch (type) { + case 'json': { + return JSON.stringify(obj) + } + case 'xml': { + const xmlJsonObj = { + i: { + chatserver: 'chat.bilibili.com', + chatid: '0', + mission: '0', + maxlimit: '0', + state: '0', + real_name: '0', + source: 'e-r', + metadata: { + user_name: account?.name, + room_id: account?.biliRoomId, + room_title: live.title, + area: live.area, + parent_area: live.parentArea, + live_start_time: new Date(live.startAt), + record_start_time: new Date(live.stopAt ?? 0), + recorder: 'https://vtsuru.live/', + }, + d: [] as any[], + gift: [] as any[], + sc: [] as any[], + }, + } + danmakus.forEach((danmaku) => { + if (danmaku.type == EventDataTypes.Message) { + xmlJsonObj.i.d.push({ + '@p': `${GetTime(danmaku)},1,25,16777215,${danmaku.time},0,-1`, + '@uid': danmaku.uId, + '@user': danmaku.uName, + '#text': danmaku.msg, + }) + } else if (danmaku.type == EventDataTypes.Gift) { + xmlJsonObj.i.gift.push({ + '@ts': GetTime(danmaku), + '@uid': danmaku.uId, + '@user': danmaku.uName, + '@giftname': danmaku.msg, + '@giftcount': danmaku.num, + '@cointype': (danmaku.price ?? 0) > 0 ? '金瓜子' : '银瓜子', + '@price': (danmaku.price ?? 0) * 1000, + }) + } else if (danmaku.type == EventDataTypes.Guard) { + xmlJsonObj.i.gift.push({ + '@ts': GetTime(danmaku), + '@uid': danmaku.uId, + '@user': danmaku.uName, + '@giftname': danmaku.msg, + '@giftcount': danmaku.num, + '@cointype': '舰长', + '@price': danmaku.price, + }) + } else if (danmaku.type == EventDataTypes.SC) { + xmlJsonObj.i.sc.push({ + '@ts': GetTime(danmaku), + '@uid': danmaku.uId, + '@user': danmaku.uName, + '@price': danmaku.price, + '#text': danmaku.msg, + }) + } + }) + function GetTime(danmaku: DanmakuModel) { + return ((danmaku.time - live.startAt) / 1000).toFixed(3) + } + return builder.build(xmlJsonObj) + } + case 'csv': { + return objectsToCSV(tempDanmakus) + } + } +} +function objectsToCSV(arr: any[]) { + const array = [Object.keys(arr[0])].concat(arr) + return array + .map((row) => { + return Object.values(row) + .map((value) => { + return typeof value === 'string' ? JSON.stringify(value) : value + }) + .toString() + }) + .join('\n') +} diff --git a/src/data/constants.ts b/src/data/constants.ts index 18f5b52..f15e002 100644 --- a/src/data/constants.ts +++ b/src/data/constants.ts @@ -26,6 +26,7 @@ export const OPEN_LIVE_API_URL = `${BASE_API}open-live/` export const SONG_REQUEST_API_URL = `${BASE_API}song-request/` export const QUEUE_API_URL = `${BASE_API}queue/` export const EVENT_API_URL = `${BASE_API}event/` +export const LIVE_API_URL = `${BASE_API}live/` export const ScheduleTemplateMap = { '': { name: '默认', compoent: defineAsyncComponent(() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')) }, diff --git a/src/router/index.ts b/src/router/index.ts index f81f61d..46216a9 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -210,6 +210,24 @@ const routes: Array = [ danmaku: true, }, }, + { + path: 'live', + name: 'manage-live', + component: () => import('@/views/manage/LiveManager.vue'), + meta: { + title: '直播记录', + keepAlive: true, + }, + }, + { + path: 'live/:id', + name: 'manage-liveDetail', + component: () => import('@/views/manage/LiveDetailManage.vue'), + meta: { + title: '直播详情', + keepAlive: true, + }, + }, ], }, { diff --git a/src/views/AboutView.vue b/src/views/AboutView.vue index 5ee2fba..15871cc 100644 --- a/src/views/AboutView.vue +++ b/src/views/AboutView.vue @@ -33,6 +33,7 @@ import { NButton, NCard, NDivider, NLayoutContent, NSpace, NText, NTimeline, NTi 更新日志 + diff --git a/src/views/IndexView.vue b/src/views/IndexView.vue index 4da9972..0c596f8 100644 --- a/src/views/IndexView.vue +++ b/src/views/IndexView.vue @@ -13,6 +13,11 @@ const functions = [ desc: '能够记录并查询上舰和SC记录', icon: VehicleShip24Filled, }, + { + name: '直播场次记录', + desc: '记录每场直播的数据以及弹幕等内容', + icon: VehicleShip24Filled, + }, { name: '日程表', desc: '提供多种样式的日程表', diff --git a/src/views/ManageLayout.vue b/src/views/ManageLayout.vue index 6cc67d0..5715281 100644 --- a/src/views/ManageLayout.vue +++ b/src/views/ManageLayout.vue @@ -22,7 +22,7 @@ import { } from 'naive-ui' import { h, onMounted, ref } from 'vue' import { BrowsersOutline, Chatbox, Moon, MusicalNote, Sunny, AnalyticsSharp } from '@vicons/ionicons5' -import { CalendarClock24Filled, Chat24Filled, Info24Filled, Lottery24Filled, PeopleQueue24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent' +import { CalendarClock24Filled, Chat24Filled, Info24Filled, Live24Filled, Lottery24Filled, PeopleQueue24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent' import { isLoadingAccount, useAccount } from '@/api/account' import RegisterAndLogin from '@/components/RegisterAndLogin.vue' import { RouterLink, useRoute } from 'vue-router' @@ -86,6 +86,21 @@ const menuOptions = [ disabled: accountInfo.value?.isEmailVerified == false, icon: renderIcon(VehicleShip24Filled), }, + { + label: () => + h( + RouterLink, + { + to: { + name: 'manage-live', + }, + }, + { default: () => '直播记录' } + ), + key: 'manage-live', + disabled: accountInfo.value?.isEmailVerified == false, + icon: renderIcon(Live24Filled), + }, { label: () => h( diff --git a/src/views/manage/LiveDetailManage.vue b/src/views/manage/LiveDetailManage.vue new file mode 100644 index 0000000..48bf9c8 --- /dev/null +++ b/src/views/manage/LiveDetailManage.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/views/manage/LiveManager.vue b/src/views/manage/LiveManager.vue new file mode 100644 index 0000000..0c4c05c --- /dev/null +++ b/src/views/manage/LiveManager.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/views/open_live/MusicRequest.vue b/src/views/open_live/MusicRequest.vue index eda5f08..2fd09f6 100644 --- a/src/views/open_live/MusicRequest.vue +++ b/src/views/open_live/MusicRequest.vue @@ -335,6 +335,7 @@ async function updateSongStatus(song: SongRequestInfo, status: SongRequestStatus } function onGetDanmaku(danmaku: DanmakuInfo) { + console.log(danmaku) if (checkMessage(danmaku.msg)) { addSong({ msg: danmaku.msg, diff --git a/yarn.lock b/yarn.lock index 40c58bc..f25aa10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1698,6 +1698,13 @@ fast-unique-numbers@^8.0.11: "@babel/runtime" "^7.23.4" tslib "^2.6.2" +fast-xml-parser@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz#761e641260706d6e13251c4ef8e3f5694d4b0d79" + integrity sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -3017,6 +3024,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + stylus@^0.55.0: version "0.55.0" resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.55.0.tgz#bd404a36dd93fa87744a9dd2d2b1b8450345e5fc"