import functools import re import gradio as gr try: import pandas as pd USE_PANDAS = True except ImportError: USE_PANDAS = False from WordStoryAudioMaker import audioStoryMaker def parse_phrases_text(phrases_text: str): parts = re.split(r'[,\n]+', phrases_text) return [p.strip() for p in parts if p.strip()] def bold_to_html(text: str) -> str: if not text: return "" return re.sub(r"\*\*(.+?)\*\*", r"\1", text) def dict_list_to_html(dict_list): if not dict_list: return "

(没有词典数据)

" col_map = { "dictKeyText": "单词或短语", "ukPhoneticSymbol": "英式音标", "usPhoneticSymbol": "美式音标", "definition": "释义", "example": "例句", "pos": "词性", } keys = [k for k in dict_list[0].keys() if k != "dictName"] for d in dict_list: if "definition" in d and isinstance(d["definition"], str): d["definition"] = d["definition"].replace("\n", "
") if USE_PANDAS: df = pd.DataFrame(dict_list)[keys] df.columns = [col_map.get(c, c) for c in df.columns] html = df.to_html(escape=False, index=False) else: header = "".join(f"{col_map.get(k, k)}" for k in keys) rows = [] for d in dict_list: row = "".join(f"{d.get(k, '')}" for k in keys) rows.append(f"{row}") body = "\n".join(rows) html = f""" {header}{body}
""" return f"
{html}
" # --------------------------- # 缓存实现部分 # --------------------------- def _normalize_input(phrases_text: str) -> str: if phrases_text is None: return "" s = phrases_text.strip() s = re.sub(r'\r\n', '\n', s) s = re.sub(r'\n+', '\n', s) s = re.sub(r'[,,]+', '\n', s) return s @functools.lru_cache(maxsize=128) def _cached_audio_for_input(norm_phrases_text: str): phrases = parse_phrases_text(norm_phrases_text) phrasesListStr, audioFilePath, dictResultList, storyJson = audioStoryMaker(phrases) return phrasesListStr, audioFilePath, dictResultList, storyJson def wrapper_run(phrases_text: str): phrases_norm = _normalize_input(phrases_text) if not phrases_norm: return "

(没有输入短语)

", None, "", "", "" phrasesListStr, audioFilePath, dictResultList, storyJson = _cached_audio_for_input(phrases_norm) story_en_html = bold_to_html(storyJson.get("en", "")) if isinstance(storyJson, dict) else "" story_zh_html = bold_to_html(storyJson.get("zh", "")) if isinstance(storyJson, dict) else "" dict_html = dict_list_to_html(dictResultList) story_en_html = f"
{story_en_html}
" story_zh_html = f"
{story_zh_html}
" phrases_html = f"

单词或短语: {phrasesListStr}

" return phrases_html, audioFilePath, story_zh_html, story_en_html, dict_html # --------------------------- # Gradio 界面部分 # --------------------------- default_phrases = """nobility underperform insane liable conspiracy proprietary ditch""" with gr.Blocks(title="单词故事") as demo: gr.Markdown("""## 单词故事:听故事 = 复习 + 记忆单词! 单词故事利用 AI 将用户提供的一组单词或短语编成一个有趣的小故事,然后合成音频,使用户可以通过听故事来复习之前记过的单词——将枯燥的单词记忆变得轻松有趣起来!""") inp = gr.Textbox( label="""输入单词或短语(每行一个),建议每次提交7个,这是由George A. Miller (1956) 的论文The Magical Number Seven, Plus or Minus Two所揭示的记忆规律。 下面默认的内容是为了方便测试用,您可以删除后使用你的""", placeholder="在这里输入英文单词或短语", value=default_phrases, lines=7 ) btn = gr.Button("生成") out_phrasesListStr = gr.HTML(label="短语列表") out_audio = gr.Audio(label="单词故事音频(播放)", type="filepath", autoplay=True) out_storyZh = gr.HTML(label="中文故事") out_storyEn = gr.HTML(label="英文故事") out_dictTable = gr.HTML(label="词典释义表格") btn.click( fn=wrapper_run, inputs=[inp], outputs=[ out_phrasesListStr, out_audio, out_storyZh, out_storyEn, out_dictTable ] ) if __name__ == "__main__": demo.launch()