Compare commits
	
		
			3 Commits
		
	
	
		
			c6a6f46ff8
			...
			bbd3adc4f7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bbd3adc4f7 | |||
| a00221e25e | |||
| ac585ff355 | 
@@ -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,21 +34,7 @@ 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) => {
 | 
			
		||||
@@ -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(() => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								src/js/log.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/js/log.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user