Mercurial > code > home > repos > video
view src/VideoPage.ts @ 45:df51269bcef4
fix back/fwd nav and player loading
author | drewp@bigasterisk.com |
---|---|
date | Fri, 06 Dec 2024 01:02:33 -0800 |
parents | 1d2c65d260d1 |
children | 882d0bb0f801 |
line wrap: on
line source
import { Router } from "@lit-labs/router"; import { LitElement, PropertyValues, TemplateResult, css, html, unsafeCSS } from "lit"; import { customElement, property, queryAsync } 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 { VBreadcrumbs } from "./VBreadcrumbs"; export { VideoSection } from "./VideoSection"; interface VideoFile { webRelPath: string; webDataPath: string; label: string; } interface Subdir { label: string; path: string; } interface PageData { webDirRelPath: string; dirLabel: string; videos: VideoFile[]; subdirs: Subdir[]; autoplay: VideoFile | null; } function subdirQuery(subdir: string): string { return "subdir=" + encodeURIComponent(subdir); } class route { path = "/video/*"; pageData: PageData | null = null; async enter(params: { [key: string]: string | undefined }): Promise<boolean> { const webRelPath = "/" + params[0]!; // could be /a/dir/ or /a/video console.log("enter", webRelPath); const resp = await fetch("/video/api/videos?" + subdirQuery(webRelPath)); if (resp.status == 404) { return false; } this.pageData = await resp.json(); if (!this.pageData) return false; return true; } render(p: { [key: string]: string | undefined }) { console.log("render", this.pageData); return html`<video-page2 .pageData=${this.pageData}></video-page2>`; } } @customElement("video-page") export class VideoPage extends LitElement { static styles = [unsafeCSS(maincss)]; private _router = new Router(this, [new route()], {}); constructor() { super(); window.rr = this._router; } render() { const pd = (this._router.routes[0] as route).pageData; if (!pd) { console.log("no page data", this._router.routes); return html`loading...`; throw new Error("no page data"); } console.log("yes page data", pd); return html` <header> <img src="/video/logo1.png" title="JelloBello" /> <v-breadcrumbs dirPath=${pd.webDirRelPath}></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() pageData?: PageData; protected firstUpdated(_changedProperties: PropertyValues): void { document.addEventListener("keydown", (e) => { if (e.key == "Escape") { this.gotoDirListingPage(); } }); } async connectedCallback() { super.connectedCallback(); if (this.pageData?.autoplay) { //this.playVideo({ detail: { manifest: this.pageData.autoplay.webDataPath } } as CustomEvent); console.log("we're a player page now", this.pageData.autoplay); this.startPlayer(this.pageData.autoplay); // this might not autoplay } else { this.closePlayer(); } } protected update(changedProperties: PropertyValues): void { super.update(changedProperties); if (changedProperties.has("pageData")) { if (this.pageData?.autoplay) { this.startPlayer(this.pageData.autoplay); } else { this.closePlayer(); } } console.log("updated", this.pageData?.autoplay); } 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: 5px; } .subdir:after { content: " › "; color: #979797; } #scrim { position: fixed; background: #000000b5; inset: 0; display: none; } `, ]; thumbSrc(vf: VideoFile) { return "/video/api/thumbnail?webRelPath=" + encodeURIComponent(vf.webRelPath); } renderSubdir(subdir: Subdir): TemplateResult { return html`<a href="/video${subdir.path}"><div class="subdir">${subdir.label}</div></a>`; } renderVideoListing(video: VideoFile) { return html`<video-section @playVideo=${this.gotoVideoPlayerPage} webRelPath=${video.webRelPath} thumbRelPath=${this.thumbSrc(video)} title="${video.label}" manifest="/video/files${video.webDataPath}" ></video-section>`; } render() { if (this.pageData == null) { return html`<div>Loading...</div>`; } const listings = [ html`${this.pageData.subdirs.map((s) => this.renderSubdir(s))}`, // html`<div>${this.pageData.videos.map((v) => this.renderVideoListing(v))}</div>`, ]; const dirTitle = this.pageData.dirLabel ? html`<h2>${this.pageData.dirLabel}</h2>` : html``; return html` ${dirTitle} <div class="listing">${listings}</div> <div id="scrim" @click=${this.gotoDirListingPage}></div> <page-player manifest=""></page-player> `; } escapeALittle(fileUri: string): string { return fileUri.replace("#", encodeURIComponent("#")); } @queryAsync("page-player") pagePlayer: Promise<PagePlayer>; async gotoVideoPlayerPage(ev: CustomEvent) { const player = await this.pagePlayer; console.log("playing", player, ev.detail.manifest, ev.detail.webRelPath); this.goto("/video" + ev.detail.webRelPath); } async gotoDirListingPage() { this.goto("/video" + this.pageData?.webDirRelPath); } async startPlayer(p: VideoFile) { const player = await this.pagePlayer; player.manifest = this.escapeALittle("/video/files" + p.webDataPath); 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"; } goto(url: string) { window.rr.goto(url); window.history.pushState({}, "", url); } async closePlayer() { const player = await this.pagePlayer; if (player.size === "hidden") { console.log("wasn't playing", player.size); return; } // return; // this.goto("/video/" + this.pageData?.webDirRelPath); player.size = "hidden"; this.shadowRoot!.querySelector("#scrim")!.style.display = "none"; } }