feat: about
This commit is contained in:
parent
4e3a72893b
commit
f359dcaac5
49
app/app.go
49
app/app.go
@ -14,32 +14,34 @@ import (
|
|||||||
"git.netflux.io/rob/octoplex/terminal"
|
"git.netflux.io/rob/octoplex/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const uiUpdateInterval = 2 * time.Second
|
// RunParams holds the parameters for running the application.
|
||||||
|
type RunParams struct {
|
||||||
|
Config config.Config
|
||||||
|
DockerClient container.DockerClient
|
||||||
|
ClipboardAvailable bool
|
||||||
|
BuildInfo domain.BuildInfo
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
// Run starts the application, and blocks until it exits.
|
// Run starts the application, and blocks until it exits.
|
||||||
func Run(
|
func Run(ctx context.Context, params RunParams) error {
|
||||||
ctx context.Context,
|
state := newStateFromRunParams(params)
|
||||||
cfg config.Config,
|
logger := params.Logger
|
||||||
dockerClient container.DockerClient,
|
|
||||||
clipboardAvailable bool,
|
|
||||||
logger *slog.Logger,
|
|
||||||
) error {
|
|
||||||
state := new(domain.AppState)
|
|
||||||
applyConfig(cfg, state)
|
|
||||||
|
|
||||||
ui, err := terminal.StartActor(ctx, terminal.StartActorParams{
|
ui, err := terminal.StartActor(ctx, terminal.StartActorParams{
|
||||||
ClipboardAvailable: clipboardAvailable,
|
ClipboardAvailable: params.ClipboardAvailable,
|
||||||
|
BuildInfo: params.BuildInfo,
|
||||||
Logger: logger.With("component", "ui"),
|
Logger: logger.With("component", "ui"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("start tui: %w", err)
|
return fmt.Errorf("start terminal user interface: %w", err)
|
||||||
}
|
}
|
||||||
defer ui.Close()
|
defer ui.Close()
|
||||||
|
|
||||||
updateUI := func() { ui.SetState(*state) }
|
updateUI := func() { ui.SetState(*state) }
|
||||||
updateUI()
|
updateUI()
|
||||||
|
|
||||||
containerClient, err := container.NewClient(ctx, dockerClient, logger.With("component", "container_client"))
|
containerClient, err := container.NewClient(ctx, params.DockerClient, logger.With("component", "container_client"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("new container client: %w", err)
|
return fmt.Errorf("new container client: %w", err)
|
||||||
}
|
}
|
||||||
@ -70,8 +72,9 @@ func Run(
|
|||||||
})
|
})
|
||||||
defer mp.Close()
|
defer mp.Close()
|
||||||
|
|
||||||
uiTicker := time.NewTicker(uiUpdateInterval)
|
const uiUpdateInterval = 2 * time.Second
|
||||||
defer uiTicker.Stop()
|
uiUpdateT := time.NewTicker(uiUpdateInterval)
|
||||||
|
defer uiUpdateT.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -89,7 +92,7 @@ func Run(
|
|||||||
case terminal.CommandQuit:
|
case terminal.CommandQuit:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case <-uiTicker.C:
|
case <-uiUpdateT.C:
|
||||||
updateUI()
|
updateUI()
|
||||||
case serverState := <-srv.C():
|
case serverState := <-srv.C():
|
||||||
applyServerState(serverState, state)
|
applyServerState(serverState, state)
|
||||||
@ -120,13 +123,17 @@ func applyMultiplexerState(mpState multiplexer.State, appState *domain.AppState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyConfig applies the configuration to the app state.
|
// newStateFromRunParams creates a new app state from the run parameters.
|
||||||
func applyConfig(cfg config.Config, appState *domain.AppState) {
|
func newStateFromRunParams(params RunParams) *domain.AppState {
|
||||||
appState.Destinations = make([]domain.Destination, 0, len(cfg.Destinations))
|
var state domain.AppState
|
||||||
for _, dest := range cfg.Destinations {
|
|
||||||
appState.Destinations = append(appState.Destinations, domain.Destination{
|
state.Destinations = make([]domain.Destination, 0, len(params.Config.Destinations))
|
||||||
|
for _, dest := range params.Config.Destinations {
|
||||||
|
state.Destinations = append(state.Destinations, domain.Destination{
|
||||||
Name: dest.Name,
|
Name: dest.Name,
|
||||||
URL: dest.URL,
|
URL: dest.URL,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &state
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,13 @@ import "time"
|
|||||||
type AppState struct {
|
type AppState struct {
|
||||||
Source Source
|
Source Source
|
||||||
Destinations []Destination
|
Destinations []Destination
|
||||||
|
BuildInfo BuildInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildInfo holds information about the build.
|
||||||
|
type BuildInfo struct {
|
||||||
|
GoVersion string
|
||||||
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source represents the source, currently always the mediaserver.
|
// Source represents the source, currently always the mediaserver.
|
||||||
|
20
main.go
20
main.go
@ -7,6 +7,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime/debug"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.netflux.io/rob/octoplex/app"
|
"git.netflux.io/rob/octoplex/app"
|
||||||
@ -73,12 +74,23 @@ func run(ctx context.Context) error {
|
|||||||
return fmt.Errorf("new docker client: %w", err)
|
return fmt.Errorf("new docker client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildInfo, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("read build info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return app.Run(
|
return app.Run(
|
||||||
ctx,
|
ctx,
|
||||||
cfg,
|
app.RunParams{
|
||||||
dockerClient,
|
Config: cfg,
|
||||||
clipboardAvailable,
|
DockerClient: dockerClient,
|
||||||
logger,
|
ClipboardAvailable: clipboardAvailable,
|
||||||
|
BuildInfo: domain.BuildInfo{
|
||||||
|
GoVersion: buildInfo.GoVersion,
|
||||||
|
Version: buildInfo.Main.Version,
|
||||||
|
},
|
||||||
|
Logger: logger,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ type Actor struct {
|
|||||||
pages *tview.Pages
|
pages *tview.Pages
|
||||||
ch chan action
|
ch chan action
|
||||||
commandCh chan Command
|
commandCh chan Command
|
||||||
|
buildInfo domain.BuildInfo
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
sourceViews sourceViews
|
sourceViews sourceViews
|
||||||
destView *tview.Table
|
destView *tview.Table
|
||||||
@ -45,6 +46,7 @@ type StartActorParams struct {
|
|||||||
ChanSize int
|
ChanSize int
|
||||||
Logger *slog.Logger
|
Logger *slog.Logger
|
||||||
ClipboardAvailable bool
|
ClipboardAvailable bool
|
||||||
|
BuildInfo domain.BuildInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartActor starts the terminal user interface actor.
|
// StartActor starts the terminal user interface actor.
|
||||||
@ -110,7 +112,8 @@ func StartActor(ctx context.Context, params StartActorParams) (*Actor, error) {
|
|||||||
aboutView.SetDirection(tview.FlexRow)
|
aboutView.SetDirection(tview.FlexRow)
|
||||||
aboutView.SetBorder(true)
|
aboutView.SetBorder(true)
|
||||||
aboutView.SetTitle("Actions")
|
aboutView.SetTitle("Actions")
|
||||||
aboutView.AddItem(tview.NewTextView().SetText("[C] Copy ingress URL"), 1, 0, false)
|
aboutView.AddItem(tview.NewTextView().SetText("[C] Copy ingress RTMP URL"), 1, 0, false)
|
||||||
|
aboutView.AddItem(tview.NewTextView().SetText("[?] About"), 1, 0, false)
|
||||||
|
|
||||||
sidebar.AddItem(aboutView, 0, 1, false)
|
sidebar.AddItem(aboutView, 0, 1, false)
|
||||||
|
|
||||||
@ -146,6 +149,7 @@ func StartActor(ctx context.Context, params StartActorParams) (*Actor, error) {
|
|||||||
actor := &Actor{
|
actor := &Actor{
|
||||||
ch: ch,
|
ch: ch,
|
||||||
commandCh: commandCh,
|
commandCh: commandCh,
|
||||||
|
buildInfo: params.BuildInfo,
|
||||||
logger: params.Logger,
|
logger: params.Logger,
|
||||||
app: app,
|
app: app,
|
||||||
pages: pages,
|
pages: pages,
|
||||||
@ -167,6 +171,8 @@ func StartActor(ctx context.Context, params StartActorParams) (*Actor, error) {
|
|||||||
switch event.Rune() {
|
switch event.Rune() {
|
||||||
case 'c', 'C':
|
case 'c', 'C':
|
||||||
actor.copySourceURLToClipboard(params.ClipboardAvailable)
|
actor.copySourceURLToClipboard(params.ClipboardAvailable)
|
||||||
|
case '?':
|
||||||
|
actor.showAbout()
|
||||||
}
|
}
|
||||||
case tcell.KeyCtrlC:
|
case tcell.KeyCtrlC:
|
||||||
actor.confirmQuit()
|
actor.confirmQuit()
|
||||||
@ -437,6 +443,26 @@ func (a *Actor) confirmQuit() {
|
|||||||
a.pages.AddPage("modal", modal, true, true)
|
a.pages.AddPage("modal", modal, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Actor) showAbout() {
|
||||||
|
modal := tview.NewModal()
|
||||||
|
modal.SetText(fmt.Sprintf(
|
||||||
|
"%s: live stream multiplexer\n\nv0.0.0 %s (%s)",
|
||||||
|
domain.AppName,
|
||||||
|
a.buildInfo.Version,
|
||||||
|
a.buildInfo.GoVersion,
|
||||||
|
)).
|
||||||
|
AddButtons([]string{"Ok"}).
|
||||||
|
SetBackgroundColor(tcell.ColorBlack).
|
||||||
|
SetTextColor(tcell.ColorWhite).
|
||||||
|
SetDoneFunc(func(buttonIndex int, _ string) {
|
||||||
|
a.pages.RemovePage("modal")
|
||||||
|
a.app.SetFocus(a.destView)
|
||||||
|
})
|
||||||
|
modal.SetBorderStyle(tcell.StyleDefault.Background(tcell.ColorBlack).Foreground(tcell.ColorWhite))
|
||||||
|
|
||||||
|
a.pages.AddPage("modal", modal, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
func rightPad(s string, n int) string {
|
func rightPad(s string, n int) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return s
|
return s
|
||||||
|
Loading…
x
Reference in New Issue
Block a user