summaryrefslogtreecommitdiff
path: root/main.go
blob: 6648aebe11b5bacd97c34087e7321c20883967a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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()))
	}
}