englishstories / app.py
englissi's picture
Update app.py
d17b338 verified
import os
import time
import gradio as gr
from gtts import gTTS
import hashlib # 🎡 파일λͺ…을 κ³ μœ ν•˜κ²Œ ν•˜κΈ° μœ„ν•΄ μΆ”κ°€
# μƒμ„±λœ μ˜€λ””μ˜€ νŒŒμΌμ„ μž¬μ‚¬μš©ν•˜κΈ° μœ„ν•œ μΊμ‹œ λ”•μ…”λ„ˆλ¦¬
cached_audio = {}
# πŸ“Œ 단λͺ¨μŒ(Short Vowel) μŠ€ν† λ¦¬ 데이터 (ν…μŠ€νŠΈ + 이미지 URL 포함)
image_base_url = "https://huggingface.co/spaces/englissi/englishstories/resolve/main/image/"
short_vowel_stories = [
{"text": "Sam has a cat.", "image": f"{image_base_url}1.webp"},
{"text": "The cat is fat and tan.", "image": f"{image_base_url}2.webp"},
{"text": "Sam and the cat nap on a mat.", "image": f"{image_base_url}3.webp"},
{"text": "Ben has a red pen.", "image": f"{image_base_url}4.webp"},
{"text": "He pets a hen in a den.", "image": f"{image_base_url}5.webp"},
{"text": "Tim sits and grins.", "image": f"{image_base_url}6.webp"},
{"text": "A big pig digs in the mud.", "image": f"{image_base_url}7.webp"},
{"text": "Dot the dog jogs and hops.", "image": f"{image_base_url}8.webp"},
{"text": "Gus the pup has a cup.", "image": f"{image_base_url}9.webp"},
{"text": "Fun in the sun is the best!", "image": f"{image_base_url}10.webp"}
]
# πŸ“Œ μž₯λͺ¨μŒ(Long Vowel) μŠ€ν† λ¦¬ 데이터
long_vowel_stories = [
{"text": "Kate ate a big cake.", "image": f"{image_base_url}11.webp"},
{"text": "The train is late today.", "image": f"{image_base_url}12.webp"},
{"text": "I see a green tree.", "image": f"{image_base_url}13.webp"},
{"text": "He likes to read a book.", "image": f"{image_base_url}14.webp"},
{"text": "The kite is flying high.", "image": f"{image_base_url}15.webp"},
{"text": "The light is very bright.", "image": f"{image_base_url}16.webp"},
{"text": "The rose is red.", "image": f"{image_base_url}17.webp"},
{"text": "We saw a boat on the lake.", "image": f"{image_base_url}18.webp"},
{"text": "He is cute and kind.", "image": f"{image_base_url}19.webp"},
{"text": "A baby bird flew away.", "image": f"{image_base_url}20.webp"}
]
# πŸ“Œ Blends & Digraphs μŠ€ν† λ¦¬ 데이터
blends_digraphs_stories = [
{"text": "Blake blows a blue blimp.", "image": f"{image_base_url}21.webp"},
{"text": "Brad brings a brown brush.", "image": f"{image_base_url}22.webp"},
{"text": "The clock clicks and claps.", "image": f"{image_base_url}23.webp"},
{"text": "Crispy crackers crunch.", "image": f"{image_base_url}24.webp"},
{"text": "A frog frogs on a free log.", "image": f"{image_base_url}25.webp"},
{"text": "Green grapes grow big.", "image": f"{image_base_url}26.webp"},
{"text": "The train trips on tracks.", "image": f"{image_base_url}27.webp"},
{"text": "The ship shines in the sun.", "image": f"{image_base_url}28.webp"},
{"text": "Chip and cheese are on my chin.", "image": f"{image_base_url}29.webp"},
{"text": "The thumb is thick.", "image": f"{image_base_url}30.webp"},
{"text": "White whales whisper.", "image": f"{image_base_url}31.webp"},
{"text": "A skunk skips sky-high.", "image": f"{image_base_url}32.webp"},
{"text": "The sleepy sloth slides.", "image": f"{image_base_url}33.webp"},
{"text": "Small smiles smell sweet.", "image": f"{image_base_url}34.webp"},
{"text": "The snail snaps a snack.", "image": f"{image_base_url}35.webp"},
{"text": "The spider spins a spotty web.", "image": f"{image_base_url}36.webp"},
{"text": "The star stands in the storm.", "image": f"{image_base_url}37.webp"},
{"text": "A swan swims in the sweet lake.", "image": f"{image_base_url}38.webp"}
]
# πŸ“Œ "The Red Ball" μŠ€ν† λ¦¬ 데이터
the_red_ball_story = [
{"text": "Tom has a red ball. He throws the ball up. The ball goes high. Then it falls down!", "image": f"{image_base_url}39.webp"}
]
# πŸ“Œ "The Big Cat" μŠ€ν† λ¦¬ 데이터
the_big_cat_story = [
{"text": "The cat is big. It jumps on the mat. The mat is soft. The cat takes a nap.", "image": f"{image_base_url}40.webp"}
]
# πŸ“Œ "The Hot Sun" μŠ€ν† λ¦¬ 데이터
the_hot_sun_story = [
{"text": "The sun is hot. I wear my hat. I drink some water. Now I feel cool!", "image": f"{image_base_url}41.webp"}
]
# πŸ“Œ gTTSλ₯Ό μ΄μš©ν•΄ μ˜€λ””μ˜€ νŒŒμΌμ„ μƒμ„±ν•˜κ±°λ‚˜, 이미 있으면 μž¬μ‚¬μš©
def generate_audio(text):
try:
# 이미 μƒμ„±λœ μŒμ„±μ΄ 있으면 μž¬μ‚¬μš© (429 였λ₯˜ 방지)
if text in cached_audio:
return cached_audio[text]
# κ³ μœ ν•œ 파일λͺ… 생성 (ν•΄μ‹œ μ•ž 10자리 μ‚¬μš©)
hash_key = hashlib.md5(text.encode()).hexdigest()[:10]
filename = f"audio_{hash_key}.mp3"
# gTTSλ₯Ό μ‚¬μš©ν•˜μ—¬ μŒμ„± 생성
tts = gTTS(text=text, lang="en")
tts.save(filename)
# μ ˆλŒ€ 경둜둜 λ³€ν™˜
abs_filename = os.path.abspath(filename)
print(f"βœ… μŒμ„± 파일 생성 μ™„λ£Œ: {abs_filename}")
# μƒμ„±λœ 파일 경둜 캐싱
cached_audio[text] = abs_filename
# API μš”μ²­ μ œν•œμ„ ν”Όν•˜κΈ° μœ„ν•΄ 1.5초 λŒ€κΈ°
time.sleep(1.5)
return abs_filename
except Exception as e:
print(f"⚠️ μŒμ„± 생성 μ‹€νŒ¨: {e}")
return None
# πŸ“Œ "λ‹€μŒ" λ²„νŠΌ 클릭 μ‹œ ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜ (HTML 포함)
def next_story(current_index, story_list):
new_index = (current_index + 1) % len(story_list)
story = story_list[new_index]
text_html = f"<div class='story-text'>{story['text']}</div>"
image = story["image"]
audio_file = generate_audio(story["text"])
return new_index, text_html, image, audio_file, story["text"]
# πŸ“Œ "μž¬μƒ" λ²„νŠΌ 클릭 μ‹œ ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜
def play_story(current_text):
return generate_audio(current_text)
# πŸ“Œ 초기 κ°’ μ„€μ •
init_index_short = 0
init_text_short = short_vowel_stories[0]["text"]
init_image_short = short_vowel_stories[0]["image"]
init_audio_short = generate_audio(init_text_short)
init_index_long = 0
init_text_long = long_vowel_stories[0]["text"]
init_image_long = long_vowel_stories[0]["image"]
init_audio_long = generate_audio(init_text_long)
init_index_blends = 0
init_text_blends = blends_digraphs_stories[0]["text"]
init_image_blends = blends_digraphs_stories[0]["image"]
init_audio_blends = generate_audio(init_text_blends)
the_red_ball_index = 0
the_red_ball_text = the_red_ball_story[0]["text"]
the_red_ball_image = the_red_ball_story[0]["image"]
the_red_ball_audio = generate_audio(the_red_ball_text)
the_big_cat_index = 0
the_big_cat_text = the_big_cat_story[0]["text"]
the_big_cat_image = the_big_cat_story[0]["image"]
the_big_cat_audio = generate_audio(the_big_cat_text)
the_hot_sun_index = 0
the_hot_sun_text = the_hot_sun_story[0]["text"]
the_hot_sun_image = the_hot_sun_story[0]["image"]
the_hot_sun_audio = generate_audio(the_hot_sun_text)
# πŸ“Œ Gradio UI ꡬ성
with gr.Blocks(title="πŸ“š κ·€μ—¬μš΄ μŠ€ν† λ¦¬ μ•±") as demo:
gr.HTML("""
<style>
body {
background-color: #FFFAF0;
font-family: 'Comic Sans MS', cursive, sans-serif;
}
h1 {
color: #FF6347;
text-align: center;
font-size: 3em;
font-weight: bold;
margin-top: 20px;
}
p {
text-align: center;
font-size: 1.2em;
color: #6B4226;
}
.story-text {
font-size: 2.5em;
font-weight: bold;
text-align: center;
color: #FF4500;
padding: 20px;
border-radius: 15px;
background: #FFF3E0;
display: inline-block;
box-shadow: 4px 4px 10px rgba(0,0,0,0.2);
}
</style>
""")
gr.HTML("<h1>🎈 μž¬λ―ΈμžˆλŠ” μ˜μ–΄ μŠ€ν† λ¦¬ νƒ€μž„! πŸ“–</h1>")
gr.HTML("<p>🐱 κ·€μ—¬μš΄ 이야기와 ν•¨κ»˜ μ˜μ–΄λ₯Ό λ°°μ›Œλ³΄μ•„μš”! 🎡</p>")
with gr.Tabs():
for story_name, story_data in [
("Short Vowel", short_vowel_stories),
("Long Vowel", long_vowel_stories),
("Blends & Digraphs", blends_digraphs_stories),
("The Red Ball", the_red_ball_story),
("The Big Cat", the_big_cat_story),
("The Hot Sun", the_hot_sun_story)
]:
with gr.TabItem(story_name):
state_index = gr.State(value=0)
state_text = gr.State(value=story_data[0]["text"])
story_text = gr.HTML(value=f"<div class='story-text'>{story_data[0]['text']}</div>")
story_image = gr.Image(value=story_data[0]["image"], width=300, height=300)
audio_output = gr.Audio(value=generate_audio(story_data[0]["text"]), autoplay=False)
with gr.Row():
next_button = gr.Button("πŸ‘‰ λ‹€μŒ 이야기", elem_classes=["btn-custom", "next-btn"])
play_button = gr.Button("πŸ”Š λ‹€μ‹œ λ“£κΈ°", elem_classes=["btn-custom", "play-btn"])
next_button.click(
fn=next_story,
inputs=[state_index, gr.State(value=story_data)],
outputs=[state_index, story_text, story_image, audio_output, state_text]
)
play_button.click(
fn=play_story,
inputs=[state_text],
outputs=[audio_output]
)
# πŸ“Œ μ•± μ‹€ν–‰
demo.launch()