
Automating Obsidian to Hugo: Script Overview and Architecture
In the previous post, we discussed the challenges of migrating content from Obsidian to Hugo. Now, let’s dive into the automation that makes this transition seamless.
The core of this automation is a custom Rust script that processes Markdown files from my Obsidian vault and converts them into Hugo-compatible blog posts. This script ensures that the migration process is efficient, structured, and repeatable. In this post, we’ll explore the high-level architecture and workflow of the script, breaking down its key stages.
Understanding the Workflow
The script follows a structured process to transform raw Markdown notes into Hugo-ready content. Here’s a high-level overview of how it works:
- Read Markdown Files: The script scans a specific folder in the Obsidian vault for new Markdown files.
- Process Content: It extracts frontmatter, converts Obsidian-specific elements, and prepares images.
- Modify Metadata: The script adjusts metadata to match Hugo’s format and structure.
- Generate and Save Blog Post: The transformed content is saved in the correct Hugo directory with a URL-friendly filename.
- Track Processed Files: A log is maintained to ensure that files are not processed multiple times.
This automation enables a smooth, hands-off conversion from Obsidian to Hugo, allowing me to focus on writing without worrying about manual formatting.
Breaking Down the Main Function
The script’s main
function orchestrates the entire workflow. Below is an overview of its key responsibilities:
fn main() {
// Setup directories
Home_Dir = Get_Home_Directory()
Posting_Dir = Home_Dir + "/OneDrive/Obsidian/Vault/Posts"
Images_Dir = Home_Dir + "/OneDrive/Obsidian/Vault/Images"
Target_Images_Dir = Home_Dir + "/Blog/static/images"
Blog_Posts_Dir = Home_Dir + "/Blog/content/en/posts"
Checked_File = Home_Dir + "/Logs/postchecked.txt"
// Create tracking file if it doesn't exist
if not exists(Checked_File) {
Create_Empty_File(Checked_File)
}
// Load list of already processed files
Processed_Files = Load_Processed_Files(Checked_File)
// Find unprocessed markdown files
Md_Files = Find_Files_In_Directory(Posting_Dir)
where extension is "md"
and not in Processed_Files
print "Found {Md_Files.length} unprocessed markdown files"
// Process each file
for each File_Path in Md_Files {
print "Processing file: {File_Path}"
// Read the content
Original_Content = Read_File(File_Path)
// STAGE 1: Extract filename for title
Filename = Get_Filename_Without_Extension(File_Path)
// STAGE 2: Parse frontmatter and content
Frontmatter, Content = Extract_Frontmatter(Content_With_Images)
// STAGE 3: Process Obsidian image links
Content_With_Images, Processed_Images = Process_Obsidian_Images(
Original_Content, Images_Dir, Target_Images_Dir
)
// STAGE 4: Extract thumbnail info
Thumbnail_Info, Remaining_Content = Extract_Thumbnail_Info(Content)
// STAGE 5: Modify frontmatter for Hugo
Modified_Frontmatter = Modify_Frontmatter(Frontmatter, Filename, Thumbnail_Info)
// STAGE 6: Process other image references in content
Processed_Content = Process_Image_References(Remaining_Content)
// STAGE 7: Combine everything
Final_Content = "---\n{Modified_Frontmatter}---\n\n{Processed_Content}"
// STAGE 8: Generate URL-friendly filename
New_Filename = Generate_URL_Friendly_Filename(Blog_Posts_Dir, Filename)
// STAGE 9: Write to new file
Write_File(New_Filename, Final_Content)
print "Created new blog post: {New_Filename}"
// STAGE 10: Mark as processed
Append_To_Processed_Files(Checked_File, File_Path)
print "Marked as processed: {File_Path}"
if Processed_Images is not empty {
print "Processed images: {Processed_Images}"
}
}
}
1. Setting Up Directories
The script first determines relevant directories, including:
- The source folder in the Obsidian vault
- The image storage directory
- The target directories in the Hugo project
- A log file to track processed posts
2. Tracking Processed Files
To avoid duplicate processing, the script maintains a log of previously converted posts. It reads this log at the start and updates it after each successful conversion.
3. Finding New Markdown Files
The script scans the designated folder for Markdown files that haven’t been processed yet. These are then queued for conversion.
4. Processing Each File
For each new Markdown file:
- The script extracts the filename as the post title.
- It parses and modifies frontmatter to align with Hugo’s format.
- It processes Obsidian’s wiki-style image links, converting them to standard Markdown and copying images to the correct Hugo directory.
- A final blog post file is generated with a properly formatted name.
5. Logging and Finalization
Once a post is successfully created, the script updates the tracking log and prints a confirmation message. If images were processed, it also lists them.
Visualizing the Process
Here’s a simple flowchart of the script’s workflow:
By automating this workflow, I can write naturally in Obsidian and publish effortlessly to my Hugo-powered blog.
Next Steps
Now that we’ve covered the script’s architecture, the next post will dive deeper into handling frontmatter. We’ll explore how the script extracts, modifies, and formats metadata for Hugo, ensuring that each post has the correct tags, categories, and publishing information.
Stay tuned!