Spaces:
Sleeping
Sleeping
Commit ·
00c0222
1
Parent(s): 1c3d8d3
discord
Browse files- README.md +55 -1
- __pycache__/app.cpython-313.pyc +0 -0
- app.py +133 -0
- prompts.py +1 -0
README.md
CHANGED
|
@@ -50,10 +50,18 @@ PDF2Podcast 是一個強大的文件轉換工具,能夠將 PDF、TXT 和 EPUB
|
|
| 50 |
- **極簡摘要**:200 字以內的節目介紹,適合平台描述
|
| 51 |
- **一鍵生成**:從腳本直接生成多種格式的摘要內容
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
### 介面優化
|
| 54 |
- **簡化設計**:隱藏複雜的多段式提示詞欄位
|
| 55 |
- **專注核心**:保留模板選擇和自定義提示詞功能
|
| 56 |
-
- **
|
| 57 |
|
| 58 |
## 功能特點
|
| 59 |
|
|
@@ -65,6 +73,11 @@ PDF2Podcast 是一個強大的文件轉換工具,能夠將 PDF、TXT 和 EPUB
|
|
| 65 |
- 講座腳本(單一演講者)
|
| 66 |
- 一般摘要和簡短摘要
|
| 67 |
- **新增**:博客式摘要和極簡摘要
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
- **智能品質控制**:自動檢測對話品質和連貫性
|
| 69 |
- **彈性 API 整合**:支援 OpenAI API 及其他相容的 API 端點
|
| 70 |
- **模型選擇**:可從連接的 API 獲取並選擇可用的語言模型
|
|
@@ -144,6 +157,31 @@ PDF2podcast-1-script/
|
|
| 144 |
- 選擇摘要類型(博客式 / 極簡)
|
| 145 |
- 點擊「生成摘要」按鈕
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
### 進階功能
|
| 148 |
|
| 149 |
#### 自定義提示詞
|
|
@@ -206,6 +244,21 @@ PDF2podcast-1-script/
|
|
| 206 |
- 確認 API Base URL 格式
|
| 207 |
- 查看錯誤日誌獲取詳細資訊
|
| 208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
#### 品質評分偏低
|
| 210 |
- 檢查原始內容品質
|
| 211 |
- 考慮調整提示詞模板
|
|
@@ -240,6 +293,7 @@ PDF2podcast-1-script/
|
|
| 240 |
- 🎯 **重大重構**:模組化架構,分離關注點
|
| 241 |
- 🚀 **解決斷裂**:提升 token 限制,修復文稿截斷問題
|
| 242 |
- 📝 **新增摘要**:Podcast 上架專用的摘要生成功能
|
|
|
|
| 243 |
- 🎨 **介面優化**:簡化複雜設定,專注核心功能
|
| 244 |
- ⚙️ **參數化**:可調整輸出 token 限制,適配不同模型
|
| 245 |
- 🔧 **品質提升**:智能品質檢查和截斷偵測
|
|
|
|
| 50 |
- **極簡摘要**:200 字以內的節目介紹,適合平台描述
|
| 51 |
- **一鍵生成**:從腳本直接生成多種格式的摘要內容
|
| 52 |
|
| 53 |
+
### Discord 分享功能 🔗 (NEW!)
|
| 54 |
+
- **一鍵分享到 Discord**:將生成的腳本和摘要直接分享到 Discord 頻道
|
| 55 |
+
- **自動文件上傳**:
|
| 56 |
+
- 腳本文件:`podcast_script_YYYYMMDD_HHMMSS.txt`
|
| 57 |
+
- 摘要文件:`podcast_summary_YYYYMMDD_HHMMSS.txt`
|
| 58 |
+
- **Webhook 支援**:透過 Discord Webhook URL 實現無縫整合
|
| 59 |
+
- **智能驗證**:URL 格式檢查和錯誤處理
|
| 60 |
+
|
| 61 |
### 介面優化
|
| 62 |
- **簡化設計**:隱藏複雜的多段式提示詞欄位
|
| 63 |
- **專注核心**:保留模板選擇和自定義提示詞功能
|
| 64 |
+
- **三輸出區域**:腳本生成 + 摘要生成 + Discord 分享並行操作
|
| 65 |
|
| 66 |
## 功能特點
|
| 67 |
|
|
|
|
| 73 |
- 講座腳本(單一演講者)
|
| 74 |
- 一般摘要和簡短摘要
|
| 75 |
- **新增**:博客式摘要和極簡摘要
|
| 76 |
+
- **Discord 整合功能**:
|
| 77 |
+
- 一鍵分享腳本和摘要到 Discord 頻道
|
| 78 |
+
- 支援 Discord Webhook URL
|
| 79 |
+
- 自動文件上傳和格式化
|
| 80 |
+
- 智能錯誤處理和狀態提示
|
| 81 |
- **智能品質控制**:自動檢測對話品質和連貫性
|
| 82 |
- **彈性 API 整合**:支援 OpenAI API 及其他相容的 API 端點
|
| 83 |
- **模型選擇**:可從連接的 API 獲取並選擇可用的語言模型
|
|
|
|
| 157 |
- 選擇摘要類型(博客式 / 極簡)
|
| 158 |
- 點擊「生成摘要」按鈕
|
| 159 |
|
| 160 |
+
8. **Discord 分享** (可選):
|
| 161 |
+
- 填寫 Discord Webhook URL
|
| 162 |
+
- 點擊「Share to Discord」按鈕
|
| 163 |
+
- 自動將腳本和摘要以文件形式發送到 Discord 頻道
|
| 164 |
+
|
| 165 |
+
### Discord Webhook 設定
|
| 166 |
+
|
| 167 |
+
#### 獲取 Webhook URL
|
| 168 |
+
1. 在 Discord 中,前往您要發送文件的頻道
|
| 169 |
+
2. 點擊頻道設置 → 整合 → Webhook
|
| 170 |
+
3. 創建新的 Webhook 或複製現有的 URL
|
| 171 |
+
4. URL 格式:`https://discord.com/api/webhooks/[ID]/[TOKEN]`
|
| 172 |
+
|
| 173 |
+
#### 使用方式
|
| 174 |
+
- **文件格式**:自動生成帶時間戳的 `.txt` 文件
|
| 175 |
+
- **發送內容**:
|
| 176 |
+
- 腳本文件:包含完整的對話腳本
|
| 177 |
+
- 摘要文件:包含生成的摘要內容(如果有)
|
| 178 |
+
- **Discord 消息**:包含專業格式的通知消息
|
| 179 |
+
|
| 180 |
+
#### 範例 Webhook URL
|
| 181 |
+
```
|
| 182 |
+
https://discord.com/api/webhooks/1423428306505830423/efDwfztnktEwTqa_US0wX4qZPvY0P0Z1Z01_rmGmaKhYg9teYgccPUsxW796_YUv5Fia
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
### 進階功能
|
| 186 |
|
| 187 |
#### 自定義提示詞
|
|
|
|
| 244 |
- 確認 API Base URL 格式
|
| 245 |
- 查看錯誤日誌獲取詳細資訊
|
| 246 |
|
| 247 |
+
#### Discord 分享失敗
|
| 248 |
+
- **常見原因及解決方案**:
|
| 249 |
+
1. **Webhook URL 格式錯誤**
|
| 250 |
+
- 確認 URL 以 `https://discord.com/api/webhooks/` 開頭
|
| 251 |
+
- 檢查是否包含完整的 ID 和 Token
|
| 252 |
+
2. **權限問題**
|
| 253 |
+
- 確認 Webhook 在目標頻道有發送訊息權限
|
| 254 |
+
- 檢查 Discord 伺服器設定
|
| 255 |
+
3. **文件大小限制**
|
| 256 |
+
- Discord 免費版限制 8MB,Nitro 用戶為 50MB
|
| 257 |
+
- 如果文件過大,考慮分段生成較短內容
|
| 258 |
+
4. **網路連線問題**
|
| 259 |
+
- 檢查網路連線穩定性
|
| 260 |
+
- 嘗試重新發送
|
| 261 |
+
|
| 262 |
#### 品質評分偏低
|
| 263 |
- 檢查原始內容品質
|
| 264 |
- 考慮調整提示詞模板
|
|
|
|
| 293 |
- 🎯 **重大重構**:模組化架構,分離關注點
|
| 294 |
- 🚀 **解決斷裂**:提升 token 限制,修復文稿截斷問題
|
| 295 |
- 📝 **新增摘要**:Podcast 上架專用的摘要生成功能
|
| 296 |
+
- 🔗 **Discord 整合**:一鍵分享腳本和摘要到 Discord 頻道
|
| 297 |
- 🎨 **介面優化**:簡化複雜設定,專注核心功能
|
| 298 |
- ⚙️ **參數化**:可調整輸出 token 限制,適配不同模型
|
| 299 |
- 🔧 **品質提升**:智能品質檢查和截斷偵測
|
__pycache__/app.cpython-313.pyc
ADDED
|
Binary file (39.1 kB). View file
|
|
|
app.py
CHANGED
|
@@ -232,6 +232,84 @@ def generate_dialogue_via_requests(
|
|
| 232 |
return "生成失敗"
|
| 233 |
|
| 234 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
def generate_summary(
|
| 236 |
script_content: str,
|
| 237 |
summary_type: str,
|
|
@@ -298,6 +376,23 @@ def generate_summary(
|
|
| 298 |
return error_msg
|
| 299 |
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts, progress_callback, max_retries, retry_delay):
|
| 302 |
"""
|
| 303 |
分批生成的備用機制,只在單次生成被截斷時使用
|
|
@@ -635,6 +730,11 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 635 |
}
|
| 636 |
#header { text-align: center; margin-bottom: 20px; }
|
| 637 |
.error { color: red; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 638 |
""") as demo:
|
| 639 |
gr.Markdown("# 腳本生成器 | Script Generator (重構版)", elem_id="header")
|
| 640 |
|
|
@@ -751,6 +851,29 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 751 |
placeholder="請先生成腳本,然後點擊「生成摘要」按鈕"
|
| 752 |
)
|
| 753 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 754 |
error_output = gr.Markdown(
|
| 755 |
visible=False,
|
| 756 |
elem_classes=["error"]
|
|
@@ -856,6 +979,16 @@ with gr.Blocks(title="Script Generator", css="""
|
|
| 856 |
],
|
| 857 |
outputs=[summary_output]
|
| 858 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 859 |
|
| 860 |
|
| 861 |
if __name__ == "__main__":
|
|
|
|
| 232 |
return "生成失敗"
|
| 233 |
|
| 234 |
|
| 235 |
+
def send_to_discord_webhook(webhook_url: str, script_content: str, summary_content: str = None) -> str:
|
| 236 |
+
"""
|
| 237 |
+
將腳本和摘要內容以文件形式發送到 Discord webhook
|
| 238 |
+
|
| 239 |
+
Args:
|
| 240 |
+
webhook_url: Discord webhook URL
|
| 241 |
+
script_content: 生成的腳本內容
|
| 242 |
+
summary_content: 摘要內容(可選)
|
| 243 |
+
|
| 244 |
+
Returns:
|
| 245 |
+
str: 發送結果消息
|
| 246 |
+
"""
|
| 247 |
+
if not webhook_url or not webhook_url.strip():
|
| 248 |
+
return "錯誤:請填寫 Discord Webhook URL"
|
| 249 |
+
|
| 250 |
+
if not script_content or not script_content.strip():
|
| 251 |
+
return "錯誤:沒有腳本內容可以發送"
|
| 252 |
+
|
| 253 |
+
try:
|
| 254 |
+
import tempfile
|
| 255 |
+
import json
|
| 256 |
+
from datetime import datetime
|
| 257 |
+
|
| 258 |
+
# 驗證 webhook URL 格式
|
| 259 |
+
if not webhook_url.startswith("https://discord.com/api/webhooks/"):
|
| 260 |
+
return "錯誤:請輸入有效的 Discord Webhook URL"
|
| 261 |
+
|
| 262 |
+
# 創建臨時文件
|
| 263 |
+
files_to_send = []
|
| 264 |
+
|
| 265 |
+
# 創建腳本文件
|
| 266 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as script_file:
|
| 267 |
+
script_file.write(script_content)
|
| 268 |
+
script_filename = f"podcast_script_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
| 269 |
+
files_to_send.append(('file', (script_filename, open(script_file.name, 'rb'), 'text/plain')))
|
| 270 |
+
|
| 271 |
+
# 如果有摘要內容,創建摘要文件
|
| 272 |
+
if summary_content and summary_content.strip():
|
| 273 |
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as summary_file:
|
| 274 |
+
summary_file.write(summary_content)
|
| 275 |
+
summary_filename = f"podcast_summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
| 276 |
+
files_to_send.append(('file', (summary_filename, open(summary_file.name, 'rb'), 'text/plain')))
|
| 277 |
+
|
| 278 |
+
# 準備 Discord 消息
|
| 279 |
+
message_data = {
|
| 280 |
+
"content": "📻 **David888 Podcast 內容分享**\n\n🎙️ 新的播客腳本和摘要已生成完成!",
|
| 281 |
+
"username": "David888 Podcast Bot"
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
# 發送到 Discord
|
| 285 |
+
response = requests.post(
|
| 286 |
+
webhook_url,
|
| 287 |
+
data=message_data,
|
| 288 |
+
files=files_to_send
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
+
# 關閉並清理臨時文件
|
| 292 |
+
for _, (_, file_obj, _) in files_to_send:
|
| 293 |
+
file_obj.close()
|
| 294 |
+
try:
|
| 295 |
+
os.unlink(file_obj.name)
|
| 296 |
+
except:
|
| 297 |
+
pass
|
| 298 |
+
|
| 299 |
+
if response.status_code == 204:
|
| 300 |
+
file_count = len(files_to_send)
|
| 301 |
+
logger.info(f"成功發送 {file_count} 個文件到 Discord")
|
| 302 |
+
return f"✅ 成功發送 {file_count} 個文件到 Discord!"
|
| 303 |
+
else:
|
| 304 |
+
logger.error(f"Discord webhook 發送失敗: {response.status_code} - {response.text}")
|
| 305 |
+
return f"❌ 發送失敗: HTTP {response.status_code} - {response.text[:100]}"
|
| 306 |
+
|
| 307 |
+
except Exception as e:
|
| 308 |
+
error_msg = f"Discord 發送過程中發生錯誤: {str(e)}"
|
| 309 |
+
logger.error(error_msg)
|
| 310 |
+
return f"❌ {error_msg}"
|
| 311 |
+
|
| 312 |
+
|
| 313 |
def generate_summary(
|
| 314 |
script_content: str,
|
| 315 |
summary_type: str,
|
|
|
|
| 376 |
return error_msg
|
| 377 |
|
| 378 |
|
| 379 |
+
def handle_discord_share(webhook_url, script_content, summary_content):
|
| 380 |
+
"""處理 Discord 分享"""
|
| 381 |
+
if not webhook_url or not webhook_url.strip():
|
| 382 |
+
return gr.update(visible=True, value="⚠️ 請先填寫 Discord Webhook URL")
|
| 383 |
+
|
| 384 |
+
if not script_content or not script_content.strip():
|
| 385 |
+
return gr.update(visible=True, value="⚠️ 請先生成腳本內容")
|
| 386 |
+
|
| 387 |
+
logger.info("開始發送內容到 Discord")
|
| 388 |
+
result = send_to_discord_webhook(webhook_url, script_content, summary_content)
|
| 389 |
+
|
| 390 |
+
if result.startswith("✅"):
|
| 391 |
+
return gr.update(visible=True, value=result)
|
| 392 |
+
else:
|
| 393 |
+
return gr.update(visible=True, value=result)
|
| 394 |
+
|
| 395 |
+
|
| 396 |
def _generate_in_batches(pdf_text, base_prompt, headers, url, model, num_parts, progress_callback, max_retries, retry_delay):
|
| 397 |
"""
|
| 398 |
分批生成的備用機制,只在單次生成被截斷時使用
|
|
|
|
| 730 |
}
|
| 731 |
#header { text-align: center; margin-bottom: 20px; }
|
| 732 |
.error { color: red; }
|
| 733 |
+
.discord-status {
|
| 734 |
+
padding: 10px;
|
| 735 |
+
border-radius: 5px;
|
| 736 |
+
margin-top: 10px;
|
| 737 |
+
}
|
| 738 |
""") as demo:
|
| 739 |
gr.Markdown("# 腳本生成器 | Script Generator (重構版)", elem_id="header")
|
| 740 |
|
|
|
|
| 851 |
placeholder="請先生成腳本,然後點擊「生成摘要」按鈕"
|
| 852 |
)
|
| 853 |
|
| 854 |
+
# Discord Webhook 功能區域
|
| 855 |
+
gr.Markdown("### 🔗 Discord 分享 | Discord Sharing")
|
| 856 |
+
|
| 857 |
+
with gr.Row():
|
| 858 |
+
discord_webhook = gr.Textbox(
|
| 859 |
+
label="Discord Webhook URL(可選)",
|
| 860 |
+
placeholder="https://discord.com/api/webhooks/...",
|
| 861 |
+
lines=2,
|
| 862 |
+
info="填寫 Discord Webhook URL 可將生成的內容發送到 Discord 頻道"
|
| 863 |
+
)
|
| 864 |
+
|
| 865 |
+
with gr.Row():
|
| 866 |
+
share_discord_button = gr.Button(
|
| 867 |
+
"📤 Share to Discord",
|
| 868 |
+
size="sm",
|
| 869 |
+
variant="secondary"
|
| 870 |
+
)
|
| 871 |
+
|
| 872 |
+
discord_status = gr.Markdown(
|
| 873 |
+
visible=False,
|
| 874 |
+
elem_classes=["discord-status"]
|
| 875 |
+
)
|
| 876 |
+
|
| 877 |
error_output = gr.Markdown(
|
| 878 |
visible=False,
|
| 879 |
elem_classes=["error"]
|
|
|
|
| 979 |
],
|
| 980 |
outputs=[summary_output]
|
| 981 |
)
|
| 982 |
+
|
| 983 |
+
share_discord_button.click(
|
| 984 |
+
fn=handle_discord_share,
|
| 985 |
+
inputs=[
|
| 986 |
+
discord_webhook, # Discord Webhook URL
|
| 987 |
+
output_text, # 腳本內容
|
| 988 |
+
summary_output # 摘要內容
|
| 989 |
+
],
|
| 990 |
+
outputs=[discord_status]
|
| 991 |
+
)
|
| 992 |
|
| 993 |
|
| 994 |
if __name__ == "__main__":
|
prompts.py
CHANGED
|
@@ -165,6 +165,7 @@ PROMPTS = {
|
|
| 165 |
- 只需要返回摘要內容,其他內容都不需要
|
| 166 |
- 摘要內容不要超過 200 字
|
| 167 |
- 簡潔有力,突出重點
|
|
|
|
| 168 |
|
| 169 |
請為以下播客內容生成極簡摘要:
|
| 170 |
|
|
|
|
| 165 |
- 只需要返回摘要內容,其他內容都不需要
|
| 166 |
- 摘要內容不要超過 200 字
|
| 167 |
- 簡潔有力,突出重點
|
| 168 |
+
- 最後一行 寫出 hashtag 5 ~ 8 個 便於社群分享
|
| 169 |
|
| 170 |
請為以下播客內容生成極簡摘要:
|
| 171 |
|