blob: 08975b2ad8945c9a429cc49a3754dcc30b620b07 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import { OVar, e } from "../jshelper/mod.ts";
import { JhlsMetadata, TimeRange } from "./jhls.d.ts";
import { SegmentDownloader } from "./download.ts";
import { PlayerTrack } from "./track.ts";
export interface BufferRange extends TimeRange { status: "buffered" | "loading" | "queued" }
export class Player {
public video = e("video")
public media_source = new MediaSource();
public tracks = new OVar<PlayerTrack[]>([]);
public downloader: SegmentDownloader = new SegmentDownloader();
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, this.node_id, 0, metadata.tracks[0]))
this.tracks.value.push(new PlayerTrack(this, 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
}
}
|