Files
2025-10-17 03:19:05 -07:00

552 lines
19 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WeChat Decrypt API - 接口文档</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.header .subtitle {
font-size: 1.2em;
opacity: 0.9;
}
.status-badge {
display: inline-block;
padding: 8px 16px;
background: rgba(255,255,255,0.2);
border-radius: 20px;
margin-top: 20px;
font-size: 0.9em;
}
.status-badge.online {
background: rgba(72,187,120,0.3);
}
.content {
padding: 40px;
}
.section {
margin-bottom: 40px;
}
.section h2 {
color: #667eea;
font-size: 1.8em;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #f0f0f0;
}
.section h3 {
color: #764ba2;
font-size: 1.3em;
margin-top: 30px;
margin-bottom: 15px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #667eea;
}
.info-card .label {
font-weight: bold;
color: #666;
font-size: 0.9em;
margin-bottom: 5px;
}
.info-card .value {
font-size: 1.1em;
color: #333;
}
.endpoint {
background: #f8f9fa;
padding: 25px;
border-radius: 8px;
margin-bottom: 25px;
border-left: 4px solid #764ba2;
}
.endpoint-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.method {
display: inline-block;
padding: 6px 12px;
border-radius: 4px;
font-weight: bold;
font-size: 0.85em;
margin-right: 10px;
}
.method.get {
background: #48bb78;
color: white;
}
.method.post {
background: #4299e1;
color: white;
}
.endpoint-path {
font-family: 'Courier New', monospace;
font-size: 1.1em;
color: #333;
}
.endpoint-desc {
color: #666;
margin-bottom: 15px;
}
.params, .response {
margin-top: 15px;
}
.params h4, .response h4 {
color: #764ba2;
font-size: 1em;
margin-bottom: 10px;
}
.param-item {
background: white;
padding: 12px;
border-radius: 4px;
margin-bottom: 8px;
border: 1px solid #e2e8f0;
}
.param-name {
font-family: 'Courier New', monospace;
font-weight: bold;
color: #667eea;
}
.param-required {
color: #e53e3e;
font-size: 0.85em;
margin-left: 5px;
}
.param-type {
color: #48bb78;
font-size: 0.9em;
margin-left: 5px;
}
.param-desc {
color: #666;
margin-top: 5px;
font-size: 0.95em;
}
.code-block {
background: #2d3748;
color: #e2e8f0;
padding: 20px;
border-radius: 6px;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9em;
line-height: 1.5;
margin-top: 10px;
white-space: pre;
}
.code-block .comment {
color: #68d391;
}
.code-block .string {
color: #f6ad55;
}
.code-block .keyword {
color: #9f7aea;
}
.feature-list {
list-style: none;
padding: 0;
}
.feature-list li {
padding: 12px 0;
padding-left: 30px;
position: relative;
}
.feature-list li:before {
content: "✅";
position: absolute;
left: 0;
}
.footer {
background: #f8f9fa;
padding: 30px;
text-align: center;
color: #666;
}
.footer a {
color: #667eea;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
.quick-links {
display: flex;
gap: 15px;
flex-wrap: wrap;
margin-top: 20px;
}
.btn {
display: inline-block;
padding: 10px 20px;
background: #667eea;
color: white;
text-decoration: none;
border-radius: 6px;
transition: background 0.3s;
}
.btn:hover {
background: #764ba2;
}
.btn-secondary {
background: #4299e1;
}
.btn-secondary:hover {
background: #3182ce;
}
@media (max-width: 768px) {
.header h1 {
font-size: 1.8em;
}
.content {
padding: 20px;
}
.info-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎬 WeChat Decrypt API</h1>
<div class="subtitle">基于 Playwright 浏览器自动化的 RPC 解密服务</div>
<div class="status-badge online" id="statusBadge">
🟢 服务运行中
</div>
</div>
<div class="content">
<!-- 服务信息 -->
<div class="section">
<h2>📊 服务信息</h2>
<div class="info-grid">
<div class="info-card">
<div class="label">服务名称</div>
<div class="value" id="serviceName">WeChat Decrypt API</div>
</div>
<div class="info-card">
<div class="label">版本</div>
<div class="value" id="version">2.0.0</div>
</div>
<div class="info-card">
<div class="label">引擎</div>
<div class="value" id="engine">Playwright + Chromium</div>
</div>
<div class="info-card">
<div class="label">WASM 状态</div>
<div class="value" id="wasmStatus">检查中...</div>
</div>
</div>
</div>
<!-- 特性 -->
<div class="section">
<h2>✨ 核心特性</h2>
<ul class="feature-list">
<li>100% 兼容微信官方 WASM 模块 (v1.2.46)</li>
<li>在真实 Chromium 浏览器中执行 Isaac64 算法</li>
<li>RPC 架构:Node.js ↔ Browser 通信</li>
<li>支持最大 500MB 视频文件</li>
<li>RESTful API 设计,易于集成</li>
<li>Docker 容器化部署</li>
<li>自动健康检查和监控</li>
</ul>
</div>
<!-- API 端点 -->
<div class="section">
<h2>🔌 API 端点</h2>
<!-- GET / -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method get">GET</span>
<span class="endpoint-path">/</span>
</div>
<div class="endpoint-desc">获取服务信息和可用端点列表</div>
<div class="response">
<h4>响应示例:</h4>
<div class="code-block">{
<span class="string">"service"</span>: <span class="string">"WeChat Channels Video Decryption API"</span>,
<span class="string">"version"</span>: <span class="string">"2.0.0"</span>,
<span class="string">"engine"</span>: <span class="string">"Playwright + Chromium"</span>,
<span class="string">"endpoints"</span>: { ... }
}</div>
</div>
</div>
<!-- GET /health -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method get">GET</span>
<span class="endpoint-path">/health</span>
</div>
<div class="endpoint-desc">健康检查,返回服务和 WASM 模块状态</div>
<div class="response">
<h4>响应示例:</h4>
<div class="code-block">{
<span class="string">"status"</span>: <span class="string">"ok"</span>,
<span class="string">"wasm"</span>: {
<span class="string">"loaded"</span>: <span class="keyword">true</span>,
<span class="string">"timestamp"</span>: <span class="string">"2024-01-15T10:30:00.000Z"</span>
}
}</div>
</div>
</div>
<!-- POST /api/keystream -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="endpoint-path">/api/keystream</span>
</div>
<div class="endpoint-desc">生成 Isaac64 密钥流(128KB)</div>
<div class="params">
<h4>请求参数:</h4>
<div class="param-item">
<span class="param-name">decode_key</span>
<span class="param-required">(必需)</span>
<span class="param-type">string/number</span>
<div class="param-desc">解密密钥,从 API 响应的 $.data.object_desc.media[0].decode_key 获取</div>
</div>
<div class="param-item">
<span class="param-name">format</span>
<span class="param-type">string</span>
<div class="param-desc">输出格式:'hex'(默认)或 'base64'</div>
</div>
</div>
<div class="code-block">curl -X POST http://localhost:3000/api/keystream \
-H <span class="string">"Content-Type: application/json"</span> \
-d <span class="string">'{"decode_key": "2136343393", "format": "hex"}'</span></div>
<div class="response">
<h4>响应示例:</h4>
<div class="code-block">{
<span class="string">"decode_key"</span>: <span class="string">"2136343393"</span>,
<span class="string">"keystream"</span>: <span class="string">"9c87e6946a410182..."</span>,
<span class="string">"format"</span>: <span class="string">"hex"</span>,
<span class="string">"size"</span>: 131072,
<span class="string">"duration_ms"</span>: 45
}</div>
</div>
</div>
<!-- POST /api/decrypt -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="endpoint-path">/api/decrypt</span>
</div>
<div class="endpoint-desc">完整视频解密(前 128KB XOR 解密)</div>
<div class="params">
<h4>请求参数(multipart/form-data):</h4>
<div class="param-item">
<span class="param-name">decode_key</span>
<span class="param-required">(必需)</span>
<span class="param-type">string</span>
<div class="param-desc">解密密钥</div>
</div>
<div class="param-item">
<span class="param-name">video</span>
<span class="param-required">(必需)</span>
<span class="param-type">file</span>
<div class="param-desc">加密的 MP4 视频文件(最大 500MB)</div>
</div>
</div>
<div class="code-block">curl -X POST http://localhost:3000/api/decrypt \
-F <span class="string">"decode_key=2136343393"</span> \
-F <span class="string">"video=@encrypted.mp4"</span> \
-o decrypted.mp4</div>
<div class="response">
<h4>响应:</h4>
<div class="code-block"><span class="comment"># 返回解密后的 MP4 视频文件(binary)</span>
Content-Type: video/mp4
Content-Disposition: attachment; filename=<span class="string">"decrypted_xxxxx.mp4"</span>
X-Decrypt-Duration: <span class="string">1295</span></div>
</div>
</div>
</div>
<!-- 使用示例 -->
<div class="section">
<h2>💡 使用示例</h2>
<h3>Python 示例</h3>
<div class="code-block"><span class="keyword">import</span> requests
<span class="comment"># 解密视频</span>
url = <span class="string">'http://localhost:3000/api/decrypt'</span>
files = {<span class="string">'video'</span>: open(<span class="string">'encrypted.mp4'</span>, <span class="string">'rb'</span>)}
data = {<span class="string">'decode_key'</span>: <span class="string">'2136343393'</span>}
response = requests.post(url, files=files, data=data)
<span class="keyword">if</span> response.status_code == 200:
<span class="keyword">with</span> open(<span class="string">'decrypted.mp4'</span>, <span class="string">'wb'</span>) <span class="keyword">as</span> f:
f.write(response.content)
print(<span class="string">'✅ 解密成功'</span>)</div>
<h3>JavaScript/Node.js 示例</h3>
<div class="code-block"><span class="keyword">const</span> FormData = require(<span class="string">'form-data'</span>);
<span class="keyword">const</span> fs = require(<span class="string">'fs'</span>);
<span class="keyword">const</span> axios = require(<span class="string">'axios'</span>);
<span class="keyword">async function</span> decryptVideo() {
<span class="keyword">const</span> form = <span class="keyword">new</span> FormData();
form.append(<span class="string">'decode_key'</span>, <span class="string">'2136343393'</span>);
form.append(<span class="string">'video'</span>, fs.createReadStream(<span class="string">'encrypted.mp4'</span>));
<span class="keyword">const</span> response = <span class="keyword">await</span> axios.post(
<span class="string">'http://localhost:3000/api/decrypt'</span>,
form,
{ headers: form.getHeaders(), responseType: <span class="string">'stream'</span> }
);
response.data.pipe(fs.createWriteStream(<span class="string">'decrypted.mp4'</span>));
}</div>
</div>
<!-- 快速链接 -->
<div class="section">
<h2>🔗 快速链接</h2>
<div class="quick-links">
<a href="/health" class="btn" target="_blank">健康检查</a>
<a href="https://github.com/Evil0ctal/WeChat-Channels-Video-File-Decryption" class="btn btn-secondary" target="_blank">GitHub 仓库</a>
<a href="https://evil0ctal.github.io/WeChat-Channels-Video-File-Decryption/" class="btn btn-secondary" target="_blank">在线工具</a>
</div>
</div>
</div>
<div class="footer">
<p><strong>WeChat Decrypt API</strong> v2.0.0</p>
<p>Made with ❤️ by <a href="https://github.com/Evil0ctal" target="_blank">Evil0ctal</a></p>
<p>License: MIT | <a href="https://github.com/Evil0ctal/WeChat-Channels-Video-File-Decryption" target="_blank">View on GitHub</a></p>
</div>
</div>
<script>
// 获取服务状态
fetch('/health')
.then(res => res.json())
.then(data => {
document.getElementById('serviceName').textContent = data.service || 'WeChat Decrypt API';
document.getElementById('version').textContent = data.version || '2.0.0';
document.getElementById('engine').textContent = data.engine || 'Playwright';
if (data.wasm && data.wasm.loaded) {
document.getElementById('wasmStatus').textContent = '✅ 已加载';
document.getElementById('statusBadge').textContent = '🟢 服务运行中';
} else {
document.getElementById('wasmStatus').textContent = '❌ 未加载';
document.getElementById('statusBadge').textContent = '🔴 WASM 未就绪';
document.getElementById('statusBadge').classList.remove('online');
}
})
.catch(err => {
console.error('Failed to fetch service status:', err);
document.getElementById('wasmStatus').textContent = '❌ 检查失败';
document.getElementById('statusBadge').textContent = '🔴 服务不可用';
document.getElementById('statusBadge').classList.remove('online');
});
</script>
</body>
</html>