| 1 | import "./clover-video.css"; |
| 2 | |
| 3 | export interface Input { |
| 4 | id?: string; |
| 5 | file: MediaFile | string; |
| 6 | poster?: MediaFile | string; |
| 7 | header?: Marko.AttrTag<{ content: Marko.Body }>; |
| 8 | minimal?: boolean; |
| 9 | noFigure?: boolean; |
| 10 | } |
| 11 | |
| 12 | <const/{ id, header, minimal, noFigure }=input> |
| 13 | <const/file=( |
| 14 | typeof UNWRAP(input.file) === "object" |
| 15 | ? input.file |
| 16 | : UNWRAP( |
| 17 | MediaFile.getByPath(input.file), |
| 18 | `No file ${JSON.stringify(input.file)}`, |
| 19 | ) |
| 20 | )> |
| 21 | <const/poster=( |
| 22 | input.poster |
| 23 | ? typeof input.poster === "object" |
| 24 | ? input.poster |
| 25 | : UNWRAP( |
| 26 | MediaFile.getByPath(input.poster), |
| 27 | `No file ${JSON.stringify(input.poster)}`, |
| 28 | ) |
| 29 | : null |
| 30 | )> |
| 31 | |
| 32 | <const/{ width, height }=file.parseDimensions() ?? { |
| 33 | width: 1920, |
| 34 | height: 1080, |
| 35 | }> |
| 36 | <!-- which streaming manifests exist ("dash", "hls", or both). the player |
| 37 | must only pick a backend whose manifest is actually present: dash can fail |
| 38 | or lag behind hls for the same file. "pending" tells it to show a |
| 39 | come-back-later note instead of silently failing --> |
| 40 | <const/streams=( |
| 41 | [ |
| 42 | derived.get(file, "dash.mpd") && "dash", |
| 43 | derived.get(file, "master.m3u8") && "hls", |
| 44 | ] |
| 45 | .filter(Boolean) |
| 46 | .join(",") || (file.pending ? "pending" : "none") |
| 47 | )> |
| 48 | |
| 49 | <define/Inner> |
| 50 | <video |
| 51 | ...{ id } |
| 52 | class={ "clover-video": noFigure, minimal: noFigure && minimal } |
| 53 | data-src=file.path |
| 54 | data-streams=streams |
| 55 | poster=poster ? `/file${poster.path}` : null |
| 56 | width=width |
| 57 | height=height |
| 58 | /> |
| 59 | </define> |
| 60 | |
| 61 | <if=!noFigure> |
| 62 | <figure.clover-video class={ minimal }> |
| 63 | <if=!minimal> |
| 64 | <figcaption> |
| 65 | <if=header> |
| 66 | <${header.content}/> |
| 67 | </if> |
| 68 | <else>${file.basename}</else> |
| 69 | </figcaption> |
| 70 | </if> |
| 71 | <div.stack style=`aspect-ratio:${width}/${height}`> |
| 72 | <Inner/> |
| 73 | <noscript.fallback> |
| 74 | <h2>scripting disabled!</h2> |
| 75 | <p> |
| 76 | to play this video, JavaScript must be enabled in your browser. the |
| 77 | default video playback behavior is not enabled here as it results in a |
| 78 | worse experience on slower internet connections, and |
| 79 | <a href="http://caniuse.com/mpeg-dash"> |
| 80 | MPEG-DASH streaming is not natively supported in any browser |
| 81 | </a> |
| 82 | without JavaScript. |
| 83 | </p> |
| 84 | <p> |
| 85 | since disabling JavaScript is not the default in any browser, you |
| 86 | likely wanted to download the raw video file anyways. those links are |
| 87 | available below, but note that you will need an AV1-compatible video |
| 88 | player, such as VLC Media Player or QuickTime Player (M3 chip or |
| 89 | newer) |
| 90 | </p> |
| 91 | </noscript> |
| 92 | </div> |
| 93 | </figure> |
| 94 | </if> |
| 95 | <else> |
| 96 | <Inner/> |
| 97 | </else> |
| 98 | |
| 99 | client import "./clover-video.client.ts"; |
| 100 | |
| 101 | import { MediaFile } from "#src/file-viewer/models/MediaFile.ts"; |
| 102 | import * as derived from "#src/file-viewer/models/derived.ts"; |
| 103 | import { UNWRAP, ASSERT } from "@clo/lib/assert"; |