openfree commited on
Commit
adf8558
ยท
verified ยท
1 Parent(s): 707248b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +448 -0
app.py CHANGED
@@ -1638,3 +1638,451 @@ class UnifiedAudioConverter:
1638
 
1639
 
1640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1638
 
1639
 
1640
 
1641
+ conversation_json["conversation"])
1642
+ )
1643
+
1644
+ return final_audio_path, conversation_text
1645
+
1646
+ def _create_output_directory(self) -> str:
1647
+ """Create a unique output directory"""
1648
+ random_bytes = os.urandom(8)
1649
+ folder_name = base64.urlsafe_b64encode(random_bytes).decode("utf-8")
1650
+ os.makedirs(folder_name, exist_ok=True)
1651
+ return folder_name
1652
+
1653
+ def _combine_audio_files(self, filenames: List[str], output_file: str) -> None:
1654
+ """Combine multiple audio files into one"""
1655
+ if not filenames:
1656
+ raise ValueError("No input files provided")
1657
+
1658
+ try:
1659
+ audio_segments = []
1660
+ for filename in filenames:
1661
+ if os.path.exists(filename):
1662
+ audio_segment = AudioSegment.from_file(filename)
1663
+ audio_segments.append(audio_segment)
1664
+
1665
+ if audio_segments:
1666
+ combined = sum(audio_segments)
1667
+ combined.export(output_file, format="wav")
1668
+
1669
+ # Clean up temporary files
1670
+ for filename in filenames:
1671
+ if os.path.exists(filename):
1672
+ os.remove(filename)
1673
+
1674
+ except Exception as e:
1675
+ raise RuntimeError(f"Failed to combine audio files: {e}")
1676
+
1677
+
1678
+ # Global converter instance
1679
+ converter = UnifiedAudioConverter(ConversationConfig())
1680
+
1681
+
1682
+ async def synthesize(article_input, input_type: str = "URL", mode: str = "Local", tts_engine: str = "Edge-TTS", language: str = "English"):
1683
+ """Main synthesis function - handles URL, PDF, and Keyword inputs"""
1684
+ try:
1685
+ # Extract text based on input type
1686
+ if input_type == "URL":
1687
+ if not article_input or not isinstance(article_input, str):
1688
+ return "Please provide a valid URL.", None
1689
+ text = converter.fetch_text(article_input)
1690
+ elif input_type == "PDF":
1691
+ if not article_input:
1692
+ return "Please upload a PDF file.", None
1693
+ text = converter.extract_text_from_pdf(article_input)
1694
+ else: # Keyword
1695
+ if not article_input or not isinstance(article_input, str):
1696
+ return "Please provide a keyword or topic.", None
1697
+ text = search_and_compile_content(article_input, language)
1698
+ text = f"Keyword-based content:\n{text}"
1699
+
1700
+ # Limit text to max words
1701
+ words = text.split()
1702
+ if len(words) > converter.config.max_words:
1703
+ text = " ".join(words[:converter.config.max_words])
1704
+
1705
+ # Extract conversation based on mode
1706
+ if mode == "Local":
1707
+ try:
1708
+ conversation_json = converter.extract_conversation_local(text, language)
1709
+ except Exception as e:
1710
+ print(f"Local mode failed: {e}, trying API fallback")
1711
+ api_key = os.environ.get("TOGETHER_API_KEY")
1712
+ if api_key:
1713
+ converter.initialize_api_mode(api_key)
1714
+ conversation_json = converter.extract_conversation_api(text, language)
1715
+ else:
1716
+ raise RuntimeError("Local mode failed and no API key available for fallback")
1717
+ else: # API mode
1718
+ api_key = os.environ.get("TOGETHER_API_KEY")
1719
+ if not api_key:
1720
+ print("API key not found, falling back to local mode")
1721
+ conversation_json = converter.extract_conversation_local(text, language)
1722
+ else:
1723
+ try:
1724
+ converter.initialize_api_mode(api_key)
1725
+ conversation_json = converter.extract_conversation_api(text, language)
1726
+ except Exception as e:
1727
+ print(f"API mode failed: {e}, falling back to local mode")
1728
+ conversation_json = converter.extract_conversation_local(text, language)
1729
+
1730
+ # Generate conversation text
1731
+ conversation_text = "\n".join(
1732
+ f"{turn.get('speaker', f'Speaker {i+1}')}: {turn['text']}"
1733
+ for i, turn in enumerate(conversation_json["conversation"])
1734
+ )
1735
+
1736
+ return conversation_text, None
1737
+
1738
+ except Exception as e:
1739
+ return f"Error: {str(e)}", None
1740
+
1741
+
1742
+ async def regenerate_audio(conversation_text: str, tts_engine: str = "Edge-TTS", language: str = "English"):
1743
+ """Regenerate audio from edited conversation text"""
1744
+ if not conversation_text.strip():
1745
+ return "Please provide conversation text.", None
1746
+
1747
+ try:
1748
+ conversation_json = converter.parse_conversation_text(conversation_text)
1749
+
1750
+ if not conversation_json["conversation"]:
1751
+ return "No valid conversation found in the text.", None
1752
+
1753
+ # Edge TTS ์ „์šฉ ์–ธ์–ด๋Š” ์ž๋™์œผ๋กœ Edge-TTS ์‚ฌ์šฉ
1754
+ if language in EDGE_TTS_ONLY_LANGUAGES and tts_engine != "Edge-TTS":
1755
+ tts_engine = "Edge-TTS"
1756
+
1757
+ # Generate audio based on TTS engine
1758
+ if tts_engine == "Edge-TTS":
1759
+ output_file, _ = await converter.text_to_speech_edge(conversation_json, language)
1760
+ elif tts_engine == "Spark-TTS":
1761
+ if not SPARK_AVAILABLE:
1762
+ return "Spark TTS not available. Please install required dependencies and clone the Spark-TTS repository.", None
1763
+ converter.initialize_spark_tts()
1764
+ output_file, _ = converter.text_to_speech_spark(conversation_json, language)
1765
+ else: # MeloTTS
1766
+ if not MELO_AVAILABLE:
1767
+ return "MeloTTS not available. Please install required dependencies.", None
1768
+ if language in EDGE_TTS_ONLY_LANGUAGES:
1769
+ return f"MeloTTS does not support {language}. Please use Edge-TTS for this language.", None
1770
+ converter.initialize_melo_tts()
1771
+ output_file, _ = converter.text_to_speech_melo(conversation_json)
1772
+
1773
+ return "Audio generated successfully!", output_file
1774
+
1775
+ except Exception as e:
1776
+ return f"Error generating audio: {str(e)}", None
1777
+
1778
+
1779
+ def synthesize_sync(article_input, input_type: str = "URL", mode: str = "Local", tts_engine: str = "Edge-TTS", language: str = "English"):
1780
+ """Synchronous wrapper for async synthesis"""
1781
+ return asyncio.run(synthesize(article_input, input_type, mode, tts_engine, language))
1782
+
1783
+
1784
+ def regenerate_audio_sync(conversation_text: str, tts_engine: str = "Edge-TTS", language: str = "English"):
1785
+ """Synchronous wrapper for async audio regeneration"""
1786
+ return asyncio.run(regenerate_audio(conversation_text, tts_engine, language))
1787
+
1788
+
1789
+ def update_tts_engine_for_language(language):
1790
+ """์–ธ์–ด๋ณ„ TTS ์—”์ง„ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ"""
1791
+ if language in EDGE_TTS_ONLY_LANGUAGES:
1792
+ language_info = {
1793
+ "Korean": "ํ•œ๊ตญ์–ด๋Š” Edge-TTS๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค",
1794
+ "Japanese": "ๆ—ฅๆœฌ่ชžใฏEdge-TTSใฎใฟใ‚ตใƒใƒผใƒˆใ•ใ‚Œใฆใ„ใพใ™",
1795
+ "French": "Le franรงais n'est pris en charge que par Edge-TTS",
1796
+ "German": "Deutsch wird nur von Edge-TTS unterstรผtzt",
1797
+ "Spanish": "El espaรฑol solo es compatible con Edge-TTS",
1798
+ "Italian": "L'italiano รจ supportato solo da Edge-TTS",
1799
+ "Portuguese": "O portuguรชs รฉ suportado apenas pelo Edge-TTS",
1800
+ "Dutch": "Nederlands wordt alleen ondersteund door Edge-TTS",
1801
+ "Thai": "เธ เธฒเธฉเธฒเน„เธ—เธขเธฃเธญเธ‡เธฃเธฑเธšเน€เธ‰เธžเธฒเธฐ Edge-TTS เน€เธ—เนˆเธฒเธ™เธฑเน‰เธ™",
1802
+ "Vietnamese": "Tiแบฟng Viแป‡t chแป‰ ฤ‘ฦฐแปฃc hแป— trแปฃ bแปŸi Edge-TTS",
1803
+ "Arabic": "ุงู„ุนุฑุจูŠุฉ ู…ุฏุนูˆู…ุฉ ูู‚ุท ู…ู† Edge-TTS",
1804
+ "Hebrew": "ืขื‘ืจื™ืช ื ืชืžื›ืช ืจืง ืขืœ ื™ื“ื™ Edge-TTS",
1805
+ "Indonesian": "Bahasa Indonesia hanya didukung oleh Edge-TTS",
1806
+ "Hindi": "เคนเคฟเค‚เคฆเฅ€ เค•เฅ‡เคตเคฒ Edge-TTS เคฆเฅเคตเคพเคฐเคพ เคธเคฎเคฐเฅเคฅเคฟเคค เคนเฅˆ",
1807
+ "Russian": "ะ ัƒััะบะธะน ะฟะพะดะดะตั€ะถะธะฒะฐะตั‚ัั ั‚ะพะปัŒะบะพ Edge-TTS",
1808
+ "Chinese": "ไธญๆ–‡ไป…ๆ”ฏๆŒEdge-TTS"
1809
+ }
1810
+ info_text = language_info.get(language, f"{language} is only supported by Edge-TTS")
1811
+
1812
+ return gr.Radio(
1813
+ choices=["Edge-TTS"],
1814
+ value="Edge-TTS",
1815
+ label="TTS Engine",
1816
+ info=info_text,
1817
+ interactive=False
1818
+ )
1819
+ else:
1820
+ return gr.Radio(
1821
+ choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
1822
+ value="Edge-TTS",
1823
+ label="TTS Engine",
1824
+ info="Edge-TTS: Cloud-based, natural voices | Spark-TTS: Local AI model | MeloTTS: Local, requires GPU",
1825
+ interactive=True
1826
+ )
1827
+
1828
+
1829
+ def toggle_input_visibility(input_type):
1830
+ """Toggle visibility of URL input, file upload, and keyword input based on input type"""
1831
+ if input_type == "URL":
1832
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
1833
+ elif input_type == "PDF":
1834
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
1835
+ else: # Keyword
1836
+ return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)
1837
+
1838
+
1839
+ # ๋ชจ๋ธ ์ดˆ๊ธฐํ™” (์•ฑ ์‹œ์ž‘ ์‹œ)
1840
+ if LLAMA_CPP_AVAILABLE:
1841
+ try:
1842
+ model_path = hf_hub_download(
1843
+ repo_id=converter.config.local_model_repo,
1844
+ filename=converter.config.local_model_name,
1845
+ local_dir="./models"
1846
+ )
1847
+ print(f"Model downloaded to: {model_path}")
1848
+ except Exception as e:
1849
+ print(f"Failed to download model at startup: {e}")
1850
+
1851
+
1852
+ # Gradio Interface - ๊ฐœ์„ ๋œ ๋‹ค๊ตญ์–ด ๋ ˆ์ด์•„์›ƒ
1853
+ with gr.Blocks(theme='soft', title="AI Podcast Generator", css="""
1854
+ .container {max-width: 1200px; margin: auto; padding: 20px;}
1855
+ .header-text {text-align: center; margin-bottom: 30px;}
1856
+ .input-group {background: #f7f7f7; padding: 20px; border-radius: 10px; margin-bottom: 20px;}
1857
+ .output-group {background: #f0f0f0; padding: 20px; border-radius: 10px;}
1858
+ .status-box {background: #e8f4f8; padding: 15px; border-radius: 8px; margin-top: 10px;}
1859
+ """) as demo:
1860
+ with gr.Column(elem_classes="container"):
1861
+ # ํ—ค๋”
1862
+ with gr.Row(elem_classes="header-text"):
1863
+ gr.Markdown("""
1864
+ # ๐ŸŽ™๏ธ AI Podcast Generator - Professional Multi-Language Edition
1865
+ ### Convert any article, blog, PDF document, or topic into an engaging professional podcast conversation in 24+ languages!
1866
+ """)
1867
+
1868
+ with gr.Row(elem_classes="discord-badge"):
1869
+ gr.HTML("""
1870
+ <p style="text-align: center;">
1871
+ <a href="https://discord.gg/openfreeai" target="_blank">
1872
+ <img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="badge">
1873
+ </a>
1874
+ </p>
1875
+ """)
1876
+
1877
+ # ์ƒํƒœ ํ‘œ์‹œ ์„น์…˜
1878
+ with gr.Row():
1879
+ with gr.Column(scale=1):
1880
+ gr.Markdown(f"""
1881
+ #### ๐Ÿค– System Status
1882
+ - **LLM**: {converter.config.local_model_name.split('.')[0]}
1883
+ - **Fallback**: {converter.config.api_model_name.split('/')[-1]}
1884
+ - **Llama CPP**: {"โœ… Ready" if LLAMA_CPP_AVAILABLE else "โŒ Not Available"}
1885
+ - **Search**: {"โœ… Brave API" if BRAVE_KEY else "โŒ No API"}
1886
+ """)
1887
+ with gr.Column(scale=1):
1888
+ gr.Markdown("""
1889
+ #### ๐ŸŒ Multi-Language Support
1890
+ - **24+ Languages**: Korean, Japanese, French, German, Spanish, Italian, etc.
1891
+ - **Native Voices**: Optimized for each language
1892
+ - **Professional Style**: Expert discussions with data & insights
1893
+ - **Auto-TTS Selection**: Best engine per language
1894
+ """)
1895
+
1896
+ # ๋ฉ”์ธ ์ž…๋ ฅ ์„น์…˜
1897
+ with gr.Group(elem_classes="input-group"):
1898
+ with gr.Row():
1899
+ # ์™ผ์ชฝ: ์ž…๋ ฅ ์˜ต์…˜๋“ค
1900
+ with gr.Column(scale=2):
1901
+ # ์ž…๋ ฅ ํƒ€์ž… ์„ ํƒ
1902
+ input_type_selector = gr.Radio(
1903
+ choices=["URL", "PDF", "Keyword"],
1904
+ value="URL",
1905
+ label="๐Ÿ“ฅ Input Type",
1906
+ info="Choose your content source"
1907
+ )
1908
+
1909
+ # URL ์ž…๋ ฅ
1910
+ url_input = gr.Textbox(
1911
+ label="๐Ÿ”— Article URL",
1912
+ placeholder="Enter the article URL here...",
1913
+ value="",
1914
+ visible=True,
1915
+ lines=2
1916
+ )
1917
+
1918
+ # PDF ์—…๋กœ๋“œ
1919
+ pdf_input = gr.File(
1920
+ label="๐Ÿ“„ Upload PDF",
1921
+ file_types=[".pdf"],
1922
+ visible=False
1923
+ )
1924
+
1925
+ # ํ‚ค์›Œ๋“œ ์ž…๋ ฅ
1926
+ keyword_input = gr.Textbox(
1927
+ label="๐Ÿ” Topic/Keyword",
1928
+ placeholder="Enter a topic (e.g., 'AI trends 2024', '์ธ๊ณต์ง€๋Šฅ', 'IA tendances', 'KI Trends')",
1929
+ value="",
1930
+ visible=False,
1931
+ info="System will search and compile latest information",
1932
+ lines=2
1933
+ )
1934
+
1935
+ # ์˜ค๋ฅธ์ชฝ: ์„ค์ • ์˜ต์…˜๋“ค
1936
+ with gr.Column(scale=1):
1937
+ # ์–ธ์–ด ์„ ํƒ
1938
+ language_selector = gr.Radio(
1939
+ choices=[
1940
+ "English", "Korean", "Japanese", "French", "German",
1941
+ "Spanish", "Italian", "Portuguese", "Dutch", "Thai",
1942
+ "Vietnamese", "Arabic", "Hebrew", "Indonesian", "Hindi",
1943
+ "Russian", "Chinese", "Norwegian", "Swedish", "Finnish",
1944
+ "Danish", "Polish", "Turkish", "Greek", "Czech"
1945
+ ],
1946
+ value="English",
1947
+ label="๐ŸŒ Language / ์–ธ์–ด / ่ฏญ่จ€",
1948
+ info="Select podcast language"
1949
+ )
1950
+
1951
+ # ์ฒ˜๋ฆฌ ๋ชจ๋“œ
1952
+ mode_selector = gr.Radio(
1953
+ choices=["Local", "API"],
1954
+ value="Local",
1955
+ label="โš™๏ธ Processing Mode",
1956
+ info="Local: On-device | API: Cloud"
1957
+ )
1958
+
1959
+ # TTS ์—”์ง„
1960
+ tts_selector = gr.Radio(
1961
+ choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
1962
+ value="Edge-TTS",
1963
+ label="๐Ÿ”Š TTS Engine",
1964
+ info="Voice synthesis engine"
1965
+ )
1966
+
1967
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ
1968
+ with gr.Row():
1969
+ convert_btn = gr.Button(
1970
+ "๐ŸŽฏ Generate Professional Conversation",
1971
+ variant="primary",
1972
+ size="lg",
1973
+ scale=1
1974
+ )
1975
+
1976
+ # ์ถœ๋ ฅ ์„น์…˜
1977
+ with gr.Group(elem_classes="output-group"):
1978
+ with gr.Row():
1979
+ # ์™ผ์ชฝ: ๋Œ€ํ™” ํ…์ŠคํŠธ
1980
+ with gr.Column(scale=3):
1981
+ conversation_output = gr.Textbox(
1982
+ label="๐Ÿ’ฌ Generated Professional Conversation (Editable)",
1983
+ lines=25,
1984
+ max_lines=50,
1985
+ interactive=True,
1986
+ placeholder="Professional podcast conversation will appear here...\n์ „๋ฌธ ํŒŸ์บ์ŠคํŠธ ๋Œ€ํ™”๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค...\nLa conversation professionnelle du podcast apparaรฎtra ici...",
1987
+ info="Edit the conversation as needed. Format: 'Speaker Name: Text'"
1988
+ )
1989
+
1990
+ # ์˜ค๋””์˜ค ์ƒ์„ฑ ๋ฒ„ํŠผ
1991
+ with gr.Row():
1992
+ generate_audio_btn = gr.Button(
1993
+ "๐ŸŽ™๏ธ Generate Audio from Text",
1994
+ variant="secondary",
1995
+ size="lg"
1996
+ )
1997
+
1998
+ # ์˜ค๋ฅธ์ชฝ: ์˜ค๋””์˜ค ์ถœ๋ ฅ ๋ฐ ์ƒํƒœ
1999
+ with gr.Column(scale=2):
2000
+ audio_output = gr.Audio(
2001
+ label="๐ŸŽง Professional Podcast Audio",
2002
+ type="filepath",
2003
+ interactive=False
2004
+ )
2005
+
2006
+ status_output = gr.Textbox(
2007
+ label="๐Ÿ“Š Status",
2008
+ interactive=False,
2009
+ lines=3,
2010
+ elem_classes="status-box"
2011
+ )
2012
+
2013
+ # ๋„์›€๋ง
2014
+ gr.Markdown("""
2015
+ #### ๐Ÿ’ก Quick Tips:
2016
+ - **URL**: Paste any article link
2017
+ - **PDF**: Upload documents directly
2018
+ - **Keyword**: Enter topics for AI research
2019
+ - **24+ Languages** fully supported
2020
+ - Edit conversation before audio generation
2021
+ - Auto TTS engine selection per language
2022
+ """)
2023
+
2024
+ # ์˜ˆ์ œ ์„น์…˜
2025
+ with gr.Accordion("๐Ÿ“š Multi-Language Examples", open=False):
2026
+ gr.Examples(
2027
+ examples=[
2028
+ ["https://huggingface.co/blog/openfreeai/cycle-navigator", "URL", "Local", "Edge-TTS", "English"],
2029
+ ["quantum computing breakthroughs", "Keyword", "Local", "Edge-TTS", "English"],
2030
+ ["์ธ๊ณต์ง€๋Šฅ ์œค๋ฆฌ์™€ ๊ทœ์ œ", "Keyword", "Local", "Edge-TTS", "Korean"],
2031
+ ["https://huggingface.co/papers/2505.14810", "URL", "Local", "Edge-TTS", "Japanese"],
2032
+ ["intelligence artificielle tendances", "Keyword", "Local", "Edge-TTS", "French"],
2033
+ ["kรผnstliche intelligenz entwicklung", "Keyword", "Local", "Edge-TTS", "German"],
2034
+ ["inteligencia artificial avances", "Keyword", "Local", "Edge-TTS", "Spanish"],
2035
+ ],
2036
+ inputs=[url_input, input_type_selector, mode_selector, tts_selector, language_selector],
2037
+ outputs=[conversation_output, status_output],
2038
+ fn=synthesize_sync,
2039
+ cache_examples=False,
2040
+ )
2041
+
2042
+ # Input type change handler
2043
+ input_type_selector.change(
2044
+ fn=toggle_input_visibility,
2045
+ inputs=[input_type_selector],
2046
+ outputs=[url_input, pdf_input, keyword_input]
2047
+ )
2048
+
2049
+ # ์–ธ์–ด ๋ณ€๊ฒฝ ์‹œ TTS ์—”์ง„ ์˜ต์…˜ ์—…๋ฐ์ดํŠธ
2050
+ language_selector.change(
2051
+ fn=update_tts_engine_for_language,
2052
+ inputs=[language_selector],
2053
+ outputs=[tts_selector]
2054
+ )
2055
+
2056
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
2057
+ def get_article_input(input_type, url_input, pdf_input, keyword_input):
2058
+ """Get the appropriate input based on input type"""
2059
+ if input_type == "URL":
2060
+ return url_input
2061
+ elif input_type == "PDF":
2062
+ return pdf_input
2063
+ else: # Keyword
2064
+ return keyword_input
2065
+
2066
+ convert_btn.click(
2067
+ fn=lambda input_type, url_input, pdf_input, keyword_input, mode, tts, lang: synthesize_sync(
2068
+ get_article_input(input_type, url_input, pdf_input, keyword_input), input_type, mode, tts, lang
2069
+ ),
2070
+ inputs=[input_type_selector, url_input, pdf_input, keyword_input, mode_selector, tts_selector, language_selector],
2071
+ outputs=[conversation_output, status_output]
2072
+ )
2073
+
2074
+ generate_audio_btn.click(
2075
+ fn=regenerate_audio_sync,
2076
+ inputs=[conversation_output, tts_selector, language_selector],
2077
+ outputs=[status_output, audio_output]
2078
+ )
2079
+
2080
+
2081
+ # Launch the app
2082
+ if __name__ == "__main__":
2083
+ demo.queue(api_open=True, default_concurrency_limit=10).launch(
2084
+ show_api=True,
2085
+ share=False,
2086
+ server_name="0.0.0.0",
2087
+ server_port=7860
2088
+ )