Skip to content

Instantly share code, notes, and snippets.

@peteretelej
Created June 4, 2024 10:27
Show Gist options
  • Save peteretelej/451563bac429dd75cdcd447a60c94186 to your computer and use it in GitHub Desktop.
Save peteretelej/451563bac429dd75cdcd447a60c94186 to your computer and use it in GitHub Desktop.
Fix Obsidian Pasted Image Locations - update to attachments folder
// Cleanup old Obsidian Pasted Image links
// Finds all images and moves them to an `attachments/` folder in the same directory as the markdown linking it
// and then deletes the old images.
package main
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
)
var cwd = ""
func main() {
cwd, _ = os.Getwd()
fmt.Println("Cleaning up attachments in ", cwd)
files := findMarkdownFiles(".")
var wg sync.WaitGroup
wg.Add(len(files))
deleteImagesChan := make(chan string)
for _, f := range files {
go func(wg *sync.WaitGroup, f string, deleteImagesChan chan string) {
if err := fixPastedImage(f, deleteImagesChan); err != nil {
fmt.Printf("\n%s Error: %v\n", f, err)
}
wg.Done()
}(&wg, f, deleteImagesChan)
}
go func() {
wg.Wait()
close(deleteImagesChan)
}()
for img := range deleteImagesChan {
if _, err := os.Stat(img); err == os.ErrNotExist {
continue
}
if err := os.Remove(img); err != nil {
fmt.Println("Error deleting image: ", img)
}
}
}
func findMarkdownFiles(root string) []string {
var files []string
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Skip directories prefixed with a dot
if info.IsDir() && len(info.Name()) > 2 && strings.HasPrefix(info.Name(), ".") {
return filepath.SkipDir
}
// Check if the file has a .md extension
if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") {
files = append(files, path)
}
return nil
})
if err != nil {
log.Fatal(err)
}
return files
}
var imageRegexp = regexp.MustCompile(`!\[\[Pasted image \d+.png\]\]`)
func fixPastedImage(file string, deleteImagesChan chan string) error {
dir := filepath.Dir(file)
dat, err := os.ReadFile(file)
if err != nil {
return err
}
if !bytes.Contains(dat, []byte("![[Pasted image")) {
return nil
}
if _, err := os.Stat(dir + "/attachments"); err != nil && os.IsNotExist(err) {
if err := os.Mkdir(dir+"/attachments", os.ModePerm); err != nil {
return err
}
}
matches := imageRegexp.FindAllStringSubmatch(string(dat), -1)
if matches == nil {
return fmt.Errorf("no image matches found")
}
content := string(dat)
for _, match := range matches {
fileName := strings.TrimRight(strings.TrimLeft(match[0], "![["), "]]")
src, err := findMarkdownAttachment(dir, fileName)
if err != nil {
log.Printf("%s - Error: %v", file, err)
continue
}
dst := filepath.Join(dir, "attachments", fileName)
if err := copyFile(src, dst); err != nil {
log.Printf("%s - Error: %v", file, err)
continue
}
deleteImagesChan <- src // flag for deletion
oldReference := fmt.Sprintf("![[%s]]", fileName)
newReference := fmt.Sprintf("![](attachments/%s)", filepath.Base(fileName))
content = strings.ReplaceAll(content, oldReference, newReference)
}
err = os.WriteFile(file, []byte(content), 0644)
if err != nil {
return err
}
log.Printf("fixed: %s", file)
return nil
}
// findMarkdownAttachment finds the image location, can be in same directory or parent directory
func findMarkdownAttachment(dir, fileName string) (string, error) {
mainDirFile := filepath.Join(cwd, fileName)
currentDirFile := filepath.Join(dir, fileName)
if _, err := os.Stat(mainDirFile); err == nil {
return mainDirFile, nil
}
if _, err := os.Stat(currentDirFile); err == nil {
return currentDirFile, nil
}
return "", fmt.Errorf("file not found - %s", fileName)
}
func copyFile(src, dst string) error {
input, err := os.ReadFile(src)
if err != nil {
return err
}
err = os.WriteFile(dst, input, 0644)
if err != nil {
return err
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment