Mercurial > code > home > repos > video
view src/VideoPage.ts @ 39:b5b29f6ef5cb
cleanup and refactor
author | drewp@bigasterisk.com |
---|---|
date | Thu, 05 Dec 2024 17:01:53 -0800 |
parents | 0aea9e55899b |
children | 44bd161e4779 |
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"; 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]; const segs = ["."].concat(requestedPath || "".split("/")); const crumbs = []; // todo: goal is to have '🏠 TOP' and every level not including current dir for (let i = 0; i < segs.length; i++) { const seg = segs[i]; const href = "../".repeat(segs.length - i - 1); const label = i == 0 ? html`<sl-icon name="house"></sl-icon>` : seg; console.log(href, label); crumbs.push(html`<sl-breadcrumb-item><a href=${href}>${label}</a></sl-breadcrumb-item>`); } return html` <header> <img src="${this._router.link("/video/logo1.png")}" title="JelloBello" /> <sl-breadcrumb>${crumbs}</sl-breadcrumb> </header> <main>${this._router.outlet()}</main> <footer>foot</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; } .listing a { font-size: 20px; text-transform: uppercase; text-underline-offset: 10px; } .subdir { vertical-align: top; color: white; padding: 11px; display: inline-block; width: 300px; background: #4ea1bd21; margin: 5px; border-bottom-right-radius: 29px; } #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`<div class="subdir"><a href="${this.link(subdir.path) + "/"}">${subdir.label}</a></div>`; } 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))}`, ]; return html` <h2>${this.dirName}</h2> <div class="listing">${listings}</div> <p><a href="ingest/">Add new videos...</a></p> <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"; } }