Compare commits
	
		
			2 Commits
		
	
	
		
			c6a6f46ff8
			...
			a00221e25e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a00221e25e | |||
| ac585ff355 | 
@@ -4,6 +4,7 @@ import { createContext, useEffect, useState } from "react";
 | 
				
			|||||||
import { MediaContainer } from "./media";
 | 
					import { MediaContainer } from "./media";
 | 
				
			||||||
import { MinistreamApiClient, StreamInfo } from "./api";
 | 
					import { MinistreamApiClient, StreamInfo } from "./api";
 | 
				
			||||||
import React from "react";
 | 
					import React from "react";
 | 
				
			||||||
 | 
					import { Log } from "./log";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface AppProps {
 | 
					interface AppProps {
 | 
				
			||||||
    api: MinistreamApiClient
 | 
					    api: MinistreamApiClient
 | 
				
			||||||
@@ -33,24 +34,10 @@ export function App({ api }: AppProps) {
 | 
				
			|||||||
        setSelectedStream(item)
 | 
					        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 = () => {
 | 
					    const updateTitle = () => {
 | 
				
			||||||
            api.siteInfo().then((info) => {
 | 
					        api.siteInfo().then((info) => {
 | 
				
			||||||
            if (info.siteName != document.title) {
 | 
					            if (info.siteName != document.title) {
 | 
				
			||||||
                setTitle(info.siteName)
 | 
					                setTitle(info.siteName)
 | 
				
			||||||
                localStorage.setItem(titleKey, info.siteName)
 | 
					                localStorage.setItem(titleKey, info.siteName)
 | 
				
			||||||
@@ -59,12 +46,32 @@ export function App({ api }: AppProps) {
 | 
				
			|||||||
        return
 | 
					        return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setInterval(() => {
 | 
					 | 
				
			||||||
        updateStreamList()
 | 
					 | 
				
			||||||
    }, 10000)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    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()
 | 
					        updateStreamList()
 | 
				
			||||||
 | 
					        const updateInterval = setInterval(() => {
 | 
				
			||||||
 | 
					            updateStreamList()
 | 
				
			||||||
 | 
					        }, 10000)
 | 
				
			||||||
 | 
					        return () => clearInterval(updateInterval)
 | 
				
			||||||
    }, [])
 | 
					    }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    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 { MinistreamApiClient } from "./api"
 | 
				
			||||||
import { resolve } from "path"
 | 
					import { resolve } from "path"
 | 
				
			||||||
import React from "react"
 | 
					import React from "react"
 | 
				
			||||||
 | 
					import { Log } from "./log"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MediaContainerProps = {
 | 
					type MediaContainerProps = {
 | 
				
			||||||
    selectedStream: string | null
 | 
					    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.
 | 
					    // if selected stream changed, recreate pc, and set all to not ready.
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        console.log("effect: selectedStream")
 | 
					        Log.Debug("Ran useEffect.", { "because_state": "selectedStream" })
 | 
				
			||||||
        setPC(new RTCPeerConnection({
 | 
					        setPC(new RTCPeerConnection({
 | 
				
			||||||
            iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
 | 
					            iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
 | 
				
			||||||
        }))
 | 
					        }))
 | 
				
			||||||
@@ -27,7 +28,7 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) {
 | 
				
			|||||||
    }, [selectedStream])
 | 
					    }, [selectedStream])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        console.log("effect: pc")
 | 
					        Log.Debug("Ran useEffect.", { "because_state": "pc" })
 | 
				
			||||||
        pc.addTransceiver("video")
 | 
					        pc.addTransceiver("video")
 | 
				
			||||||
        pc.addTransceiver("audio")
 | 
					        pc.addTransceiver("audio")
 | 
				
			||||||
        pc.ontrack = (event) => {
 | 
					        pc.ontrack = (event) => {
 | 
				
			||||||
@@ -38,30 +39,30 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        pc.onicecandidate = (event) => {
 | 
					        pc.onicecandidate = (event) => {
 | 
				
			||||||
            if (!event.candidate) {
 | 
					            if (!event.candidate) {
 | 
				
			||||||
                console.log("ICE gathering complete.")
 | 
					                Log.Info("ICE gathering complete.")
 | 
				
			||||||
                setICEReady(true)
 | 
					                setICEReady(true)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                console.log("Adding ICE candidate: " + event.candidate)
 | 
					                Log.Debug("Adding ICE candidate.", { "candidate": event.candidate.candidate })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pc.oniceconnectionstatechange = () => {
 | 
					        pc.oniceconnectionstatechange = () => {
 | 
				
			||||||
            console.log("ICE state changed to " + pc.iceConnectionState)
 | 
					            Log.Info("ICE gathering complete.")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pc.createOffer().then((offer) => {
 | 
					        pc.createOffer().then((offer) => {
 | 
				
			||||||
            pc.setLocalDescription(offer).then(() => { console.log("Local description set.") })
 | 
					            pc.setLocalDescription(offer).then(() => { Log.Debug("Local description set.") })
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }, [pc])
 | 
					    }, [pc])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        console.log("effect: iceReady")
 | 
					        Log.Debug("Ran useEffect", { "because_state": "iceReady" })
 | 
				
			||||||
        if (!iceReady || !selectedStream) {
 | 
					        if (!iceReady || !selectedStream) {
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const localOfferSdp = pc.localDescription?.sdp
 | 
					        const localOfferSdp = pc.localDescription?.sdp
 | 
				
			||||||
        if (!localOfferSdp) {
 | 
					        if (!localOfferSdp) {
 | 
				
			||||||
            console.log("Unable to get local description")
 | 
					            Log.Error("Unable to get local description.")
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const localOffer = new RTCSessionDescription({ type: "offer", sdp: localOfferSdp })
 | 
					        const localOffer = new RTCSessionDescription({ type: "offer", sdp: localOfferSdp })
 | 
				
			||||||
@@ -73,7 +74,7 @@ export function MediaContainer({ selectedStream, api }: MediaContainerProps) {
 | 
				
			|||||||
    }, [iceReady])
 | 
					    }, [iceReady])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    useEffect(() => {
 | 
					    useEffect(() => {
 | 
				
			||||||
        console.log("effect: remoteReady")
 | 
					        Log.Debug("Ran useEffect", { "because_state": "remoteReady" })
 | 
				
			||||||
        if (!iceReady || !selectedStream || !remoteReady) {
 | 
					        if (!iceReady || !selectedStream || !remoteReady) {
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user