#!/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]} 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'] 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) last_seen_verse = 0 # to start; TODO: does this include extra e.g. headers sometimes? for element in content: if element['type'] == 'verse': last_seen_verse = element['number'] if chapter_number == start_chapter and last_seen_verse < start_verse: continue if chapter_number == end_chapter and last_seen_verse >= end_verse: break 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 verse_number == 1: # First verse: show chapter number latex_lines.append(rf"\ch{{{chapter_number}}}{verse_body}") 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))