russell.ballestrini.net/lib/rst2md.py
russell@unturf.com e1fdae237e Generate index-slim.md without metadata for blog.json
- rst2md.py now creates both index.md (with metadata) and index-slim.md (clean)
- generate_jsonblog.py references index-slim.md for clean content
- Fixes "Error: No content found" in blog viewer
- strip_pelican_metadata() removes title and metadata fields
2026-01-20 06:47:11 -05:00

133 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""
Convert RST files to Markdown using pandoc.
Strips Pelican metadata headers from the resulting markdown.
"""
import subprocess
import sys
from pathlib import Path
import re
def strip_pelican_metadata(content):
"""Remove Pelican metadata header from markdown content.
Pandoc converts RST metadata to a format like:
# Title
field_name
field_value
another_field
another_value
Content starts here...
"""
lines = content.split('\n')
i = 0
# First line is usually the title (keep it)
if i < len(lines) and lines[i].strip().startswith('#'):
i += 1
# Skip blank lines after title
while i < len(lines) and not lines[i].strip():
i += 1
# Skip metadata fields (field name on one line, value on next)
metadata_fields = ['author', 'slug', 'date', 'tags', 'status', 'category', 'summary']
while i < len(lines):
line = lines[i].strip().lower()
# Check if this line is a metadata field name
if any(line == field for field in metadata_fields):
# Skip field name line
i += 1
# Skip value line(s) - could be multiline
while i < len(lines) and lines[i].strip():
# If next line is another field name, stop
if any(lines[i].strip().lower() == field for field in metadata_fields):
break
i += 1
# Skip blank line after field
if i < len(lines) and not lines[i].strip():
i += 1
elif not line:
# Skip blank lines between metadata
i += 1
else:
# Hit content - stop stripping
break
# Return content from this point forward
return '\n'.join(lines[i:]).strip() + '\n'
def convert_file(rst_path, md_path=None, create_slim=True):
"""Convert a single RST file to Markdown using pandoc.
Creates two versions:
- index.md: Full markdown with metadata
- index-slim.md: Clean markdown without metadata
"""
rst_path = Path(rst_path)
if md_path is None:
md_path = rst_path.with_suffix('.md')
else:
md_path = Path(md_path)
# First convert with pandoc (full version with metadata)
subprocess.run(
['pandoc', '-f', 'rst', '-t', 'gfm', '-o', str(md_path), str(rst_path)],
check=True
)
# Create slim version without metadata
if create_slim:
content = md_path.read_text()
clean_content = strip_pelican_metadata(content)
# Create -slim.md version
slim_path = md_path.parent / md_path.name.replace('.md', '-slim.md')
slim_path.write_text(clean_content)
return md_path
def convert_output_directory(output_dir):
"""Convert all index.rst files in output directory to index.md and index-slim.md."""
output_path = Path(output_dir)
count = 0
for rst_file in output_path.rglob('index.rst'):
md_file = rst_file.with_suffix('.md')
try:
convert_file(rst_file, md_file, create_slim=True)
count += 1
except subprocess.CalledProcessError as e:
print(f"Warning: Failed to convert {rst_file}: {e}")
print(f"Generated {count} index.md files and {count} index-slim.md files")
return count
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: rst2md.py <output_directory>")
print(" rst2md.py <file.rst> [output.md]")
sys.exit(1)
path = Path(sys.argv[1])
if path.is_dir():
count = convert_output_directory(path)
print(f"Converted {count} RST files to Markdown")
elif path.is_file():
output = sys.argv[2] if len(sys.argv) > 2 else None
md_path = convert_file(path, output)
print(f"Converted {path} -> {md_path}")
else:
print(f"Error: {path} not found")
sys.exit(1)