From e157883c90673a36e7407385d3ec22b70ef7115e Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Sun, 31 Dec 2023 12:23:51 +0100 Subject: [PATCH] initial commit --- Containerfile | 12 +++ action.yml | 12 +++ md2gmi.go | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 Containerfile create mode 100644 action.yml create mode 100644 md2gmi.go diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..6ca8cb6 --- /dev/null +++ b/Containerfile @@ -0,0 +1,12 @@ +FROM docker.io/golang:1.21-alpine3.19 AS builder + +ADD md2gmi.go /md2gmi.go +RUN go build -o /md2gmi /md2gmi.go + + +FROM scratch + +COPY --from=builder /md2gmi /md2gmi + +ENTRYPOINT ["/md2gmi"] +CMD ["${INPUT_DIR}", "${OUTPUT_DIR}"] diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..0a9f09d --- /dev/null +++ b/action.yml @@ -0,0 +1,12 @@ +name: "Markdown2Gemini" +description: "Simple conversion of a tree of markdown files to the gemini format." +inputs: + input-dir: + description: "Path to the input directory" + required: true + output-dir: + description: "Path to the output directory" + required: true +runs: + using: "docker" + image: "Containerfile" diff --git a/md2gmi.go b/md2gmi.go new file mode 100644 index 0000000..d6f89f0 --- /dev/null +++ b/md2gmi.go @@ -0,0 +1,197 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +type link struct { + full string + title string + url string +} + +func procAudio(fileContent string, audioRegex *regexp.Regexp) string { + audios := audioRegex.FindAllStringSubmatch(fileContent, -1) + + if audios != nil { + for i := range audios { + full := audios[i][0] + url := audios[i][1] + link := link{full, "audio", url} + + replacement := fmt.Sprintf("=> %s", link.url) + fileContent = strings.Replace(fileContent, link.full, replacement, 1) + } + } + + return fileContent +} + +func procVideos(fileContent string, videoRegex *regexp.Regexp) string { + videos := videoRegex.FindAllStringSubmatch(fileContent, -1) + + if videos != nil { + for i := range videos { + full := videos[i][0] + url := videos[i][1] + url = fmt.Sprintf("%s.webm", url) + link := link{full, "video", url} + + replacement := fmt.Sprintf("=> %s", link.url) + fileContent = strings.Replace(fileContent, link.full, replacement, 1) + } + } + + return fileContent +} + +func procImages(fileContent string, imgRegex *regexp.Regexp) string { + images := imgRegex.FindAllStringSubmatch(fileContent, -1) + + if images != nil { + for i := range images { + full := images[i][1] + title := images[i][2] + url := images[i][3] + urlParts := strings.Split(url, " ") + link := link{full, title, urlParts[0]} + + replacement := fmt.Sprintf("%s\n=> %s", link.title, link.url) + fileContent = strings.Replace(fileContent, link.full, replacement, 1) + } + } + + return fileContent +} + +func procLinks(fileContent string, linkRegex *regexp.Regexp) string { + links := linkRegex.FindAllStringSubmatch(fileContent, -1) + linkUrls := make([]link, len(links)) + + if links != nil { + for i := range links { + full := links[i][1] + title := links[i][2] + url := links[i][3] + link := link{full, title, url} + + replacement := fmt.Sprintf("*%s*", link.title) + fileContent = strings.Replace(fileContent, link.full, replacement, 1) + + linkUrls[i] = link + } + } + + var sb strings.Builder + for _, link := range linkUrls { + fmtLink := fmt.Sprintf("=> %s %s\n", link.url, link.title) + sb.WriteString(fmtLink) + } + fmtLinks := sb.String() + fileContent += fmt.Sprintf("\n%s", fmtLinks) + + return fileContent +} + +func procFile(src string, linkRegex *regexp.Regexp, imgRegex *regexp.Regexp, videoRegex *regexp.Regexp, audioRegex *regexp.Regexp) (string, error) { + fileData, err := ioutil.ReadFile(src) + if err != nil { + return "", err + } + + fileContent := string(fileData) + fileContent = procLinks(fileContent, linkRegex) + fileContent = procImages(fileContent, imgRegex) + fileContent = procVideos(fileContent, videoRegex) + fileContent = procAudio(fileContent, audioRegex) + + return fileContent, nil +} + +func procFiles(srcDir string, destDir string) error { + libRegex := regexp.MustCompile("^.+\\.(md)$") + linkRegex := regexp.MustCompile("[^!](\\[(.+?)\\]\\((.+?)\\))") + imgRegex := regexp.MustCompile("([!]\\[(.+?)\\]\\((.+?)\\))") + videoRegex := regexp.MustCompile("{{< video src=\"(.*)\" >}}") + audioRegex := regexp.MustCompile("{{< audio src=\"(.*)\" >}}") + + srcDir, err := filepath.Abs(srcDir) + if err != nil { + return err + } + + destDir, err = filepath.Abs(destDir) + if err != nil { + return err + } + + err = filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error { + if err == nil && libRegex.MatchString(info.Name()) { + processedFile, err := procFile(path, linkRegex, imgRegex, videoRegex, audioRegex) + if err != nil { + return err + } + + path, err = filepath.Abs(path) + if err != nil { + return err + } + + srcFile := strings.Replace(path, srcDir, "", 1) + destFile := filepath.Join(destDir, srcFile) + destDir := filepath.Dir(destFile) + + err = os.MkdirAll(destDir, os.ModePerm) + if err != nil { + return err + } + + err = ioutil.WriteFile(destFile, []byte(processedFile), 0644) + if err != nil { + return err + } + log.Printf("processed %s", path) + } + return nil + }) + + return err +} + +func main() { + if len(os.Args) < 2 { + log.Fatal("no source directory specified") + os.Exit(1) + } + + if len(os.Args) < 3 { + log.Fatal("no dest directory specified") + os.Exit(1) + } + + srcDir := os.Args[1] + srcInfo, err := os.Stat(srcDir) + if os.IsNotExist(err) { + log.Fatalf("source directory %s does not exist", srcDir) + os.Exit(1) + } + + if !srcInfo.IsDir() { + log.Fatalf("%s is not a directory", srcDir) + os.Exit(1) + } + + destDir := os.Args[2] + + err = procFiles(srcDir, destDir) + if err != nil { + log.Fatal(err) + os.Exit(1) + } +}