audio_pipeline

This commit is contained in:
sanek5g
2026-06-10 17:12:58 +03:00
commit 00ddac5af7
29 changed files with 3297 additions and 0 deletions

115
watcher/cmd/watcher/main.go Normal file
View 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
}