Skip to content

Commit 5514218

Browse files
kennethreitzclaude
andcommitted
Use asyncio.to_thread for OG image generation
Pillow image generation is CPU-bound, so run it in thread pool via asyncio.to_thread() to avoid blocking the event loop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent edecac4 commit 5514218

File tree

1 file changed

+24
-6
lines changed

1 file changed

+24
-6
lines changed

kjvstudy_org/routes/misc.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ async def og_image_verse(
406406
verse: int = Path(..., description="Verse number")
407407
):
408408
"""Generate OG image for a specific verse."""
409+
import asyncio
410+
409411
verse_text = bible.get_verse_text(book, chapter, verse)
410412
if not verse_text:
411413
# Return default image if verse not found
@@ -416,7 +418,9 @@ async def og_image_verse(
416418
title = f"{book} {chapter}:{verse}"
417419
cache_key = f"verse:{book}:{chapter}:{verse}"
418420

419-
image_bytes = get_cached_or_generate(
421+
# Run CPU-bound image generation in thread pool to avoid blocking
422+
image_bytes = await asyncio.to_thread(
423+
get_cached_or_generate,
420424
cache_key=cache_key,
421425
title=title,
422426
subtitle="King James Version",
@@ -437,13 +441,16 @@ async def og_image_chapter(
437441
chapter: int = Path(..., description="Chapter number")
438442
):
439443
"""Generate OG image for a chapter."""
444+
import asyncio
445+
440446
# Get first verse as preview
441447
verse_text = bible.get_verse_text(book, chapter, 1)
442448

443449
title = f"{book} {chapter}"
444450
cache_key = f"chapter:{book}:{chapter}"
445451

446-
image_bytes = get_cached_or_generate(
452+
image_bytes = await asyncio.to_thread(
453+
get_cached_or_generate,
447454
cache_key=cache_key,
448455
title=title,
449456
subtitle="King James Version",
@@ -461,10 +468,13 @@ async def og_image_chapter(
461468
@router.get("/og/book/{book}.png", response_class=Response)
462469
async def og_image_book(book: str = Path(..., description="Book name")):
463470
"""Generate OG image for a book."""
471+
import asyncio
472+
464473
title = book
465474
cache_key = f"book:{book}"
466475

467-
image_bytes = get_cached_or_generate(
476+
image_bytes = await asyncio.to_thread(
477+
get_cached_or_generate,
468478
cache_key=cache_key,
469479
title=title,
470480
subtitle="King James Version Bible",
@@ -481,13 +491,16 @@ async def og_image_book(book: str = Path(..., description="Book name")):
481491
@router.get("/og/topic/{topic}.png", response_class=Response)
482492
async def og_image_topic(topic: str = Path(..., description="Topic name")):
483493
"""Generate OG image for a topic."""
494+
import asyncio
484495
from urllib.parse import unquote
496+
485497
topic_name = unquote(topic)
486498

487499
title = topic_name
488500
cache_key = f"topic:{topic_name}"
489501

490-
image_bytes = get_cached_or_generate(
502+
image_bytes = await asyncio.to_thread(
503+
get_cached_or_generate,
491504
cache_key=cache_key,
492505
title=title,
493506
subtitle="Topical Bible Study",
@@ -504,6 +517,7 @@ async def og_image_topic(topic: str = Path(..., description="Topic name")):
504517
@router.get("/og/story/{slug}.png", response_class=Response)
505518
async def og_image_story(slug: str = Path(..., description="Story slug")):
506519
"""Generate OG image for a Bible story."""
520+
import asyncio
507521
import json
508522
from pathlib import Path as PathLib
509523

@@ -521,7 +535,8 @@ async def og_image_story(slug: str = Path(..., description="Story slug")):
521535

522536
cache_key = f"story:{slug}"
523537

524-
image_bytes = get_cached_or_generate(
538+
image_bytes = await asyncio.to_thread(
539+
get_cached_or_generate,
525540
cache_key=cache_key,
526541
title=title,
527542
subtitle="Bible Stories",
@@ -538,10 +553,13 @@ async def og_image_story(slug: str = Path(..., description="Story slug")):
538553
@router.get("/og/guide/{slug}.png", response_class=Response)
539554
async def og_image_guide(slug: str = Path(..., description="Study guide slug")):
540555
"""Generate OG image for a study guide."""
556+
import asyncio
557+
541558
title = slug.replace("-", " ").title() # Fallback title
542559
cache_key = f"guide:{slug}"
543560

544-
image_bytes = get_cached_or_generate(
561+
image_bytes = await asyncio.to_thread(
562+
get_cached_or_generate,
545563
cache_key=cache_key,
546564
title=title,
547565
subtitle="Bible Study Guide",

0 commit comments

Comments
 (0)