mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-03 15:08:25 +00:00
fix(http): stream live sns media and clarify docs
This commit is contained in:
@@ -466,10 +466,15 @@ curl "http://127.0.0.1:5031/api/v1/sns/timeline?limit=3&media=1&inline=1"
|
||||
|
||||
媒体字段说明(`media=1`):
|
||||
|
||||
- `media[].url/thumb`:你应该优先直接使用的字段。
|
||||
- `replace=1`(默认)时,`media[].url/thumb` 会直接被替换成可访问地址,等价于 `resolvedUrl/resolvedThumbUrl`。
|
||||
- `replace=0` 时,`media[].url/thumb` 仍保留微信原始地址;这时再结合下面的 `raw/proxy/resolved` 字段自己决定用哪一个。
|
||||
- `media[].rawUrl/rawThumb`:原始朋友圈地址
|
||||
- `media[].proxyUrl/proxyThumbUrl`:可直接访问的代理地址
|
||||
- `media[].resolvedUrl/resolvedThumbUrl`:最终可用地址(`inline=1` 时可能是 `data:` URL)
|
||||
- `replace=1` 时,`media[].url/thumb` 会被替换为可用地址
|
||||
- `media[].token/key/encIdx`:微信源数据里的访问/解密参数。通常不需要你自己处理;如果你手动调用 `/api/v1/sns/media/proxy`,把当前条目的 `url` 和 `key` 原样传回即可。
|
||||
- `media[].livePhoto`:实况图的视频部分。外层 `media[].url/thumb` 仍是封面图,`livePhoto` 内部会再提供一组自己的 `url/thumb/raw*/proxy*/resolved*` 字段。
|
||||
- `media=0` 时,不会补充 `raw*/proxy*/resolved*`,接口只返回原始 `url/thumb` 以及源字段(如 `key/token/encIdx`)。
|
||||
|
||||
### 7.2 获取朋友圈发布者
|
||||
|
||||
|
||||
@@ -891,15 +891,42 @@ class HttpService {
|
||||
|
||||
const key = this.toSnsMediaKey(url.searchParams.get('key'))
|
||||
const result = await snsService.downloadImage(mediaUrl, key)
|
||||
if (!result.success || !result.data) {
|
||||
if (!result.success) {
|
||||
this.sendError(res, 502, result.error || 'Failed to proxy sns media')
|
||||
return
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', result.contentType || 'application/octet-stream')
|
||||
res.setHeader('Content-Length', result.data.length)
|
||||
res.writeHead(200)
|
||||
res.end(result.data)
|
||||
if (result.data) {
|
||||
res.setHeader('Content-Type', result.contentType || 'application/octet-stream')
|
||||
res.setHeader('Content-Length', result.data.length)
|
||||
res.writeHead(200)
|
||||
res.end(result.data)
|
||||
return
|
||||
}
|
||||
|
||||
if (result.cachePath && fs.existsSync(result.cachePath)) {
|
||||
try {
|
||||
const stat = fs.statSync(result.cachePath)
|
||||
res.setHeader('Content-Type', result.contentType || 'application/octet-stream')
|
||||
res.setHeader('Content-Length', stat.size)
|
||||
res.writeHead(200)
|
||||
|
||||
const stream = fs.createReadStream(result.cachePath)
|
||||
stream.on('error', () => {
|
||||
if (!res.headersSent) {
|
||||
this.sendError(res, 500, 'Failed to read proxied sns media')
|
||||
} else {
|
||||
try { res.destroy() } catch {}
|
||||
}
|
||||
})
|
||||
stream.pipe(res)
|
||||
return
|
||||
} catch (error) {
|
||||
console.error('[HttpService] Failed to stream sns media cache:', error)
|
||||
}
|
||||
}
|
||||
|
||||
this.sendError(res, 502, result.error || 'Failed to proxy sns media')
|
||||
}
|
||||
|
||||
private async handleSnsExport(url: URL, res: http.ServerResponse): Promise<void> {
|
||||
|
||||
@@ -1240,7 +1240,7 @@ class SnsService {
|
||||
return { success: false, error: result.error }
|
||||
}
|
||||
|
||||
async downloadImage(url: string, key?: string | number): Promise<{ success: boolean; data?: Buffer; contentType?: string; error?: string }> {
|
||||
async downloadImage(url: string, key?: string | number): Promise<{ success: boolean; data?: Buffer; contentType?: string; cachePath?: string; error?: string }> {
|
||||
return this.fetchAndDecryptImage(url, key)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user