File size: 10,813 Bytes
cc05f1f
11cf154
5ae4c06
8d272f9
 
5ae4c06
7ad7fe5
b093de7
b69138c
7ad7fe5
fa5d44d
 
 
 
 
 
 
 
 
 
5ae4c06
 
7ad7fe5
 
 
 
 
 
 
 
 
 
 
 
 
 
f18dceb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7065a6a
f18dceb
8d272f9
5ae4c06
0733e62
11cf154
 
 
0733e62
 
8d272f9
 
0733e62
8d272f9
cc05f1f
8d272f9
0733e62
 
 
11cf154
0733e62
 
11cf154
0733e62
11cf154
 
0733e62
5ae4c06
cc05f1f
c0f3d5f
5ae4c06
0733e62
19f646c
7ad7fe5
 
 
19f646c
fa5d44d
19f646c
 
5ae4c06
11cf154
5ae4c06
11cf154
5ae4c06
8d272f9
 
 
f18dceb
 
5ae4c06
8d272f9
a4ad2c0
3bd37b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7896be3
3bd37b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c43a73a
bd65f6a
 
3bd37b5
 
 
 
 
 
 
 
 
 
 
 
7ad7fe5
 
 
 
 
afce848
bd65f6a
8f78a85
7ad7fe5
9b0ebaa
3bd37b5
 
9b0ebaa
7ad7fe5
 
 
 
 
 
9b0ebaa
 
 
 
 
 
7ad7fe5
 
 
 
afce848
1cdffe3
22e18da
7ad7fe5
9b0ebaa
3bd37b5
 
9b0ebaa
7ad7fe5
 
 
 
 
 
9b0ebaa
 
 
 
 
e5625ca
f18dceb
 
 
 
 
 
 
 
 
 
 
 
a318156
f18dceb
9bd8b3e
f18dceb
 
 
 
 
 
 
55d5d0f
9b0ebaa
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
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()