diff --git a/src/core/dycast.ts b/src/core/dycast.ts index f84d957..aea9a3f 100644 --- a/src/core/dycast.ts +++ b/src/core/dycast.ts @@ -60,6 +60,7 @@ export interface LiveRoom { /** 直播间信息-连接信息 */ export interface DyLiveInfo { + roomNum?: string; roomId: string; uniqueId: string; avatar: string; @@ -1121,4 +1122,14 @@ export class DyCast { msg }; } + + /** + * 获取直播间信息 + */ + public getLiveInfo(): DyLiveInfo { + return { + ...this.info, + roomNum: this.roomNum + }; + } } diff --git a/src/core/relay.ts b/src/core/relay.ts new file mode 100644 index 0000000..2951bb5 --- /dev/null +++ b/src/core/relay.ts @@ -0,0 +1,105 @@ +import { Emitter, type EventMap } from './emitter'; + +interface RelayCastEvent extends EventMap { + open: (ev: Event) => void; + close: (code?: number, msg?: string) => void; + error: (ev: Error) => void; + message: (data: any) => void; +} + +/** + * 弹幕转发器 + * - 简单封装一下,有问题再优化 + */ +export class RelayCast { + private url: string; + + private ws: WebSocket | undefined; + + private emitter: Emitter; + + constructor(url: string) { + this.url = url; + this.emitter = new Emitter(); + } + + /** + * 监听 + * @param event + * @param listener + */ + public on(event: K, listener: RelayCastEvent[K]) { + this.emitter.on(event, listener); + } + + /** + * 取消监听 + * @param event + * @param listener + */ + public off(event: K, listener: RelayCastEvent[K]) { + this.emitter.off(event, listener); + } + + /** + * 一次性监听 + * - 如监听打开关闭 + * @param event + * @param listener + */ + public once(event: K, listener: RelayCastEvent[K]) { + this.emitter.once(event, listener); + } + + /** + * 连接 + */ + connect() { + try { + this.ws = new WebSocket(this.url); + this.ws.addEventListener('open', ev => { + this.emitter.emit('open', ev); + }); + this.ws.addEventListener('close', ev => { + this.emitter.emit('close', ev.code, ev.type); + }); + this.ws.addEventListener('error', ev => { + this.emitter.emit('error', Error(ev.type)); + }); + this.ws.addEventListener('message', ev => { + this.emitter.emit('message', ev.data); + }); + } catch (err) { + this.emitter.emit('error', Error('转发服务器连接出错')); + this.emitter.emit('close', 4002); + } + } + + /** + * 是否连接 + */ + isConnected() { + if (this.ws) return this.ws.readyState === WebSocket.OPEN; + else return false; + } + + /** + * 发送消息 + * @param data + */ + send(data: string | ArrayBuffer) { + if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(data); + } + + /** + * 关闭转发 + * @param code + * @param msg + */ + close(code: number = 1000, msg: string = 'close replay') { + if (this.ws) { + this.ws.close(code, msg); + this.ws = void 0; + } + } +} diff --git a/src/views/IndexView.vue b/src/views/IndexView.vue index b987e77..2947f0d 100644 --- a/src/views/IndexView.vue +++ b/src/views/IndexView.vue @@ -67,6 +67,7 @@ import { verifyRoomNum, verifyWsUrl } from '@/utils/verifyUtil'; import { ref, useTemplateRef } from 'vue'; import { CLog } from '@/utils/logUtil'; import { getId } from '@/utils/idUtil'; +import { RelayCast } from '@/core/relay'; // 连接状态 const connectStatus = ref(0); // 转发状态 @@ -101,6 +102,8 @@ const allCasts: DyMessage[] = []; const castSet = new Set(); // 弹幕客户端 let castWs: DyCast | undefined; +// 转发客户端 +let relayWs: RelayCast | undefined; /** * 验证房间号 @@ -215,6 +218,9 @@ const handleMessages = function (msgs: DyMessage[]) { allCasts.push(...newCasts); if (castRef.value) castRef.value.appendCasts(mainCasts); if (otherRef.value) otherRef.value.appendCasts(otherCasts); + if (relayWs && relayWs.isConnected()) { + relayWs.send(JSON.stringify(msgs)); + } }; /** @@ -251,6 +257,7 @@ const connectLive = function () { // 清空上一次连接的消息 clearMessageList(); CLog.debug('正在连接:', roomNum.value); + SkMessage.info(`正在连接:${roomNum.value}`); const cast = new DyCast(roomNum.value); cast.on('open', (ev, info) => { CLog.info('DyCast 房间连接成功'); @@ -323,9 +330,50 @@ const disconnectLive = function () { }; /** 连接转发房间 */ -const relayCast = function () {}; +const relayCast = function () { + try { + CLog.info('正在连接转发中 =>', relayUrl.value); + SkMessage.info(`转发连接中: ${relayUrl.value}`); + const cast = new RelayCast(relayUrl.value); + cast.on('open', () => { + CLog.info(`DyCast 转发连接成功`); + SkMessage.success(`已开始转发`); + relayInputStatus.value = true; + relayStatus.value = 1; + addConsoleMessage('转发客户端已连接'); + if (castWs) { + // 发送直播间信息给转发地址 + cast.send(JSON.stringify(castWs.getLiveInfo())); + } + }); + cast.on('close', (code, msg) => { + CLog.info(`(${code})dycast 转发已关闭: ${msg || '未知原因'}`); + if (code === 1000) SkMessage.info(`已停止转发`); + else SkMessage.warning(`转发已停止: ${msg || '未知原因'}`); + relayInputStatus.value = false; + relayStatus.value = 0; + addConsoleMessage('转发已关闭'); + }); + cast.on('error', ev => { + CLog.warn(`dycast 转发出错: ${ev.message}`); + SkMessage.error(`转发出错了: ${ev.message}`); + relayInputStatus.value = false; + relayStatus.value = 2; + }); + cast.connect(); + relayWs = cast; + } catch (err) { + CLog.error('弹幕转发出错:', err); + SkMessage.error('转发出错: ${err.message}'); + relayInputStatus.value = false; + relayStatus.value = 2; + relayWs = void 0; + } +}; /** 暂停转发 */ -const stopRelayCast = function () {}; +const stopRelayCast = function () { + if (relayWs) relayWs.close(1000); +};