diff --git a/src/js/app.tsx b/src/js/app.tsx index a332c3d..73d754d 100644 --- a/src/js/app.tsx +++ b/src/js/app.tsx @@ -4,6 +4,7 @@ import { createContext, useEffect, useState } from "react"; import { MediaContainer } from "./media"; import { MinistreamApiClient, StreamInfo } from "./api"; import React from "react"; +import { Log } from "./log"; interface AppProps { api: MinistreamApiClient @@ -33,24 +34,10 @@ export function App({ api }: AppProps) { setSelectedStream(item) } - const updateStreamList = () => { - api.listStreams().then((list) => { - setStreamList(list) - if (list.length != streamList.length) { - setStreamList(list) - return - } - if (!list.every((_, idx) => { - return list[idx].streamKey === streamList[idx].streamKey - })) { - setStreamList(list) - } - }) - } const updateTitle = () => { - api.siteInfo().then((info) => { + api.siteInfo().then((info) => { if (info.siteName != document.title) { setTitle(info.siteName) localStorage.setItem(titleKey, info.siteName) @@ -59,12 +46,32 @@ export function App({ api }: AppProps) { return } - setInterval(() => { - updateStreamList() - }, 10000) useEffect(() => { + const updateStreamList = () => { + api.listStreams().then((list) => { + setStreamList((current) => { + if (list.length != current.length) { + Log.Debug("Updated streamList!") + return list + } + if (!list.every((_, idx) => { + return list[idx].streamKey === current[idx].streamKey + })) { + Log.Debug("Updated streamList..") + return list + } + return current + }) + }) + + } + updateStreamList() + const updateInterval = setInterval(() => { + updateStreamList() + }, 10000) + return () => clearInterval(updateInterval) }, []) useEffect(() => { diff --git a/src/js/log.ts b/src/js/log.ts new file mode 100644 index 0000000..93dbbb5 --- /dev/null +++ b/src/js/log.ts @@ -0,0 +1,61 @@ + + + +export namespace Log { + var currentLevel: Level = "INFO" + + if (process.env.NODE_ENV !== 'production') { + currentLevel = "DEBUG" + } + + export type Level = "ERROR" | "WARN" | "INFO" | "DEBUG" + + export function setLevel(level: Level) { + currentLevel = level + } + + + export interface LogArgs { + [key: string]: string + } + + export function Error(message: string, extras?: LogArgs) { + doLog("ERROR", message, extras) + } + export function Warn(message: string, extras?: LogArgs) { + doLog("WARN", message, extras) + } + export function Info(message: string, extras?: LogArgs) { + doLog("INFO", message, extras) + } + export function Debug(message: string, extras?: LogArgs) { + doLog("DEBUG", message, extras) + } + + function doLog(level: Level, message: string, extras?: LogArgs) { + var logLine = `[${level}] ${message}` + + if (extras) { + Object.keys(extras).forEach((key) => { + logLine = logLine + ` ${key}=${extras[key]}` + }) + } + + if (levelToNumber(level) >= levelToNumber(currentLevel)) { + console.log(logLine) + } + } + + function levelToNumber(level: Level): number { + switch (level) { + case "DEBUG": + return 0 + case "INFO": + return 1 + case "ERROR": + return 2 + case "WARN": + return 3 + } + } +} \ No newline at end of file diff --git a/src/js/media.tsx b/src/js/media.tsx index dcd70cf..6b0655e 100644 --- a/src/js/media.tsx +++ b/src/js/media.tsx @@ -2,6 +2,7 @@ import { ComponentProps, useRef, useEffect, useState } from "react" import { MinistreamApiClient } from "./api" import { resolve } from "path" import React from "react" +import { Log } from "./log" type MediaContainerProps = { selectedStream: string | null @@ -16,7 +17,7 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) { // if selected stream changed, recreate pc, and set all to not ready. useEffect(() => { - console.log("effect: selectedStream") + Log.Debug("Ran useEffect.", { "because_state": "selectedStream" }) setPC(new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] })) @@ -27,7 +28,7 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) { }, [selectedStream]) useEffect(() => { - console.log("effect: pc") + Log.Debug("Ran useEffect.", { "because_state": "pc" }) pc.addTransceiver("video") pc.addTransceiver("audio") pc.ontrack = (event) => { @@ -38,30 +39,30 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) { pc.onicecandidate = (event) => { if (!event.candidate) { - console.log("ICE gathering complete.") + Log.Info("ICE gathering complete.") setICEReady(true) } else { - console.log("Adding ICE candidate: " + event.candidate) + Log.Debug("Adding ICE candidate.", { "candidate": event.candidate.candidate }) } } pc.oniceconnectionstatechange = () => { - console.log("ICE state changed to " + pc.iceConnectionState) + Log.Info("ICE gathering complete.") } pc.createOffer().then((offer) => { - pc.setLocalDescription(offer).then(() => { console.log("Local description set.") }) + pc.setLocalDescription(offer).then(() => { Log.Debug("Local description set.") }) }) }, [pc]) useEffect(() => { - console.log("effect: iceReady") + Log.Debug("Ran useEffect", { "because_state": "iceReady" }) if (!iceReady || !selectedStream) { return } const localOfferSdp = pc.localDescription?.sdp if (!localOfferSdp) { - console.log("Unable to get local description") + Log.Error("Unable to get local description.") return } const localOffer = new RTCSessionDescription({ type: "offer", sdp: localOfferSdp }) @@ -73,7 +74,7 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) { }, [iceReady]) useEffect(() => { - console.log("effect: remoteReady") + Log.Debug("Ran useEffect", { "because_state": "remoteReady" }) if (!iceReady || !selectedStream || !remoteReady) { return }