Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -973,9 +973,9 @@ class UnifiedAudioConverter:
|
|
973 |
except Exception as e:
|
974 |
raise RuntimeError(f"Failed to extract conversation: {e}")
|
975 |
|
976 |
-
def parse_conversation_text(self,
|
977 |
"""Parse conversation text back to JSON format"""
|
978 |
-
lines =
|
979 |
conversation_data = {"conversation": []}
|
980 |
|
981 |
for line in lines:
|
@@ -1327,7 +1327,7 @@ def update_tts_engine_for_korean(language):
|
|
1327 |
choices=["Edge-TTS"],
|
1328 |
value="Edge-TTS",
|
1329 |
label="TTS Engine",
|
1330 |
-
info="
|
1331 |
interactive=False
|
1332 |
)
|
1333 |
else:
|
@@ -1363,399 +1363,192 @@ if LLAMA_CPP_AVAILABLE:
|
|
1363 |
print(f"Failed to download model at startup: {e}")
|
1364 |
|
1365 |
|
1366 |
-
#
|
1367 |
-
|
1368 |
-
|
1369 |
-
|
1370 |
-
|
1371 |
-
.
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
|
1377 |
-
|
1378 |
-
|
1379 |
-
|
1380 |
-
|
1381 |
-
|
1382 |
-
|
1383 |
-
|
1384 |
-
|
1385 |
-
|
1386 |
-
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
}
|
1391 |
-
|
1392 |
-
|
1393 |
-
.gr-markdown h1 {
|
1394 |
-
background: linear-gradient(120deg, #ffffff 0%, #e0e0e0 100%);
|
1395 |
-
-webkit-background-clip: text;
|
1396 |
-
-webkit-text-fill-color: transparent;
|
1397 |
-
font-size: 3em !important;
|
1398 |
-
font-weight: 700 !important;
|
1399 |
-
text-align: center !important;
|
1400 |
-
margin-bottom: 0.5em !important;
|
1401 |
-
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
1402 |
-
}
|
1403 |
-
|
1404 |
-
/* Subheader styling */
|
1405 |
-
.subtitle {
|
1406 |
-
text-align: center !important;
|
1407 |
-
color: rgba(255, 255, 255, 0.9) !important;
|
1408 |
-
font-size: 0.95em !important;
|
1409 |
-
margin-bottom: 1.5em !important;
|
1410 |
-
font-weight: 400 !important;
|
1411 |
-
line-height: 1.6 !important;
|
1412 |
-
}
|
1413 |
-
|
1414 |
-
/* Card-like sections */
|
1415 |
-
.gr-form, .gr-box {
|
1416 |
-
background: rgba(255, 255, 255, 0.95) !important;
|
1417 |
-
backdrop-filter: blur(10px) !important;
|
1418 |
-
border-radius: 16px !important;
|
1419 |
-
padding: 24px !important;
|
1420 |
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important;
|
1421 |
-
border: 1px solid rgba(255, 255, 255, 0.2) !important;
|
1422 |
-
margin-bottom: 20px !important;
|
1423 |
-
}
|
1424 |
-
|
1425 |
-
/* Button styling */
|
1426 |
-
.gr-button {
|
1427 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
1428 |
-
border: none !important;
|
1429 |
-
color: white !important;
|
1430 |
-
padding: 12px 24px !important;
|
1431 |
-
font-weight: 600 !important;
|
1432 |
-
font-size: 1.05em !important;
|
1433 |
-
border-radius: 8px !important;
|
1434 |
-
cursor: pointer !important;
|
1435 |
-
transition: all 0.3s ease !important;
|
1436 |
-
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4) !important;
|
1437 |
-
}
|
1438 |
-
|
1439 |
-
.gr-button:hover {
|
1440 |
-
transform: translateY(-2px) !important;
|
1441 |
-
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6) !important;
|
1442 |
-
}
|
1443 |
-
|
1444 |
-
.gr-button.primary {
|
1445 |
-
background: linear-gradient(135deg, #8E37D7 0%, #667eea 100%) !important;
|
1446 |
-
font-size: 1.1em !important;
|
1447 |
-
padding: 14px 28px !important;
|
1448 |
-
}
|
1449 |
-
|
1450 |
-
.gr-button.secondary {
|
1451 |
-
background: linear-gradient(135deg, #764ba2 0%, #6B8DD6 100%) !important;
|
1452 |
-
}
|
1453 |
-
|
1454 |
-
/* Input fields styling */
|
1455 |
-
.gr-textbox, .gr-dropdown {
|
1456 |
-
border: 2px solid #e1e4f5 !important;
|
1457 |
-
border-radius: 8px !important;
|
1458 |
-
font-size: 1em !important;
|
1459 |
-
transition: all 0.3s ease !important;
|
1460 |
-
background-color: rgba(255, 255, 255, 0.9) !important;
|
1461 |
-
}
|
1462 |
-
|
1463 |
-
.gr-textbox:focus, .gr-dropdown:focus {
|
1464 |
-
border-color: #667eea !important;
|
1465 |
-
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
|
1466 |
-
}
|
1467 |
-
|
1468 |
-
/* Radio buttons styling */
|
1469 |
-
.gr-radio {
|
1470 |
-
background: rgba(255, 255, 255, 0.8) !important;
|
1471 |
-
border-radius: 8px !important;
|
1472 |
-
padding: 12px !important;
|
1473 |
-
}
|
1474 |
-
|
1475 |
-
.gr-radio label {
|
1476 |
-
color: #374151 !important;
|
1477 |
-
font-weight: 500 !important;
|
1478 |
-
cursor: pointer !important;
|
1479 |
-
transition: all 0.2s ease !important;
|
1480 |
-
}
|
1481 |
-
|
1482 |
-
.gr-radio input[type="radio"]:checked + label {
|
1483 |
-
color: #667eea !important;
|
1484 |
-
font-weight: 600 !important;
|
1485 |
-
}
|
1486 |
-
|
1487 |
-
/* Tab styling */
|
1488 |
-
.gr-tabs {
|
1489 |
-
border-radius: 12px !important;
|
1490 |
-
background: rgba(255, 255, 255, 0.9) !important;
|
1491 |
-
padding: 4px !important;
|
1492 |
-
}
|
1493 |
-
|
1494 |
-
.gr-tab {
|
1495 |
-
border-radius: 8px !important;
|
1496 |
-
padding: 10px 20px !important;
|
1497 |
-
font-weight: 500 !important;
|
1498 |
-
transition: all 0.3s ease !important;
|
1499 |
-
}
|
1500 |
-
|
1501 |
-
.gr-tab.selected {
|
1502 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
1503 |
-
color: white !important;
|
1504 |
-
}
|
1505 |
-
|
1506 |
-
/* Status message styling */
|
1507 |
-
.gr-markdown.status {
|
1508 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
1509 |
-
color: white !important;
|
1510 |
-
padding: 16px !important;
|
1511 |
-
border-radius: 8px !important;
|
1512 |
-
margin: 16px 0 !important;
|
1513 |
-
font-weight: 500 !important;
|
1514 |
-
}
|
1515 |
-
|
1516 |
-
/* Textbox for conversation output */
|
1517 |
-
.conversation-output {
|
1518 |
-
background: rgba(249, 250, 251, 0.95) !important;
|
1519 |
-
border: 2px solid #e5e7eb !important;
|
1520 |
-
border-radius: 12px !important;
|
1521 |
-
font-family: 'Inter', monospace !important;
|
1522 |
-
font-size: 0.95em !important;
|
1523 |
-
line-height: 1.8 !important;
|
1524 |
-
padding: 20px !important;
|
1525 |
-
}
|
1526 |
-
|
1527 |
-
/* Examples section */
|
1528 |
-
.gr-examples {
|
1529 |
-
background: rgba(255, 255, 255, 0.8) !important;
|
1530 |
-
border-radius: 12px !important;
|
1531 |
-
padding: 20px !important;
|
1532 |
-
margin-top: 24px !important;
|
1533 |
-
}
|
1534 |
-
|
1535 |
-
/* Badge container */
|
1536 |
-
.badge-container {
|
1537 |
-
text-align: center !important;
|
1538 |
-
margin-bottom: 1em !important;
|
1539 |
-
}
|
1540 |
-
|
1541 |
-
/* Info boxes */
|
1542 |
-
.info-box {
|
1543 |
-
background: rgba(102, 126, 234, 0.1) !important;
|
1544 |
-
border-left: 4px solid #667eea !important;
|
1545 |
-
padding: 16px !important;
|
1546 |
-
border-radius: 8px !important;
|
1547 |
-
margin: 16px 0 !important;
|
1548 |
-
}
|
1549 |
-
|
1550 |
-
/* Loading animation */
|
1551 |
-
.gr-loading {
|
1552 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
1553 |
-
}
|
1554 |
-
|
1555 |
-
/* Audio player styling */
|
1556 |
-
.gr-audio {
|
1557 |
-
border-radius: 8px !important;
|
1558 |
-
background: rgba(255, 255, 255, 0.9) !important;
|
1559 |
-
padding: 16px !important;
|
1560 |
-
}
|
1561 |
-
|
1562 |
-
/* Group styling */
|
1563 |
-
.gr-group {
|
1564 |
-
background: rgba(255, 255, 255, 0.05) !important;
|
1565 |
-
border-radius: 12px !important;
|
1566 |
-
padding: 16px !important;
|
1567 |
-
margin: 12px 0 !important;
|
1568 |
-
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
1569 |
-
}
|
1570 |
-
|
1571 |
-
/* Markdown content in groups */
|
1572 |
-
.gr-group .gr-markdown {
|
1573 |
-
color: #1f2937 !important;
|
1574 |
-
font-size: 0.95em !important;
|
1575 |
-
line-height: 1.6 !important;
|
1576 |
-
}
|
1577 |
-
|
1578 |
-
/* Status output specific styling */
|
1579 |
-
#status_output {
|
1580 |
-
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
|
1581 |
-
color: white !important;
|
1582 |
-
padding: 12px 20px !important;
|
1583 |
-
border-radius: 8px !important;
|
1584 |
-
font-weight: 500 !important;
|
1585 |
-
text-align: center !important;
|
1586 |
-
}
|
1587 |
-
|
1588 |
-
/* Error message styling */
|
1589 |
-
.error-message {
|
1590 |
-
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%) !important;
|
1591 |
-
color: white !important;
|
1592 |
-
padding: 12px 20px !important;
|
1593 |
-
border-radius: 8px !important;
|
1594 |
-
}
|
1595 |
-
"""
|
1596 |
-
|
1597 |
-
# Gradio Interface
|
1598 |
-
with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="AI Podcast Generator") as demo:
|
1599 |
-
# Discord Badge
|
1600 |
-
gr.HTML("""
|
1601 |
-
<div class="badge-container">
|
1602 |
-
<p>
|
1603 |
-
<a href="https://discord.gg/openfreeai" target="_blank">
|
1604 |
-
<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">
|
1605 |
-
</a>
|
1606 |
-
</p>
|
1607 |
-
</div>
|
1608 |
-
""")
|
1609 |
-
|
1610 |
-
gr.Markdown("# ๐๏ธ AI Podcast Generator - Professional Edition")
|
1611 |
-
gr.Markdown(
|
1612 |
-
"""<div class="subtitle">
|
1613 |
-
Transform any content into engaging professional podcast conversations with deep insights and expert analysis<br>
|
1614 |
-
Powered by advanced AI with real-time web search integration for comprehensive, up-to-date discussions
|
1615 |
-
</div>""",
|
1616 |
-
elem_classes=["subtitle"]
|
1617 |
-
)
|
1618 |
-
|
1619 |
-
# Status display
|
1620 |
-
with gr.Row():
|
1621 |
-
gr.Markdown(f"""
|
1622 |
-
### ๐ค System Configuration:
|
1623 |
-
- **Primary Mode**: Local LLM ({converter.config.local_model_name})
|
1624 |
-
- **Fallback**: API ({converter.config.api_model_name})
|
1625 |
-
- **Status**: {"โ
Ready" if LLAMA_CPP_AVAILABLE else "โ Install llama-cpp-python"}
|
1626 |
-
- **Search**: {"โ
Enabled" if BRAVE_KEY else "โ Set BSEARCH_API"}
|
1627 |
-
""", elem_classes=["info-box"])
|
1628 |
-
|
1629 |
-
with gr.Row():
|
1630 |
-
with gr.Column(scale=3):
|
1631 |
-
# Input type selector
|
1632 |
-
input_type_selector = gr.Radio(
|
1633 |
-
choices=["URL", "PDF", "Keyword"],
|
1634 |
-
value="URL",
|
1635 |
-
label="Input Type",
|
1636 |
-
info="Choose your content source"
|
1637 |
-
)
|
1638 |
-
|
1639 |
-
# URL input
|
1640 |
-
url_input = gr.Textbox(
|
1641 |
-
label="Article URL",
|
1642 |
-
placeholder="Enter the article URL here...",
|
1643 |
-
value="",
|
1644 |
-
visible=True
|
1645 |
-
)
|
1646 |
-
|
1647 |
-
# PDF upload
|
1648 |
-
pdf_input = gr.File(
|
1649 |
-
label="Upload PDF",
|
1650 |
-
file_types=[".pdf"],
|
1651 |
-
visible=False
|
1652 |
-
)
|
1653 |
-
|
1654 |
-
# Keyword input
|
1655 |
-
keyword_input = gr.Textbox(
|
1656 |
-
label="Topic/Keyword",
|
1657 |
-
placeholder="Enter a topic (e.g., 'AI trends', 'quantum computing')",
|
1658 |
-
value="",
|
1659 |
-
visible=False,
|
1660 |
-
info="AI will search and create comprehensive content about this topic"
|
1661 |
-
)
|
1662 |
-
|
1663 |
-
with gr.Column(scale=1):
|
1664 |
-
# Language selector
|
1665 |
-
language_selector = gr.Radio(
|
1666 |
-
choices=["English", "Korean"],
|
1667 |
-
value="English",
|
1668 |
-
label="Language",
|
1669 |
-
info="Select output language"
|
1670 |
-
)
|
1671 |
-
|
1672 |
-
mode_selector = gr.Radio(
|
1673 |
-
choices=["Local", "API"],
|
1674 |
-
value="Local",
|
1675 |
-
label="Processing Mode",
|
1676 |
-
info="Local: On-device | API: Cloud"
|
1677 |
-
)
|
1678 |
-
|
1679 |
-
# TTS Engine
|
1680 |
-
with gr.Group():
|
1681 |
-
gr.Markdown("### TTS Engine")
|
1682 |
-
tts_selector = gr.Radio(
|
1683 |
-
choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
|
1684 |
-
value="Edge-TTS",
|
1685 |
-
label="Voice Engine",
|
1686 |
-
info="Select voice synthesis engine"
|
1687 |
-
)
|
1688 |
-
|
1689 |
gr.Markdown("""
|
1690 |
-
|
1691 |
-
-
|
1692 |
-
-
|
1693 |
-
-
|
1694 |
-
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
with gr.Row():
|
1701 |
-
with gr.Column():
|
1702 |
-
conversation_output = gr.Textbox(
|
1703 |
-
label="Generated Professional Conversation (Editable)",
|
1704 |
-
lines=35,
|
1705 |
-
max_lines=70,
|
1706 |
-
interactive=True,
|
1707 |
-
placeholder="Professional podcast conversation will appear here...",
|
1708 |
-
elem_classes=["conversation-output"]
|
1709 |
-
)
|
1710 |
-
|
1711 |
-
# Audio generation button
|
1712 |
with gr.Row():
|
1713 |
-
|
1714 |
-
gr.
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1722 |
|
1723 |
-
#
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1729 |
)
|
1730 |
|
1731 |
-
|
1732 |
-
gr.Examples(
|
1733 |
-
examples=[
|
1734 |
-
["https://huggingface.co/blog/openfree/cycle-navigator", "URL", "Local", "Edge-TTS", "English"],
|
1735 |
-
["quantum computing breakthroughs", "Keyword", "Local", "Edge-TTS", "English"],
|
1736 |
-
["https://huggingface.co/papers/2505.14810", "URL", "Local", "Edge-TTS", "English"],
|
1737 |
-
["AI ethics and regulation", "Keyword", "Local", "Edge-TTS", "English"],
|
1738 |
-
],
|
1739 |
-
inputs=[url_input, input_type_selector, mode_selector, tts_selector, language_selector],
|
1740 |
-
outputs=[conversation_output, status_output],
|
1741 |
-
fn=synthesize_sync,
|
1742 |
-
cache_examples=False,
|
1743 |
-
)
|
1744 |
-
|
1745 |
-
# Event handlers
|
1746 |
input_type_selector.change(
|
1747 |
fn=toggle_input_visibility,
|
1748 |
inputs=[input_type_selector],
|
1749 |
outputs=[url_input, pdf_input, keyword_input]
|
1750 |
)
|
1751 |
|
|
|
1752 |
language_selector.change(
|
1753 |
fn=update_tts_engine_for_korean,
|
1754 |
inputs=[language_selector],
|
1755 |
outputs=[tts_selector]
|
1756 |
)
|
1757 |
|
1758 |
-
#
|
1759 |
def get_article_input(input_type, url_input, pdf_input, keyword_input):
|
1760 |
"""Get the appropriate input based on input type"""
|
1761 |
if input_type == "URL":
|
@@ -1785,7 +1578,6 @@ if __name__ == "__main__":
|
|
1785 |
demo.queue(api_open=True, default_concurrency_limit=10).launch(
|
1786 |
show_api=True,
|
1787 |
share=False,
|
1788 |
-
mcp_server=True,
|
1789 |
server_name="0.0.0.0",
|
1790 |
server_port=7860
|
1791 |
-
)
|
|
|
973 |
except Exception as e:
|
974 |
raise RuntimeError(f"Failed to extract conversation: {e}")
|
975 |
|
976 |
+
def parse_conversation_text(self, conversation_text: str) -> Dict:
|
977 |
"""Parse conversation text back to JSON format"""
|
978 |
+
lines = conversation_text.strip().split('\n')
|
979 |
conversation_data = {"conversation": []}
|
980 |
|
981 |
for line in lines:
|
|
|
1327 |
choices=["Edge-TTS"],
|
1328 |
value="Edge-TTS",
|
1329 |
label="TTS Engine",
|
1330 |
+
info="ํ๊ตญ์ด๋ Edge-TTS๋ง ์ง์๋ฉ๋๋ค",
|
1331 |
interactive=False
|
1332 |
)
|
1333 |
else:
|
|
|
1363 |
print(f"Failed to download model at startup: {e}")
|
1364 |
|
1365 |
|
1366 |
+
# Gradio Interface - ๊ฐ์ ๋ ๋ ์ด์์
|
1367 |
+
with gr.Blocks(theme='soft', title="AI Podcast Generator", css="""
|
1368 |
+
.container {max-width: 1200px; margin: auto; padding: 20px;}
|
1369 |
+
.header-text {text-align: center; margin-bottom: 30px;}
|
1370 |
+
.input-group {background: #f7f7f7; padding: 20px; border-radius: 10px; margin-bottom: 20px;}
|
1371 |
+
.output-group {background: #f0f0f0; padding: 20px; border-radius: 10px;}
|
1372 |
+
.status-box {background: #e8f4f8; padding: 15px; border-radius: 8px; margin-top: 10px;}
|
1373 |
+
""") as demo:
|
1374 |
+
with gr.Column(elem_classes="container"):
|
1375 |
+
# ํค๋
|
1376 |
+
with gr.Row(elem_classes="header-text"):
|
1377 |
+
gr.Markdown("""
|
1378 |
+
# ๐๏ธ AI Podcast Generator - Professional Edition
|
1379 |
+
### Convert any article, blog, PDF document, or topic into an engaging professional podcast conversation with in-depth analysis!
|
1380 |
+
""")
|
1381 |
+
|
1382 |
+
# ์ํ ํ์ ์น์
|
1383 |
+
with gr.Row():
|
1384 |
+
with gr.Column(scale=1):
|
1385 |
+
gr.Markdown(f"""
|
1386 |
+
#### ๐ค System Status
|
1387 |
+
- **LLM**: {converter.config.local_model_name.split('.')[0]}
|
1388 |
+
- **Fallback**: {converter.config.api_model_name.split('/')[-1]}
|
1389 |
+
- **Llama CPP**: {"โ
Ready" if LLAMA_CPP_AVAILABLE else "โ Not Available"}
|
1390 |
+
- **Search**: {"โ
Brave API" if BRAVE_KEY else "โ No API"}
|
1391 |
+
""")
|
1392 |
+
with gr.Column(scale=1):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1393 |
gr.Markdown("""
|
1394 |
+
#### ๐ป Podcast Features
|
1395 |
+
- **Length**: 12-15 professional exchanges
|
1396 |
+
- **Style**: Expert discussions with data & insights
|
1397 |
+
- **Languages**: English & Korean (ํ๊ตญ์ด)
|
1398 |
+
- **Input**: URL, PDF, or Keywords
|
1399 |
+
""")
|
1400 |
+
|
1401 |
+
# ๋ฉ์ธ ์
๋ ฅ ์น์
|
1402 |
+
with gr.Group(elem_classes="input-group"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1403 |
with gr.Row():
|
1404 |
+
# ์ผ์ชฝ: ์
๋ ฅ ์ต์
๋ค
|
1405 |
+
with gr.Column(scale=2):
|
1406 |
+
# ์
๋ ฅ ํ์
์ ํ
|
1407 |
+
input_type_selector = gr.Radio(
|
1408 |
+
choices=["URL", "PDF", "Keyword"],
|
1409 |
+
value="URL",
|
1410 |
+
label="๐ฅ Input Type",
|
1411 |
+
info="Choose your content source"
|
1412 |
+
)
|
1413 |
+
|
1414 |
+
# URL ์
๋ ฅ
|
1415 |
+
url_input = gr.Textbox(
|
1416 |
+
label="๐ Article URL",
|
1417 |
+
placeholder="Enter the article URL here...",
|
1418 |
+
value="",
|
1419 |
+
visible=True,
|
1420 |
+
lines=2
|
1421 |
+
)
|
1422 |
+
|
1423 |
+
# PDF ์
๋ก๋
|
1424 |
+
pdf_input = gr.File(
|
1425 |
+
label="๐ Upload PDF",
|
1426 |
+
file_types=[".pdf"],
|
1427 |
+
visible=False
|
1428 |
+
)
|
1429 |
+
|
1430 |
+
# ํค์๋ ์
๋ ฅ
|
1431 |
+
keyword_input = gr.Textbox(
|
1432 |
+
label="๐ Topic/Keyword",
|
1433 |
+
placeholder="Enter a topic (e.g., 'AI trends 2024', '์ธ๊ณต์ง๋ฅ ์ต์ ๋ํฅ')",
|
1434 |
+
value="",
|
1435 |
+
visible=False,
|
1436 |
+
info="System will search and compile latest information",
|
1437 |
+
lines=2
|
1438 |
+
)
|
1439 |
+
|
1440 |
+
# ์ค๋ฅธ์ชฝ: ์ค์ ์ต์
๋ค
|
1441 |
+
with gr.Column(scale=1):
|
1442 |
+
# ์ธ์ด ์ ํ
|
1443 |
+
language_selector = gr.Radio(
|
1444 |
+
choices=["English", "Korean"],
|
1445 |
+
value="English",
|
1446 |
+
label="๐ Language / ์ธ์ด",
|
1447 |
+
info="Output language"
|
1448 |
+
)
|
1449 |
+
|
1450 |
+
# ์ฒ๋ฆฌ ๋ชจ๋
|
1451 |
+
mode_selector = gr.Radio(
|
1452 |
+
choices=["Local", "API"],
|
1453 |
+
value="Local",
|
1454 |
+
label="โ๏ธ Processing Mode",
|
1455 |
+
info="Local: On-device | API: Cloud"
|
1456 |
+
)
|
1457 |
+
|
1458 |
+
# TTS ์์ง
|
1459 |
+
tts_selector = gr.Radio(
|
1460 |
+
choices=["Edge-TTS", "Spark-TTS", "MeloTTS"],
|
1461 |
+
value="Edge-TTS",
|
1462 |
+
label="๐ TTS Engine",
|
1463 |
+
info="Voice synthesis engine"
|
1464 |
+
)
|
1465 |
|
1466 |
+
# ์์ฑ ๋ฒํผ
|
1467 |
+
with gr.Row():
|
1468 |
+
convert_btn = gr.Button(
|
1469 |
+
"๐ฏ Generate Professional Conversation",
|
1470 |
+
variant="primary",
|
1471 |
+
size="lg",
|
1472 |
+
scale=1
|
1473 |
+
)
|
1474 |
+
|
1475 |
+
# ์ถ๋ ฅ ์น์
|
1476 |
+
with gr.Group(elem_classes="output-group"):
|
1477 |
+
with gr.Row():
|
1478 |
+
# ์ผ์ชฝ: ๋ํ ํ
์คํธ
|
1479 |
+
with gr.Column(scale=3):
|
1480 |
+
conversation_output = gr.Textbox(
|
1481 |
+
label="๐ฌ Generated Professional Conversation (Editable)",
|
1482 |
+
lines=25,
|
1483 |
+
max_lines=50,
|
1484 |
+
interactive=True,
|
1485 |
+
placeholder="Professional podcast conversation will appear here...\n์ ๋ฌธ ํ์บ์คํธ ๋ํ๊ฐ ์ฌ๊ธฐ์ ํ์๋ฉ๋๋ค...",
|
1486 |
+
info="Edit the conversation as needed. Format: 'Speaker Name: Text'"
|
1487 |
+
)
|
1488 |
+
|
1489 |
+
# ์ค๋์ค ์์ฑ ๋ฒํผ
|
1490 |
+
with gr.Row():
|
1491 |
+
generate_audio_btn = gr.Button(
|
1492 |
+
"๐๏ธ Generate Audio from Text",
|
1493 |
+
variant="secondary",
|
1494 |
+
size="lg"
|
1495 |
+
)
|
1496 |
+
|
1497 |
+
# ์ค๋ฅธ์ชฝ: ์ค๋์ค ์ถ๋ ฅ ๋ฐ ์ํ
|
1498 |
+
with gr.Column(scale=2):
|
1499 |
+
audio_output = gr.Audio(
|
1500 |
+
label="๐ง Professional Podcast Audio",
|
1501 |
+
type="filepath",
|
1502 |
+
interactive=False
|
1503 |
+
)
|
1504 |
+
|
1505 |
+
status_output = gr.Textbox(
|
1506 |
+
label="๐ Status",
|
1507 |
+
interactive=False,
|
1508 |
+
lines=3,
|
1509 |
+
elem_classes="status-box"
|
1510 |
+
)
|
1511 |
+
|
1512 |
+
# ๋์๋ง
|
1513 |
+
gr.Markdown("""
|
1514 |
+
#### ๐ก Quick Tips:
|
1515 |
+
- **URL**: Paste any article link
|
1516 |
+
- **PDF**: Upload documents directly
|
1517 |
+
- **Keyword**: Enter topics for AI research
|
1518 |
+
- Edit conversation before audio generation
|
1519 |
+
- Korean (ํ๊ตญ์ด) fully supported
|
1520 |
+
""")
|
1521 |
+
|
1522 |
+
# ์์ ์น์
|
1523 |
+
with gr.Accordion("๐ Examples", open=False):
|
1524 |
+
gr.Examples(
|
1525 |
+
examples=[
|
1526 |
+
["https://huggingface.co/blog/openfree/cycle-navigator", "URL", "Local", "Edge-TTS", "English"],
|
1527 |
+
["quantum computing breakthroughs", "Keyword", "Local", "Edge-TTS", "English"],
|
1528 |
+
["https://huggingface.co/papers/2505.14810", "URL", "Local", "Edge-TTS", "Korean"],
|
1529 |
+
["์ธ๊ณต์ง๋ฅ ์ค๋ฆฌ์ ๊ท์ ", "Keyword", "Local", "Edge-TTS", "Korean"],
|
1530 |
+
],
|
1531 |
+
inputs=[url_input, input_type_selector, mode_selector, tts_selector, language_selector],
|
1532 |
+
outputs=[conversation_output, status_output],
|
1533 |
+
fn=synthesize_sync,
|
1534 |
+
cache_examples=False,
|
1535 |
)
|
1536 |
|
1537 |
+
# Input type change handler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1538 |
input_type_selector.change(
|
1539 |
fn=toggle_input_visibility,
|
1540 |
inputs=[input_type_selector],
|
1541 |
outputs=[url_input, pdf_input, keyword_input]
|
1542 |
)
|
1543 |
|
1544 |
+
# ์ธ์ด ๋ณ๊ฒฝ ์ TTS ์์ง ์ต์
์
๋ฐ์ดํธ
|
1545 |
language_selector.change(
|
1546 |
fn=update_tts_engine_for_korean,
|
1547 |
inputs=[language_selector],
|
1548 |
outputs=[tts_selector]
|
1549 |
)
|
1550 |
|
1551 |
+
# ์ด๋ฒคํธ ์ฐ๊ฒฐ
|
1552 |
def get_article_input(input_type, url_input, pdf_input, keyword_input):
|
1553 |
"""Get the appropriate input based on input type"""
|
1554 |
if input_type == "URL":
|
|
|
1578 |
demo.queue(api_open=True, default_concurrency_limit=10).launch(
|
1579 |
show_api=True,
|
1580 |
share=False,
|
|
|
1581 |
server_name="0.0.0.0",
|
1582 |
server_port=7860
|
1583 |
+
)
|