Initial commit: organize Primary song slides repo

One folder per song under songs/. Tracks PPTX slide files and
Python generation scripts. PDFs and videos excluded (too large
for git; originals are in Google Drive).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 21:20:22 -06:00
commit 5a45320057
17 changed files with 788 additions and 0 deletions
@@ -0,0 +1,124 @@
import fitz
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
import io
PDF_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\EDITABLEMyOwnSacredGrove.pdf"
OUT_PATH = r"C:\Users\jhodgkin\Documents\dev\primary-song-slides\MyOwnSacredGrove.pptx"
DPI = 96
SLIDE_W = Inches(13.333) # 16:9 widescreen
SLIDE_H = Inches(7.5)
TEXT_BOX_H = Inches(2.1)
FONT_SIZE = Pt(44)
TEXT_COLOR = RGBColor(0x1B, 0x43, 0x32) # deep forest green
# (0-based PDF page index, lyric line)
# Pages: 0=boy→grove, 1=boy reading+praying, 2=boy praying+pillar of light,
# 3=HF+Jesus plain, 4=HF+Jesus in clouds, 5=two kids praying by bed (warm),
# 6=child with headphones/globe, 7=two kids praying+HF in clouds,
# 8=two kids praying by bed (same scene), 9=confused boy+arrows,
# 10=boy at fork in road, 11=wheelchair child thinking of Jesus,
# 12=Black child praying+golden light, 13=girl reading+boy pondering+stars,
# 14=girl praying+HF in clouds, 15=boy with clock, 16=three children,
# 17=girl holding light+stars, 18=girl by window, 19=two kids praying in grove
SLIDES = [
# Verse 1
(0, "Joseph Smith went to a grove full of trees"),
(1, "Seeking God's wisdom, he fell to his knees"),
(2, "As he pled with the heavens, the sky filled with light"),
(3, "And the Father appeared with His Son Jesus Christ"),
(4, "Standing above in the air\nComing to answer his prayer"),
# Chorus
(19, "I will find my own sacred grove"),
(6, "Away from all of the noise of the world"),
(8, "I will turn to prayer"),
(7, "For I know He's there"),
(19, "I will find my own sacred grove"),
# Verse 2
(9, "So many choices with so much at stake"),
(10, "Life's full of pathways, but which should I take?"),
(12, "If I lift up in prayer in the name of the Son"),
(13, "Through the power of the Holy Ghost, answers will come"),
(14, "Heavenly Father is there\nReady to answer my prayer"),
# Chorus
(19, "I will find my own sacred grove"),
(6, "Away from all of the noise of the world"),
(5, "I will turn to prayer"),
(7, "For I know He's there"),
(19, "I will find my own sacred grove"),
# Bridge
(15, "Sometimes the answers take time"),
(16, "So I'll listen in heart and mind"),
(17, "Revelation will come my way"),
(18, "As I wait patiently in faith"),
# Chorus
(19, "I will find my own sacred grove"),
(6, "Away from all of the noise of the world"),
(8, "I will turn to prayer"),
(7, "For I know He's there"),
(19, "I will find my own sacred grove"),
# Ending
(11, "I will find my own sacred grove"),
]
def render_page(page):
mat = fitz.Matrix(DPI / 72, DPI / 72)
pix = page.get_pixmap(matrix=mat, alpha=False)
return io.BytesIO(pix.tobytes("jpeg", jpg_quality=85)), pix.width, pix.height
def add_slide(doc, prs, blank_layout, page_idx, lyric):
page = doc[page_idx]
img_bytes, pw, ph = render_page(page)
slide = prs.slides.add_slide(blank_layout)
# Scale image to fit within slide, centered
aspect = pw / ph
h = SLIDE_H
w = int(h * aspect)
if w > SLIDE_W:
w = SLIDE_W
h = int(w / aspect)
left = (SLIDE_W - w) // 2
top = (SLIDE_H - h) // 2
slide.shapes.add_picture(img_bytes, left, top, width=w, height=h)
# Lyric text box at the bottom of the slide
tb_top = SLIDE_H - TEXT_BOX_H
txBox = slide.shapes.add_textbox(Inches(0.25), tb_top, SLIDE_W - Inches(0.5), TEXT_BOX_H)
tf = txBox.text_frame
tf.word_wrap = True
lines = lyric.split("\n")
for i, line in enumerate(lines):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.alignment = PP_ALIGN.CENTER
run = p.add_run()
run.text = line
run.font.bold = True
run.font.size = FONT_SIZE
run.font.color.rgb = TEXT_COLOR
print(f" Slide {len(prs.slides):2d} (pg {page_idx+1:2d}): {lyric[:55]}")
def main():
doc = fitz.open(PDF_PATH)
prs = Presentation()
prs.slide_width = SLIDE_W
prs.slide_height = SLIDE_H
blank_layout = prs.slide_layouts[6]
for page_idx, lyric in SLIDES:
add_slide(doc, prs, blank_layout, page_idx, lyric)
prs.save(OUT_PATH)
print(f"\nSaved: {OUT_PATH} ({len(SLIDES)} slides)")
if __name__ == "__main__":
main()