add: live encode progress tracking and read-only status web UI
Stream ffmpeg -progress during the video encode into an in-memory tracker (internal/status) so the long-running step is no longer a black box: percent, fps, speed, and ETA are derived from the source duration and updated ~1/s. Progress prints to stdout only (no logs.db row) to avoid burying real events. Expose it over HTTP (internal/server, default :8080, set http_addr to "" to disable): GET /status returns the live snapshot, the pending input queue, and recent non-debug events; GET / serves an embedded dashboard that polls /status every second. The server shuts down on the same SIGINT/SIGTERM context as the watcher.
This commit is contained in:
+29
-3
@@ -6,18 +6,22 @@ import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"videnc-vibe/internal/config"
|
||||
"videnc-vibe/internal/encoder"
|
||||
"videnc-vibe/internal/logger"
|
||||
"videnc-vibe/internal/metadata"
|
||||
"videnc-vibe/internal/mover"
|
||||
"videnc-vibe/internal/server"
|
||||
"videnc-vibe/internal/status"
|
||||
"videnc-vibe/internal/watcher"
|
||||
"videnc-vibe/pkg/types"
|
||||
)
|
||||
@@ -53,7 +57,8 @@ func main() {
|
||||
}
|
||||
defer log.Close()
|
||||
|
||||
enc := encoder.New(log)
|
||||
tracker := status.New()
|
||||
enc := encoder.New(log, tracker)
|
||||
if err := enc.CheckDeps(); err != nil {
|
||||
log.Error("Dependency check failed", err.Error())
|
||||
fmt.Fprintf(os.Stderr, "Dependency check failed: %v\n", err)
|
||||
@@ -65,17 +70,38 @@ func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
if addr := *cfg.HTTPAddr; addr != "" {
|
||||
srv := &http.Server{Addr: addr, Handler: server.New(tracker, log, cfg.Paths.Input).Handler()}
|
||||
go func() {
|
||||
log.Info(fmt.Sprintf("Status server listening on %s", addr))
|
||||
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Error("Status server", err.Error())
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
shutCtx, shutCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer shutCancel()
|
||||
_ = srv.Shutdown(shutCtx)
|
||||
}()
|
||||
}
|
||||
|
||||
w := watcher.New(cfg.Paths.Input, 15)
|
||||
w.Start(ctx, func(ctx context.Context, inputPath string) error {
|
||||
return processFile(ctx, inputPath, cfg, enc, metaClient, log)
|
||||
return processFile(ctx, inputPath, cfg, enc, metaClient, log, tracker)
|
||||
})
|
||||
|
||||
log.Info("videnc-vibe started")
|
||||
}
|
||||
|
||||
func processFile(ctx context.Context, inputPath string, cfg *types.Config, enc *encoder.Encoder, metaClient *metadata.Client, log *logger.Logger) error {
|
||||
func processFile(ctx context.Context, inputPath string, cfg *types.Config, enc *encoder.Encoder, metaClient *metadata.Client, log *logger.Logger, tracker *status.Tracker) error {
|
||||
log.Info(fmt.Sprintf("Processing: %s", inputPath))
|
||||
|
||||
// Mark this file as the active job (phase: probing) and clear the tracker
|
||||
// back to idle on every exit path — success, failure, or cancellation.
|
||||
tracker.Begin(inputPath)
|
||||
defer tracker.Idle()
|
||||
|
||||
filename := filepath.Base(inputPath)
|
||||
isSeries, imdbID, tvmazeID, season, episode := metadata.ParseFilename(filename)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user