Files @ 2ce77421c0b7
Branch filter:

Location: light9/web/ascoltami/Light9AscoltamiUi.ts

drewp@bigasterisk.com
put a big time display on ascoltami
import debug from "debug";
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { NamedNode } from "n3";
import { PlainViewState } from "../Light9CursorCanvas";
import { getTopGraph } from "../RdfdbSyncedGraph";
import { SyncedGraph } from "../SyncedGraph";
import { PlayerState } from "./PlayerState";
export { RdfdbSyncedGraph } from "../RdfdbSyncedGraph";
export { ResourceDisplay } from "../ResourceDisplay";
export { Light9AscoltamiTimeline } from "./Light9AscoltamiTimeline";
export { Light9SongListing } from "./Light9SongListing";

debug.enable("*");
const log = debug("asco");

async function postJson(url: string, jsBody: Object) {
  return fetch(url, {
    method: "POST",
    headers: { "Content-Type": "applcation/json" },
    body: JSON.stringify(jsBody),
  });
}

@customElement("light9-ascoltami-ui")
export class Light9AscoltamiUi extends LitElement {
  graph!: SyncedGraph;
  @property() show: NamedNode | null = null;
  @property() song: NamedNode | null = null;
  @property() selectedSong: NamedNode | null = null;
  @property() viewState: PlainViewState | null = null;
  @property() host: any;
  @property() playerState: PlayerState = { duration: null, endOfSong: null, pausedSongTime: null, playing: null, song: null, wallStartTime: null };
  @property() playerTime: number = 0;
  static styles = [
    css`
      :host {
        display: flex;
        flex-direction: column;
      }

      .keyCap {
        color: #ccc;
        background: #525252;
        display: inline-block;
        border: 1px outset #b3b3b3;
        padding: 2px 3px;
        margin: 3px 0;
        margin-left: 0.4em;
        font-size: 16px;
        box-shadow: 0.9px 0.9px 0px 2px #565656;
        border-radius: 2px;
      }

      button {
        min-height: 48pt;
        min-width: 65pt;
      }

      #mainRow {
        display: flex;
        flex-direction: row;
      }

      light9-song-listing {
        flex-grow: 1;
      }

      th {
        text-align: right;
      }
#bigTime {
    color: green;
    font-size: 60pt;
}
    `,
  ];

  constructor() {
    super();
    getTopGraph().then((g) => {
      this.graph = g;
      this.graph.runHandler(this.updatePlayState.bind(this), "playstate-ui");
    });
    setInterval(this.updateT.bind(this), 100);
  }

  protected async firstUpdated(_changedProperties: PropertyValues<this>) {
    this.bindKeys();
    const config = await (await fetch("/service/ascoltami/config")).json();
    document.title = document.title.replace("{{host}}", config.host);
    this.host = config.host;
  }

  updatePlayState() {
    const U = this.graph.U();
    const asco = U(":ascoltami");
    this.playerState = {
      duration: this.graph.optionalFloatValue(asco, U(":duration")),
      endOfSong: this.graph.optionalBooleanValue(asco, U(":endOfSong")),
      pausedSongTime: this.graph.optionalFloatValue(asco, U(":pausedSongTime")),
      wallStartTime: this.graph.optionalFloatValue(asco, U(":wallStartTime")),
      playing: this.graph.optionalBooleanValue(asco, U(":playing")),
      song: this.graph.optionalUriValue(asco, U(":song")),
    };
    this.updateT();
  }

  updateT() {
    if (this.playerState.wallStartTime !== null) {
      this.playerTime = Date.now() / 1000 - this.playerState.wallStartTime;
    } else if (this.playerState.pausedSongTime !== null) {
      this.playerTime = this.playerState.pausedSongTime;
    } else {
      this.playerTime = 0;
    }
  }

  render() {
    return html`
      <h1>ascoltami on ${this.host}</h1>

      <span><rdfdb-synced-graph></rdfdb-synced-graph></span>
          <div id="mainRow">
          <div>
        <light9-song-listing
          @selectsong=${(ev: any) => {
            this.selectedSong = ev.detail.song;
          }}
          .selectedSong=${this.selectedSong}
          ></light9-song-listing>
          <div id="bigTime">t=${this.playerTime.toFixed(1)}</div>
          </div>
        <div>
          <light9-ascoltami-timeline .playerState=${this.playerState} .playerTime=${this.playerTime}></light9-ascoltami-timeline>
          <div>
            <button ?disabled=${this.selectedSong == null} @click=${this.onLoadSelected}>Change to and play selected <span class="keyCap">l</span></button>
            <button ?disabled=${false} @click=${this.onCmdZero}>Seek to zero <span class="keyCap">z</span></button>
            <button ?disabled=${this.playerState.playing || this.playerState.song == null} @click=${this.onCmdPlay}>Play <span class="keyCap">p</span></button>
            <button ?disabled=${!this.playerState.playing} @click=${this.onCmdStop}>Stop <span class="keyCap">s</span></button>
            <button ?disabled=${true} @click=${this.onCmdGo}>Go <span class="keyCap">g</span></button>
          </div>
          ${this.renderPlayerStateTable()}
        </div>
      </div>
    `;
  }
  renderPlayerStateTable() {
    return html` <table>
      <tr>
        <th>duration</th>
        <td>${this.playerState.duration}</td>
      </tr>
      <tr>
        <th>endOfSong</th>
        <td>${this.playerState.endOfSong}</td>
      </tr>
      <tr>
        <th>pausedSongTime</th>
        <td>${this.playerState.pausedSongTime?.toFixed(3)}</td>
      </tr>
      <tr>
        <th>playing</th>
        <td>${this.playerState.playing}</td>
      </tr>
      <tr>
        <th>song</th>
        <td>${this.playerState.song?.value}</td>
      </tr>
      <tr>
        <th>wallStartTime</th>
        <td>${this.playerState.wallStartTime}</td>
      </tr>
      <tr>
        <th>t</th>
        <td>${this.playerTime.toFixed(3)}</td>
      </tr>
    </table>`;
  }

  onSelectSong(song: NamedNode, ev: MouseEvent) {
    if (this.selectedSong && song.equals(this.selectedSong)) {
      this.selectedSong = null;
    } else {
      this.selectedSong = song;
    }
  }

  async onLoadSelected() {
    if (!this.selectedSong) {
      return;
    }
    await fetch("/service/ascoltami/song", { method: "POST", body: this.selectedSong.value });
    this.selectedSong = null;
  }

  onCmdStop(ev?: MouseEvent): void {
    postJson("/service/ascoltami/time", { pause: true });
  }

  onCmdPlay(ev?: MouseEvent): void {
    postJson("/service/ascoltami/time", { resume: true });
  }

  onCmdGo(ev?: MouseEvent): void {
    postJson("/service/ascoltami/go", {});
  }

  onCmdZero(ev?: MouseEvent): void {
    postJson("/service/ascoltami/time", { t: 0 });
  }

  bindKeys() {
    document.addEventListener("keypress", (ev) => {
      if (ev.key == "l") {
        this.onLoadSelected();
      } else if (ev.key == "z") {
        this.onCmdZero();
      } else if (ev.key == "p") {
        this.onCmdPlay();
      } else if (ev.key == "s") {
        this.onCmdStop();
      } else if (ev.key == "g") {
        this.onCmdGo();
      } else {
        return true;
      }
    });
  }
}