Files @ cc69faa87c27
Branch filter:

Location: light9/web/ascoltami/main.ts

drewp@bigasterisk.com
tear up and rewrite ascoltami to emit player state into the graph. web ui works but displays nothing but songs
function byId(id: string): HTMLElement {
  return document.getElementById(id)!;
}

// obsolete: watch the graph instead
export interface TimingUpdate {
  // GET /ascoltami/time response
  duration: number;
  next: string; // e.g. 'play'
  playing: boolean;
  song: string;
  started: number; // unix sec
  t: number; // seconds into song
  state: { current: { name: string }; pending: { name: string } };
}

(window as any).finishOldStyleSetup = async (times: { intro: number; post: number }, timingUpdate: (data: TimingUpdate) => void) => {
  let currentHighlightedSong = "";
  // let lastPlaying = false;

  
  const events = new EventSource("/service/ascoltami/time/stream");
  events.addEventListener("message", (m)=>{
    const update = JSON.parse(m.data) as TimingUpdate
    updateCurrent(update)
    markUpdateTiming();
  })

  async function updateCurrent(data:TimingUpdate) {
    byId("currentSong").innerText = data.song;
    if (data.song != currentHighlightedSong) {
      showCurrentSong(data.song);
    }
    byId("currentTime").innerText = data.t.toFixed(1);
    byId("leftTime").innerText = (data.duration - data.t).toFixed(1);
    byId("leftAutoStopTime").innerText = Math.max(0, data.duration - times.post - data.t).toFixed(1);
    byId("states").innerText = JSON.stringify(data.state);
    //   document.querySelector("#timeSlider").slider({ value: data.t, max: data.duration });
    timingUpdate(data);
  }
  let recentUpdates: Array<number> = [];
  function markUpdateTiming() {
    recentUpdates.push(+new Date());
    recentUpdates = recentUpdates.slice(Math.max(recentUpdates.length - 5, 0));
  }

  function refreshUpdateFreqs() {
    if (recentUpdates.length > 1) {
      if (+new Date() - recentUpdates[recentUpdates.length - 1] > 1000) {
        byId("updateActual").innerText = "(stalled)";
        return;
      }

      var avgMs = (recentUpdates[recentUpdates.length - 1] - recentUpdates[0]) / (recentUpdates.length - 1);
      byId("updateActual").innerText = "" + Math.round(1000 / avgMs);
    }
  }
  setInterval(refreshUpdateFreqs, 2000);

  function showCurrentSong(uri: string) {
    document.querySelectorAll(".songs div").forEach((row: Element, i: number) => {
      if (row.querySelector("button")!.dataset.uri == uri) {
        row.classList.add("currentSong");
      } else {
        row.classList.remove("currentSong");
      }
    });
    currentHighlightedSong = uri;
  }

  const data = await (await fetch("api/songs")).json();
  data.songs.forEach((song: { uri: string; label: string }) => {
    const button = document.createElement("button");
    // link is just for dragging, not clicking
    const link = document.createElement("a");
    const n = document.createElement("span");
    n.classList.add("num");
    n.innerText = song.label.slice(0, 2);
    link.appendChild(n);

    const sn = document.createElement("span");
    sn.classList.add("song-name");
    sn.innerText = song.label.slice(2).trim();
    link.appendChild(sn);
    link.setAttribute("href", song.uri);
    link.addEventListener("click", (ev) => {
      ev.stopPropagation();
      button.click();
    });
    button.appendChild(link);
    button.dataset.uri = song.uri;
    button.addEventListener("click", async (ev) => {
      await fetch("api/song", { method: "POST", body: song.uri });
      showCurrentSong(song.uri);
    });
    const dv = document.createElement("div");
    dv.appendChild(button);
    document.querySelector(".songs")!.appendChild(dv);
  });

};