diff options
Diffstat (limited to 'web/script/player/player.ts')
| -rw-r--r-- | web/script/player/player.ts | 98 | 
1 files changed, 98 insertions, 0 deletions
| diff --git a/web/script/player/player.ts b/web/script/player/player.ts new file mode 100644 index 0000000..5c38dc8 --- /dev/null +++ b/web/script/player/player.ts @@ -0,0 +1,98 @@ +import { OVar, e } from "../jshelper/mod.ts"; +import { JhlsMetadata, TimeRange } from "./jhls.d.ts"; +import { PlayerTrack } from "./track.ts"; + +export interface BufferRange extends TimeRange { status: "buffered" | "loading" | "queued" } +export class Player { +    public video = e("video") +    private media_source = new MediaSource(); +    public tracks = new OVar<PlayerTrack[]>([]); + +    public position = new OVar(0) +    public duration = new OVar(1) +    public playing = new OVar(false) +    public canplay = new OVar(false) +    public buffering_status = new OVar<string | undefined>(undefined) +    public error = new OVar<string | undefined>(undefined) + +    constructor(private node_id: string) { +        this.video.onloadedmetadata = () => { } +        this.video.ondurationchange = () => { } +        this.video.ontimeupdate = () => { +            this.position.value = this.video.currentTime +            this.update() // TODO maybe not here +        } +        this.video.onplay = () => { +            console.log("play"); +            this.buffering_status.value = "Resuming playback..."; +        } +        this.video.onwaiting = () => { +            console.log("waiting"); +            this.buffering_status.value = "Buffering..."; +            this.canplay.value = false; +        } +        this.video.onplaying = () => { +            console.log("playing"); +            this.playing.value = true; +            this.buffering_status.value = undefined; +        } +        this.video.onpause = () => { +            console.log("pause"); +            this.playing.value = false +        } +        this.video.oncanplay = () => { +            console.log("canplay"); +            this.buffering_status.value = undefined +            this.canplay.value = true +        } +        this.video.onseeking = () => { +            console.log("seeking"); +            this.buffering_status.value = "Seeking..." +        } +        this.video.onseeked = () => { +            console.log("seeked"); +            this.buffering_status.value = undefined +        } +        this.fetch_meta() +    } + +    async fetch_meta() { +        this.buffering_status.value = "Loading JHLS metadata..." +        const res = await fetch(`/n/${encodeURIComponent(this.node_id)}/stream?format=jhls`) +        if (!res.ok) return this.error.value = "Cannot download JHLS metadata" +        const metadata = await res.json() as JhlsMetadata +        this.buffering_status.value = undefined + +        this.duration.value = metadata.duration +        this.video.src = URL.createObjectURL(this.media_source) +        this.media_source.addEventListener("sourceopen", async () => { +            this.tracks.value.push(new PlayerTrack(this.media_source, this.node_id, 0, metadata.tracks[0])) +            this.tracks.value.push(new PlayerTrack(this.media_source, this.node_id, 1, metadata.tracks[1])) +            this.tracks.change() +            this.buffering_status.value = "Fetching initial segments..." +            this.update() +            await this.canplay.wait_for(true) +            this.buffering_status.value = undefined +        }) +    } +    async update(newt?: number) { +        await Promise.all(this.tracks.value.map(t => t.update(newt ?? this.video.currentTime))) +    } + +    play() { +        this.video.play() +    } +    pause() { +        this.video.pause() +    } +    frame_forward() { +        //@ts-ignore trust me bro +        this.video["seekToNextFrame"]() +    } +    async seek(p: number) { +        this.buffering_status.value = "Buffering at target..." +        await this.update(p) +        this.video.currentTime = p +    } +} + | 
