Stephen Cagle

Menu

  • Home
  • Archives
  • About Me
  • RSS
January 6, 2025

My Link Blog Setup

You should start a blog. Having your own little corner of the internet is good for the soul!

Inspired by Simon Wilson. I decided to try to resurect my long discarded blog. I wrote 2 things that I think are somewhat interesting.

  1. A capture template in emacs for quickly capturing urls that I think are worth noting.
  2. A script that takes those captures and commits and publishes them to my blog.

Here is the capture template

* %^{Title}
:PROPERTIES:
:CREATED: %T
:ATTRIBUTION: %^{Who should I attribute this to?}
:QUOTE: %^{Quote for this entry | nil}
:END:
%?

Here is the code in my .spacemacs that adds the template

(defun dotspacemacs/user-config ()
  ;; 2025-01-06 - Trying to setup a easier way to do quick blogs
  (setq org-capture-templates
    '(("f" "Found Blog Entry" entry (file+headline "~/blog/found.org" "Found")
      (file "~/orgs/capture_templates/found.org")))))

Now I can simply type org-capture Enter f to fill in this template and have it added to ~/blog/found.org under the Found root heading.

I then had Claude help me write a small script that basically parses every entry in ~/blog/found.org and adds it as a markdown entry to my static blog. The code is as follows.

import sys
import os
from datetime import datetime
import orgparse

def create_markdown(node):
    """Convert an org node to markdown blog post content."""
    # Extract link and title from heading
    title = node.get_heading().split(']')[1].strip() if '][' in node.get_heading() else node.get_heading()
    link = node.get_heading().split(']')[0].strip() if '][' in node.get_heading() else node.get_heading()

    # Get properties
    created = node.properties.get('CREATED', '').strip('<>')
    quote = node.properties.get('QUOTE', '')
    categories = node.properties.get('CATEGORIES', '')

    # Format date for filename and frontmatter
    date_obj = datetime.strptime(created, '%Y-%m-%d %a %H:%M')
    formatted_date = date_obj.strftime('%Y-%m-%d %H:%M:00')
    filename = date_obj.strftime('%Y-%m-%d') + '-' + title.lower().replace(' ', '-') + '.md'
    categories_formated = [s for s in categories.split(",") if s]

    if link and 'link' not in categories:
        categories_formated.append('link')

    categories_formated = [f'"{s}"' for s in categories_formated]

    # Create frontmatter
    md_content = (
        '{\n'
        ':layout :post\n'
        f':title  "{title}"\n'
        f':date   "{formatted_date}"\n'
        f':categories [{",".join(categories_formated)}]\n'
        '}\n\n'
    )

    # Add quote if exists
    if quote and quote != 'nil':
        md_content += f'<blockquote>{quote}</blockquote>\n\n'

    # Add main content
    md_content += node.get_body()

    return filename, md_content

def process_org_file(org_path, output_dir):
    """Process org file and create markdown files."""
    if not os.path.exists(org_path):
        print(f"Error: Input file '{org_path}' not found.")
        return False

    os.makedirs(output_dir, exist_ok=True)

    try:
        root = orgparse.load(org_path)
    except Exception as e:
        print(f"Error parsing org file: {e}")
        return False

    # Process each node that has the right depth (2 in your case, for ++)
    for node in root[1:]:  # Skip the root node
        if node.level == 2:  # Only process ++ entries
            try:
                filename, md_content = create_markdown(node)
                output_path = os.path.join(output_dir, filename)

                with open(output_path, 'w') as f:
                    f.write(md_content)

                print(f"Created: {output_path}")
            except Exception as e:
                print(f"Error processing entry {node.get_heading()}: {e}")
                continue

    return True

def main():
    if len(sys.argv) != 3:
        print("Usage: ./write_found_to_md.py <org_file> <output_directory>")
        sys.exit(1)

    org_path = sys.argv[1]
    output_dir = sys.argv[2]

    if process_org_file(org_path, output_dir):
        print("Conversion completed successfully.")
    else:
        print("Conversion failed.")
        sys.exit(1)

if __name__ == "__main__":
    main()

Humorously, I'll have to look a little harder to figure out how to actually make my title into a link. But I think it is a good start. :]


« Better Man Sonic the Hedgehog 3 »

Copyright © 2025 Stephen Cagle

Powered by Cryogen | Free Website Template by Download Website Templates