128 lines
3.1 KiB
TypeScript
128 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)));
|
|
console.log("payload:", text);
|
|
const parsed = JSON.parse(text);
|
|
return parsed;
|
|
}
|
|
}
|
|
}
|