Files @ d41530a84d4b
Branch filter:

Location: light9/light9/ascoltami/main.ts

drewp@bigasterisk.com
pdm_local_install
function byId(id: string): HTMLElement {
  return document.getElementById(id)!;
}

async function onLoad() {
  const config = await (await fetch("api/config")).json();
  const times = config.times;
  document.title = document.title.replace("{{host}}", config.host);
  const h1 = document.querySelector("h1")!;
  h1.innerText = h1.innerText.replace("{{host}}", config.host);

  byId("nav").innerText = navigator.userAgent;
  var updateFreq = navigator.userAgent.indexOf("Linux") != -1 ? 10 : 2;
  if (navigator.userAgent.match(/Windows NT/)) {
    // helper laptop
    updateFreq = 10;
  }
  byId("updateReq").innerText = "" + updateFreq;

  let currentDuration = 0;
  let currentHighlightedSong = "";
  let lastPlaying = false;

  async function updateCurrent() {
    const data = await (await fetch("api/time")).json();
    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);
    currentDuration = data.duration;
    //   document.querySelector("#timeSlider").slider({ value: data.t, max: data.duration });
    if (data.playing != lastPlaying) {
      document.querySelectorAll(".playMode").forEach((e: Element) => e.classList.remove("active"));
      byId(data.playing ? "cmd-play" : "cmd-stop").classList.add("active");
      lastPlaying = data.playing;
    }
    byId("next").innerText = data.next;
  }

  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)=> {
        const button = document.createElement("button");
        // link is just for dragging, not clicking
        const link = document.createElement("a");
        link.appendChild($("<span>").addClass("num").text(song.label.slice(0, 2)));
        link.appendChild($("<span>").addClass("song-name").text(song.label.slice(2).trim()));
        link.setAttribute("href", song.uri);
        link.addEventListener("click", (ev) => {
			ev.stopPropagation()
          button.click();
        });
        button.appendChild(link);
        button.dataset.uri=(song);
        button.click(function () {
          fetch('api/song', {method: 'POST', body: song$.post("song", button.data("uri"), function (data, textStatus, xhr) {
            showCurrentSong(song.uri);
          });
        });
        $(".songs").append($("<div>").append(button));
      });
    });

  document.addEventListener("keypress", (ev) => {
    if (ev.which == 115) {
      byId("cmd-stop").click();
      return false;
    }
    if (ev.which == 112) {
      byId("cmd-play").click();
      return false;
    }
    if (ev.which == 105) {
      byId("cmd-intro").click();
      return false;
    }
    if (ev.which == 116) {
      byId("cmd-post").click();
      return false;
    }

    if (ev.key == "g") {
      byId("cmd-go").click();
      return false;
    }
    return true;
  });

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

  byId("cmd-stop").addEventListener("click", (ev: Event) => postJson("time", { pause: true }));
  byId("cmd-play").addEventListener("click", (ev: Event) => postJson("time", { resume: true }));
  byId("cmd-intro").addEventListener("click", (ev: Event) => postJson("time", { t: times.intro, resume: true }));
  byId("cmd-post").addEventListener("click", (ev: Event) => postJson("time", { t: currentDuration - times.post, resume: true }));
  byId("cmd-go").addEventListener("click", (ev: Event) => postJson("go", {}));
  byId("cmd-out0").addEventListener("click", (ev: Event) => postJson("output", { sink: "0" }));
  byId("cmd-out1").addEventListener("click", (ev: Event) => postJson("output", { sink: "1" }));

  //   var pendingSlide = false;
  //   $("#timeSlider").slider({
  //     step: 0.01,
  //     slide: function (event, ui) {
  //       if (pendingSlide) {
  //         return;
  //       }
  //       pendingSlide = true;
  //       $.post("time", '{"t" : ' + ui.value + "}", function (data, status, xhr) {
  //         pendingSlide = false;
  //       });
  //     },
  //   });

  let recentUpdates: Array<number> = [];
  function onUpdate() {
    recentUpdates.push(+new Date());
    recentUpdates = recentUpdates.slice(Math.max(recentUpdates.length - 5, 0));
    refreshUpdateFreqs();
  }

  function refreshUpdateFreqs() {
    if (recentUpdates.length > 1) {
      if (+new Date() - recentUpdates[recentUpdates.length - 1] > 1000) {
        byId("updateActual").innerText = "(stalled)";
        document.querySelectorAll(".dimStalled").forEach((el) => el.classList.add("stalled"));
        return;
      }

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

  async function updateLoop() {
    var whenDone = function () {
      setTimeout(function () {
        requestAnimationFrame(updateLoop);
      }, 1000 / updateFreq);
    };
    onUpdate();
    await updateCurrent();
	whenDone();
  }
  updateLoop();
}
onLoad();