view src/VideoPage.ts @ 36:ed16fdbb3996

rewrite WIP. scan fs separately; store in db. thumbs are broken for now
author drewp@bigasterisk.com
date Tue, 03 Dec 2024 00:08:22 -0800
parents 9edb3df3cdb3
children 7cacfae58430
line wrap: on
line source

import { LitElement, PropertyValues, 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";


interface VideoFile {
  webRelPath: string;
  label: string;
  thumbRelPath: string;
  webDataPath: string;
}
interface Subdir {
  label: string;
  path: string;
}

function subdirQuery(subdir: string): string {
  return "subdir=" + encodeURIComponent(subdir);
}

@customElement("video-page")
export class VideoPage extends LitElement {
  videos: VideoFile[];
  subdirs: Subdir[];
  @property() pathSegs: { label: string; subdir: string }[];
  constructor() {
    super();
    this.videos = [];
    this.subdirs = [];
    this.pathSegs = [];
    this.loadVideos();
  }
  protected firstUpdated(_changedProperties: PropertyValues): void {
    document.addEventListener("keydown", (e) => {
      if (e.key == "Escape") {
        this.closePlayer();
      }
    });
  }
  updatePathSegs(subdir: string) {
    this.pathSegs = [{ label: "TOP", subdir: "/" }];
    if (subdir != "/") {
      let acc = "";
      const segs = subdir.split("/");
      segs.splice(0, 1); // the root
      for (let seg of segs) {
        acc += "/" + seg;
        this.pathSegs.push({ label: seg, subdir: acc });
      }
    }
  }
  async loadVideos() {
    const u = new URL(location.href);
    const subdir = u.searchParams.get("subdir") || "/";
    this.updatePathSegs(subdir);
    const resp = await (await fetch("api/videos?" + subdirQuery(subdir))).json();
    this.videos = resp.videos as VideoFile[];
    this.subdirs = resp.subdirs as Subdir[];
    this.requestUpdate();
  }
  static styles = [
    unsafeCSS(maincss),
    css`
   
      .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;
      }
    `,
  ];
  render() {
    return html`
      <sl-breadcrumb>
        ${this.pathSegs.map(
          (seg, i) => 
          html`<sl-breadcrumb-item>
                 <a href="./?${subdirQuery(seg.subdir)}">
                 ${i == 0 ? html`<sl-icon name="house"></sl-icon>`:''}
                 ${seg.label}
                 </a></sl-breadcrumb-item>`)}
      </sl-breadcrumb>

      <div class="listing">
      ${this.subdirs.map((s) => html`<div class="subdir"><a href="${"./?" + subdirQuery(s.path)}">${s.label}</a></div>`)}
      ${this.videos.map(
        (v) => html`<video-section @playVideo=${this.playVideo} thumbRelPath="${v.thumbRelPath}" title="${v.label}" manifest="/video/files/${v.webDataPath}"></video-section>`
      )}
      </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";
  }
}