Mercurial > code > home > repos > video
view src/VideoPage.ts @ 42:1d2c65d260d1
factor out breadcrumbs
author | drewp@bigasterisk.com |
---|---|
date | Thu, 05 Dec 2024 21:34:00 -0800 |
parents | 44bd161e4779 |
children | df51269bcef4 |
line wrap: on
line source
import { LitElement, PropertyValues, TemplateResult, css, html, unsafeCSS } from "lit"; import { customElement, property } from "lit/decorators.js"; import { PagePlayer, ShakaVideoElement } from "./PagePlayer"; import maincss from "./main.css?inline"; export { SlProgressBar } from "@shoelace-style/shoelace"; export { PagePlayer } from "./PagePlayer"; export { VideoSection } from "./VideoSection"; import { Routes, Router } from "@lit-labs/router"; export { VBreadcrumbs } from "./VBreadcrumbs"; interface VideoFile { webRelPath: string; webDataPath: string; label: string; } interface Subdir { label: string; path: string; } interface VideoListings { videos: VideoFile[]; subdirs: Subdir[]; } function subdirQuery(subdir: string): string { return "subdir=" + encodeURIComponent(subdir); } class route { path = "/video/*"; videoListings: VideoListings | null = null; showVid: string | null = null; dirName?: string; link(wrp: string): string { return "/video/" + wrp; } async enter(params: { [key: string]: string | undefined }): Promise<boolean> { const webRelPath = "/" + params[0]!; this.dirName = webRelPath.replace(/.*\/([^/]+)/, "$1"); const resp = await fetch("/video/api/videos?" + subdirQuery(webRelPath)); if (resp.status == 404) { return false; } this.videoListings = await resp.json(); if (webRelPath.endsWith("/")) { this.showVid = null; } else { this.showVid = webRelPath; } return true; } render(p: { [key: string]: string | undefined }) { return html`<video-page2 .link=${this.link.bind(this)} .showVid=${this.showVid} .videoListings=${this.videoListings} .dirName=${this.dirName} ></video-page2>`; } } @customElement("video-page") export class VideoPage extends LitElement { static styles = [unsafeCSS(maincss)]; private _router = new Router(this, [new route()], {}); render() { const requestedPath = "/" + this._router.params[0]; return html` <header> <img src=${this._router.link("/video/logo1.png")} title="JelloBello" /> <v-breadcrumbs .link=${this._router.link} dirPath=${requestedPath}></v-breadcrumbs> </header> <main>${this._router.outlet()}</main> <footer> <bigast-loginbar></bigast-loginbar> <span><a href="ingest/">Add new videos...</a></span> </footer> `; } } @customElement("video-page2") export class VideoPage2 extends LitElement { @property() showVid?: string; @property() videoListings?: VideoListings; @property() link!: (s: string) => string; @property() dirName?: string; protected firstUpdated(_changedProperties: PropertyValues): void { document.addEventListener("keydown", (e) => { if (e.key == "Escape") { this.closePlayer(); } }); } protected update(changedProperties: PropertyValues<this>): void { const resp = changedProperties.has("videoListings"); if (resp) { // if (this.showVid) { // this.openPlayer(); // } else { // this.closePlayer(); // } } super.update(changedProperties); } static styles = [ unsafeCSS(maincss), css` :host { display: block; } video-section { margin: 8px; } .listing a { font-size: 20px; text-transform: uppercase; text-underline-offset: 10px; } .listing a .subdir { transition: background 0.5s; transition-delay: 0.1s; background: #4ea1bd21; } .listing a:hover .subdir { transition: background 0.5s; background: #4ea1bd66; } .subdir { vertical-align: top; color: white; padding: 11px; display: inline-block; width: 300px; margin: 5px; border-radius: 2px; } .subdir:after { content: " › "; color: #979797; } #scrim { position: fixed; background: #000000b5; inset: 0; display: none; } `, ]; thumbSrc(v: VideoFile) { return "/video/api/thumbnail?webRelPath=" + encodeURIComponent(v.webRelPath); } renderSubdir(subdir: Subdir): TemplateResult { return html`<a href="${this.link(subdir.path) + "/"}"><div class="subdir">${subdir.label}</div></a>`; } renderVideoListing(video: VideoFile) { return html`<video-section @playVideo=${this.playVideo} thumbRelPath=${this.thumbSrc(video)} title="${video.label}" manifest="/video/files/${video.webDataPath}" ></video-section>`; } render() { if (this.videoListings == null) { return html`<div>Loading...</div>`; } const listings = [ html`${this.videoListings.subdirs.map((s) => this.renderSubdir(s))}`, // html`${this.videoListings.videos.map((v) => this.renderVideoListing(v))}`, ]; const dirTitle = this.dirName ? html`<h2>${this.dirName.slice(0, -1)}</h2>` : html``; return html` ${dirTitle} <div class="listing">${listings}</div> <div id="scrim" @click=${this.closePlayer}></div> <page-player manifest=""></page-player> `; } escapeALittle(fileUri: string): string { return fileUri.replace("#", encodeURIComponent("#")); } playVideo(ev: CustomEvent) { const player = this.shadowRoot!.querySelector("page-player")! as PagePlayer; player.manifest = this.escapeALittle(ev.detail.manifest); const sv = player.shadowRoot!.querySelector("shaka-video")! as ShakaVideoElement; sv.src = player.manifest; sv.autoplay = true; player.size = "big"; this.shadowRoot!.querySelector("#scrim")!.style.display = "block"; } closePlayer() { const player = this.shadowRoot!.querySelector("page-player")! as PagePlayer; player.size = "hidden"; this.shadowRoot!.querySelector("#scrim")!.style.display = "none"; } }