englishstories / app.py
englissi's picture
Update app.py
0733e62 verified
raw
history blame
10.8 kB
import os
import time
import gradio as gr
from gtts import gTTS
import hashlib # 🎡 파일λͺ…을 κ³ μœ ν•˜κ²Œ ν•˜κΈ° μœ„ν•΄ μΆ”κ°€
# πŸ“Œ 단λͺ¨μŒ(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"}
]
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
# μš”μ²­ μ œν•œμ„ ν”Όν•˜κΈ° μœ„ν•΄ 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 = f"<div class='story-text'>{story['text']}</div>"
image = story["image"]
audio_file = generate_audio(story["text"])
return new_index, text, image, audio_file, story["text"]
# πŸ“Œ "μž¬μƒ" λ²„νŠΌ 클릭 μ‹œ ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜ (Gradio μ΅œμ‹  버전에 맞게 μˆ˜μ •)
def play_story(current_text):
return generate_audio(current_text)
# πŸ“Œ 초기 데이터 μ„€μ •
init_index_short, init_text_short, init_image_short, init_audio_short = 0, short_vowel_stories[0]["text"], short_vowel_stories[0]["image"], generate_audio(short_vowel_stories[0]["text"])
init_index_long, init_text_long, init_image_long, init_audio_long = 0, long_vowel_stories[0]["text"], long_vowel_stories[0]["image"], generate_audio(long_vowel_stories[0]["text"])
init_index_blends, init_text_blends, init_image_blends, init_audio_blends = 0, blends_digraphs_stories[0]["text"], blends_digraphs_stories[0]["image"], generate_audio(blends_digraphs_stories[0]["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: 2em;
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);
}
.btn-custom {
font-size: 1.5em;
font-weight: bold;
border-radius: 20px;
padding: 10px 20px;
margin: 5px;
border: none;
cursor: pointer;
box-shadow: 3px 3px 8px rgba(0,0,0,0.2);
}
.next-btn {
background-color: #FFD700; /* λ…Έλž€μƒ‰ λ²„νŠΌ */
}
.play-btn {
background-color: #90EE90; /* μ—°ν•œ μ΄ˆλ‘μƒ‰ λ²„νŠΌ */
}
.story-image img {
width: 300px !important;
height: 300px !important;
border-radius: 15px;
border: 5px solid #FFFFFF;
box-shadow: 4px 4px 10px rgba(0,0,0,0.2);
display: block;
margin: auto;
}
</style>
""")
gr.HTML("<h1>🎈 μž¬λ―ΈμžˆλŠ” μ˜μ–΄ μŠ€ν† λ¦¬ νƒ€μž„! πŸ“–</h1>")
gr.HTML("<p>🐱 κ·€μ—¬μš΄ 이야기와 ν•¨κ»˜ μ˜μ–΄λ₯Ό λ°°μ›Œλ³΄μ•„μš”! <br> λ²„νŠΌμ„ 눌러 λ‹€μŒ μ΄μ•ΌκΈ°λ‘œ λ„˜μ–΄κ°€κ³ , μŒμ„±μ„ λ“€μœΌλ©° 따라 μ½μ–΄λ³΄μ„Έμš”! 🎡</p>")
with gr.Tabs():
with gr.TabItem("Short Vowel (단λͺ¨μŒ)"):
state_index_short = gr.State(value=init_index_short)
state_text_short = gr.State(value=init_text_short)
story_text_short = gr.HTML(value=f"<div class='story-text'>{init_text_short}</div>")
story_image_short = gr.Image(value=init_image_short, width=300, height=300)
audio_output_short = gr.Audio(value=init_audio_short, autoplay=False)
with gr.Row():
next_button_short = gr.Button("πŸ‘‰ λ‹€μŒ 이야기", elem_classes=["btn-custom", "next-btn"])
play_button_short = gr.Button("πŸ”Š λ‹€μ‹œ λ“£κΈ°", elem_classes=["btn-custom", "play-btn"])
next_button_short.click(
fn=next_story,
inputs=[state_index_short, gr.State(value=short_vowel_stories)],
outputs=[state_index_short, story_text_short, story_image_short, audio_output_short, state_text_short]
)
play_button_short.click(
fn=play_story,
inputs=[state_text_short],
outputs=[audio_output_short]
)
with gr.TabItem("Long Vowel (μž₯λͺ¨μŒ)"):
state_index_long = gr.State(value=init_index_long)
state_text_long = gr.State(value=init_text_long)
story_text_long = gr.HTML(value=f"<div class='story-text'>{init_text_long}</div>")
story_image_long = gr.Image(value=init_image_long, width=300, height=300)
audio_output_long = gr.Audio(value=init_audio_long, autoplay=False)
with gr.Row():
next_button_long = gr.Button("πŸ‘‰ λ‹€μŒ 이야기", elem_classes=["btn-custom", "next-btn"])
play_button_long = gr.Button("πŸ”Š λ‹€μ‹œ λ“£κΈ°", elem_classes=["btn-custom", "play-btn"])
next_button_long.click(
fn=next_story,
inputs=[state_index_long, gr.State(value=long_vowel_stories)],
outputs=[state_index_long, story_text_long, story_image_long, audio_output_long, state_text_long]
)
play_button_long.click(
fn=play_story,
inputs=[state_text_long],
outputs=[audio_output_long]
)
with gr.TabItem("Blends (μ΄μ€‘μžμŒ)"):
state_index_blends = gr.State(value=init_index_blends)
state_text_blends = gr.State(value=init_text_blends)
story_text_blends = gr.HTML(value=f"<div class='story-text'>{init_text_blends}</div>")
story_image_blends = gr.Image(value=init_image_blends, width=300, height=300)
audio_output_blends = gr.Audio(value=init_audio_blends, autoplay=False)
with gr.Row():
next_button_blends = gr.Button("πŸ‘‰ λ‹€μŒ 이야기", elem_classes=["btn-custom", "next-btn"])
play_button_blends = gr.Button("πŸ”Š λ‹€μ‹œ λ“£κΈ°", elem_classes=["btn-custom", "play-btn"])
next_button_blends.click(
fn=next_story,
inputs=[state_index_blends, gr.State(value=blends_digraphs_stories)],
outputs=[state_index_blends, story_text_blends, story_image_blends, audio_output_blends, state_text_blends]
)
play_button_blends.click(
fn=play_story,
inputs=[state_text_blends],
outputs=[audio_output_blends]
)
# πŸ“Œ μ•± μ‹€ν–‰
demo.launch()