Files
pythagoras/client/pythagoras_client.ts

127 lines
3.1 KiB
TypeScript

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<void> {
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<Blob> {
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<Uint8Array> {
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<Uint8Array> {
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<PythagorasIncomingMessage> {
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;
}
}
}