package main import ( "context" "fmt" "io" "log/slog" "os" "time" "git.netflux.io/rob/termstream/config" "git.netflux.io/rob/termstream/container" "git.netflux.io/rob/termstream/domain" "git.netflux.io/rob/termstream/mediaserver" "git.netflux.io/rob/termstream/terminal" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if err := run(ctx, config.FromFile()); err != nil { _, _ = os.Stderr.WriteString("Error: " + err.Error() + "\n") } } const uiUpdateInterval = 2 * time.Second func run(ctx context.Context, cfgReader io.Reader) error { cfg, err := config.Load(cfgReader) if err != nil { return fmt.Errorf("load config: %w", err) } state := new(domain.AppState) applyConfig(cfg, state) logFile, err := os.OpenFile("termstream.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return fmt.Errorf("error opening log file: %w", err) } logger := slog.New(slog.NewTextHandler(logFile, nil)) logger.Info("Starting termstream", slog.Any("initial_state", state)) containerClient, err := container.NewClient(ctx, logger.With("component", "container_client")) if err != nil { return fmt.Errorf("new container client: %w", err) } defer containerClient.Close() srv, err := mediaserver.StartActor(ctx, mediaserver.StartActorParams{ ContainerClient: containerClient, Logger: logger.With("component", "mediaserver"), }) if err != nil { return fmt.Errorf("start media server: %w", err) } applyServerState(srv.State(), state) ui, err := terminal.StartActor(ctx, terminal.StartActorParams{Logger: logger.With("component", "ui")}) if err != nil { return fmt.Errorf("start tui: %w", err) } defer ui.Close() updateUI := func() { ui.SetState(*state) } updateUI() uiTicker := time.NewTicker(uiUpdateInterval) defer uiTicker.Stop() for { select { case cmd, ok := <-ui.C(): if !ok { logger.Info("UI closed") return nil } logger.Info("Command received", "cmd", cmd) case <-uiTicker.C: // TODO: update UI with current state? updateUI() case serverState := <-srv.C(): applyServerState(serverState, state) updateUI() } } } // applyServerState applies the current server state to the app state. func applyServerState(serverState domain.Source, appState *domain.AppState) { appState.Source = serverState } // applyConfig applies the configuration to the app state. func applyConfig(cfg config.Config, appState *domain.AppState) { appState.Destinations = make([]domain.Destination, 0, len(cfg.Destinations)) for _, dest := range cfg.Destinations { appState.Destinations = append(appState.Destinations, domain.Destination{URL: dest.URL}) } }