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"
{story['text']}
" 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(""" """) gr.HTML("

🎈 μž¬λ―ΈμžˆλŠ” μ˜μ–΄ μŠ€ν† λ¦¬ νƒ€μž„! πŸ“–

") gr.HTML("

🐱 κ·€μ—¬μš΄ 이야기와 ν•¨κ»˜ μ˜μ–΄λ₯Ό λ°°μ›Œλ³΄μ•„μš”! 🎡

") 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"
{story_data[0]['text']}
") 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()