Files
mediawatcher/internal/syncer/syncer.go
2025-12-08 07:56:46 -05:00

82 lines
1.9 KiB
Go

package syncer
import (
"bytes"
"fmt"
"log"
"os/exec"
"path/filepath"
"mediawatcher/internal/classifier"
"mediawatcher/internal/config"
)
type Syncer struct {
cfg *config.SyncConfig
}
func New(cfg *config.SyncConfig) *Syncer {
if cfg == nil || !cfg.Enabled {
return nil
}
return &Syncer{cfg: cfg}
}
func (s *Syncer) Sync(res classifier.Result) {
if s == nil || !s.cfg.Enabled {
return
}
for _, t := range s.cfg.Targets {
if err := s.syncToTarget(t, res); err != nil {
log.Printf("[sync] error syncing to %s: %v", t.Host, err)
}
}
}
func (s *Syncer) syncToTarget(t config.RsyncTarget, res classifier.Result) error {
if t.Host == "" || t.DestBase == "" {
return fmt.Errorf("invalid target config: host and dest_base required")
}
user := t.User
if user == "" {
user = "media"
}
port := t.Port
if port == 0 {
port = 22
}
// Recreate relative dest under DestBase
rel, err := filepath.Rel(res.DestDir, filepath.Join(res.DestDir, res.DestName))
if err != nil {
rel = res.DestName
}
remoteDir := t.DestBase
remote := fmt.Sprintf("%s@%s:%s/", user, t.Host, remoteDir)
args := []string{"-avh"}
if port != 22 {
args = append(args, "-e", fmt.Sprintf("ssh -p %d", port))
}
args = append(args, t.ExtraArgs...)
args = append(args, filepath.Join(res.DestDir, res.DestName), remote)
log.Printf("[sync] rsync %v", args)
cmd := exec.Command(s.cfg.RsyncBinary, args...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
log.Printf("[sync] stdout: %s", stdout.String())
log.Printf("[sync] stderr: %s", stderr.String())
return fmt.Errorf("rsync failed: %w", err)
}
return nil
}