@@ -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// 导出
262438export { ZhihuPlatform , fillZhihuContent , syncZhihuContent }
0 commit comments