package main import ( "flag" "io/fs" "log/slog" "os" "path/filepath" "strings" "sync" "github.com/SayaAndy/saya-today-article-metadata-add/config" "github.com/SayaAndy/saya-today-article-metadata-add/internal/draft" "github.com/SayaAndy/saya-today-article-metadata-add/internal/transcoder" ) var configPath = flag.String("c", "config.json", "Path to the configuration file") func main() { flag.Parse() cfg, err := config.InitConfig(*configPath) if err != nil { slog.Error("fail to load configuration", slog.String("error", err.Error())) os.Exit(1) } slog.SetLogLoggerLevel(cfg.LogLevel) slog.Info("starting metadata extractor...") transcoders := make([]transcoder.Transcoder, 0, len(cfg.Transcoders)) for _, tcCfg := range cfg.Transcoders { newTranscoder, ok := transcoder.NewTranscoderMap[tcCfg.Type] if !ok { slog.Error("unsupported transcoder type", slog.String("type", tcCfg.Type)) os.Exit(1) } t, err := newTranscoder(tcCfg.Config) if err != nil { slog.Error("fail to initialize transcoder", slog.String("type", tcCfg.Type), slog.String("error", err.Error())) os.Exit(1) } transcoders = append(transcoders, t) slog.Info("initialized transcoder", slog.String("type", tcCfg.Type)) } var drafts []string err = filepath.WalkDir(cfg.DraftDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } if strings.HasSuffix(path, cfg.DraftSuffix) { drafts = append(drafts, path) } return nil }) if err != nil { slog.Error("fail to scan draft directory", slog.String("dir", cfg.DraftDir), slog.String("error", err.Error())) os.Exit(1) } slog.Info("scanned drafts", slog.Int("draft_count", len(drafts))) semaphore := make(chan struct{}, cfg.MaxConcurrentJobs) var wg sync.WaitGroup wg.Add(len(drafts)) for _, draftPath := range drafts { semaphore <- struct{}{} go func(path string) { defer wg.Done() defer func() { <-semaphore }() codename := strings.TrimSuffix(filepath.Base(path), cfg.DraftSuffix) fileLogger := slog.With(slog.String("draft", path), slog.String("codename", codename)) content, err := os.ReadFile(path) if err != nil { fileLogger.Warn("fail to read draft", slog.String("error", err.Error())) return } doc, err := draft.ParseDraft(path, codename, content) if err != nil { fileLogger.Warn("fail to parse draft", slog.String("error", err.Error())) return } if doc.Metadata == nil { fileLogger.Info("skip draft without frontmatter metadata") return } for _, t := range transcoders { if err := t.Transcode(doc); err != nil { fileLogger.Warn("transcoder failed", slog.String("transcoder", t.Name()), slog.String("error", err.Error())) } } }(draftPath) } wg.Wait() for _, t := range transcoders { if err := t.Finalize(); err != nil { slog.Error("fail to finalize transcoder", slog.String("transcoder", t.Name()), slog.String("error", err.Error())) os.Exit(1) } slog.Info("finalized transcoder", slog.String("transcoder", t.Name())) } }