diff options
Diffstat (limited to 'web/script/player/player.ts')
| -rw-r--r-- | web/script/player/player.ts | 173 |
1 files changed, 0 insertions, 173 deletions
diff --git a/web/script/player/player.ts b/web/script/player/player.ts deleted file mode 100644 index 4f59f8a..0000000 --- a/web/script/player/player.ts +++ /dev/null @@ -1,173 +0,0 @@ -/* - This file is part of jellything (https://codeberg.org/metamuffin/jellything) - which is licensed under the GNU Affero General Public License (version 3); see /COPYING. - Copyright (C) 2026 metamuffin <metamuffin.org> -*/ -/// <reference lib="dom" /> -import { OVar, e } from "../jshelper/mod.ts"; -import { SegmentDownloader } from "./download.ts"; -import { PlayerTrack } from "./track/mod.ts"; -import { Logger } from "../jshelper/src/log.ts"; -import { create_track } from "./track/create.ts"; -import { StreamInfo, TimeRange, TrackInfo } from "./types_stream.ts"; - -export interface BufferRange extends TimeRange { status: "buffered" | "loading" | "queued" } -export class Player { - public video = e("video") - public media_source = new MediaSource(); - public streaminfo?: StreamInfo; - public tracks?: TrackInfo[]; - public active_tracks = new OVar<PlayerTrack[]>([]); - public downloader: SegmentDownloader = new SegmentDownloader(); - - public position = new OVar(0) - public duration = new OVar(1) - public volume = new OVar(0) - public playing = new OVar(false) - public canplay = new OVar(false) - public error = new OVar<string | undefined>(undefined) - - private cancel_buffering_pers: undefined | (() => void) - set_pers(s?: string) { - if (this.cancel_buffering_pers) this.cancel_buffering_pers(), this.cancel_buffering_pers = undefined - if (s) this.cancel_buffering_pers = this.logger?.log_persistent(s) - } - - constructor(public base_url: string, poster: string, private start_time: number, public logger?: Logger<string>) { - this.video.poster = poster - this.volume.value = this.video.volume - let skip_change = false; - this.volume.onchange(v => { - if (v > 1.) return this.volume.value = 1; - if (v < 0.) return this.volume.value = 0; - if (!skip_change) this.video.volume = v - skip_change = false - }) - this.video.onvolumechange = () => { - skip_change = true; - this.volume.value = this.video.volume - } - - 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.set_pers("Resuming playback...") - } - this.video.onwaiting = () => { - console.log("waiting"); - if (this.video.currentTime > this.duration.value - 0.2) return this.set_pers("Playback finished") - this.set_pers("Buffering...") - this.canplay.value = false; - } - this.video.onplaying = () => { - console.log("playing"); - this.playing.value = true; - this.set_pers() - } - this.video.onpause = () => { - console.log("pause"); - this.playing.value = false - } - this.video.oncanplay = () => { - console.log("canplay"); - this.set_pers() - this.canplay.value = true - } - this.video.onseeking = () => { - console.log("seeking"); - this.set_pers("Seeking...") - } - this.video.onseeked = () => { - console.log("seeked"); - this.set_pers() - } - this.video.onerror = e => { - console.error("video element error:", e); - this.set_pers("MSE crash -_-"); - } - this.video.onabort = e => { - console.error("video element abort:", e); - this.set_pers("Aborted"); - } - this.fetch_meta() - } - - async fetch_meta() { - this.set_pers("Loading stream metadata...") - const res = await fetch(`${this.base_url}?info`, { headers: { "Accept": "application/json" } }) - if (!res.ok) return this.error.value = "Cannot download stream info." - - let streaminfo!: StreamInfo & { error: string } - try { streaminfo = await res.json() } - catch (_) { this.set_pers("Error: Node data invalid") } - if (streaminfo.error) return this.set_pers("server error: " + streaminfo.error) - - this.set_pers() - //! bad code: assignment order is important because chapter callbacks use duration - this.duration.value = streaminfo.duration - this.streaminfo = streaminfo - this.tracks = streaminfo!.tracks; - console.log("aaa", this.tracks); - this.video.src = URL.createObjectURL(this.media_source) - this.media_source.addEventListener("sourceopen", async () => { - let video = false, audio = false, subtitle = false; - for (let i = 0; i < this.tracks!.length; i++) { - const t = this.tracks![i]; - if (t.kind == "video" && !video) - video = true, await this.set_track_enabled(i, true, false) - if (t.kind == "audio" && !audio) - audio = true, await this.set_track_enabled(i, true, false) - if (t.kind == "subtitle" && !subtitle) - subtitle = true, await this.set_track_enabled(i, true, false) - } - - this.set_pers("Buffering initial stream fragments...") - - this.update(this.start_time) - this.video.currentTime = this.start_time - - await this.canplay.wait_for(true) - this.set_pers() - }) - } - - async update(newt?: number) { - await Promise.all(this.active_tracks.value.map(t => t.update(newt ?? this.video.currentTime))) - } - - async set_track_enabled(index: number, state: boolean, update = true) { - console.log(`(${index}) set enabled ${state}`); - const active_index = this.active_tracks.value.findIndex(t => t.track_index == index) - if (!state && active_index != -1) { - this.logger?.log(`Disabled track ${index}: ${display_track(this.tracks![index])}`) - const [track] = this.active_tracks.value.splice(active_index, 1) - track.abort.abort() - } else if (state && active_index == -1) { - this.logger?.log(`Enabled track ${index}: ${display_track(this.tracks![index])}`) - this.active_tracks.value.push(create_track(this, this.base_url, index, this.tracks![index])!) - if (update) await this.update() - } - this.active_tracks.change() - } - - play() { this.video.play() } - pause() { this.video.pause() } - frame_forward() { - //@ts-ignore trust me bro - this.video["seekToNextFrame"]() - } - async seek(p: number) { - this.set_pers("Buffering at target...") - await this.update(p) - this.video.currentTime = p - } -} - -function display_track(t: TrackInfo): string { - return `${t.name}` -} |