mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
Compare commits
2 Commits
4e93b371f4
...
389515bc5b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
389515bc5b | ||
|
|
fc465a8e0d |
@@ -1,425 +0,0 @@
|
|||||||
import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemplate.vue';
|
|
||||||
import { defineAsyncComponent, ref, markRaw } from 'vue';
|
|
||||||
|
|
||||||
const debugAPI =
|
|
||||||
import.meta.env.VITE_API == 'dev'
|
|
||||||
? import.meta.env.VITE_DEBUG_DEV_API
|
|
||||||
: import.meta.env.VITE_DEBUG_RELEASE_API;
|
|
||||||
const releseAPI = `https://vtsuru.suki.club/`;
|
|
||||||
const failoverAPI = `https://failover-api.vtsuru.suki.club/`;
|
|
||||||
|
|
||||||
export const isBackendUsable = ref(true);
|
|
||||||
export const isDev = import.meta.env.MODE === 'development';
|
|
||||||
// @ts-ignore
|
|
||||||
export const isTauri = () => window.__TAURI__ !== undefined || window.__TAURI_INTERNAL__ !== undefined || '__TAURI__' in window;
|
|
||||||
|
|
||||||
export const AVATAR_URL = 'https://workers.vrp.moe/api/bilibili/avatar/';
|
|
||||||
export const FILE_BASE_URL = 'https://files.vtsuru.suki.club';
|
|
||||||
export const IMGUR_URL = FILE_BASE_URL + '/imgur/';
|
|
||||||
export const THINGS_URL = FILE_BASE_URL + '/things/';
|
|
||||||
export const apiFail = ref(false);
|
|
||||||
|
|
||||||
export const BASE_URL =
|
|
||||||
process.env.NODE_ENV === 'development'
|
|
||||||
? debugAPI
|
|
||||||
: apiFail.value
|
|
||||||
? failoverAPI
|
|
||||||
: releseAPI;
|
|
||||||
export const BASE_API_URL = BASE_URL + 'api/';
|
|
||||||
export const FETCH_API = 'https://fetch.vtsuru.live/';
|
|
||||||
export const BASE_HUB_URL =
|
|
||||||
(process.env.NODE_ENV === 'development'
|
|
||||||
? debugAPI
|
|
||||||
: apiFail.value
|
|
||||||
? failoverAPI
|
|
||||||
: releseAPI) + 'hub/';
|
|
||||||
|
|
||||||
export const TURNSTILE_KEY = '0x4AAAAAAAETUSAKbds019h0';
|
|
||||||
|
|
||||||
export const CURRENT_HOST = `${window.location.protocol}//${window.location.host}/`;
|
|
||||||
export const CN_HOST = 'https://vtsuru.suki.club/';
|
|
||||||
|
|
||||||
export const USER_API_URL = BASE_API_URL + 'user/';
|
|
||||||
export const ACCOUNT_API_URL = BASE_API_URL + 'account/';
|
|
||||||
export const BILI_API_URL = BASE_API_URL + 'bili/';
|
|
||||||
export const SONG_API_URL = BASE_API_URL + 'song-list/';
|
|
||||||
export const NOTIFACTION_API_URL = BASE_API_URL + 'notification/';
|
|
||||||
export const QUESTION_API_URL = BASE_API_URL + 'qa/';
|
|
||||||
export const LOTTERY_API_URL = BASE_API_URL + 'lottery/';
|
|
||||||
export const HISTORY_API_URL = BASE_API_URL + 'history/';
|
|
||||||
export const SCHEDULE_API_URL = BASE_API_URL + 'schedule/';
|
|
||||||
export const VIDEO_COLLECT_API_URL = BASE_API_URL + 'video-collect/';
|
|
||||||
export const OPEN_LIVE_API_URL = BASE_API_URL + 'open-live/';
|
|
||||||
export const SONG_REQUEST_API_URL = BASE_API_URL + 'live-request/';
|
|
||||||
export const QUEUE_API_URL = BASE_API_URL + 'queue/';
|
|
||||||
export const EVENT_API_URL = BASE_API_URL + 'event/';
|
|
||||||
export const LIVE_API_URL = BASE_API_URL + 'live/';
|
|
||||||
export const FEEDBACK_API_URL = BASE_API_URL + 'feedback/';
|
|
||||||
export const MUSIC_REQUEST_API_URL = BASE_API_URL + 'music-request/';
|
|
||||||
export const VTSURU_API_URL = BASE_API_URL + 'vtsuru/';
|
|
||||||
export const POINT_API_URL = BASE_API_URL + 'point/';
|
|
||||||
export const BILI_AUTH_API_URL = BASE_API_URL + 'bili-auth/';
|
|
||||||
export const FORUM_API_URL = BASE_API_URL + 'forum/';
|
|
||||||
export const USER_INDEX_API_URL = BASE_API_URL + 'user-index/';
|
|
||||||
export const ANALYZE_API_URL = BASE_API_URL + 'analyze/';
|
|
||||||
|
|
||||||
export type TemplateMapType = {
|
|
||||||
[key: string]: {
|
|
||||||
name: string;
|
|
||||||
settingName?: string;
|
|
||||||
component: any;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export const ScheduleTemplateMap: TemplateMapType = {
|
|
||||||
'': {
|
|
||||||
name: '默认',
|
|
||||||
//settingName: 'Template.Schedule.Default',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')
|
|
||||||
))
|
|
||||||
},
|
|
||||||
pinky: {
|
|
||||||
name: '粉粉',
|
|
||||||
//settingName: 'Template.Schedule.Pinky',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() => import('@/views/view/scheduleTemplate/PinkySchedule.vue')
|
|
||||||
))
|
|
||||||
},
|
|
||||||
kawaii: {
|
|
||||||
name: '可爱<E58FAF><E788B1>?,
|
|
||||||
//settingName: 'Template.Schedule.Kawaii',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() => import('@/views/view/scheduleTemplate/KawaiiScheduleTemplate.vue')
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const SongListTemplateMap: TemplateMapType = {
|
|
||||||
'': {
|
|
||||||
name: '默认',
|
|
||||||
//settingName: 'Template.SongList.Default',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() => import('@/views/view/songListTemplate/DefaultSongListTemplate.vue')
|
|
||||||
))
|
|
||||||
},
|
|
||||||
simple: {
|
|
||||||
name: '简<><E7AE80>?,
|
|
||||||
//settingName: 'Template.SongList.Simple',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() => import('@/views/view/songListTemplate/SimpleSongListTemplate.vue')
|
|
||||||
))
|
|
||||||
},
|
|
||||||
traditional: {
|
|
||||||
name: '列表',
|
|
||||||
settingName: 'Template.SongList.Traditional',
|
|
||||||
component: markRaw(defineAsyncComponent(
|
|
||||||
() =>
|
|
||||||
import('@/views/view/songListTemplate/TraditionalSongListTemplate.vue')
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const IndexTemplateMap: TemplateMapType = {
|
|
||||||
'': {
|
|
||||||
name: '默认',
|
|
||||||
//settingName: 'Template.Index.Default',
|
|
||||||
component: markRaw(DefaultIndexTemplateVue)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultDanmujiCss = `@import url("https://fonts.googleapis.com/css?family=Changa%20One");
|
|
||||||
@import url("https://fonts.googleapis.com/css?family=Imprima");
|
|
||||||
|
|
||||||
/* Transparent background */
|
|
||||||
yt-live-chat-renderer {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-ticker-renderer {
|
|
||||||
background-color: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-author-chip #author-name {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-item-list-renderer #item-scroller {
|
|
||||||
overflow: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-interact-message-renderer #content,
|
|
||||||
yt-live-chat-text-message-renderer #content,
|
|
||||||
yt-live-chat-membership-item-renderer #content {
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide header and input */
|
|
||||||
yt-live-chat-header-renderer,
|
|
||||||
yt-live-chat-message-input-renderer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide unimportant messages */
|
|
||||||
yt-live-chat-interact-message-renderer[is-deleted],
|
|
||||||
yt-live-chat-text-message-renderer[is-deleted],
|
|
||||||
yt-live-chat-membership-item-renderer[is-deleted] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-mode-change-message-renderer,
|
|
||||||
yt-live-chat-viewer-engagement-message-renderer,
|
|
||||||
yt-live-chat-restricted-participation-renderer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer a,
|
|
||||||
yt-live-chat-membership-item-renderer a {
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Global Setting */
|
|
||||||
yt-live-chat-renderer {
|
|
||||||
|
|
||||||
}
|
|
||||||
#item-scroller {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reduce side padding */
|
|
||||||
yt-live-chat-interact-message-renderer,
|
|
||||||
yt-live-chat-text-message-renderer {
|
|
||||||
padding-left: 4px !important;
|
|
||||||
padding-right: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Outlines */
|
|
||||||
yt-live-chat-renderer * {
|
|
||||||
text-shadow: -2px -2px #000000, -2px -1px #000000, -2px 0px #000000, -2px 1px #000000, -2px 2px #000000, -1px -2px #000000, -1px -1px #000000, -1px 0px #000000, -1px 1px #000000, -1px 2px #000000, 0px -2px #000000, 0px -1px #000000, 0px 0px #000000, 0px 1px #000000, 0px 2px #000000, 1px -2px #000000, 1px -1px #000000, 1px 0px #000000, 1px 1px #000000, 1px 2px #000000, 2px -2px #000000, 2px -1px #000000, 2px 0px #000000, 2px 1px #000000, 2px 2px #000000;
|
|
||||||
font-family: "Imprima", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 18px !important;
|
|
||||||
line-height: 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Avatars */
|
|
||||||
yt-live-chat-interact-message-renderer #author-photo,
|
|
||||||
yt-live-chat-interact-message-renderer #author-photo img,
|
|
||||||
yt-live-chat-text-message-renderer #author-photo,
|
|
||||||
yt-live-chat-text-message-renderer #author-photo img,
|
|
||||||
yt-live-chat-paid-message-renderer #author-photo,
|
|
||||||
yt-live-chat-paid-message-renderer #author-photo img,
|
|
||||||
yt-live-chat-membership-item-renderer #author-photo,
|
|
||||||
yt-live-chat-membership-item-renderer #author-photo img {
|
|
||||||
|
|
||||||
width: 24px !important;
|
|
||||||
height: 24px !important;
|
|
||||||
border-radius: 24px !important;
|
|
||||||
margin-right: 6px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Channel names */
|
|
||||||
yt-live-chat-interact-message-renderer #content #author-name,
|
|
||||||
yt-live-chat-text-message-renderer #content #author-name {
|
|
||||||
|
|
||||||
}
|
|
||||||
yt-live-chat-interact-message-renderer #author-name[type="owner"],
|
|
||||||
yt-live-chat-interact-message-renderer yt-live-chat-author-badge-renderer[type="owner"],
|
|
||||||
yt-live-chat-text-message-renderer #author-name[type="owner"],
|
|
||||||
yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="owner"] {
|
|
||||||
color: #ffd600 !important;
|
|
||||||
}
|
|
||||||
yt-live-chat-interact-message-renderer #author-name[type="moderator"],
|
|
||||||
yt-live-chat-interact-message-renderer yt-live-chat-author-badge-renderer[type="moderator"],
|
|
||||||
yt-live-chat-text-message-renderer #author-name[type="moderator"],
|
|
||||||
yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="moderator"] {
|
|
||||||
color: #5e84f1 !important;
|
|
||||||
}
|
|
||||||
yt-live-chat-interact-message-renderer #author-name[type="member"],
|
|
||||||
yt-live-chat-interact-message-renderer yt-live-chat-author-badge-renderer[type="member"],
|
|
||||||
yt-live-chat-text-message-renderer #author-name[type="member"],
|
|
||||||
yt-live-chat-text-message-renderer yt-live-chat-author-badge-renderer[type="member"] {
|
|
||||||
color: #0f9d58 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-interact-message-renderer #author-name,
|
|
||||||
yt-live-chat-text-message-renderer #author-name {
|
|
||||||
|
|
||||||
color: #cccccc !important;
|
|
||||||
font-family: "Changa One", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 20px !important;
|
|
||||||
line-height: 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Show colon */
|
|
||||||
yt-live-chat-text-message-renderer #author-name::after {
|
|
||||||
content: ":";
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide badges */
|
|
||||||
yt-live-chat-interact-message-renderer #chat-badges,
|
|
||||||
yt-live-chat-text-message-renderer #chat-badges {
|
|
||||||
|
|
||||||
vertical-align: text-top !important;
|
|
||||||
}
|
|
||||||
img.yt-live-chat-author-badge-renderer, yt-icon.yt-live-chat-author-badge-renderer {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Medal */
|
|
||||||
yt-live-chat-author-medal-renderer {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
}
|
|
||||||
yt-live-chat-author-medal-renderer[is-fan-group] {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
#medal-name.yt-live-chat-author-medal-renderer {
|
|
||||||
|
|
||||||
font-size: 14px !important;
|
|
||||||
line-height: 14px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#medal-level.yt-live-chat-author-medal-renderer {
|
|
||||||
|
|
||||||
font-size: 14px !important;
|
|
||||||
line-height: 14px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Messages */
|
|
||||||
yt-live-chat-interact-message-renderer #message,
|
|
||||||
yt-live-chat-interact-message-renderer #message *,
|
|
||||||
yt-live-chat-text-message-renderer #message,
|
|
||||||
yt-live-chat-text-message-renderer #message * {
|
|
||||||
color: #ffffff !important;
|
|
||||||
font-family: "Imprima", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 18px !important;
|
|
||||||
line-height: 18px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer #image-and-message {
|
|
||||||
display: inline !important;
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer #message {
|
|
||||||
display: inline !important;
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer #image-and-message .emoji {
|
|
||||||
width: auto !important;
|
|
||||||
height: 48px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#image-and-message img[display="block"] {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#image-and-message img[display="inline"] {
|
|
||||||
position: relative;
|
|
||||||
top: 3px;
|
|
||||||
border-radius: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Timestamps */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Background colors */
|
|
||||||
body {
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer,
|
|
||||||
yt-live-chat-text-message-renderer[is-highlighted] {
|
|
||||||
background-color: rgba(204, 204, 204, 0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer[author-type="owner"],
|
|
||||||
yt-live-chat-text-message-renderer[author-type="owner"][is-highlighted] {
|
|
||||||
background-color: rgba(255, 214, 0, 0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer[author-type="moderator"],
|
|
||||||
yt-live-chat-text-message-renderer[author-type="moderator"][is-highlighted] {
|
|
||||||
background-color: rgba(94, 132, 241, 0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-text-message-renderer[author-type="member"],
|
|
||||||
yt-live-chat-text-message-renderer[author-type="member"][is-highlighted] {
|
|
||||||
background-color: rgba(15, 157, 88, 0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SuperChat/Fan Funding Messages */
|
|
||||||
yt-live-chat-paid-message-renderer {
|
|
||||||
margin: 4px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-paid-message-renderer #author-name,
|
|
||||||
yt-live-chat-paid-message-renderer #author-name *,
|
|
||||||
yt-live-chat-membership-item-renderer #header-content-inner-column,
|
|
||||||
yt-live-chat-membership-item-renderer #header-content-inner-column * {
|
|
||||||
color: #ffffff !important;
|
|
||||||
font-family: "Changa One", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 20px !important;
|
|
||||||
line-height: 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-paid-message-renderer #purchase-amount,
|
|
||||||
yt-live-chat-paid-message-renderer #purchase-amount *,
|
|
||||||
yt-live-chat-membership-item-renderer #header-subtext,
|
|
||||||
yt-live-chat-membership-item-renderer #header-subtext * {
|
|
||||||
color: #ffffff !important;
|
|
||||||
font-family: "Imprima", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 18px !important;
|
|
||||||
line-height: 18px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-paid-message-renderer #content,
|
|
||||||
yt-live-chat-paid-message-renderer #content * {
|
|
||||||
color: #ffffff !important;
|
|
||||||
font-family: "Imprima", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
font-size: 18px !important;
|
|
||||||
line-height: 18px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-membership-item-renderer #card,
|
|
||||||
yt-live-chat-membership-item-renderer #header {
|
|
||||||
background-color: #0f9d58 !important;
|
|
||||||
margin: 4px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-ticker-renderer {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SuperChat Ticker */
|
|
||||||
yt-live-chat-ticker-paid-message-item-renderer,
|
|
||||||
yt-live-chat-ticker-paid-message-item-renderer *,
|
|
||||||
yt-live-chat-ticker-sponsor-item-renderer,
|
|
||||||
yt-live-chat-ticker-sponsor-item-renderer * {
|
|
||||||
color: #ffffff !important;
|
|
||||||
font-family: "Imprima", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", SimHei, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Animation */
|
|
||||||
@keyframes anim {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
yt-live-chat-interact-message-renderer,
|
|
||||||
yt-live-chat-text-message-renderer,
|
|
||||||
yt-live-chat-membership-item-renderer,
|
|
||||||
yt-live-chat-paid-message-renderer {
|
|
||||||
animation: anim 0ms;
|
|
||||||
animation-fill-mode: both;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"@vueuse/router": "^13.9.0",
|
"@vueuse/router": "^13.9.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"bilibili-live-ws": "^6.3.1",
|
"bilibili-live-danmaku": "^0.7.14",
|
||||||
"cropperjs": "^2.0.1",
|
"cropperjs": "^2.0.1",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.53.0",
|
"monaco-editor": "^0.53.0",
|
||||||
"naive-ui": "^2.43.1",
|
"naive-ui": "2.42.0",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"peerjs": "^1.5.5",
|
"peerjs": "^1.5.5",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import {
|
|||||||
NAlert,
|
NAlert,
|
||||||
NButton,
|
NButton,
|
||||||
NCard,
|
NCard,
|
||||||
|
NDataTable,
|
||||||
NDescriptions,
|
NDescriptions,
|
||||||
NDescriptionsItem,
|
NDescriptionsItem,
|
||||||
NDivider,
|
NDivider,
|
||||||
@@ -65,7 +66,6 @@ import {
|
|||||||
NQrCode,
|
NQrCode,
|
||||||
NRadioButton,
|
NRadioButton,
|
||||||
NRadioGroup,
|
NRadioGroup,
|
||||||
NScrollbar,
|
|
||||||
NSpin,
|
NSpin,
|
||||||
NStatistic,
|
NStatistic,
|
||||||
NTabPane,
|
NTabPane,
|
||||||
@@ -75,7 +75,8 @@ import {
|
|||||||
NTooltip,
|
NTooltip,
|
||||||
useMessage,
|
useMessage,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { computed, nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
import type { DataTableColumns } from 'naive-ui'
|
||||||
|
import { computed, h, nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'
|
||||||
import VChart from 'vue-echarts'
|
import VChart from 'vue-echarts'
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
import { useWebFetcher } from '@/store/useWebFetcher'
|
import { useWebFetcher } from '@/store/useWebFetcher'
|
||||||
@@ -238,6 +239,60 @@ const sortedTodayTypes = computed(() => {
|
|||||||
.sort(([, countA], [, countB]) => countB - countA)
|
.sort(([, countA], [, countB]) => countB - countA)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
type TodayTypeRow = {
|
||||||
|
key: string
|
||||||
|
rank: number
|
||||||
|
type: string
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const todayTypeColumns: DataTableColumns<TodayTypeRow> = [
|
||||||
|
{
|
||||||
|
title: '排名',
|
||||||
|
key: 'rank',
|
||||||
|
align: 'center',
|
||||||
|
width: 72,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
key: 'type',
|
||||||
|
minWidth: 160,
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true,
|
||||||
|
},
|
||||||
|
render: row => h(
|
||||||
|
NTag,
|
||||||
|
{
|
||||||
|
size: 'small',
|
||||||
|
type: 'info',
|
||||||
|
bordered: false,
|
||||||
|
style: 'max-width: 100%; justify-content: flex-start;',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => row.type,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '事件数',
|
||||||
|
key: 'count',
|
||||||
|
align: 'right',
|
||||||
|
width: 120,
|
||||||
|
render: row => row.count.toLocaleString(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const todayTypeTableData = computed<TodayTypeRow[]>(() => {
|
||||||
|
return sortedTodayTypes.value.map(([type, count], index) => ({
|
||||||
|
key: type,
|
||||||
|
rank: index + 1,
|
||||||
|
type,
|
||||||
|
count,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const todayTypeRowKey = (row: TodayTypeRow) => row.key
|
||||||
|
|
||||||
// Login Status (Computed from original snippet)
|
// Login Status (Computed from original snippet)
|
||||||
const loginStatusString = computed(() => {
|
const loginStatusString = computed(() => {
|
||||||
switch (loginStatus.value) {
|
switch (loginStatus.value) {
|
||||||
@@ -1039,6 +1094,7 @@ onUnmounted(() => {
|
|||||||
<VChart
|
<VChart
|
||||||
ref="gaugeChart"
|
ref="gaugeChart"
|
||||||
:option="gaugeOption"
|
:option="gaugeOption"
|
||||||
|
:manual-update="true"
|
||||||
autoresize
|
autoresize
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1055,6 +1111,7 @@ onUnmounted(() => {
|
|||||||
<VChart
|
<VChart
|
||||||
ref="typeDistributionChart"
|
ref="typeDistributionChart"
|
||||||
:option="typeDistributionOption"
|
:option="typeDistributionOption"
|
||||||
|
:manual-update="true"
|
||||||
autoresize
|
autoresize
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1123,36 +1180,26 @@ onUnmounted(() => {
|
|||||||
<NText strong>
|
<NText strong>
|
||||||
类型明细:
|
类型明细:
|
||||||
</NText>
|
</NText>
|
||||||
<NScrollbar style="max-height: 200px; margin-top: 8px;">
|
<div style="margin-top: 8px;">
|
||||||
<div v-if="sortedTodayTypes.length > 0">
|
<NDataTable
|
||||||
<NFlex
|
v-if="todayTypeTableData.length > 0"
|
||||||
vertical
|
:columns="todayTypeColumns"
|
||||||
spacing="small"
|
:data="todayTypeTableData"
|
||||||
>
|
:row-key="todayTypeRowKey"
|
||||||
<NFlex
|
|
||||||
v-for="[type, count] in sortedTodayTypes"
|
|
||||||
:key="type"
|
|
||||||
justify="space-between"
|
|
||||||
align="center"
|
|
||||||
>
|
|
||||||
<NTag
|
|
||||||
size="small"
|
size="small"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
type="info"
|
striped
|
||||||
style="max-width: 70%;"
|
:pagination="false"
|
||||||
>
|
:max-height="220"
|
||||||
<NEllipsis>{{ type }}</NEllipsis>
|
single-line
|
||||||
</NTag>
|
:scrollbar-props="{ size: 6 }"
|
||||||
<NText>{{ count.toLocaleString() }}</NText>
|
/>
|
||||||
</NFlex>
|
|
||||||
</NFlex>
|
|
||||||
</div>
|
|
||||||
<NEmpty
|
<NEmpty
|
||||||
v-else
|
v-else
|
||||||
description="今日暂无数据"
|
description="今日暂无数据"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</NScrollbar>
|
</div>
|
||||||
</NGi>
|
</NGi>
|
||||||
</NGrid>
|
</NGrid>
|
||||||
</div>
|
</div>
|
||||||
@@ -1174,6 +1221,7 @@ onUnmounted(() => {
|
|||||||
v-if="historicalData.length > 0"
|
v-if="historicalData.length > 0"
|
||||||
ref="historyChart"
|
ref="historyChart"
|
||||||
:option="historyOption"
|
:option="historyOption"
|
||||||
|
:manual-update="true"
|
||||||
autoresize
|
autoresize
|
||||||
/>
|
/>
|
||||||
<NEmpty
|
<NEmpty
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { initAll, OnClientUnmounted } from './data/initialize'
|
|||||||
import { useDanmakuWindow } from './store/useDanmakuWindow'
|
import { useDanmakuWindow } from './store/useDanmakuWindow'
|
||||||
// 引入子组件
|
// 引入子组件
|
||||||
import WindowBar from './WindowBar.vue'
|
import WindowBar from './WindowBar.vue'
|
||||||
|
import { BASE_URL } from '@/data/constants'
|
||||||
|
|
||||||
// --- 响应式状态 ---
|
// --- 响应式状态 ---
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ onMounted(() => {
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NA
|
<NA
|
||||||
class="token-get-link"
|
class="token-get-link"
|
||||||
@click="openUrl('https://vtsuru.suki.club/manage')"
|
@click="openUrl(`https://${BASE_URL}/manage`)"
|
||||||
>
|
>
|
||||||
前往获取
|
前往获取
|
||||||
</NA>
|
</NA>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const filterTypeOptions = [
|
|||||||
{ label: 'SC', value: 'SC' },
|
{ label: 'SC', value: 'SC' },
|
||||||
{ label: '舰长', value: 'Guard' },
|
{ label: '舰长', value: 'Guard' },
|
||||||
{ label: '进场', value: 'Enter' },
|
{ label: '进场', value: 'Enter' },
|
||||||
|
{ label: '点赞', value: 'Like' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 分组预设
|
// 分组预设
|
||||||
@@ -211,8 +212,6 @@ const separatorOptions = [
|
|||||||
<NGi>
|
<NGi>
|
||||||
<NFormItem label="背景颜色">
|
<NFormItem label="背景颜色">
|
||||||
<NColorPicker
|
<NColorPicker
|
||||||
v-model:value="danmakuWindow.danmakuWindowSetting.backgroundColor"
|
|
||||||
:show-alpha="true"
|
|
||||||
/>
|
/>
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
</NGi>
|
</NGi>
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ const {
|
|||||||
<template v-if="item.type === EventDataTypes.Enter">
|
<template v-if="item.type === EventDataTypes.Enter">
|
||||||
<span class="enter-badge">进入了直播间</span>
|
<span class="enter-badge">进入了直播间</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="item.type === EventDataTypes.Like">
|
||||||
|
<span class="like-badge">❤️ 点赞了</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="item.type === EventDataTypes.Message && (item?.msg || parsedMessage.length > 0 || item.emoji)"
|
v-if="item.type === EventDataTypes.Message && (item?.msg || parsedMessage.length > 0 || item.emoji)"
|
||||||
@@ -450,4 +453,15 @@ const {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.like-badge {
|
||||||
|
color: #F56C6C;
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 1px 6px;
|
||||||
|
background-color: rgba(245, 108, 108, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ export function useDanmakuUtils(
|
|||||||
case EventDataTypes.SC: return `sc-item ${scColorClass.value}`
|
case EventDataTypes.SC: return `sc-item ${scColorClass.value}`
|
||||||
case EventDataTypes.Guard: return 'guard-item'
|
case EventDataTypes.Guard: return 'guard-item'
|
||||||
case EventDataTypes.Enter: return 'enter-item'
|
case EventDataTypes.Enter: return 'enter-item'
|
||||||
|
case EventDataTypes.Like: return 'like-item'
|
||||||
default: return ''
|
default: return ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -171,6 +172,7 @@ export function useDanmakuUtils(
|
|||||||
case EventDataTypes.SC: return '【SC】'
|
case EventDataTypes.SC: return '【SC】'
|
||||||
case EventDataTypes.Guard: return '【舰长】'
|
case EventDataTypes.Guard: return '【舰长】'
|
||||||
case EventDataTypes.Enter: return '【进场】'
|
case EventDataTypes.Enter: return '【进场】'
|
||||||
|
case EventDataTypes.Like: return '【点赞】'
|
||||||
default: return ''
|
default: return ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -202,6 +204,8 @@ export function useDanmakuUtils(
|
|||||||
return props.item.msg || '开通了舰长'
|
return props.item.msg || '开通了舰长'
|
||||||
case EventDataTypes.Enter:
|
case EventDataTypes.Enter:
|
||||||
return '进入了直播间'
|
return '进入了直播间'
|
||||||
|
case EventDataTypes.Like:
|
||||||
|
return '点赞了'
|
||||||
default:
|
default:
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
@@ -217,6 +221,8 @@ export function useDanmakuUtils(
|
|||||||
return guardColor.value // 舰长消息使用舰长颜色
|
return guardColor.value // 舰长消息使用舰长颜色
|
||||||
} else if (props.item.type === EventDataTypes.Enter) {
|
} else if (props.item.type === EventDataTypes.Enter) {
|
||||||
return '#67C23A' // 入场消息绿色
|
return '#67C23A' // 入场消息绿色
|
||||||
|
} else if (props.item.type === EventDataTypes.Like) {
|
||||||
|
return '#F56C6C' // 点赞消息红色
|
||||||
}
|
}
|
||||||
return undefined // 普通消息使用默认颜色
|
return undefined // 普通消息使用默认颜色
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -143,23 +143,7 @@ export function recordEvent(eventType: string) {
|
|||||||
* (需要根据实际接收到的数据结构调整)
|
* (需要根据实际接收到的数据结构调整)
|
||||||
*/
|
*/
|
||||||
export function getEventType(command: any): string {
|
export function getEventType(command: any): string {
|
||||||
if (typeof command === 'string') {
|
return command.cmd
|
||||||
try {
|
|
||||||
command = JSON.parse(command)
|
|
||||||
} catch (e) {
|
|
||||||
return 'UNKNOWN_FORMAT'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command && typeof command === 'object') {
|
|
||||||
// 优先使用 'cmd' 字段 (常见于 Web 或 OpenLive)
|
|
||||||
if (command.cmd) return command.cmd
|
|
||||||
// 备选 'command' 字段
|
|
||||||
if (command.command) return command.command
|
|
||||||
// 备选 'type' 字段
|
|
||||||
if (command.type) return command.type
|
|
||||||
}
|
|
||||||
return 'UNKNOWN' // 未知类型
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,6 +29,35 @@ const accountInfo = useAccount()
|
|||||||
|
|
||||||
export const clientInited = ref(false)
|
export const clientInited = ref(false)
|
||||||
let tray: TrayIcon
|
let tray: TrayIcon
|
||||||
|
let heartbeatTimer: number | null = null
|
||||||
|
|
||||||
|
async function sendHeartbeat() {
|
||||||
|
try {
|
||||||
|
await invoke('heartbeat')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送心跳失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startHeartbeat() {
|
||||||
|
// 立即发送一次,确保后端在加载后快速收到心跳
|
||||||
|
void sendHeartbeat()
|
||||||
|
|
||||||
|
// 之后每 5 秒发送一次心跳(后端超时时间为 15 秒)
|
||||||
|
heartbeatTimer = window.setInterval(() => {
|
||||||
|
void sendHeartbeat()
|
||||||
|
}, 2000)
|
||||||
|
info('[心跳] 定时器已启动,间隔 2 秒')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopHeartbeat() {
|
||||||
|
if (heartbeatTimer !== null) {
|
||||||
|
clearInterval(heartbeatTimer)
|
||||||
|
heartbeatTimer = null
|
||||||
|
info('[心跳] 定时器已停止')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function initAll(isOnBoot: boolean) {
|
export async function initAll(isOnBoot: boolean) {
|
||||||
const setting = useSettings()
|
const setting = useSettings()
|
||||||
if (clientInited.value) {
|
if (clientInited.value) {
|
||||||
@@ -150,6 +179,7 @@ export async function initAll(isOnBoot: boolean) {
|
|||||||
useAutoAction().init()
|
useAutoAction().init()
|
||||||
useBiliFunction().init()
|
useBiliFunction().init()
|
||||||
|
|
||||||
|
//startHeartbeat()
|
||||||
clientInited.value = true
|
clientInited.value = true
|
||||||
}
|
}
|
||||||
export function OnClientUnmounted() {
|
export function OnClientUnmounted() {
|
||||||
@@ -157,6 +187,7 @@ export function OnClientUnmounted() {
|
|||||||
clientInited.value = false
|
clientInited.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopHeartbeat()
|
||||||
tray.close()
|
tray.close()
|
||||||
// useDanmakuWindow().closeWindow();
|
// useDanmakuWindow().closeWindow();
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/components.d.ts
vendored
6
src/components.d.ts
vendored
@@ -22,19 +22,13 @@ declare module 'vue' {
|
|||||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NCard: typeof import('naive-ui')['NCard']
|
NCard: typeof import('naive-ui')['NCard']
|
||||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
|
||||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
|
||||||
NFlex: typeof import('naive-ui')['NFlex']
|
NFlex: typeof import('naive-ui')['NFlex']
|
||||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
|
||||||
NGridItem: typeof import('naive-ui')['NGridItem']
|
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
NImage: typeof import('naive-ui')['NImage']
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
NTag: typeof import('naive-ui')['NTag']
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NText: typeof import('naive-ui')['NText']
|
NText: typeof import('naive-ui')['NText']
|
||||||
NTime: typeof import('naive-ui')['NTime']
|
|
||||||
PointGoodsItem: typeof import('./components/manage/PointGoodsItem.vue')['default']
|
PointGoodsItem: typeof import('./components/manage/PointGoodsItem.vue')['default']
|
||||||
PointHistoryCard: typeof import('./components/manage/PointHistoryCard.vue')['default']
|
PointHistoryCard: typeof import('./components/manage/PointHistoryCard.vue')['default']
|
||||||
PointOrderCard: typeof import('./components/manage/PointOrderCard.vue')['default']
|
PointOrderCard: typeof import('./components/manage/PointOrderCard.vue')['default']
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { KeepLiveWS } from 'bilibili-live-ws/browser' // 导入 bilibili-live-ws 库
|
import { LiveWS } from "bilibili-live-danmaku";
|
||||||
// BaseDanmakuClient.ts
|
// BaseDanmakuClient.ts
|
||||||
import type { EventModel } from '@/api/api-models'
|
import type { EventModel } from '@/api/api-models'
|
||||||
// 导入事件模型和类型枚举
|
// 导入事件模型和类型枚举
|
||||||
@@ -13,7 +13,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket 客户端实例
|
// WebSocket 客户端实例
|
||||||
public client: KeepLiveWS | null
|
public client: LiveWS | null
|
||||||
|
|
||||||
// 客户端连接状态
|
// 客户端连接状态
|
||||||
public state: 'padding' | 'connected' | 'connecting' | 'disconnected'
|
public state: 'padding' | 'connected' | 'connecting' | 'disconnected'
|
||||||
@@ -35,6 +35,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
scDel: ((arg1: EventModel, arg2?: any) => void)[] // 新增: SC 删除事件
|
scDel: ((arg1: EventModel, arg2?: any) => void)[] // 新增: SC 删除事件
|
||||||
all: ((arg1: any) => void)[] // 'all' 事件监听器接收原始消息或特定事件包
|
all: ((arg1: any) => void)[] // 'all' 事件监听器接收原始消息或特定事件包
|
||||||
follow: ((arg1: EventModel, arg2?: any) => void)[] // 新增: 关注事件
|
follow: ((arg1: EventModel, arg2?: any) => void)[] // 新增: 关注事件
|
||||||
|
like: ((arg1: EventModel, arg2?: any) => void)[] // 新增: 点赞事件
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 事件系统 2: 使用原始数据类型 ---
|
// --- 事件系统 2: 使用原始数据类型 ---
|
||||||
@@ -48,6 +49,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
scDel: ((arg1: any, arg2?: any) => void)[] // 新增: SC 删除事件
|
scDel: ((arg1: any, arg2?: any) => void)[] // 新增: SC 删除事件
|
||||||
all: ((arg1: any) => void)[] // 'all' 事件监听器接收原始消息或特定事件包
|
all: ((arg1: any) => void)[] // 'all' 事件监听器接收原始消息或特定事件包
|
||||||
follow: ((arg1: any, arg2?: any) => void)[] // 新增: 关注事件
|
follow: ((arg1: any, arg2?: any) => void)[] // 新增: 关注事件
|
||||||
|
like: ((arg1: any, arg2?: any) => void)[] // 新增: 点赞事件
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建空的 EventModel 监听器对象
|
// 创建空的 EventModel 监听器对象
|
||||||
@@ -61,6 +63,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
scDel: [],
|
scDel: [],
|
||||||
all: [],
|
all: [],
|
||||||
follow: [], // 初始化 follow 事件
|
follow: [], // 初始化 follow 事件
|
||||||
|
like: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +78,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
scDel: [],
|
scDel: [],
|
||||||
all: [],
|
all: [],
|
||||||
follow: [], // 初始化 follow 事件
|
follow: [], // 初始化 follow 事件
|
||||||
|
like: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,27 +182,27 @@ export default abstract class BaseDanmakuClient {
|
|||||||
* @returns Promise<{ success: boolean; message: string }> 连接结果
|
* @returns Promise<{ success: boolean; message: string }> 连接结果
|
||||||
*/
|
*/
|
||||||
protected async initClientInner(
|
protected async initClientInner(
|
||||||
chatClient: KeepLiveWS,
|
chatClient: LiveWS,
|
||||||
): Promise<{ success: boolean, message: string }> {
|
): Promise<{ success: boolean, message: string }> {
|
||||||
let isConnected = false // 标记是否连接成功
|
let isConnected = false // 标记是否连接成功
|
||||||
let isError = false // 标记是否发生错误
|
let isError = false // 标记是否发生错误
|
||||||
let errorMsg = '' // 存储错误信息
|
let errorMsg = '' // 存储错误信息
|
||||||
|
|
||||||
// 监听错误事件
|
// 监听错误事件
|
||||||
chatClient.on('error', (err: any) => {
|
chatClient.addEventListener('error', (err: any) => {
|
||||||
console.error(`[${this.type}] 客户端发生错误:`, err)
|
console.error(`[${this.type}] 客户端发生错误:`, err)
|
||||||
isError = true
|
isError = true
|
||||||
errorMsg = err?.message || err?.toString() || '未知错误'
|
errorMsg = err?.message || err?.toString() || '未知错误'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听连接成功事件
|
// 监听连接成功事件
|
||||||
chatClient.on('live', () => {
|
chatClient.addEventListener('CONNECT_SUCCESS', () => {
|
||||||
console.log(`[${this.type}] 弹幕客户端连接成功`)
|
console.log(`[${this.type}] 弹幕客户端连接成功`)
|
||||||
isConnected = true
|
isConnected = true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听连接关闭事件
|
// 监听连接关闭事件
|
||||||
chatClient.on('close', () => {
|
chatClient.addEventListener('close', () => {
|
||||||
console.log(`[${this.type}] 弹幕客户端连接已关闭`)
|
console.log(`[${this.type}] 弹幕客户端连接已关闭`)
|
||||||
if (this.state !== 'disconnected') {
|
if (this.state !== 'disconnected') {
|
||||||
this.state = 'disconnected'
|
this.state = 'disconnected'
|
||||||
@@ -209,7 +213,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
|
|
||||||
// 监听原始消息事件 (通用)
|
// 监听原始消息事件 (通用)
|
||||||
// 注意: 子类可能也会监听特定事件名, 这里的 'msg' 是备用或处理未被特定监听器捕获的事件
|
// 注意: 子类可能也会监听特定事件名, 这里的 'msg' 是备用或处理未被特定监听器捕获的事件
|
||||||
chatClient.on('msg', (command: any) => this.onRawMessage(command))
|
chatClient.addEventListener('MESSAGE', (command: any) => this.onRawMessage(command.data))
|
||||||
|
|
||||||
this.client = chatClient // 保存客户端实例
|
this.client = chatClient // 保存客户端实例
|
||||||
|
|
||||||
@@ -301,6 +305,12 @@ export default abstract class BaseDanmakuClient {
|
|||||||
* @param rawCommand - 完整的原始消息对象 (可选, any 类型)
|
* @param rawCommand - 完整的原始消息对象 (可选, any 类型)
|
||||||
*/
|
*/
|
||||||
public abstract onScDel(comand: any): void
|
public abstract onScDel(comand: any): void
|
||||||
|
/**
|
||||||
|
* 处理点赞消息 (子类实现)
|
||||||
|
* @param data - 原始消息数据部分 (any 类型)
|
||||||
|
* @param rawCommand - 完整的原始消息对象 (可选, any 类型)
|
||||||
|
*/
|
||||||
|
public abstract onLike(comand: any): void
|
||||||
|
|
||||||
// --- 事件系统 1: on/off (使用 EventModel) ---
|
// --- 事件系统 1: on/off (使用 EventModel) ---
|
||||||
public onEvent(eventName: 'danmaku', listener: (arg1: EventModel, arg2?: any) => void): this
|
public onEvent(eventName: 'danmaku', listener: (arg1: EventModel, arg2?: any) => void): this
|
||||||
@@ -311,6 +321,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
public onEvent(eventName: 'scDel', listener: (arg1: EventModel, arg2?: any) => void): this // 新增
|
public onEvent(eventName: 'scDel', listener: (arg1: EventModel, arg2?: any) => void): this // 新增
|
||||||
public onEvent(eventName: 'all', listener: (arg1: any) => void): this
|
public onEvent(eventName: 'all', listener: (arg1: any) => void): this
|
||||||
public onEvent(eventName: 'follow', listener: (arg1: EventModel, arg2?: any) => void): this // 新增
|
public onEvent(eventName: 'follow', listener: (arg1: EventModel, arg2?: any) => void): this // 新增
|
||||||
|
public onEvent(eventName: 'like', listener: (arg1: EventModel, arg2?: any) => void): this // 新增
|
||||||
public onEvent(eventName: keyof BaseDanmakuClient['eventsAsModel'], listener: (...args: any[]) => void): this {
|
public onEvent(eventName: keyof BaseDanmakuClient['eventsAsModel'], listener: (...args: any[]) => void): this {
|
||||||
if (!this.eventsAsModel[eventName]) {
|
if (!this.eventsAsModel[eventName]) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -342,6 +353,7 @@ export default abstract class BaseDanmakuClient {
|
|||||||
public on(eventName: 'scDel', listener: (arg1: any, arg2?: any) => void): this // 新增
|
public on(eventName: 'scDel', listener: (arg1: any, arg2?: any) => void): this // 新增
|
||||||
public on(eventName: 'all', listener: (arg1: any) => void): this
|
public on(eventName: 'all', listener: (arg1: any) => void): this
|
||||||
public on(eventName: 'follow', listener: (arg1: any, arg2?: any) => void): this // 新增
|
public on(eventName: 'follow', listener: (arg1: any, arg2?: any) => void): this // 新增
|
||||||
|
public on(eventName: 'like', listener: (arg1: any, arg2?: any) => void): this // 新增
|
||||||
public on(eventName: keyof BaseDanmakuClient['eventsRaw'], listener: (...args: any[]) => void): this {
|
public on(eventName: keyof BaseDanmakuClient['eventsRaw'], listener: (...args: any[]) => void): this {
|
||||||
if (!this.eventsRaw[eventName]) {
|
if (!this.eventsRaw[eventName]) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { KeepLiveWS } from 'bilibili-live-ws/browser'
|
import { DataEvent, LiveWS, MessageData } from 'bilibili-live-danmaku'
|
||||||
import { EventDataTypes, GuardLevel } from '@/api/api-models'
|
import { EventDataTypes, GuardLevel } from '@/api/api-models'
|
||||||
import { GuidUtils } from '@/Utils'
|
import { GuidUtils } from '@/Utils'
|
||||||
import { AVATAR_URL } from '../constants'
|
import { AVATAR_URL } from '../constants'
|
||||||
import BaseDanmakuClient from './BaseDanmakuClient'
|
import BaseDanmakuClient from './BaseDanmakuClient'
|
||||||
|
import Long from 'long'
|
||||||
|
|
||||||
export interface DirectClientAuthInfo {
|
export interface DirectClientAuthInfo {
|
||||||
token: string
|
token: string
|
||||||
@@ -28,22 +29,34 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
|
|
||||||
protected async initClient(): Promise<{ success: boolean, message: string }> {
|
protected async initClient(): Promise<{ success: boolean, message: string }> {
|
||||||
if (this.authInfo) {
|
if (this.authInfo) {
|
||||||
const chatClient = new KeepLiveWS(this.authInfo.roomId, {
|
const chatClient = new LiveWS(this.authInfo.roomId, {
|
||||||
key: this.authInfo.token,
|
key: this.authInfo.token,
|
||||||
buvid: this.authInfo.buvid,
|
buvid: this.authInfo.buvid,
|
||||||
uid: this.authInfo.tokenUserId,
|
uid: this.authInfo.tokenUserId,
|
||||||
protover: 3,
|
protover: 3,
|
||||||
})
|
})
|
||||||
|
|
||||||
chatClient.on('live', () => {
|
chatClient.addEventListener('CONNECT_SUCCESS', () => {
|
||||||
console.log(`[direct] 已连接房间: ${this.authInfo.roomId}`)
|
console.log(`[direct] 已连接房间: ${this.authInfo.roomId}`)
|
||||||
})
|
})
|
||||||
chatClient.on('DANMU_MSG', data => this.onDanmaku(data))
|
chatClient.addEventListener('DANMU_MSG', data => this.onDanmaku(data.data))
|
||||||
chatClient.on('SEND_GIFT', data => this.onGift(data))
|
chatClient.addEventListener('SEND_GIFT', data => this.onGift(data.data))
|
||||||
chatClient.on('GUARD_BUY', data => this.onGuard(data))
|
chatClient.addEventListener('GUARD_BUY', data => this.onGuard(data.data))
|
||||||
chatClient.on('SUPER_CHAT_MESSAGE', data => this.onSC(data))
|
chatClient.addEventListener('SUPER_CHAT_MESSAGE', data => this.onSC(data.data))
|
||||||
chatClient.on('INTERACT_WORD', data => this.onEnter(data))
|
//chatClient.addEventListener('INTERACT_WORD', data => this.onEnter(data.data))
|
||||||
chatClient.on('SUPER_CHAT_MESSAGE_DELETE', data => this.onScDel(data))
|
chatClient.addEventListener('MESSAGE', data => {
|
||||||
|
switch (data.data.cmd) {
|
||||||
|
case 'INTERACT_WORD_V2':
|
||||||
|
this.onEnter(data.data)
|
||||||
|
break
|
||||||
|
case 'LIKE_INFO_V3_CLICK':
|
||||||
|
this.onLike(data.data)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//chatClient.addEventListener('SUPER_CHAT_MESSAGE_DELETE', data => this.onScDel(data))
|
||||||
|
|
||||||
return super.initClientInner(chatClient)
|
return super.initClientInner(chatClient)
|
||||||
} else {
|
} else {
|
||||||
@@ -55,7 +68,7 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDanmaku(command: any): void {
|
public onDanmaku(command: MessageData.DANMU_MSG): void {
|
||||||
const info = command.info
|
const info = command.info
|
||||||
this.eventsRaw?.danmaku?.forEach((d) => {
|
this.eventsRaw?.danmaku?.forEach((d) => {
|
||||||
d(info, command)
|
d(info, command)
|
||||||
@@ -84,7 +97,7 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGift(command: any): void {
|
public onGift(command: MessageData.SEND_GIFT): void {
|
||||||
const data = command.data
|
const data = command.data
|
||||||
this.eventsRaw?.gift?.forEach((d) => {
|
this.eventsRaw?.gift?.forEach((d) => {
|
||||||
d(data, command)
|
d(data, command)
|
||||||
@@ -96,13 +109,13 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
uname: data.uname,
|
uname: data.uname,
|
||||||
uid: data.uid,
|
uid: data.uid,
|
||||||
msg: data.giftName,
|
msg: data.giftName,
|
||||||
price: data.price / 1000,
|
price: data.total_coin / 1000,
|
||||||
num: data.num,
|
num: data.num,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
guard_level: data.guard_level,
|
guard_level: data.guard_level,
|
||||||
fans_medal_level: data.medal_info.medal_level,
|
fans_medal_level: data.fans_medal?.medal_level,
|
||||||
fans_medal_name: data.medal_info.medal_name,
|
fans_medal_name: data.fans_medal?.medal_name,
|
||||||
fans_medal_wearing_status: data.medal_info.is_lighted === 1,
|
fans_medal_wearing_status: data.fans_medal !== null || data.fans_medal !== undefined,
|
||||||
uface: data.face.replace('http://', 'https://'),
|
uface: data.face.replace('http://', 'https://'),
|
||||||
open_id: '',
|
open_id: '',
|
||||||
ouid: GuidUtils.numToGuid(data.uid),
|
ouid: GuidUtils.numToGuid(data.uid),
|
||||||
@@ -112,7 +125,7 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSC(command: any): void {
|
public onSC(command: MessageData.SUPER_CHAT_MESSAGE): void {
|
||||||
const data = command.data
|
const data = command.data
|
||||||
this.eventsRaw?.sc?.forEach((d) => {
|
this.eventsRaw?.sc?.forEach((d) => {
|
||||||
d(data, command)
|
d(data, command)
|
||||||
@@ -130,7 +143,7 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
guard_level: data.user_info.guard_level,
|
guard_level: data.user_info.guard_level,
|
||||||
fans_medal_level: data.medal_info.medal_level,
|
fans_medal_level: data.medal_info.medal_level,
|
||||||
fans_medal_name: data.medal_info.medal_name,
|
fans_medal_name: data.medal_info.medal_name,
|
||||||
fans_medal_wearing_status: data.medal_info.is_lighted === 1,
|
fans_medal_wearing_status: data.medal_info !== null || data.medal_info !== undefined,
|
||||||
uface: data.user_info.face.replace('http://', 'https://'),
|
uface: data.user_info.face.replace('http://', 'https://'),
|
||||||
open_id: '',
|
open_id: '',
|
||||||
ouid: GuidUtils.numToGuid(data.uid),
|
ouid: GuidUtils.numToGuid(data.uid),
|
||||||
@@ -140,7 +153,7 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGuard(command: any): void {
|
public onGuard(command: MessageData.GUARD_BUY): void {
|
||||||
const data = command.data
|
const data = command.data
|
||||||
this.eventsRaw?.guard?.forEach((d) => {
|
this.eventsRaw?.guard?.forEach((d) => {
|
||||||
d(data, command)
|
d(data, command)
|
||||||
@@ -168,9 +181,9 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public onEnter(command: any): void {
|
public onEnter(command: MessageData.INTERACT_WORD_V2): void {
|
||||||
const data = command.data
|
const data = command.decoded
|
||||||
const msgType = data.msg_type
|
const msgType = data?.msgType
|
||||||
|
|
||||||
if (msgType === 1) {
|
if (msgType === 1) {
|
||||||
this.eventsRaw?.enter?.forEach((d) => {
|
this.eventsRaw?.enter?.forEach((d) => {
|
||||||
@@ -180,19 +193,19 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
d(
|
d(
|
||||||
{
|
{
|
||||||
type: EventDataTypes.Enter,
|
type: EventDataTypes.Enter,
|
||||||
uname: data.uname,
|
uname: data?.uname || '',
|
||||||
uid: data.uid,
|
uid: this.convertToNumber(data?.uid) || 0,
|
||||||
msg: '',
|
msg: '',
|
||||||
price: 0,
|
price: 0,
|
||||||
num: 1,
|
num: 1,
|
||||||
time: data.timestamp ? data.timestamp * 1000 : Date.now(),
|
time: data?.timestamp ? this.convertToNumber(data.timestamp) * 1000 : Date.now(),
|
||||||
guard_level: data.privilege_type || GuardLevel.None,
|
guard_level: this.convertToNumber(data?.privilegeType) || GuardLevel.None,
|
||||||
fans_medal_level: data.fans_medal?.medal_level || 0,
|
fans_medal_level: this.convertToNumber(data?.fansMedal?.medalLevel) || 0,
|
||||||
fans_medal_name: data.fans_medal?.medal_name || '',
|
fans_medal_name: data?.fansMedal?.medalName || '',
|
||||||
fans_medal_wearing_status: data.fans_medal?.is_lighted === 1,
|
fans_medal_wearing_status: data?.fansMedal?.isLighted === 1,
|
||||||
uface: data.face?.replace('http://', 'https://') || (AVATAR_URL + data.uid),
|
uface: data?.uinfo?.uheadFrame?.frameImg?.replace('http://', 'https://') || (AVATAR_URL + this.convertToNumber(data?.uid)),
|
||||||
open_id: '',
|
open_id: '',
|
||||||
ouid: GuidUtils.numToGuid(data.uid),
|
ouid: GuidUtils.numToGuid(this.convertToNumber(data?.uid)),
|
||||||
},
|
},
|
||||||
command,
|
command,
|
||||||
)
|
)
|
||||||
@@ -205,17 +218,53 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
d(
|
d(
|
||||||
{
|
{
|
||||||
type: EventDataTypes.Follow,
|
type: EventDataTypes.Follow,
|
||||||
uname: data.uname,
|
uname: data?.uname || '',
|
||||||
uid: data.uid,
|
uid: this.convertToNumber(data?.uid),
|
||||||
msg: '关注了主播',
|
msg: '关注了主播',
|
||||||
price: 0,
|
price: 0,
|
||||||
num: 1,
|
num: 1,
|
||||||
time: data.timestamp ? data.timestamp * 1000 : Date.now(),
|
time: data?.timestamp ? this.convertToNumber(data.timestamp) * 1000 : Date.now(),
|
||||||
guard_level: data.privilege_type || GuardLevel.None,
|
guard_level: this.convertToNumber(data?.privilegeType) || GuardLevel.None,
|
||||||
fans_medal_level: data.fans_medal?.medal_level || 0,
|
fans_medal_level: this.convertToNumber(data?.fansMedal?.medalLevel) || 0,
|
||||||
fans_medal_name: data.fans_medal?.medal_name || '',
|
fans_medal_name: data?.fansMedal?.medalName || '',
|
||||||
fans_medal_wearing_status: data.fans_medal?.is_lighted === 1,
|
fans_medal_wearing_status: data?.fansMedal?.isLighted === 1,
|
||||||
uface: data.face?.replace('http://', 'https://') || (AVATAR_URL + data.uid),
|
uface: data?.uinfo?.uheadFrame?.frameImg?.replace('http://', 'https://') || (AVATAR_URL + data?.uid),
|
||||||
|
open_id: '',
|
||||||
|
ouid: GuidUtils.numToGuid(this.convertToNumber(data?.uid)),
|
||||||
|
},
|
||||||
|
command,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToNumber(value: number | Long | null | undefined): number {
|
||||||
|
if (value instanceof Long) {
|
||||||
|
return value.toNumber()
|
||||||
|
}
|
||||||
|
return value || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLike(command: any): void {
|
||||||
|
const data = command.data
|
||||||
|
this.eventsRaw?.like?.forEach((d) => {
|
||||||
|
d(data, command)
|
||||||
|
})
|
||||||
|
this.eventsAsModel.like?.forEach((d) => {
|
||||||
|
d(
|
||||||
|
{
|
||||||
|
type: EventDataTypes.Like,
|
||||||
|
uname: data.uname,
|
||||||
|
uid: data.uid,
|
||||||
|
msg: '为直播间点赞',
|
||||||
|
price: 0,
|
||||||
|
num: 1,
|
||||||
|
time: Date.now(),
|
||||||
|
guard_level: 0,
|
||||||
|
fans_medal_level: data.medal_info?.medal_level ?? 0,
|
||||||
|
fans_medal_name: data.medal_info?.medal_name ?? '',
|
||||||
|
fans_medal_wearing_status: data.medal_info?.is_lighted === 1,
|
||||||
|
uface: data.uface.replace('http://', 'https://'),
|
||||||
open_id: '',
|
open_id: '',
|
||||||
ouid: GuidUtils.numToGuid(data.uid),
|
ouid: GuidUtils.numToGuid(data.uid),
|
||||||
},
|
},
|
||||||
@@ -223,7 +272,6 @@ export default class DirectClient extends BaseDanmakuClient {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public onScDel(command: any): void {
|
public onScDel(command: any): void {
|
||||||
const data = command.data
|
const data = command.data
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { OpenLiveInfo } from '@/api/api-models'
|
import type { OpenLiveInfo } from '@/api/api-models'
|
||||||
import { KeepLiveWS } from 'bilibili-live-ws/browser'
|
import { LiveWS } from 'bilibili-live-danmaku'
|
||||||
import { clearInterval, setInterval } from 'worker-timers'
|
import { clearInterval, setInterval } from 'worker-timers'
|
||||||
import { EventDataTypes } from '@/api/api-models'
|
import { EventDataTypes } from '@/api/api-models'
|
||||||
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||||
@@ -41,17 +41,35 @@ export default class OpenLiveClient extends BaseDanmakuClient {
|
|||||||
protected async initClient(): Promise<{ success: boolean, message: string }> {
|
protected async initClient(): Promise<{ success: boolean, message: string }> {
|
||||||
const auth = await this.getAuthInfo()
|
const auth = await this.getAuthInfo()
|
||||||
if (auth.data) {
|
if (auth.data) {
|
||||||
const chatClient = new KeepLiveWS(auth.data.anchor_info.room_id, {
|
const chatClient = new LiveWS(auth.data.anchor_info.room_id, {
|
||||||
authBody: JSON.parse(auth.data.websocket_info.auth_body),
|
authBody: JSON.parse(auth.data.websocket_info.auth_body),
|
||||||
address: auth.data.websocket_info.wss_link[0],
|
address: auth.data.websocket_info.wss_link[0],
|
||||||
})
|
})
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_DM', cmd => this.onDanmaku(cmd))
|
chatClient.addEventListener('MESSAGE', cmd => {
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_GIFT', cmd => this.onGift(cmd))
|
switch (cmd.data.cmd as string) {
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_GUARD', cmd => this.onGuard(cmd))
|
case 'LIVE_OPEN_PLATFORM_DM':
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_SC', cmd => this.onSC(cmd))
|
this.onDanmaku(cmd.data)
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_LIVE_ROOM_ENTER', cmd => this.onEnter(cmd))
|
break
|
||||||
chatClient.on('LIVE_OPEN_PLATFORM_SUPER_CHAT_DEL', cmd => this.onScDel(cmd))
|
case 'LIVE_OPEN_PLATFORM_GIFT':
|
||||||
chatClient.on('live', () => {
|
this.onGift(cmd.data)
|
||||||
|
break
|
||||||
|
case 'LIVE_OPEN_PLATFORM_GUARD':
|
||||||
|
this.onGuard(cmd.data)
|
||||||
|
break
|
||||||
|
case 'LIVE_OPEN_PLATFORM_SC':
|
||||||
|
this.onSC(cmd.data)
|
||||||
|
break
|
||||||
|
case 'LIVE_OPEN_PLATFORM_LIVE_ROOM_ENTER':
|
||||||
|
this.onEnter(cmd.data)
|
||||||
|
break
|
||||||
|
case 'LIVE_OPEN_PLATFORM_SUPER_CHAT_DEL':
|
||||||
|
this.onScDel(cmd.data)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
chatClient.addEventListener('CONNECT_SUCCESS', () => {
|
||||||
console.log(
|
console.log(
|
||||||
`[${this.type}] 已连接房间: ${auth.data?.anchor_info.room_id}`,
|
`[${this.type}] 已连接房间: ${auth.data?.anchor_info.room_id}`,
|
||||||
)
|
)
|
||||||
@@ -270,6 +288,10 @@ export default class OpenLiveClient extends BaseDanmakuClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onLike(_command: any): void {
|
||||||
|
// OpenLiveClient does not support like events
|
||||||
|
}
|
||||||
|
|
||||||
public onScDel(command: any): void {
|
public onScDel(command: any): void {
|
||||||
const data = command.data as SCDelInfo
|
const data = command.data as SCDelInfo
|
||||||
this.eventsRaw.scDel?.forEach((d) => {
|
this.eventsRaw.scDel?.forEach((d) => {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import App from './App.vue'
|
|||||||
import { InitVTsuru } from './data/Initializer'
|
import { InitVTsuru } from './data/Initializer'
|
||||||
import emitter from './mitt'
|
import emitter from './mitt'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
import { isTauri } from './data/constants'
|
||||||
|
import { startHeartbeat } from './client/data/initialize'
|
||||||
|
|
||||||
loader.config({
|
loader.config({
|
||||||
'paths': {
|
'paths': {
|
||||||
@@ -24,5 +26,8 @@ const app = createApp(App)
|
|||||||
app.use(router).use(pinia).mount('#app')
|
app.use(router).use(pinia).mount('#app')
|
||||||
|
|
||||||
InitVTsuru()
|
InitVTsuru()
|
||||||
|
if (isTauri()) {
|
||||||
|
startHeartbeat();
|
||||||
|
}
|
||||||
|
|
||||||
window.$mitt = emitter
|
window.$mitt = emitter
|
||||||
|
|||||||
Reference in New Issue
Block a user