Add initial attempt at make_scripture command

Heavily changed from this output:

https://chatgpt.com/share/68807254-9e40-800c-b6ac-5695710dd5d8

Currently does not respect verses; only chapters, despite help text
saying otherwise.
This commit is contained in:
Chandler Swift 2025-07-23 00:24:24 -05:00
parent 83f6b25a15
commit 3208ac7251
Signed by: chandlerswift
GPG key ID: A851D929D52FB93F

116
make_scripture.py Executable file
View file

@ -0,0 +1,116 @@
#!/usr/bin/env nix-shell
#! nix-shell -i python3
#! nix-shell -p python3 python3Packages.requests
import requests
import sys
url_base = "https://bible.chandlerswift.com/api/"
# url_base = "https://bible.helloao.org/api/"
translation="BSB" # Berean Standard Bible
def help():
print(f"""
usage: {sys.argv[0]} <book> <start verse> <end verse (inclusive)>
Generates a LaTeX rendering of the given bible verses
For example, `{sys.argv[0]} Genesis 1:1 2:3`
Note that the start and end can span chapters of a book, but not books.
""")
if len(sys.argv) != 4:
help()
raise SystemExit
book = sys.argv[1]
start_chapter, start_verse = map(int, sys.argv[2].split(':'))
end_chapter, end_verse = map(int, sys.argv[3].split(':'))
books = requests.get(f"{url_base}{translation}/books.json").json()
book_id = [b for b in books['books'] if b['name'] == book or b['commonName'] == book][0]['id']
import json
def escape_latex(text):
return (text.replace('\\', r'\textbackslash{}')
.replace('&', r'\&')
.replace('%', r'\%')
.replace('$', r'\$')
.replace('#', r'\#')
.replace('_', r'\_')
.replace('{', r'\{')
.replace('}', r'\}')
.replace('~', r'\textasciitilde{}')
.replace('^', r'\textasciicircum{}'))
def render_latex(json_data):
content = json_data['chapter']['content']
footnotes = {note['noteId']: note for note in json_data['chapter'].get('footnotes', [])}
latex_lines = []
chapter_number = json_data['chapter']['number']
first_verse_written = False
def render_verse_content(parts):
result = []
for part in parts:
if isinstance(part, str):
result.append(escape_latex(part))
elif isinstance(part, dict):
if 'noteId' in part:
note = footnotes.get(part['noteId'])
if note:
result.append(rf"\footnote{{{escape_latex(note['text'])}}}")
elif 'lineBreak' in part and part['lineBreak']:
# Only add \\ if already inside a paragraph
result.append(r"\newline ")
elif 'heading' in part:
result.append(rf"\textit{{{escape_latex(part['heading'])}}}")
elif 'text' in part:
result.append(escape_latex(part['text']))
return ' '.join(result)
for element in content:
etype = element['type']
if etype == 'heading':
heading_text = ' '.join(element['content'])
latex_lines.append(rf"\heading{{{escape_latex(heading_text)}}}")
latex_lines.append("") # Blank line to start new paragraph
elif etype == 'line_break':
# Blank line for paragraph break
latex_lines.append("")
elif etype == 'verse':
verse_number = element['number']
verse_body = render_verse_content(element['content'])
if not first_verse_written:
# First verse: show chapter number
latex_lines.append(rf"\ch{{{chapter_number}}}{verse_body}")
first_verse_written = True
else:
latex_lines.append(rf"\vs{{{verse_number}}}{verse_body}")
elif etype == 'hebrew_subtitle':
subtitle_parts = []
for part in element['content']:
if isinstance(part, str):
subtitle_parts.append(escape_latex(part))
elif isinstance(part, dict):
if 'noteId' in part and part['noteId'] in footnotes:
subtitle_parts.append(rf"\footnote{{{escape_latex(footnotes[part['noteId']]['text'])}}}")
elif 'text' in part:
subtitle_parts.append(escape_latex(part['text']))
subtitle = ' '.join(subtitle_parts)
latex_lines.append(rf"\textit{{{subtitle}}}")
latex_lines.append("")
return '\n'.join(latex_lines)
for chapter in range(start_chapter, end_chapter + 1):
chapter_data = requests.get(f"{url_base}{translation}/{book_id}/{chapter}.json").json()
print(render_latex(chapter_data))