audio_pipeline
This commit is contained in:
115
watcher/cmd/watcher/main.go
Normal file
115
watcher/cmd/watcher/main.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
|
||||
"github.com/yourorg/watcher/internal/config"
|
||||
"github.com/yourorg/watcher/internal/publisher"
|
||||
"github.com/yourorg/watcher/internal/scanner"
|
||||
)
|
||||
|
||||
func main() {
|
||||
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})))
|
||||
|
||||
cfg := config.Load()
|
||||
sc := scanner.New(scanner.Config{
|
||||
StorageRoot: cfg.StorageRoot,
|
||||
IncomingDir: cfg.IncomingDir,
|
||||
ProcessingDir: cfg.ProcessingDir,
|
||||
FailedDir: cfg.FailedDir,
|
||||
StableWindow: cfg.StableWindow,
|
||||
StableChecks: cfg.StableChecks,
|
||||
})
|
||||
if err := sc.EnsureDirs(); err != nil {
|
||||
slog.Error("ensure dirs failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ch := mustRabbit(cfg.RabbitURL)
|
||||
if err := ch.ExchangeDeclare(cfg.Exchange, "direct", true, false, false, false, nil); err != nil {
|
||||
slog.Error("declare exchange failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
pub, err := publisher.New(ch, cfg.Exchange, cfg.RoutingKey)
|
||||
if err != nil {
|
||||
slog.Error("publisher init failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
slog.Info("watcher started",
|
||||
"storage_root", cfg.StorageRoot,
|
||||
"poll_interval", cfg.PollInterval.String(),
|
||||
"exchange", cfg.Exchange,
|
||||
"routing_key", cfg.RoutingKey,
|
||||
)
|
||||
|
||||
ticker := time.NewTicker(cfg.PollInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
slog.Info("watcher stopping")
|
||||
return
|
||||
case <-ticker.C:
|
||||
claimed, err := sc.ScanOnce()
|
||||
if err != nil {
|
||||
slog.Warn("scan failed", "error", err)
|
||||
continue
|
||||
}
|
||||
for _, cf := range claimed {
|
||||
task := publisher.AudioTask{
|
||||
TaskID: cf.TaskID,
|
||||
FilePath: cf.FilePath,
|
||||
Filename: cf.Filename,
|
||||
Size: cf.Size,
|
||||
}
|
||||
pubCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
err := pub.Publish(pubCtx, task)
|
||||
cancel()
|
||||
if err != nil {
|
||||
slog.Warn("publish failed, rolling back", "task_id", cf.TaskID, "error", err)
|
||||
if rbErr := sc.RollbackToIncoming(cf.FilePath, cf.Filename); rbErr != nil {
|
||||
slog.Error("rollback failed, moving to failed", "task_id", cf.TaskID, "error", rbErr)
|
||||
_ = sc.MoveToFailed(cf.FilePath, cf.Filename)
|
||||
}
|
||||
continue
|
||||
}
|
||||
slog.Info("task published", "task_id", cf.TaskID, "filename", cf.Filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mustRabbit(url string) *amqp.Channel {
|
||||
var conn *amqp.Connection
|
||||
var err error
|
||||
for i := 0; i < 30; i++ {
|
||||
conn, err = amqp.Dial(url)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
slog.Info("waiting for rabbit", "attempt", i+1, "error", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
slog.Error("rabbit unreachable", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ch, err := conn.Channel()
|
||||
if err != nil {
|
||||
slog.Error("rabbit channel failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return ch
|
||||
}
|
||||
Reference in New Issue
Block a user