Skip to content

Commit 25f526e

Browse files
authored
feat(zhihu): wait for image uploads to complete before refreshing page (#235)
* feat(zhihu): wait for image uploads to complete before refreshing page * chore: bump version to 1.3.3
1 parent 4c4c490 commit 25f526e

2 files changed

Lines changed: 186 additions & 10 deletions

File tree

apps/extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cose-extension",
3-
"version": "1.3.2.2",
3+
"version": "1.3.3",
44
"description": "Create Once, Sync Everywhere. 一键将文章同步到多个平台",
55
"type": "module",
66
"scripts": {

packages/core/src/platforms/zhihu.js

Lines changed: 185 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,16 +240,24 @@ async function syncZhihuContent(tab, content, helpers) {
240240
// 等待 2 秒确保内容已保存
241241
await new Promise(resolve => setTimeout(resolve, 2000))
242242

243-
// 模拟用户刷新页面
244-
try {
245-
if (chrome?.tabs && tab?.id) {
246-
await chrome.tabs.reload(tab.id, { bypassCache: false })
247-
console.log('[COSE] 已模拟用户刷新知乎页面')
248-
} else {
249-
console.log('[COSE] chrome.tabs 或 tab.id 不可用,跳过刷新')
243+
// 等待图片上传完成后再刷新
244+
console.log('[COSE] 开始监听图片上传请求...')
245+
const uploadComplete = await waitForImageUploadComplete(tab.id)
246+
247+
if (uploadComplete) {
248+
console.log('[COSE] 图片上传完成,准备刷新页面')
249+
try {
250+
if (chrome?.tabs && tab?.id) {
251+
await chrome.tabs.reload(tab.id, { bypassCache: false })
252+
console.log('[COSE] 已模拟用户刷新知乎页面')
253+
} else {
254+
console.log('[COSE] chrome.tabs 或 tab.id 不可用,跳过刷新')
255+
}
256+
} catch (err) {
257+
console.log('[COSE] 刷新页面失败:', err.message || err)
250258
}
251-
} catch (err) {
252-
console.log('[COSE] 刷新页面失败:', err.message || err)
259+
} else {
260+
console.log('[COSE] 未检测到图片上传请求或超时,跳过刷新')
253261
}
254262

255263
return { success: true, message: '已打开知乎并同步内容', tabId: tab.id }
@@ -258,5 +266,173 @@ async function syncZhihuContent(tab, content, helpers) {
258266
}
259267
}
260268

269+
/**
270+
* 等待图片上传完成
271+
* @param {number} tabId - 标签页 ID
272+
* @param {number} timeout - 超时时间(毫秒)
273+
* @returns {Promise<boolean>}
274+
*/
275+
async function waitForImageUploadComplete(tabId, timeout = 30000) {
276+
const startTime = Date.now()
277+
278+
// 在页面中注入监听脚本
279+
const result = await globalThis.chrome.scripting.executeScript({
280+
target: { tabId: tabId },
281+
func: () => {
282+
return new Promise((resolve) => {
283+
const pendingUploads = new Map() // uploadId -> { url, completed }
284+
let hasUploadRequests = false
285+
let lastUploadTime = 0
286+
287+
// 检查是否所有上传都完成
288+
const checkAllComplete = () => {
289+
if (pendingUploads.size === 0) return false
290+
291+
for (const [id, info] of pendingUploads) {
292+
if (!info.completed) return false
293+
}
294+
return true
295+
}
296+
297+
// 监听 fetch 请求
298+
const originalFetch = window.fetch
299+
window.fetch = function(...args) {
300+
const url = args[0]
301+
const options = args[1] || {}
302+
303+
// 检测图片上传请求(知乎的图片上传通常包含这些特征)
304+
const isImageUpload = typeof url === 'string' && (
305+
url.includes('/api/v4/images') ||
306+
url.includes('/api/v4/upload') ||
307+
url.includes('upload') ||
308+
(options.method === 'POST' && url.includes('zhihu.com'))
309+
)
310+
311+
if (isImageUpload) {
312+
hasUploadRequests = true
313+
lastUploadTime = Date.now()
314+
const uploadId = Date.now() + Math.random()
315+
pendingUploads.set(uploadId, { url, completed: false })
316+
console.log('[COSE] 检测到图片上传请求:', url, uploadId)
317+
}
318+
319+
return originalFetch.apply(this, args)
320+
.then(response => {
321+
if (isImageUpload) {
322+
console.log('[COSE] 图片上传请求完成:', url, response.status)
323+
// 标记为已完成
324+
for (const [id, info] of pendingUploads) {
325+
if (info.url === url) {
326+
info.completed = true
327+
break
328+
}
329+
}
330+
}
331+
return response
332+
})
333+
.catch(error => {
334+
if (isImageUpload) {
335+
console.log('[COSE] 图片上传请求失败:', url, error)
336+
// 即使失败也标记为已完成(有反馈结果)
337+
for (const [id, info] of pendingUploads) {
338+
if (info.url === url) {
339+
info.completed = true
340+
break
341+
}
342+
}
343+
}
344+
throw error
345+
})
346+
}
347+
348+
// 监听 XMLHttpRequest
349+
const originalOpen = XMLHttpRequest.prototype.open
350+
const originalSend = XMLHttpRequest.prototype.send
351+
352+
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
353+
this._url = url
354+
this._method = method
355+
return originalOpen.apply(this, [method, url, ...rest])
356+
}
357+
358+
XMLHttpRequest.prototype.send = function(...args) {
359+
const isImageUpload = this._url && (
360+
this._url.includes('/api/v4/images') ||
361+
this._url.includes('/api/v4/upload') ||
362+
this._url.includes('upload') ||
363+
(this._method === 'POST' && this._url.includes('zhihu.com'))
364+
)
365+
366+
if (isImageUpload) {
367+
hasUploadRequests = true
368+
lastUploadTime = Date.now()
369+
const uploadId = Date.now() + Math.random()
370+
pendingUploads.set(uploadId, { url: this._url, completed: false })
371+
console.log('[COSE] 检测到图片上传 XHR:', this._url, uploadId)
372+
373+
this.addEventListener('loadend', () => {
374+
console.log('[COSE] 图片上传 XHR 完成:', this._url, this.status)
375+
// 标记为已完成
376+
for (const [id, info] of pendingUploads) {
377+
if (info.url === this._url) {
378+
info.completed = true
379+
break
380+
}
381+
}
382+
})
383+
}
384+
385+
return originalSend.apply(this, args)
386+
}
387+
388+
// 定期检查是否所有上传都完成
389+
const checkTimer = setInterval(() => {
390+
// 如果没有检测到任何上传请求,说明可能没有图片需要上传
391+
if (!hasUploadRequests) {
392+
console.log('[COSE] 未检测到图片上传请求')
393+
clearInterval(checkTimer)
394+
resolve(true)
395+
return
396+
}
397+
398+
// 如果所有上传都完成,并且距离最后一个上传请求已经过去2秒(确保没有新请求)
399+
if (checkAllComplete() && Date.now() - lastUploadTime > 2000) {
400+
console.log('[COSE] 所有图片上传请求已完成')
401+
clearInterval(checkTimer)
402+
resolve(true)
403+
return
404+
}
405+
}, 500)
406+
407+
// 10秒后如果没有检测到上传请求,认为没有图片需要上传
408+
setTimeout(() => {
409+
if (!hasUploadRequests) {
410+
console.log('[COSE] 10秒内未检测到上传请求,认为无图片')
411+
clearInterval(checkTimer)
412+
resolve(true)
413+
}
414+
}, 10000)
415+
416+
// 超时后无论如何都返回
417+
setTimeout(() => {
418+
console.log('[COSE] 等待图片上传超时')
419+
clearInterval(checkTimer)
420+
resolve(true) // 即使超时也刷新
421+
}, timeout)
422+
})
423+
},
424+
world: 'MAIN',
425+
})
426+
427+
// 等待监听结果
428+
const uploadResult = result?.[0]?.result
429+
console.log('[COSE] 图片上传监听结果:', uploadResult)
430+
431+
// 给一个额外的缓冲时间,确保图片已经完全加载和渲染
432+
await new Promise(resolve => setTimeout(resolve, 2000))
433+
434+
return uploadResult !== false
435+
}
436+
261437
// 导出
262438
export { ZhihuPlatform, fillZhihuContent, syncZhihuContent }

0 commit comments

Comments
 (0)