import { sleep, uint_bytes_to_num, utf8_decode, utf8_encode } from "./tools"; import { WSClient } from "./ws"; export enum PythagorasIncomingMessageType { // subtitles SubEnUpdateCur = "subtitle_en_update_current", SubEnSubmit = "subtitle_en_submit_sentence", SubCzSubmit = "subtitle_cs_submit_sentence", // mode management SetScreen = "setscreen", // video PlayVideo = "playvideo", SeekVideo = "seekvideo", // message SelectedMessage = "selectedmessage", }; export type PythagorasIncomingMessage = ( { type: ( PythagorasIncomingMessageType.SubEnUpdateCur | PythagorasIncomingMessageType.SubEnSubmit | PythagorasIncomingMessageType.SubCzSubmit ), text: string, } | { type: PythagorasIncomingMessageType.SelectedMessage, message: string | null, } | { type: PythagorasIncomingMessageType.SetScreen, screen: "main" | "video" | "idle", } | { type: PythagorasIncomingMessageType.PlayVideo, filename: string, subtitles: string, seconds_from_start: number, } | { type: PythagorasIncomingMessageType.SeekVideo, timestamp: number, } ); export class PythagorasClient { private sock: WSClient | null; private addr: string; private buf: Uint8Array; public constructor(addr: string) { this.sock = null; this.addr = addr; this.buf = new Uint8Array(0); } private async reconnect(): Promise { this.sock = new WSClient(this.addr); await this.sock.wait_for_connection(); } /** * Force receive from the underlying `WSClient`. * This will try to receive (or reconnect) up to `PythagorasClient.max_recv_retry` times. */ private async recv_inner(): Promise { if (this.sock === null) { await this.reconnect(); } while (true) { const received = await this.sock!.receive(); if (received !== null) { return received; } await sleep(500); await this.reconnect(); } } /** * Force receive from the underlying `WSClient` and convert the result to bytes (if the result * is not bytes already). */ private async recv_inner_bytes(): Promise { const received = await this.recv_inner(); if (received.bytes !== undefined) { return await received.bytes(); } const text = await received.text(); return utf8_encode(text); } /** * Receive `target` bytes from the underlying stream (or leftovers from previous reads). */ private async recv_length(target: number): Promise { if (target == 0) { return new Uint8Array(0); } while (this.buf.length < target) { const received = await this.recv_inner_bytes(); const merged = new Uint8Array(this.buf.length + received.length); merged.set(this.buf); merged.set(received, this.buf.length); this.buf = merged; } const total = this.buf; this.buf = new Uint8Array(total.length - target); this.buf.set(total.slice(target)); return total.slice(0, target); } public async recv(): Promise { while (true) { const payload = await this.recv_inner(); const text = payload.bytes === undefined ? (await payload.text()).slice(4) : (utf8_decode((await payload.bytes()).slice(4))); const parsed = JSON.parse(text); return parsed; } } }