mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
- Updated SongList.vue to enable default sorting with a boolean flag. - Modified TempComponent.vue to include useNotification for better user feedback. - Refined useLiveRequest.ts to enforce type safety on song request settings. - Adjusted RTCClient.ts to improve event handling and connection logic. - Enhanced useVTsuruHub.ts to ensure SignalR connection is awaited during initialization. - Improved ManageLayout.vue by reorganizing menu options for better user experience. - Added folder scanning functionality in SongListManageView.vue for importing songs from local directories. - Implemented OBS notification system in various OBS-related components for real-time updates. - Removed outdated documentation files related to local question saving functionality. - Introduced a new store useOBSNotification.ts to manage OBS notifications effectively.
241 lines
6.6 KiB
TypeScript
241 lines
6.6 KiB
TypeScript
import type { DataConnection } from 'peerjs'
|
|
import Peer from 'peerjs'
|
|
import { useVTsuruHub } from '@/store/useVTsuruHub'
|
|
|
|
export interface ComponentsEventHubModel {
|
|
IsMaster: boolean
|
|
Token: string
|
|
}
|
|
export interface RTCData {
|
|
Key: string
|
|
Data: any
|
|
}
|
|
|
|
export abstract class BaseRTCClient {
|
|
constructor(user: string, pass: string) {
|
|
this.user = user
|
|
this.pass = pass
|
|
}
|
|
|
|
protected user: string
|
|
protected pass: string
|
|
protected vhub = useVTsuruHub()
|
|
|
|
public isInited = false
|
|
|
|
public peer?: Peer
|
|
|
|
protected connections: DataConnection[] = []
|
|
protected handledEvents: { [key: string]: string[] } = {}
|
|
|
|
protected events: {
|
|
[key: string]: ((args: any) => void)[]
|
|
} = {}
|
|
|
|
abstract type: 'master' | 'slave'
|
|
|
|
protected abstract connectRTC(): void
|
|
protected abstract processData(conn: DataConnection, data: RTCData): void
|
|
|
|
public on(eventName: string, listener: (args: any) => void) {
|
|
eventName = eventName.toLowerCase()
|
|
if (!this.events[eventName]) {
|
|
this.events[eventName] = []
|
|
}
|
|
this.events[eventName].push(listener)
|
|
|
|
this.send('VTsuru.RTCEvent.On', eventName)
|
|
}
|
|
|
|
public off(eventName: string, listener: (args: any) => void) {
|
|
if (this.events[eventName]) {
|
|
const index = this.events[eventName].indexOf(listener)
|
|
if (index > -1) {
|
|
this.events[eventName].splice(index, 1)
|
|
}
|
|
}
|
|
|
|
this.send('VTsuru.RTCEvent.Off', eventName)
|
|
}
|
|
|
|
public send(eventName: string, data: any) {
|
|
this.connections.forEach((item) => {
|
|
if (item && item.open) {
|
|
item.send({
|
|
Key: eventName,
|
|
Data: data,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
protected onConnectionClose(id: string) {
|
|
this.connections = this.connections.filter(item => item && item.peer != id)
|
|
delete this.handledEvents[id]
|
|
|
|
console.log(
|
|
`[Components-Event] <${this.connections.length}> ${this.type == 'master' ? 'Slave' : 'Master'} 下线: ${
|
|
id}`,
|
|
)
|
|
}
|
|
|
|
public async Init() {
|
|
if (!this.isInited) {
|
|
this.isInited = true
|
|
await this.vhub.on('RTCOffline', (...args: unknown[]) => {
|
|
const id = args[0] as string
|
|
this.onConnectionClose(id)
|
|
})
|
|
this.connectRTC()
|
|
}
|
|
return this
|
|
}
|
|
}
|
|
|
|
export class SlaveRTCClient extends BaseRTCClient {
|
|
constructor(user: string, pass: string) {
|
|
super(user, pass)
|
|
}
|
|
|
|
type: 'slave' = 'slave' as const
|
|
|
|
protected async getAllRTC(): Promise<ComponentsEventHubModel[]> {
|
|
return await this.vhub.invoke<ComponentsEventHubModel[]>('GetOnlineRTC') || []
|
|
}
|
|
|
|
public async connectToAllMaster() {
|
|
const masters = (await this.getAllRTC()).filter(
|
|
(item: ComponentsEventHubModel) =>
|
|
item.IsMaster
|
|
&& item.Token != this.peer.id
|
|
&& !this.connections.some(conn => conn.peer == item.Token),
|
|
)
|
|
masters.forEach((item: ComponentsEventHubModel) => {
|
|
this.connectToMaster(item.Token)
|
|
// console.log('[Components-Event] 正在连接到现有 Master: ' + item.Token)
|
|
})
|
|
}
|
|
|
|
public connectToMaster(id: string) {
|
|
if (this.connections.some(conn => conn.peer == id)) return
|
|
const c = this.peer?.connect(id)
|
|
c?.on('open', () => {
|
|
this.connections.push(c)
|
|
|
|
this.handledEvents[id] = []
|
|
|
|
console.log(
|
|
`[Components-Event] <${this.connections.length}> ==> Master 连接已建立: ${
|
|
id}`,
|
|
)
|
|
})
|
|
c?.on('error', err => console.error(err))
|
|
c?.on('data', data => this.processData(c, data as RTCData))
|
|
c?.on('close', () => this.onConnectionClose(c.peer))
|
|
}
|
|
|
|
protected connectRTC(): void {
|
|
this.peer = new Peer()
|
|
this.peer.on('open', (id) => {
|
|
console.log('[Components-Event] Slave Peer ID:', id)
|
|
this.vhub.send('RegisterRTC', false, id)
|
|
})
|
|
this.peer.on('error', err => console.error('[Components-Event] Slave Peer Error:', err))
|
|
}
|
|
|
|
protected processData(conn: DataConnection, data: RTCData): void {
|
|
if (data.Key === 'VTsuru.RTCEvent.On') {
|
|
if (!this.handledEvents[conn.peer]) {
|
|
this.handledEvents[conn.peer] = []
|
|
}
|
|
if (!this.handledEvents[conn.peer].includes(data.Data)) {
|
|
this.handledEvents[conn.peer].push(data.Data)
|
|
}
|
|
} else if (data.Key === 'VTsuru.RTCEvent.Off') {
|
|
if (this.handledEvents[conn.peer]) {
|
|
const index = this.handledEvents[conn.peer].indexOf(data.Data)
|
|
if (index > -1) {
|
|
this.handledEvents[conn.peer].splice(index, 1)
|
|
}
|
|
}
|
|
} else {
|
|
const eventName = data.Key.toLowerCase()
|
|
if (this.events[eventName]) {
|
|
this.events[eventName].forEach(listener => listener(data.Data))
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Init() {
|
|
await super.Init()
|
|
this.vhub?.on('MasterOnline', (...args: unknown[]) => {
|
|
const data = args[0] as string
|
|
this.connectToMaster(data)
|
|
})
|
|
setTimeout(() => {
|
|
this.connectToAllMaster()
|
|
}, 500)
|
|
setInterval(() => {
|
|
this.connectToAllMaster()
|
|
}, 30000)
|
|
|
|
return this
|
|
}
|
|
}
|
|
export class MasterRTCClient extends BaseRTCClient {
|
|
constructor(user: string, pass: string) {
|
|
super(user, pass)
|
|
}
|
|
|
|
type: 'master' = 'master' as const
|
|
|
|
protected connectRTC(): void {
|
|
this.peer?.on('connection', (conn) => {
|
|
conn.on('open', () => {
|
|
this.connections.push(conn)
|
|
this.handledEvents[conn.peer] = []
|
|
console.log(
|
|
`[Components-Event] <${this.connections.length}> Slave 上线: ${
|
|
conn.peer}`,
|
|
)
|
|
})
|
|
conn.on('data', d => this.processData(conn, d as RTCData))
|
|
conn.on('error', err => console.error(err))
|
|
conn.on('close', () => this.onConnectionClose(conn.peer))
|
|
})
|
|
|
|
this.peer = new Peer()
|
|
this.peer.on('open', (id) => {
|
|
console.log('[Components-Event] Master Peer ID:', id)
|
|
this.vhub.send('RegisterRTC', true, id)
|
|
})
|
|
this.peer.on('error', err => console.error('[Components-Event] Master Peer Error:', err))
|
|
}
|
|
|
|
protected processData(conn: DataConnection, data: RTCData): void {
|
|
if (data.Key === 'VTsuru.RTCEvent.On') {
|
|
if (!this.handledEvents[conn.peer]) {
|
|
this.handledEvents[conn.peer] = []
|
|
}
|
|
if (!this.handledEvents[conn.peer].includes(data.Data)) {
|
|
this.handledEvents[conn.peer].push(data.Data)
|
|
}
|
|
} else if (data.Key === 'VTsuru.RTCEvent.Off') {
|
|
if (this.handledEvents[conn.peer]) {
|
|
const index = this.handledEvents[conn.peer].indexOf(data.Data)
|
|
if (index > -1) {
|
|
this.handledEvents[conn.peer].splice(index, 1)
|
|
}
|
|
}
|
|
} else {
|
|
if (this.handledEvents[conn.peer]?.includes(data.Key.toLowerCase())) {
|
|
conn.send(data)
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Init() {
|
|
return super.Init()
|
|
}
|
|
}
|