mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
add web-fetcher
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@microsoft/signalr": "^8.0.0",
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "^8.0.0",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.19",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||||
"@vicons/fluent": "^0.12.0",
|
"@vicons/fluent": "^0.12.0",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { ref } from 'vue'
|
|||||||
import { APIRoot, AccountInfo, FunctionTypes } from './api-models'
|
import { APIRoot, AccountInfo, FunctionTypes } from './api-models'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
export const ACCOUNT = ref<AccountInfo>()
|
export const ACCOUNT = ref<AccountInfo>({} as AccountInfo)
|
||||||
export const isLoadingAccount = ref(true)
|
export const isLoadingAccount = ref(true)
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,20 @@ export interface UserInfo {
|
|||||||
streamerInfo?: StreamerModel
|
streamerInfo?: StreamerModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export interface EventFetcherStateModel {
|
||||||
|
online: boolean
|
||||||
|
status: { [errorCode: string]: string }
|
||||||
|
version?: string
|
||||||
|
todayReceive: number
|
||||||
|
useCookie: boolean
|
||||||
|
type: EventFetcherType
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EventFetcherType {
|
||||||
|
Application,
|
||||||
|
OBS,
|
||||||
|
Server,
|
||||||
|
}
|
||||||
export interface AccountInfo extends UserInfo {
|
export interface AccountInfo extends UserInfo {
|
||||||
isEmailVerified: boolean
|
isEmailVerified: boolean
|
||||||
isBiliVerified: boolean
|
isBiliVerified: boolean
|
||||||
@@ -41,11 +55,7 @@ export interface AccountInfo extends UserInfo {
|
|||||||
biliAuthCode?: string
|
biliAuthCode?: string
|
||||||
biliAuthCodeStatus: BiliAuthCodeStatusType
|
biliAuthCodeStatus: BiliAuthCodeStatusType
|
||||||
|
|
||||||
eventFetcherOnline: boolean
|
eventFetcherState: EventFetcherStateModel
|
||||||
eventFetcherStatus: string
|
|
||||||
eventFetcherStatusV3: { [errorCode: string]: string }
|
|
||||||
eventFetcherTodayReceive: number
|
|
||||||
eventFetcherVersion?: string
|
|
||||||
|
|
||||||
nextSendEmailTime?: number
|
nextSendEmailTime?: number
|
||||||
isServerFetcherOnline: boolean
|
isServerFetcherOnline: boolean
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function QueryPostAPIWithParams<T>(
|
|||||||
const url = new URL(urlString)
|
const url = new URL(urlString)
|
||||||
url.search = getParams(params)
|
url.search = getParams(params)
|
||||||
headers ??= []
|
headers ??= []
|
||||||
headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
if (cookie.value) headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
||||||
|
|
||||||
if (contentType) headers?.push(['Content-Type', contentType])
|
if (contentType) headers?.push(['Content-Type', contentType])
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ export async function QueryGetAPI<T>(
|
|||||||
url.search = getParams(params)
|
url.search = getParams(params)
|
||||||
if (cookie.value) {
|
if (cookie.value) {
|
||||||
headers ??= []
|
headers ??= []
|
||||||
headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
if (cookie.value) headers?.push(['Authorization', `Bearer ${cookie.value}`])
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const data = await fetch(url.toString(), {
|
const data = await fetch(url.toString(), {
|
||||||
@@ -81,6 +81,9 @@ function getParams(params?: [string, string][]) {
|
|||||||
if (urlParams.has('as')) {
|
if (urlParams.has('as')) {
|
||||||
resultParams.set('as', urlParams.get('as') || '')
|
resultParams.set('as', urlParams.get('as') || '')
|
||||||
}
|
}
|
||||||
|
if (urlParams.has('token')) {
|
||||||
|
resultParams.set('token', urlParams.get('token') || '')
|
||||||
|
}
|
||||||
return resultParams.toString()
|
return resultParams.toString()
|
||||||
}
|
}
|
||||||
export async function QueryPostPaginationAPI<T>(url: string, body?: unknown): Promise<APIRoot<PaginationResponse<T>>> {
|
export async function QueryPostPaginationAPI<T>(url: string, body?: unknown): Promise<APIRoot<PaginationResponse<T>>> {
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
|
import { EventFetcherType } from '@/api/api-models'
|
||||||
import { FlashCheckmark16Filled, Info24Filled } from '@vicons/fluent'
|
import { FlashCheckmark16Filled, Info24Filled } from '@vicons/fluent'
|
||||||
import { NAlert, NButton, NDivider, NIcon, NTag, NText, NTooltip } from 'naive-ui'
|
import { NAlert, NButton, NDivider, NIcon, NTag, NText, NTooltip } from 'naive-ui'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
|
const state = accountInfo.value?.eventFetcherState
|
||||||
|
|
||||||
const status = computed(() => {
|
const status = computed(() => {
|
||||||
if (!accountInfo.value) return 'error'
|
if (state.online == true) {
|
||||||
if (accountInfo.value.eventFetcherOnline == true || accountInfo.value.isServerFetcherOnline == true) {
|
if (state.status == undefined || state.status == null) {
|
||||||
if (accountInfo.value.eventFetcherStatus) {
|
|
||||||
return 'warning'
|
return 'warning'
|
||||||
} else if (Object.keys(accountInfo.value.eventFetcherStatusV3 ?? {}).length > 0) {
|
} else if (Object.keys(state.status ?? {}).length > 0) {
|
||||||
return 'warning'
|
return 'warning'
|
||||||
} else {
|
} else {
|
||||||
return 'success'
|
return 'success'
|
||||||
@@ -23,7 +24,7 @@ const status = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NAlert :type="status">
|
<NAlert :type="status" v-if="status">
|
||||||
<template #header>
|
<template #header>
|
||||||
EVENT-FETCHER 状态
|
EVENT-FETCHER 状态
|
||||||
<NTooltip>
|
<NTooltip>
|
||||||
@@ -50,7 +51,7 @@ const status = computed(() => {
|
|||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NTag size="small" :color="{ borderColor: 'white', textColor: 'white', color: '#4b6159' }">
|
<NTag size="small" :color="{ borderColor: 'white', textColor: 'white', color: '#4b6159' }">
|
||||||
<NIcon :component="FlashCheckmark16Filled" />
|
<NIcon :component="FlashCheckmark16Filled" />
|
||||||
{{ accountInfo?.eventFetcherVersion ?? '未知' }}
|
{{ state.type == EventFetcherType.OBS ? 'OBS/网页端' : state.version ?? '未知' }}
|
||||||
</NTag>
|
</NTag>
|
||||||
</template>
|
</template>
|
||||||
你所使用的版本
|
你所使用的版本
|
||||||
@@ -58,7 +59,7 @@ const status = computed(() => {
|
|||||||
<NDivider vertical />
|
<NDivider vertical />
|
||||||
</template>
|
</template>
|
||||||
<NTag :type="status">
|
<NTag :type="status">
|
||||||
<template v-if="accountInfo?.eventFetcherOnline == true && accountInfo?.eventFetcherStatus">
|
<template v-if="state?.online == true && (state?.status == null || state?.status == undefined)">
|
||||||
此版本已过期, 请更新
|
此版本已过期, 请更新
|
||||||
<NTooltip trigger="click">
|
<NTooltip trigger="click">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
@@ -75,19 +76,17 @@ const status = computed(() => {
|
|||||||
</NText>
|
</NText>
|
||||||
| 今日已接收
|
| 今日已接收
|
||||||
<NText color="white" strong>
|
<NText color="white" strong>
|
||||||
{{ accountInfo?.eventFetcherTodayReceive }}
|
{{ state.todayReceive }}
|
||||||
</NText>
|
</NText>
|
||||||
条
|
条
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="status == 'warning'">
|
<template v-else-if="status == 'warning'">
|
||||||
<template v-if="accountInfo?.eventFetcherStatusV3">
|
<template v-if="state.status"> 异常: {{ Object.values(state.status).join('; ') }} </template>
|
||||||
异常: {{ Object.values(accountInfo.eventFetcherStatusV3).join('; ') }}
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="status == 'info'"> 未连接 </template>
|
<template v-else-if="status == 'info'"> 未连接 </template>
|
||||||
</template>
|
</template>
|
||||||
</NTag>
|
</NTag>
|
||||||
<template v-if="accountInfo?.eventFetcherOnline != true">
|
<template v-if="!state.online">
|
||||||
<NDivider vertical />
|
<NDivider vertical />
|
||||||
<NButton
|
<NButton
|
||||||
type="info"
|
type="info"
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ interface DanmakuEventsMap {
|
|||||||
gift: (arg1: GiftInfo, arg2?: any) => void
|
gift: (arg1: GiftInfo, arg2?: any) => void
|
||||||
sc: (arg1: SCInfo, arg2?: any) => void
|
sc: (arg1: SCInfo, arg2?: any) => void
|
||||||
guard: (arg1: GuardInfo, arg2?: any) => void
|
guard: (arg1: GuardInfo, arg2?: any) => void
|
||||||
|
all: (arg1: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DanmakuClient {
|
export default class DanmakuClient {
|
||||||
@@ -167,54 +168,83 @@ export default class DanmakuClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private client: any
|
private client: any
|
||||||
private authInfo: AuthInfo | null
|
|
||||||
private timer: any | undefined
|
private timer: any | undefined
|
||||||
|
private isStarting = false
|
||||||
|
|
||||||
|
public authInfo: AuthInfo | null
|
||||||
public roomAuthInfo = ref<RoomAuthInfo>({} as RoomAuthInfo)
|
public roomAuthInfo = ref<RoomAuthInfo>({} as RoomAuthInfo)
|
||||||
public authCode: string | undefined
|
public authCode: string | undefined
|
||||||
|
|
||||||
|
public isRunning: boolean = false
|
||||||
|
|
||||||
private events: {
|
private events: {
|
||||||
danmaku: ((arg1: DanmakuInfo, arg2?: any) => void)[]
|
danmaku: ((arg1: DanmakuInfo, arg2?: any) => void)[]
|
||||||
gift: ((arg1: GiftInfo, arg2?: any) => void)[]
|
gift: ((arg1: GiftInfo, arg2?: any) => void)[]
|
||||||
sc: ((arg1: SCInfo, arg2?: any) => void)[]
|
sc: ((arg1: SCInfo, arg2?: any) => void)[]
|
||||||
guard: ((arg1: GuardInfo, arg2?: any) => void)[]
|
guard: ((arg1: GuardInfo, arg2?: any) => void)[]
|
||||||
|
all: ((arg1: any) => void)[]
|
||||||
} = {
|
} = {
|
||||||
danmaku: [],
|
danmaku: [],
|
||||||
gift: [],
|
gift: [],
|
||||||
sc: [],
|
sc: [],
|
||||||
guard: [],
|
guard: [],
|
||||||
|
all: [],
|
||||||
}
|
}
|
||||||
private eventsAsModel: {
|
private eventsAsModel: {
|
||||||
danmaku: ((arg1: EventModel, arg2?: any) => void)[]
|
danmaku: ((arg1: EventModel, arg2?: any) => void)[]
|
||||||
gift: ((arg1: EventModel, arg2?: any) => void)[]
|
gift: ((arg1: EventModel, arg2?: any) => void)[]
|
||||||
sc: ((arg1: EventModel, arg2?: any) => void)[]
|
sc: ((arg1: EventModel, arg2?: any) => void)[]
|
||||||
guard: ((arg1: EventModel, arg2?: any) => void)[]
|
guard: ((arg1: EventModel, arg2?: any) => void)[]
|
||||||
|
all: ((arg1: any) => void)[]
|
||||||
} = {
|
} = {
|
||||||
danmaku: [],
|
danmaku: [],
|
||||||
gift: [],
|
gift: [],
|
||||||
sc: [],
|
sc: [],
|
||||||
guard: [],
|
guard: [],
|
||||||
|
all: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Start(): Promise<{ success: boolean; message: string }> {
|
public async Start(): Promise<{ success: boolean; message: string }> {
|
||||||
if (!this.client) {
|
if (this.isRunning) {
|
||||||
console.log('[OPEN-LIVE] 正在启动弹幕客户端')
|
|
||||||
const result = await this.initClient()
|
|
||||||
if (result.success) {
|
|
||||||
this.timer = setInterval(() => {
|
|
||||||
this.sendHeartbeat()
|
|
||||||
}, 20 * 1000)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
} else {
|
|
||||||
console.warn('[OPEN-LIVE] 弹幕客户端已被启动过')
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: '弹幕客户端已被启动过',
|
message: '弹幕客户端已启动',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.isStarting) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '弹幕客户端正在启动',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isStarting = true
|
||||||
|
try {
|
||||||
|
if (!this.client) {
|
||||||
|
console.log('[OPEN-LIVE] 正在启动弹幕客户端')
|
||||||
|
const result = await this.initClient()
|
||||||
|
if (result.success) {
|
||||||
|
this.isRunning = true
|
||||||
|
this.timer ??= setInterval(() => {
|
||||||
|
this.sendHeartbeat()
|
||||||
|
}, 20 * 1000)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
console.warn('[OPEN-LIVE] 弹幕客户端已被启动过')
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '弹幕客户端已被启动过',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.isStarting = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public Stop() {
|
public Stop() {
|
||||||
|
if (!this.isRunning) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.isRunning = false
|
||||||
if (this.client) {
|
if (this.client) {
|
||||||
console.log('[OPEN-LIVE] 正在停止弹幕客户端')
|
console.log('[OPEN-LIVE] 正在停止弹幕客户端')
|
||||||
this.client.stop()
|
this.client.stop()
|
||||||
@@ -223,35 +253,51 @@ export default class DanmakuClient {
|
|||||||
}
|
}
|
||||||
if (this.timer) {
|
if (this.timer) {
|
||||||
clearInterval(this.timer)
|
clearInterval(this.timer)
|
||||||
|
this.timer = undefined
|
||||||
}
|
}
|
||||||
this.events = {
|
this.events = {
|
||||||
danmaku: [],
|
danmaku: [],
|
||||||
gift: [],
|
gift: [],
|
||||||
sc: [],
|
sc: [],
|
||||||
guard: [],
|
guard: [],
|
||||||
|
all: [],
|
||||||
}
|
}
|
||||||
this.eventsAsModel = {
|
this.eventsAsModel = {
|
||||||
danmaku: [],
|
danmaku: [],
|
||||||
gift: [],
|
gift: [],
|
||||||
sc: [],
|
sc: [],
|
||||||
guard: [],
|
guard: [],
|
||||||
|
all: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private sendHeartbeat() {
|
private sendHeartbeat() {
|
||||||
if (this.client) {
|
if (!this.isRunning) {
|
||||||
const query = this.authInfo
|
clearInterval(this.timer)
|
||||||
? QueryPostAPI<OpenLiveInfo>(OPEN_LIVE_API_URL + 'heartbeat', this.authInfo)
|
this.timer = undefined
|
||||||
: QueryGetAPI<OpenLiveInfo>(OPEN_LIVE_API_URL + 'heartbeat-internal')
|
return
|
||||||
query.then((data) => {
|
|
||||||
if (data.code != 200) {
|
|
||||||
console.error('[OPEN-LIVE] 心跳失败')
|
|
||||||
this.client.stop()
|
|
||||||
this.client = null
|
|
||||||
this.initClient()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
const query = this.authInfo
|
||||||
|
? QueryPostAPI<OpenLiveInfo>(OPEN_LIVE_API_URL + 'heartbeat', this.authInfo)
|
||||||
|
: QueryGetAPI<OpenLiveInfo>(OPEN_LIVE_API_URL + 'heartbeat-internal')
|
||||||
|
query.then((data) => {
|
||||||
|
if (data.code != 200) {
|
||||||
|
console.error('[OPEN-LIVE] 心跳失败, 将重新连接')
|
||||||
|
this.client?.stop()
|
||||||
|
this.client = null
|
||||||
|
this.initClient()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onRawMessage = (command: any) => {
|
||||||
|
this.eventsAsModel.all?.forEach((d) => {
|
||||||
|
d(command)
|
||||||
|
})
|
||||||
|
this.events.all?.forEach((d) => {
|
||||||
|
d(command)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private onDanmaku = (command: any) => {
|
private onDanmaku = (command: any) => {
|
||||||
const data = command.data as DanmakuInfo
|
const data = command.data as DanmakuInfo
|
||||||
|
|
||||||
@@ -378,7 +424,8 @@ export default class DanmakuClient {
|
|||||||
public onEvent(eventName: 'gift', listener: (arg1: EventModel, arg2?: any) => void): this
|
public onEvent(eventName: 'gift', listener: (arg1: EventModel, arg2?: any) => void): this
|
||||||
public onEvent(eventName: 'sc', listener: (arg1: EventModel, arg2?: any) => void): this
|
public onEvent(eventName: 'sc', listener: (arg1: EventModel, arg2?: any) => void): this
|
||||||
public onEvent(eventName: 'guard', listener: (arg1: EventModel, arg2?: any) => void): this
|
public onEvent(eventName: 'guard', listener: (arg1: EventModel, arg2?: any) => void): this
|
||||||
public onEvent(eventName: 'danmaku' | 'gift' | 'sc' | 'guard', listener: (...args: any[]) => void): this {
|
public onEvent(eventName: 'all', listener: (arg1: any) => void): this
|
||||||
|
public onEvent(eventName: 'danmaku' | 'gift' | 'sc' | 'guard' | 'all', listener: (...args: any[]) => void): this {
|
||||||
if (!this.eventsAsModel[eventName]) {
|
if (!this.eventsAsModel[eventName]) {
|
||||||
this.eventsAsModel[eventName] = []
|
this.eventsAsModel[eventName] = []
|
||||||
}
|
}
|
||||||
@@ -418,7 +465,7 @@ export default class DanmakuClient {
|
|||||||
message: '',
|
message: '',
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[OPEN-LIVE] 无法开启场次')
|
console.log('[OPEN-LIVE] 无法开启场次: ' + auth.message)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: auth.message,
|
message: auth.message,
|
||||||
@@ -456,5 +503,6 @@ export default class DanmakuClient {
|
|||||||
LIVE_OPEN_PLATFORM_SEND_GIFT: this.onGift.bind(this),
|
LIVE_OPEN_PLATFORM_SEND_GIFT: this.onGift.bind(this),
|
||||||
LIVE_OPEN_PLATFORM_SUPER_CHAT: this.onSC.bind(this),
|
LIVE_OPEN_PLATFORM_SUPER_CHAT: this.onSC.bind(this),
|
||||||
LIVE_OPEN_PLATFORM_GUARD: this.onGuard.bind(this),
|
LIVE_OPEN_PLATFORM_GUARD: this.onGuard.bind(this),
|
||||||
|
RAW_MESSAGE: this.onRawMessage.bind(this),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ export default class ChatClientDirectOpenLive extends ChatClientOfficialBase {
|
|||||||
}
|
}
|
||||||
this.onDelSuperChat({ ids })
|
this.onDelSuperChat({ ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rawMessageCallback(command) {
|
||||||
|
if (!this.onRawMessage) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.onRawMessage(command)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CMD_CALLBACK_MAP = {
|
const CMD_CALLBACK_MAP = {
|
||||||
@@ -142,4 +149,5 @@ const CMD_CALLBACK_MAP = {
|
|||||||
LIVE_OPEN_PLATFORM_GUARD: ChatClientDirectOpenLive.prototype.guardCallback,
|
LIVE_OPEN_PLATFORM_GUARD: ChatClientDirectOpenLive.prototype.guardCallback,
|
||||||
LIVE_OPEN_PLATFORM_SUPER_CHAT: ChatClientDirectOpenLive.prototype.superChatCallback,
|
LIVE_OPEN_PLATFORM_SUPER_CHAT: ChatClientDirectOpenLive.prototype.superChatCallback,
|
||||||
LIVE_OPEN_PLATFORM_SUPER_CHAT_DEL: ChatClientDirectOpenLive.prototype.superChatDelCallback,
|
LIVE_OPEN_PLATFORM_SUPER_CHAT_DEL: ChatClientDirectOpenLive.prototype.superChatDelCallback,
|
||||||
|
RAW_MESSAGE: ChatClientDirectOpenLive.prototype.rawMessageCallback,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
// @ts-nocheck
|
||||||
import { BrotliDecode } from './brotli_decode'
|
import { BrotliDecode } from './brotli_decode'
|
||||||
import { setInterval, clearInterval, setTimeout, clearTimeout } from 'worker-timers'
|
import { setInterval, clearInterval, setTimeout, clearTimeout } from 'worker-timers'
|
||||||
|
|
||||||
@@ -145,7 +147,7 @@ export default class ChatClientOfficialBase {
|
|||||||
this.sendAuth()
|
this.sendAuth()
|
||||||
this.heartbeatTimerId = setInterval(this.sendHeartbeat.bind(this), HEARTBEAT_INTERVAL)
|
this.heartbeatTimerId = setInterval(this.sendHeartbeat.bind(this), HEARTBEAT_INTERVAL)
|
||||||
this.refreshReceiveTimeoutTimer()
|
this.refreshReceiveTimeoutTimer()
|
||||||
console.log('ws 已连接')
|
//console.log('ws 已连接')
|
||||||
}
|
}
|
||||||
|
|
||||||
sendHeartbeat() {
|
sendHeartbeat() {
|
||||||
@@ -270,7 +272,10 @@ export default class ChatClientOfficialBase {
|
|||||||
// 没压缩过的直接反序列化
|
// 没压缩过的直接反序列化
|
||||||
if (body.length !== 0) {
|
if (body.length !== 0) {
|
||||||
try {
|
try {
|
||||||
body = JSON.parse(textDecoder.decode(body))
|
const text = textDecoder.decode(body)
|
||||||
|
this.onRawMessage(text)
|
||||||
|
this.CMD_CALLBACK_MAP['RAW_MESSAGE']?.call(this, text)
|
||||||
|
body = JSON.parse(text)
|
||||||
this.handlerCommand(body)
|
this.handlerCommand(body)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('body=', body)
|
console.error('body=', body)
|
||||||
@@ -299,6 +304,7 @@ export default class ChatClientOfficialBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onRawMessage(command) {}
|
||||||
|
|
||||||
handlerCommand(command) {
|
handlerCommand(command) {
|
||||||
let cmd = command.cmd || ''
|
let cmd = command.cmd || ''
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import DefaultIndexTemplateVue from '@/views/view/indexTemplate/DefaultIndexTemp
|
|||||||
import { defineAsyncComponent, ref } from 'vue'
|
import { defineAsyncComponent, ref } from 'vue'
|
||||||
|
|
||||||
const debugAPI = import.meta.env.VITE_DEBUG_API
|
const debugAPI = import.meta.env.VITE_DEBUG_API
|
||||||
const releseAPI = `https://vtsuru.suki.club/api/`
|
const releseAPI = `https://vtsuru.suki.club/`
|
||||||
const failoverAPI = `https://failover-api.vtsuru.suki.club/api/`
|
const failoverAPI = `https://failover-api.vtsuru.suki.club/`
|
||||||
|
|
||||||
export const isBackendUsable = ref(true)
|
export const isBackendUsable = ref(true)
|
||||||
|
|
||||||
@@ -13,32 +13,38 @@ export const IMGUR_URL = FILE_BASE_URL + '/imgur/'
|
|||||||
export const THINGS_URL = FILE_BASE_URL + '/things/'
|
export const THINGS_URL = FILE_BASE_URL + '/things/'
|
||||||
export const apiFail = ref(false)
|
export const apiFail = ref(false)
|
||||||
|
|
||||||
export const BASE_API = () =>
|
export const BASE_API = {
|
||||||
process.env.NODE_ENV === 'development' ? debugAPI : apiFail.value ? failoverAPI : releseAPI
|
toString: () =>
|
||||||
|
(process.env.NODE_ENV === 'development' ? debugAPI : apiFail.value ? failoverAPI : releseAPI) + 'api/',
|
||||||
|
}
|
||||||
export const FETCH_API = 'https://fetch.vtsuru.live/'
|
export const FETCH_API = 'https://fetch.vtsuru.live/'
|
||||||
|
export const BASE_HUB_URL = {
|
||||||
|
toString: () =>
|
||||||
|
(process.env.NODE_ENV === 'development' ? debugAPI : apiFail.value ? failoverAPI : releseAPI) + 'hub/',
|
||||||
|
}
|
||||||
|
|
||||||
export const TURNSTILE_KEY = '0x4AAAAAAAETUSAKbds019h0'
|
export const TURNSTILE_KEY = '0x4AAAAAAAETUSAKbds019h0'
|
||||||
|
|
||||||
export const USER_API_URL = { toString: () => `${BASE_API()}user/` }
|
export const USER_API_URL = { toString: () => `${BASE_API}user/` }
|
||||||
export const ACCOUNT_API_URL = { toString: () => `${BASE_API()}account/` }
|
export const ACCOUNT_API_URL = { toString: () => `${BASE_API}account/` }
|
||||||
export const BILI_API_URL = { toString: () => `${BASE_API()}bili/` }
|
export const BILI_API_URL = { toString: () => `${BASE_API}bili/` }
|
||||||
export const SONG_API_URL = { toString: () => `${BASE_API()}song-list/` }
|
export const SONG_API_URL = { toString: () => `${BASE_API}song-list/` }
|
||||||
export const NOTIFACTION_API_URL = { toString: () => `${BASE_API()}notifaction/` }
|
export const NOTIFACTION_API_URL = { toString: () => `${BASE_API}notifaction/` }
|
||||||
export const QUESTION_API_URL = { toString: () => `${BASE_API()}qa/` }
|
export const QUESTION_API_URL = { toString: () => `${BASE_API}qa/` }
|
||||||
export const LOTTERY_API_URL = { toString: () => `${BASE_API()}lottery/` }
|
export const LOTTERY_API_URL = { toString: () => `${BASE_API}lottery/` }
|
||||||
export const HISTORY_API_URL = { toString: () => `${BASE_API()}history/` }
|
export const HISTORY_API_URL = { toString: () => `${BASE_API}history/` }
|
||||||
export const SCHEDULE_API_URL = { toString: () => `${BASE_API()}schedule/` }
|
export const SCHEDULE_API_URL = { toString: () => `${BASE_API}schedule/` }
|
||||||
export const VIDEO_COLLECT_API_URL = { toString: () => `${BASE_API()}video-collect/` }
|
export const VIDEO_COLLECT_API_URL = { toString: () => `${BASE_API}video-collect/` }
|
||||||
export const OPEN_LIVE_API_URL = { toString: () => `${BASE_API()}open-live/` }
|
export const OPEN_LIVE_API_URL = { toString: () => `${BASE_API}open-live/` }
|
||||||
export const SONG_REQUEST_API_URL = { toString: () => `${BASE_API()}song-request/` }
|
export const SONG_REQUEST_API_URL = { toString: () => `${BASE_API}song-request/` }
|
||||||
export const QUEUE_API_URL = { toString: () => `${BASE_API()}queue/` }
|
export const QUEUE_API_URL = { toString: () => `${BASE_API}queue/` }
|
||||||
export const EVENT_API_URL = { toString: () => `${BASE_API()}event/` }
|
export const EVENT_API_URL = { toString: () => `${BASE_API}event/` }
|
||||||
export const LIVE_API_URL = { toString: () => `${BASE_API()}live/` }
|
export const LIVE_API_URL = { toString: () => `${BASE_API}live/` }
|
||||||
export const FEEDBACK_API_URL = { toString: () => `${BASE_API()}feedback/` }
|
export const FEEDBACK_API_URL = { toString: () => `${BASE_API}feedback/` }
|
||||||
export const MUSIC_REQUEST_API_URL = { toString: () => `${BASE_API()}music-request/` }
|
export const MUSIC_REQUEST_API_URL = { toString: () => `${BASE_API}music-request/` }
|
||||||
export const VTSURU_API_URL = { toString: () => `${BASE_API()}vtsuru/` }
|
export const VTSURU_API_URL = { toString: () => `${BASE_API}vtsuru/` }
|
||||||
export const POINT_API_URL = { toString: () => `${BASE_API()}point/` }
|
export const POINT_API_URL = { toString: () => `${BASE_API}point/` }
|
||||||
export const BILI_AUTH_API_URL = { toString: () => `${BASE_API()}bili-auth/` }
|
export const BILI_AUTH_API_URL = { toString: () => `${BASE_API}bili-auth/` }
|
||||||
|
|
||||||
export const ScheduleTemplateMap = {
|
export const ScheduleTemplateMap = {
|
||||||
'': {
|
'': {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ let currentVersion: string
|
|||||||
let isHaveNewVersion = false
|
let isHaveNewVersion = false
|
||||||
|
|
||||||
const { notification } = createDiscreteApi(['notification'])
|
const { notification } = createDiscreteApi(['notification'])
|
||||||
QueryGetAPI<string>(BASE_API() + 'vtsuru/version')
|
QueryGetAPI<string>(BASE_API + 'vtsuru/version')
|
||||||
.then((version) => {
|
.then((version) => {
|
||||||
if (version.code == 200) {
|
if (version.code == 200) {
|
||||||
currentVersion = version.data
|
currentVersion = version.data
|
||||||
@@ -42,7 +42,7 @@ QueryGetAPI<string>(BASE_API() + 'vtsuru/version')
|
|||||||
if (isHaveNewVersion) {
|
if (isHaveNewVersion) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
QueryGetAPI<string>(BASE_API() + 'vtsuru/version').then((keepCheckData) => {
|
QueryGetAPI<string>(BASE_API + 'vtsuru/version').then((keepCheckData) => {
|
||||||
if (keepCheckData.code == 200 && keepCheckData.data != currentVersion) {
|
if (keepCheckData.code == 200 && keepCheckData.data != currentVersion) {
|
||||||
isHaveNewVersion = true
|
isHaveNewVersion = true
|
||||||
currentVersion = version.data
|
currentVersion = version.data
|
||||||
|
|||||||
@@ -42,5 +42,13 @@ export default {
|
|||||||
title: '棉花糖展示',
|
title: '棉花糖展示',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'web-fetcher',
|
||||||
|
name: 'obs-web-fetcher',
|
||||||
|
component: () => import('@/views/obs/WebFetcherOBS.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '弹幕收集器 (OBS版',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
150
src/store/useWebFetcher.ts
Normal file
150
src/store/useWebFetcher.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import DanmakuClient from '@/data/DanmakuClient'
|
||||||
|
import { BASE_HUB_URL } from '@/data/constants'
|
||||||
|
import * as signalR from '@microsoft/signalr'
|
||||||
|
import * as msgpack from '@microsoft/signalr-protocol-msgpack'
|
||||||
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
export const useWebFetcher = defineStore('WebFetcher', () => {
|
||||||
|
const cookie = useLocalStorage('JWT_Token', '')
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const client = new DanmakuClient(null)
|
||||||
|
|
||||||
|
const events: string[] = []
|
||||||
|
const isStarted = ref(false)
|
||||||
|
let timer: any
|
||||||
|
let signalRClient: signalR.HubConnection | null = null
|
||||||
|
let disconnectedByServer = false
|
||||||
|
|
||||||
|
async function Start() {
|
||||||
|
if (isStarted.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
while (!(await connectSignalR())) {
|
||||||
|
console.log('[WEB-FETCHER] 连接失败, 5秒后重试')
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function Stop() {
|
||||||
|
if (!isStarted.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isStarted.value = false
|
||||||
|
client.Stop()
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = undefined
|
||||||
|
}
|
||||||
|
signalRClient?.stop()
|
||||||
|
signalRClient = null
|
||||||
|
}
|
||||||
|
async function connectDanmakuClient() {
|
||||||
|
console.log('[WEB-FETCHER] 正在连接弹幕客户端...')
|
||||||
|
const result = await client.Start()
|
||||||
|
if (result.success) {
|
||||||
|
console.log('[WEB-FETCHER] 加载完成, 开始监听弹幕')
|
||||||
|
client.onEvent('all', onGetDanmakus)
|
||||||
|
timer ??= setInterval(() => {
|
||||||
|
sendEvents()
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
console.log('[WEB-FETCHER] 弹幕客户端启动失败: ' + result.message)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
async function connectSignalR() {
|
||||||
|
console.log('[WEB-FETCHER] 正在连接到 vtsuru 服务器...')
|
||||||
|
const connection = new signalR.HubConnectionBuilder()
|
||||||
|
.withUrl(BASE_HUB_URL + 'web-fetcher?token=' + route.query.token, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${cookie.value}`,
|
||||||
|
},
|
||||||
|
skipNegotiation: true,
|
||||||
|
transport: signalR.HttpTransportType.WebSockets,
|
||||||
|
})
|
||||||
|
.withAutomaticReconnect([0, 2000, 10000, 30000])
|
||||||
|
.withHubProtocol(new msgpack.MessagePackHubProtocol())
|
||||||
|
.build()
|
||||||
|
connection.on('Disconnect', (reason: unknown) => {
|
||||||
|
console.log('[WEB-FETCHER] 被服务器断开连接: ' + reason)
|
||||||
|
disconnectedByServer = true
|
||||||
|
connection.stop()
|
||||||
|
signalRClient = null
|
||||||
|
})
|
||||||
|
connection.on('ConnectClient', async () => {
|
||||||
|
if (client.isRunning) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let result = await connectDanmakuClient()
|
||||||
|
while (!result.success) {
|
||||||
|
console.log('[WEB-FETCHER] 弹幕客户端启动失败, 5秒后重试')
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000))
|
||||||
|
result = await connectDanmakuClient()
|
||||||
|
}
|
||||||
|
isStarted.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
connection.onclose(reconnect)
|
||||||
|
try {
|
||||||
|
await connection.start()
|
||||||
|
console.log('[WEB-FETCHER] 已连接到 vtsuru 服务器')
|
||||||
|
signalRClient = connection
|
||||||
|
return true
|
||||||
|
} catch (e) {
|
||||||
|
console.log('[WEB-FETCHER] 无法连接到 vtsuru 服务器: ' + e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function reconnect() {
|
||||||
|
if (disconnectedByServer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await signalRClient?.start()
|
||||||
|
console.log('[WEB-FETCHER] 已重新连接')
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
setTimeout(reconnect, 5000) // 如果连接失败,则每5秒尝试一次重新启动连接
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onGetDanmakus(command: any) {
|
||||||
|
events.push(command)
|
||||||
|
}
|
||||||
|
async function sendEvents() {
|
||||||
|
if (signalRClient?.state !== 'Connected') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let tempEvents: string[] = []
|
||||||
|
let count = events.length
|
||||||
|
if (events.length > 20) {
|
||||||
|
tempEvents = events.slice(0, 20)
|
||||||
|
count = 20
|
||||||
|
} else {
|
||||||
|
tempEvents = events
|
||||||
|
count = events.length
|
||||||
|
}
|
||||||
|
if (tempEvents.length > 0) {
|
||||||
|
const result = await signalRClient?.invoke<{
|
||||||
|
Success: boolean
|
||||||
|
Message: string
|
||||||
|
}>('UploadEvents', tempEvents, false)
|
||||||
|
if (result?.Success) {
|
||||||
|
events.splice(0, count)
|
||||||
|
console.log(`[WEB-FETCHER] <${format(new Date(), 'HH:mm:ss')}> 上传了 ${count} 条弹幕`)
|
||||||
|
} else {
|
||||||
|
console.error('[WEB-FETCHER] 上传弹幕失败: ' + result?.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
Start,
|
||||||
|
Stop,
|
||||||
|
client,
|
||||||
|
isStarted,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -454,7 +454,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NLayout v-if="accountInfo" style="height: 100vh">
|
<NLayout v-if="accountInfo.id" style="height: 100vh">
|
||||||
<NLayoutHeader bordered style="height: 50px; padding: 10px 15px 5px 15px">
|
<NLayoutHeader bordered style="height: 50px; padding: 10px 15px 5px 15px">
|
||||||
<NPageHeader>
|
<NPageHeader>
|
||||||
<template #title>
|
<template #title>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const props = defineProps<{
|
|||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
let client = new DanmakuClient(null)
|
const client = new DanmakuClient(null)
|
||||||
const isClientLoading = ref(true)
|
const isClientLoading = ref(true)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -54,12 +54,15 @@ onMounted(() => {
|
|||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
//@ts-expect-error 这里获取不了
|
//@ts-expect-error 这里获取不了
|
||||||
window.obsstudio.onVisibilityChange = function (visibility: boolean) {
|
if (window.obsstudio) {
|
||||||
visiable.value = visibility
|
//@ts-expect-error 这里获取不了
|
||||||
}
|
window.obsstudio.onVisibilityChange = function (visibility: boolean) {
|
||||||
//@ts-expect-error 这里获取不了
|
visiable.value = visibility
|
||||||
window.obsstudio.onActiveChange = function (a: boolean) {
|
}
|
||||||
active.value = a
|
//@ts-expect-error 这里获取不了
|
||||||
|
window.obsstudio.onActiveChange = function (a: boolean) {
|
||||||
|
active.value = a
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|||||||
41
src/views/obs/WebFetcherOBS.vue
Normal file
41
src/views/obs/WebFetcherOBS.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useWebFetcher } from '@/store/useWebFetcher'
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const webFetcher = useWebFetcher()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
webFetcher.Start()
|
||||||
|
})
|
||||||
|
onUnmounted(() => {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="web-fetcher-status"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: webFetcher.isStarted ? '#6dc56d' : '#e34a4a',
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.web-fetcher-status {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
border: 3px solid #00000033;
|
||||||
|
animation: animated-border 3s infinite;
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
}
|
||||||
|
@keyframes animated-border {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0px #589580;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(255, 255, 255, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
0
src/views/others
Normal file
0
src/views/others
Normal file
161
yarn.lock
161
yarn.lock
@@ -754,6 +754,36 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@microsoft/signalr-protocol-msgpack@npm:^8.0.0":
|
||||||
|
version: 8.0.0
|
||||||
|
resolution: "@microsoft/signalr-protocol-msgpack@npm:8.0.0"
|
||||||
|
dependencies:
|
||||||
|
"@microsoft/signalr": "npm:>=8.0.0"
|
||||||
|
"@msgpack/msgpack": "npm:^2.7.0"
|
||||||
|
checksum: 9ee6846464dc2bcd6da8db691aad4a4e6c1d0b3baa1d291cf588e1cf3999b03d28e7f6e47b31730f4a4d5529bc67ca7b60f8acae1322fd9d05e48b30fcdd4537
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@microsoft/signalr@npm:>=8.0.0, @microsoft/signalr@npm:^8.0.0":
|
||||||
|
version: 8.0.0
|
||||||
|
resolution: "@microsoft/signalr@npm:8.0.0"
|
||||||
|
dependencies:
|
||||||
|
abort-controller: "npm:^3.0.0"
|
||||||
|
eventsource: "npm:^2.0.2"
|
||||||
|
fetch-cookie: "npm:^2.0.3"
|
||||||
|
node-fetch: "npm:^2.6.7"
|
||||||
|
ws: "npm:^7.4.5"
|
||||||
|
checksum: 215ac66c9262803ae41c8499739be6fa9a66eeb4f5cb9a9c4f3bd23683a6ad011bb61631fe8257dceda53cbfdb59068b7902d8a5cfc94e2a61d3500196cba172
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@msgpack/msgpack@npm:^2.7.0":
|
||||||
|
version: 2.8.0
|
||||||
|
resolution: "@msgpack/msgpack@npm:2.8.0"
|
||||||
|
checksum: 5964ed3daad9ccf314238da81c91152dc693bca167b2469445c1d3ce0495443612e543d052281061a91ec48ed44a6a49dd3a334b5d0dbe2dc2db6ea6143e5787
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@nodelib/fs.scandir@npm:2.1.5":
|
"@nodelib/fs.scandir@npm:2.1.5":
|
||||||
version: 2.1.5
|
version: 2.1.5
|
||||||
resolution: "@nodelib/fs.scandir@npm:2.1.5"
|
resolution: "@nodelib/fs.scandir@npm:2.1.5"
|
||||||
@@ -2826,6 +2856,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"eventsource@npm:^2.0.2":
|
||||||
|
version: 2.0.2
|
||||||
|
resolution: "eventsource@npm:2.0.2"
|
||||||
|
checksum: 0b8c70b35e45dd20f22ff64b001be9d530e33b92ca8bdbac9e004d0be00d957ab02ef33c917315f59bf2f20b178c56af85c52029bc8e6cc2d61c31d87d943573
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"evtd@npm:^0.2.2, evtd@npm:^0.2.4":
|
"evtd@npm:^0.2.2, evtd@npm:^0.2.4":
|
||||||
version: 0.2.4
|
version: 0.2.4
|
||||||
resolution: "evtd@npm:0.2.4"
|
resolution: "evtd@npm:0.2.4"
|
||||||
@@ -2920,6 +2957,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"fetch-cookie@npm:^2.0.3":
|
||||||
|
version: 2.2.0
|
||||||
|
resolution: "fetch-cookie@npm:2.2.0"
|
||||||
|
dependencies:
|
||||||
|
set-cookie-parser: "npm:^2.4.8"
|
||||||
|
tough-cookie: "npm:^4.0.0"
|
||||||
|
checksum: bb6bec943ae0fc0d442661838b8ecc43310d34b0a2509124f6f79dabae012dd23e54b7827d20236fb0f98fb07fe493460056d70634b59ba7421862a3dfa68dd9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"file-entry-cache@npm:^6.0.1":
|
"file-entry-cache@npm:^6.0.1":
|
||||||
version: 6.0.1
|
version: 6.0.1
|
||||||
resolution: "file-entry-cache@npm:6.0.1"
|
resolution: "file-entry-cache@npm:6.0.1"
|
||||||
@@ -4171,6 +4218,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"node-fetch@npm:^2.6.7":
|
||||||
|
version: 2.7.0
|
||||||
|
resolution: "node-fetch@npm:2.7.0"
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: "npm:^5.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
checksum: b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"node-gyp@npm:latest":
|
"node-gyp@npm:latest":
|
||||||
version: 10.0.1
|
version: 10.0.1
|
||||||
resolution: "node-gyp@npm:10.0.1"
|
resolution: "node-gyp@npm:10.0.1"
|
||||||
@@ -4510,6 +4571,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"psl@npm:^1.1.33":
|
||||||
|
version: 1.9.0
|
||||||
|
resolution: "psl@npm:1.9.0"
|
||||||
|
checksum: 6a3f805fdab9442f44de4ba23880c4eba26b20c8e8e0830eff1cb31007f6825dace61d17203c58bfe36946842140c97a1ba7f67bc63ca2d88a7ee052b65d97ab
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"punycode.js@npm:^2.3.1":
|
"punycode.js@npm:^2.3.1":
|
||||||
version: 2.3.1
|
version: 2.3.1
|
||||||
resolution: "punycode.js@npm:2.3.1"
|
resolution: "punycode.js@npm:2.3.1"
|
||||||
@@ -4517,7 +4585,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"punycode@npm:^2.1.0":
|
"punycode@npm:^2.1.0, punycode@npm:^2.1.1":
|
||||||
version: 2.3.1
|
version: 2.3.1
|
||||||
resolution: "punycode@npm:2.3.1"
|
resolution: "punycode@npm:2.3.1"
|
||||||
checksum: 14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
|
checksum: 14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
|
||||||
@@ -4533,6 +4601,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"querystringify@npm:^2.1.1":
|
||||||
|
version: 2.2.0
|
||||||
|
resolution: "querystringify@npm:2.2.0"
|
||||||
|
checksum: 3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"queue-microtask@npm:^1.2.2":
|
"queue-microtask@npm:^1.2.2":
|
||||||
version: 1.2.3
|
version: 1.2.3
|
||||||
resolution: "queue-microtask@npm:1.2.3"
|
resolution: "queue-microtask@npm:1.2.3"
|
||||||
@@ -4609,6 +4684,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"requires-port@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "requires-port@npm:1.0.0"
|
||||||
|
checksum: b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"resize-detector@npm:^0.3.0":
|
"resize-detector@npm:^0.3.0":
|
||||||
version: 0.3.0
|
version: 0.3.0
|
||||||
resolution: "resize-detector@npm:0.3.0"
|
resolution: "resize-detector@npm:0.3.0"
|
||||||
@@ -4817,6 +4899,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"set-cookie-parser@npm:^2.4.8":
|
||||||
|
version: 2.6.0
|
||||||
|
resolution: "set-cookie-parser@npm:2.6.0"
|
||||||
|
checksum: 739da029f0e56806a103fcd5501d9c475e19e77bd8274192d7ae5c374ae714a82bba9a7ac00b0330a18227c5644b08df9e442240527be578f5a6030f9bb2bb80
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"set-function-length@npm:^1.1.1":
|
"set-function-length@npm:^1.1.1":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "set-function-length@npm:1.1.1"
|
resolution: "set-function-length@npm:1.1.1"
|
||||||
@@ -5198,6 +5287,25 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tough-cookie@npm:^4.0.0":
|
||||||
|
version: 4.1.3
|
||||||
|
resolution: "tough-cookie@npm:4.1.3"
|
||||||
|
dependencies:
|
||||||
|
psl: "npm:^1.1.33"
|
||||||
|
punycode: "npm:^2.1.1"
|
||||||
|
universalify: "npm:^0.2.0"
|
||||||
|
url-parse: "npm:^1.5.3"
|
||||||
|
checksum: 4fc0433a0cba370d57c4b240f30440c848906dee3180bb6e85033143c2726d322e7e4614abb51d42d111ebec119c4876ed8d7247d4113563033eebbc1739c831
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"tr46@npm:~0.0.3":
|
||||||
|
version: 0.0.3
|
||||||
|
resolution: "tr46@npm:0.0.3"
|
||||||
|
checksum: 047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"treemate@npm:^0.3.11":
|
"treemate@npm:^0.3.11":
|
||||||
version: 0.3.11
|
version: 0.3.11
|
||||||
resolution: "treemate@npm:0.3.11"
|
resolution: "treemate@npm:0.3.11"
|
||||||
@@ -5367,6 +5475,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"universalify@npm:^0.2.0":
|
||||||
|
version: 0.2.0
|
||||||
|
resolution: "universalify@npm:0.2.0"
|
||||||
|
checksum: cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"unplugin-vue-markdown@npm:^0.26.0":
|
"unplugin-vue-markdown@npm:^0.26.0":
|
||||||
version: 0.26.0
|
version: 0.26.0
|
||||||
resolution: "unplugin-vue-markdown@npm:0.26.0"
|
resolution: "unplugin-vue-markdown@npm:0.26.0"
|
||||||
@@ -5419,6 +5534,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"url-parse@npm:^1.5.3":
|
||||||
|
version: 1.5.10
|
||||||
|
resolution: "url-parse@npm:1.5.10"
|
||||||
|
dependencies:
|
||||||
|
querystringify: "npm:^2.1.1"
|
||||||
|
requires-port: "npm:^1.0.0"
|
||||||
|
checksum: bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2":
|
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "util-deprecate@npm:1.0.2"
|
resolution: "util-deprecate@npm:1.0.2"
|
||||||
@@ -5522,6 +5647,8 @@ __metadata:
|
|||||||
resolution: "vtsuru.live@workspace:."
|
resolution: "vtsuru.live@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/eslintrc": "npm:^3.0.1"
|
"@eslint/eslintrc": "npm:^3.0.1"
|
||||||
|
"@microsoft/signalr": "npm:^8.0.0"
|
||||||
|
"@microsoft/signalr-protocol-msgpack": "npm:^8.0.0"
|
||||||
"@types/eslint": "npm:^8.56.2"
|
"@types/eslint": "npm:^8.56.2"
|
||||||
"@types/node": "npm:^20.11.19"
|
"@types/node": "npm:^20.11.19"
|
||||||
"@types/obs-studio": "npm:^2.17.2"
|
"@types/obs-studio": "npm:^2.17.2"
|
||||||
@@ -5765,6 +5892,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"webidl-conversions@npm:^3.0.0":
|
||||||
|
version: 3.0.1
|
||||||
|
resolution: "webidl-conversions@npm:3.0.1"
|
||||||
|
checksum: 5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"webpack-sources@npm:^3.2.3":
|
"webpack-sources@npm:^3.2.3":
|
||||||
version: 3.2.3
|
version: 3.2.3
|
||||||
resolution: "webpack-sources@npm:3.2.3"
|
resolution: "webpack-sources@npm:3.2.3"
|
||||||
@@ -5779,6 +5913,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"whatwg-url@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "whatwg-url@npm:5.0.0"
|
||||||
|
dependencies:
|
||||||
|
tr46: "npm:~0.0.3"
|
||||||
|
webidl-conversions: "npm:^3.0.0"
|
||||||
|
checksum: 1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"which-boxed-primitive@npm:^1.0.2":
|
"which-boxed-primitive@npm:^1.0.2":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "which-boxed-primitive@npm:1.0.2"
|
resolution: "which-boxed-primitive@npm:1.0.2"
|
||||||
@@ -5904,6 +6048,21 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ws@npm:^7.4.5":
|
||||||
|
version: 7.5.9
|
||||||
|
resolution: "ws@npm:7.5.9"
|
||||||
|
peerDependencies:
|
||||||
|
bufferutil: ^4.0.1
|
||||||
|
utf-8-validate: ^5.0.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bufferutil:
|
||||||
|
optional: true
|
||||||
|
utf-8-validate:
|
||||||
|
optional: true
|
||||||
|
checksum: aec4ef4eb65821a7dde7b44790f8699cfafb7978c9b080f6d7a98a7f8fc0ce674c027073a78574c94786ba7112cc90fa2cc94fc224ceba4d4b1030cff9662494
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"xlsx@npm:^0.18.5":
|
"xlsx@npm:^0.18.5":
|
||||||
version: 0.18.5
|
version: 0.18.5
|
||||||
resolution: "xlsx@npm:0.18.5"
|
resolution: "xlsx@npm:0.18.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user