mirror of
https://github.com/JoeanAmier/XHS-Downloader.git
synced 2025-12-26 04:48:05 +08:00
fix(server): 修复 API 模式失效的问题
1. 拆分 API 模式和 MCP 模式 2. 修改服务器模式启动命令 3. 修正示例代码错误 Closes #273
This commit is contained in:
parent
0c7f5eb32c
commit
d5081f0ff9
18
README.md
18
README.md
@ -121,10 +121,10 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<img src="static/screenshot/命令行模式截图CN2.png" alt="">
|
<img src="static/screenshot/命令行模式截图CN2.png" alt="">
|
||||||
<h1>🖥 服务器模式</h1>
|
<h1>🖥 服务器模式</h1>
|
||||||
<p>⭐ 服务器模式同时支持 API 调用和 MCP 调用!</p>
|
<p>服务器模式包含 API 模式和 MCP 模式!</p>
|
||||||
<p><b>启动:</b>运行命令:<code>python .\main.py server</code></p>
|
<h2>API 模式</h2>
|
||||||
|
<p><b>启动:</b>运行命令:<code>python .\main.py api</code></p>
|
||||||
<p><b>关闭:</b>按下 <code>Ctrl</code> + <code>C</code> 关闭服务器</p>
|
<p><b>关闭:</b>按下 <code>Ctrl</code> + <code>C</code> 关闭服务器</p>
|
||||||
<h2>API 调用</h2>
|
|
||||||
<p>访问 <code>http://127.0.0.1:5556/docs</code> 或者 <code>http://127.0.0.1:5556/redoc</code>;你会看到自动生成的交互式 API 文档!</p>
|
<p>访问 <code>http://127.0.0.1:5556/docs</code> 或者 <code>http://127.0.0.1:5556/redoc</code>;你会看到自动生成的交互式 API 文档!</p>
|
||||||
<p><b>请求接口:</b><code>/xhs/detail</code></p>
|
<p><b>请求接口:</b><code>/xhs/detail</code></p>
|
||||||
<p><b>请求方法:</b><code>POST</code></p>
|
<p><b>请求方法:</b><code>POST</code></p>
|
||||||
@ -182,7 +182,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
async def example_api():
|
async def example_api():
|
||||||
"""通过 API 设置参数,适合二次开发"""
|
"""通过 API 设置参数,适合二次开发"""
|
||||||
server = "http://127.0.0.1:5556/xhs/"
|
server = "http://127.0.0.1:5556/xhs/detail"
|
||||||
data = {
|
data = {
|
||||||
"url": "", # 必需参数
|
"url": "", # 必需参数
|
||||||
"download": True,
|
"download": True,
|
||||||
@ -196,10 +196,14 @@ async def example_api():
|
|||||||
response = post(server, json=data, timeout=10)
|
response = post(server, json=data, timeout=10)
|
||||||
print(response.json())
|
print(response.json())
|
||||||
</pre>
|
</pre>
|
||||||
<h2>MCP 调用</h2>
|
<h2>MCP 模式</h2>
|
||||||
<p><b>MCP URL:</b><code>http://127.0.0.1:5556/xhs/mcp</code></p>
|
<p><b>启动:</b>运行命令:<code>python .\main.py mcp</code></p>
|
||||||
<p><b>MCP 传输机制:</b><code>可流式传输的 HTTP (streamableHttp)</code></p>
|
<p><b>关闭:</b>按下 <code>Ctrl</code> + <code>C</code> 关闭服务器</p>
|
||||||
<h3>MCP 配置示例</h3>
|
<h3>MCP 配置示例</h3>
|
||||||
|
|
||||||
|
[//]: # (<h4>STDIO</h4>)
|
||||||
|
<h4>Streamable HTTP</h4>
|
||||||
|
<p><b>MCP URL:</b><code>http://127.0.0.1:5556/mcp/</code></p>
|
||||||
<img src="static/screenshot/MCP配置示例.png" alt="MCP配置示例">
|
<img src="static/screenshot/MCP配置示例.png" alt="MCP配置示例">
|
||||||
<h3>MCP 调用示例</h3>
|
<h3>MCP 调用示例</h3>
|
||||||
<h4><strong>获取小红书作品信息</strong></h4>
|
<h4><strong>获取小红书作品信息</strong></h4>
|
||||||
|
|||||||
18
README_EN.md
18
README_EN.md
@ -122,10 +122,10 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<img src="static/screenshot/命令行模式截图EN2.png" alt="">
|
<img src="static/screenshot/命令行模式截图EN2.png" alt="">
|
||||||
<h1>🖥 Server Mode</h1>
|
<h1>🖥 Server Mode</h1>
|
||||||
<p>⭐ Server mode supports both API calls and MCP calls!</p>
|
<p>Server modes include API mode and MCP mode!</p>
|
||||||
<p><b>Start:</b> Run the command: <code>python .\main.py server</code></p>
|
<h2>API Mode</h2>
|
||||||
|
<p><b>Start:</b> Run the command: <code>python .\main.py api</code></p>
|
||||||
<p><b>Stop:</b> Press <code>Ctrl</code> + <code>C</code> to stop the server</p>
|
<p><b>Stop:</b> Press <code>Ctrl</code> + <code>C</code> to stop the server</p>
|
||||||
<h2>API Calls</h2>
|
|
||||||
<p>Open <code>http://127.0.0.1:5556/docs</code> or <code>http://127.0.0.1:5556/redoc</code>; you will see automatically generated interactive API documentation!</p>
|
<p>Open <code>http://127.0.0.1:5556/docs</code> or <code>http://127.0.0.1:5556/redoc</code>; you will see automatically generated interactive API documentation!</p>
|
||||||
<p><b>Request endpoint:</b>
|
<p><b>Request endpoint:</b>
|
||||||
<code>/xhs/detail</code></p>
|
<code>/xhs/detail</code></p>
|
||||||
@ -186,7 +186,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
async def example_api():
|
async def example_api():
|
||||||
"""通过 API 设置参数,适合二次开发"""
|
"""通过 API 设置参数,适合二次开发"""
|
||||||
server = "http://127.0.0.1:5556/xhs/"
|
server = "http://127.0.0.1:5556/xhs/detail"
|
||||||
data = {
|
data = {
|
||||||
"url": "", # 必需参数
|
"url": "", # 必需参数
|
||||||
"download": True,
|
"download": True,
|
||||||
@ -200,10 +200,14 @@ async def example_api():
|
|||||||
response = post(server, json=data, timeout=10)
|
response = post(server, json=data, timeout=10)
|
||||||
print(response.json())
|
print(response.json())
|
||||||
</pre>
|
</pre>
|
||||||
<h2>MCP Calls</h2>
|
<h2>MCP Mode</h2>
|
||||||
<p><b>MCP URL:</b><code>http://127.0.0.1:5556/xhs/mcp</code></p>
|
<p><b>Start:</b> Run the command: <code>python .\main.py mcp</code></p>
|
||||||
<p><b>MCP Transmission Mechanism:</b><code>streamableHttp</code></p>
|
<p><b>Stop:</b> Press <code>Ctrl</code> + <code>C</code> to stop the server</p>
|
||||||
<h3>MCP Configuration Example</h3>
|
<h3>MCP Configuration Example</h3>
|
||||||
|
|
||||||
|
[//]: # (<h4>STDIO</h4>)
|
||||||
|
<h4>Streamable HTTP</h4>
|
||||||
|
<p><b>MCP URL:</b><code>http://127.0.0.1:5556/mcp/</code></p>
|
||||||
<img src="static/screenshot/MCP配置示例.png" alt="MCP Configuration Example">
|
<img src="static/screenshot/MCP配置示例.png" alt="MCP Configuration Example">
|
||||||
<h3>MCP Invocation Example</h3>
|
<h3>MCP Invocation Example</h3>
|
||||||
<h4><strong>Retrieve RedNote Works Information</strong></h4>
|
<h4><strong>Retrieve RedNote Works Information</strong></h4>
|
||||||
|
|||||||
@ -76,7 +76,7 @@ async def example():
|
|||||||
|
|
||||||
async def example_api():
|
async def example_api():
|
||||||
"""通过 API 设置参数,适合二次开发"""
|
"""通过 API 设置参数,适合二次开发"""
|
||||||
server = "http://127.0.0.1:5556/xhs/"
|
server = "http://127.0.0.1:5556/xhs/detail"
|
||||||
data = {
|
data = {
|
||||||
"url": "", # 必需参数
|
"url": "", # 必需参数
|
||||||
"download": True,
|
"download": True,
|
||||||
|
|||||||
31
main.py
31
main.py
@ -14,27 +14,46 @@ async def app():
|
|||||||
await xhs.run_async()
|
await xhs.run_async()
|
||||||
|
|
||||||
|
|
||||||
async def server(
|
async def api_server(
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=5556,
|
port=5556,
|
||||||
log_level="info",
|
log_level="info",
|
||||||
):
|
):
|
||||||
async with XHS(**Settings().run()) as xhs:
|
async with XHS(**Settings().run()) as xhs:
|
||||||
await xhs.run_server(
|
await xhs.run_api_server(
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
log_level,
|
log_level,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def mcp_server(
|
||||||
|
transport="streamable-http",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=5556,
|
||||||
|
log_level="INFO",
|
||||||
|
):
|
||||||
|
async with XHS(**Settings().run()) as xhs:
|
||||||
|
await xhs.run_mcp_server(
|
||||||
|
transport=transport,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
log_level=log_level,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
with suppress(
|
with suppress(
|
||||||
KeyboardInterrupt,
|
KeyboardInterrupt,
|
||||||
CancelledError,
|
CancelledError,
|
||||||
):
|
):
|
||||||
|
# TODO: 重构优化
|
||||||
if len(argv) == 1:
|
if len(argv) == 1:
|
||||||
run(app())
|
run(app())
|
||||||
elif argv[1] == "server":
|
elif argv[1] == "api":
|
||||||
run(server())
|
run(api_server())
|
||||||
|
elif argv[1] == "mcp":
|
||||||
|
run(mcp_server())
|
||||||
|
# run(mcp_server("stdio"))
|
||||||
else:
|
else:
|
||||||
cli()
|
cli()
|
||||||
|
|||||||
@ -154,9 +154,6 @@ class XHS:
|
|||||||
self.clipboard_cache: str = ""
|
self.clipboard_cache: str = ""
|
||||||
self.queue = Queue()
|
self.queue = Queue()
|
||||||
self.event = Event()
|
self.event = Event()
|
||||||
# self.runner = self.init_server()
|
|
||||||
# self.site = None
|
|
||||||
self.server = None
|
|
||||||
|
|
||||||
def __extract_image(self, container: dict, data: Namespace):
|
def __extract_image(self, container: dict, data: Namespace):
|
||||||
container["下载地址"], container["动图地址"] = self.image.get_image_link(
|
container["下载地址"], container["动图地址"] = self.image.get_image_link(
|
||||||
@ -528,23 +525,20 @@ class XHS:
|
|||||||
# await self.runner.cleanup()
|
# await self.runner.cleanup()
|
||||||
# logging(log, _("Web API 服务器已关闭!"))
|
# logging(log, _("Web API 服务器已关闭!"))
|
||||||
|
|
||||||
async def run_server(
|
async def run_api_server(
|
||||||
self,
|
self,
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=5556,
|
port=5556,
|
||||||
log_level="info",
|
log_level="info",
|
||||||
):
|
):
|
||||||
mcp = self.init_mcp_server()
|
api = FastAPI(
|
||||||
self.server = FastAPI(
|
|
||||||
debug=self.VERSION_BETA,
|
debug=self.VERSION_BETA,
|
||||||
title="XHS-Downloader",
|
title="XHS-Downloader",
|
||||||
version=__VERSION__,
|
version=__VERSION__,
|
||||||
lifespan=mcp.lifespan,
|
|
||||||
)
|
)
|
||||||
self.server.mount("/xhs", mcp)
|
self.setup_routes(api)
|
||||||
self.setup_routes()
|
|
||||||
config = Config(
|
config = Config(
|
||||||
self.server,
|
api,
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
log_level=log_level,
|
log_level=log_level,
|
||||||
@ -552,8 +546,11 @@ class XHS:
|
|||||||
server = Server(config)
|
server = Server(config)
|
||||||
await server.serve()
|
await server.serve()
|
||||||
|
|
||||||
def setup_routes(self):
|
def setup_routes(
|
||||||
@self.server.get(
|
self,
|
||||||
|
server: FastAPI,
|
||||||
|
):
|
||||||
|
@server.get(
|
||||||
"/",
|
"/",
|
||||||
summary=_("访问项目 GitHub 仓库"),
|
summary=_("访问项目 GitHub 仓库"),
|
||||||
description=_("重定向至项目 GitHub 仓库主页"),
|
description=_("重定向至项目 GitHub 仓库主页"),
|
||||||
@ -562,7 +559,7 @@ class XHS:
|
|||||||
async def index():
|
async def index():
|
||||||
return RedirectResponse(url=REPOSITORY)
|
return RedirectResponse(url=REPOSITORY)
|
||||||
|
|
||||||
@self.server.post(
|
@server.post(
|
||||||
"/xhs/detail",
|
"/xhs/detail",
|
||||||
summary=_("获取作品数据及下载地址"),
|
summary=_("获取作品数据及下载地址"),
|
||||||
description=_(
|
description=_(
|
||||||
@ -601,7 +598,13 @@ class XHS:
|
|||||||
msg = _("获取小红书作品数据失败")
|
msg = _("获取小红书作品数据失败")
|
||||||
return ExtractData(message=msg, params=extract, data=data)
|
return ExtractData(message=msg, params=extract, data=data)
|
||||||
|
|
||||||
def init_mcp_server(self):
|
async def run_mcp_server(
|
||||||
|
self,
|
||||||
|
transport="streamable-http",
|
||||||
|
host="0.0.0.0",
|
||||||
|
port=5556,
|
||||||
|
log_level="INFO",
|
||||||
|
):
|
||||||
mcp = FastMCP(
|
mcp = FastMCP(
|
||||||
"XHS-Downloader",
|
"XHS-Downloader",
|
||||||
instructions=dedent("""
|
instructions=dedent("""
|
||||||
@ -631,6 +634,9 @@ class XHS:
|
|||||||
- data:作品信息数据,不需要返回作品信息数据时固定为 None
|
- data:作品信息数据,不需要返回作品信息数据时固定为 None
|
||||||
"""),
|
"""),
|
||||||
version=__VERSION__,
|
version=__VERSION__,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
log_level=log_level,
|
||||||
)
|
)
|
||||||
|
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
@ -749,7 +755,9 @@ class XHS:
|
|||||||
case _:
|
case _:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
return mcp.http_app(path="/mcp")
|
await mcp.run_async(
|
||||||
|
transport=transport,
|
||||||
|
)
|
||||||
|
|
||||||
async def deal_detail_mcp(
|
async def deal_detail_mcp(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
1. 修复作品类型判断失败时异常退出的问题
|
1. 修复作品类型判断失败时异常退出的问题
|
||||||
2. 修改服务器模式请求路径为 `/xhs/detail`
|
2. 修改服务器模式请求路径为 `/xhs/detail`
|
||||||
3. 服务器模式支持通过 MCP 调用
|
3. 修改服务器模式默认端口为 `5556`
|
||||||
4. 修改服务器模式默认端口为 `5556`
|
4. 服务器模式新增 MCP 模式
|
||||||
5. 优化提取链接的正则表达式
|
5. 优化提取链接的正则表达式
|
||||||
6. 支持更多作品链接格式
|
6. 修改服务器模式启动命令
|
||||||
7. 支持音乐图集作品下载
|
7. 支持更多作品链接格式
|
||||||
8. 其他细节优化
|
8. 支持音乐图集作品下载
|
||||||
|
9. 其他细节优化
|
||||||
|
|
||||||
*****
|
*****
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user