Last active
July 16, 2025 11:20
-
-
Save andrewrjones/f70f58fbcf7e9c2860061adbd5f352f1 to your computer and use it in GitHub Desktop.
A script to add alt text to figures in a Hugo site, using ChatGPT to generate the alt text
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.11" | |
| # dependencies = ["llm", "requests"] | |
| # /// | |
| # | |
| # A script to add alt text to figures in a Hugo site, using ChatGPT to generate the alt text. | |
| # Works for both images saved locally and those with a URL. | |
| # Save this to the root of your Hugo site and, if you have `uv` installed, simply run with `./alt-text.py`. | |
| # See also https://andrew-jones.com/blog/adding-alt-text-to-my-all-my-images-with-openai/ | |
| import os | |
| import glob | |
| import llm | |
| import requests | |
| def process_markdown_for_alt_text(content): | |
| lines = content.splitlines() | |
| updated_lines = [] | |
| for line in lines: | |
| if '{{< figure src="' in line and not 'alt="' in line: | |
| src_start = line.find('src="') + 5 | |
| src_end = line.find('"', src_start) | |
| image_src = line[src_start:src_end] | |
| attachments = [] | |
| if image_src.startswith("http"): | |
| response = requests.head(image_src, allow_redirects=False, timeout=5) | |
| if response.status_code == 200: | |
| attachments.append(llm.Attachment(url=image_src)) | |
| else: | |
| image_path = f"static{image_src}" | |
| if os.path.exists(image_path): | |
| attachments.append(llm.Attachment(path=image_path)) | |
| else: | |
| print(f"Warning: Image not found: {image_path}") | |
| if attachments: | |
| model = llm.get_model("gpt-4.1-nano") | |
| try: | |
| response = model.prompt( | |
| "Generate a concise alt text for the image. The alt text should be descriptive and suitable for a screen reader. Do not include any introductory phrases like 'Alt text:' or 'An image of'", | |
| attachments=attachments, | |
| ) | |
| alt_text = response.text().replace('"', '\\"') | |
| updated_line = line.replace('>}}', f'alt="{alt_text}" >}}}}') | |
| updated_lines.append(updated_line) | |
| except Exception as e: | |
| print("Exception from OpenAI: " + e) | |
| updated_lines.append(line) | |
| else: | |
| updated_lines.append(line) | |
| else: | |
| updated_lines.append(line) | |
| content = "\n".join(updated_lines) | |
| return content | |
| def generate_alt_text_for_all_markdown_files(): | |
| content_directory = "content" | |
| markdown_files = glob.glob(os.path.join(content_directory, "**/*.md"), recursive=True) | |
| for file_path in markdown_files: | |
| with open(file_path, 'r') as f: | |
| content = f.read() | |
| updated_content = process_markdown_for_alt_text(content) | |
| with open(file_path, 'w') as f: | |
| f.write(updated_content) | |
| if __name__ == "__main__": | |
| generate_alt_text_for_all_markdown_files() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment