// 入口片段 main.py —— 完整源码见仓库
#!/usr/bin/env python3
"""qiaomu-anything-to-notebooklm - 多源内容智能处理器
自动识别输入类型,上传到 NotebookLM 并生成指定格式
支持深度分析模式:三轮递进提问(概览→深度挖掘→综合反刍)
"""
import sys
import os
import subprocess
import tempfile
import json
import time
import re
from pathlib import Path
def detect_input_type(input_path):
"""检测输入类型"""
if input_path.startswith('http'):
if 'mp.weixin.qq.com' in input_path:
return 'weixin'
elif 'youtube.com' in input_path or 'youtu.be' in input_path:
return 'youtube'
elif 'xiaoyuzhoufm.com' in input_path or 'ximalaya.com' in input_path or 'bilibili.com' in input_path:
return 'podcast'
elif 'x.com' in input_path or 'twitter.com' in input_path:
return 'x_twitter'
else:
return 'url'
path = Path(input_path).expanduser()
if not path.exists():
return 'search' # 不是文件路径,当作搜索关键词
suffix = path.suffix.lower()
if suffix == '.epub':
return 'epub'
elif suffix in ['.pdf', '.txt', '.md']:
return 'document'
elif suffix in ['.docx', '.pptx', '.xlsx']:
return 'office'
elif suffix in ['.jpg', '.jpeg', '.png', '.gif', '.webp']:
return 'image'
elif suffix in ['.mp3', '.wav']:
return 'audio'
elif suffix == '.zip':
return 'zip'
else:
return 'unknown'
def extract_epub_to_txt(epub_path):
"""提取 EPUB 到 TXT"""
import ebooklib
from ebooklib import epub
from bs4 import BeautifulSoup
book = epub.read_epub(str(epub_path))
content = []
for item in book.get_items():
if item.get_type() == ebooklib.ITEM_DOCUMENT:
soup = BeautifulSoup(item.get_content(), 'html.parser')
content.append(soup.get_text())
# 保存到临时文件
txt_path = tempfile.mktemp(suffix='.txt', prefix='epub_')
with open(txt_path, 'w', encoding='utf-8') as f:
f.write('\n\n'.join(content))
return txt_path
def upload_to_notebooklm(file_path, title):
"""上传文件到 NotebookLM"""
# 创建笔记本
result = subprocess.run(
['notebooklm', 'create', title],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"❌ 创建笔记本失败: {result.stderr}", file=sys.stderr)
return False
# 上传文件
result = subprocess.run(
['notebooklm', 'source', 'add', file_path, '--title', title],
capture_output=True,
text=True
)
if result.returncode != 0:
print(f"❌ 上传文件失败: {result.stderr}", file=sys.stderr)
return False
print(f"✅ 已上传到 NotebookLM: {title}")
return True
def label_for(content_type):
"""根据内容类型返回合适的中文指代词"""
labels = {
'epub': '本书',
'document': '这份文档',
'podcast': '这期播客',
'x_twitter': '这条推文',
'youtube': '这个视频',
'url': '这篇文章',
'weixin': '这篇文章',
'search': '这份内容',
}
return labels.get(content_type, '这份内容')
def generate_questions_progressive(content_type):
"""
生成三轮回合递进的深度问题。
本函数与具体内容解耦,不使用 {title} 等占位符,
统一用 label_for(content_type) 生成的指代词(如"本书""这个视频")。
设计原则:
- 第一轮(4题):建立整体认知框架
- 第二轮(5题):深入挖掘细节与矛盾
- 第三轮(3题):综合反刍与认知升级
- NotebookLM 在同一 conversation 中保持上下文,后续回合受益于前序回答
问题设计技巧:
- "请基于提供的文档内容回答" 防止 NotebookLM 触发网络搜索
- "列出、拆解、指出、提取" 等动作词引导结构化回答
- 避免 yes/no 式问题
"""
name = label_for(content_type)
# ── 第一轮:概览与框架 ──
round1 = [
f"请用一段话概括{name}的核心主题和写作目的。注意:完全基于已上传的文档内容回答,不要搜索网络。",
f"{name}的整体结构是什么?请按章节或逻辑模块逐一列出,每个模块用2-3句话概括核心内容。完全基于文档回答。",
f"{name}提出了哪些核心论点或主张?请逐一列出并用文档中的具体内容支撑每个论点。完全基于文档回答。",
f"{name}中最具颠覆性或反常识的内容是什么?请列出3-5条,并解释每条为什么让人意外。完全基于文档回答。",
]
# ── 第二轮:深度挖掘 ──
if content_type in ['epub', 'document']:
# 书籍/文档类:侧重论证逻辑与文本细读
round2 = [
f"请拆解{name}的核心论证逻辑:作者的前提假设是什么?推理过程是怎样的?最终结论是什么?请引用具体文本段落说明。",
f"{name}中引用了哪些关键案例、数据或文本证据?请逐一列出并说明每个证据在整体论证中起到什么作用。",
f"{name}中是否存在内部矛盾或值得商榷的观点?如果有,请指出并分析矛盾的根源。如