diff --git a/app/api/scene-fusion/route.ts b/app/api/scene-fusion/route.ts index 4314177f2..cafad687e 100644 --- a/app/api/scene-fusion/route.ts +++ b/app/api/scene-fusion/route.ts @@ -683,105 +683,115 @@ export async function POST(request: Request) { async start(controller) { const encoder = new TextEncoder(); - // 发送初始消息 - controller.enqueue(encoder.encode(JSON.stringify({ type: "start", total: processedScenes.length }) + "\n")); - - let successCount = 0; - let failCount = 0; - - for (let i = 0; i < processedScenes.length; i++) { - const scene = processedScenes[i]; + try { + // 发送初始消息 + controller.enqueue(encoder.encode(JSON.stringify({ type: "start", total: processedScenes.length }) + "\n")); - console.log(`[SceneFusion] ========== Processing scene ${i + 1}/${processedScenes.length} ==========`); - console.log(`[SceneFusion] Scene ID: ${scene.id}`); - console.log(`[SceneFusion] Product Image URL: ${productImageUrl.substring(0, 80)}...`); - console.log(`[SceneFusion] Scene Image URL: ${scene.imageUrl.substring(0, 80)}...`); - console.log(`[SceneFusion] Target Name: ${scene.targetName || '(not specified)'}`); - console.log(`[SceneFusion] Custom Prompt: ${scene.customPrompt || '(none)'}`); - console.log(`[SceneFusion] Provider Type: ${provider.type}`); + let successCount = 0; + let failCount = 0; - // 如果不是第一张图,等待5秒 - if (i > 0) { - console.log(`[SceneFusion] Waiting 5 seconds before processing...`); - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - - try { - // 使用新的通用处理函数,支持多种模型类型 - const result = await processSceneWithProvider( - productImageUrl, - scene.imageUrl, - productName, - scene.targetName, - scene.customPrompt, - config.defaultPrompt, - { type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey }, - model.modelId, - aspectRatio, - imageSize - ); + for (let i = 0; i < processedScenes.length; i++) { + const scene = processedScenes[i]; - // 将分辨率等级用于数据库存储 - const dbImageSize = imageSize || "2K"; + console.log(`[SceneFusion] ========== Processing scene ${i + 1}/${processedScenes.length} ==========`); + console.log(`[SceneFusion] Scene ID: ${scene.id}`); + console.log(`[SceneFusion] Product Image URL: ${productImageUrl.substring(0, 80)}...`); + console.log(`[SceneFusion] Scene Image URL: ${scene.imageUrl.substring(0, 80)}...`); + console.log(`[SceneFusion] Target Name: ${scene.targetName || '(not specified)'}`); + console.log(`[SceneFusion] Custom Prompt: ${scene.customPrompt || '(none)'}`); + console.log(`[SceneFusion] Provider Type: ${provider.type}`); - // 保存到数据库(chatId 和 messageId 设为 null,因为场景裂变不关联聊天) - try { - await db.insert(generatedImage).values({ - userId: session.user.id, - chatId: null, // 场景裂变不关联聊天 - messageId: null, // 场景裂变不关联消息 - thumbnailUrl: result.thumbnail, - originalUrl: result.original, - imageSize: dbImageSize, - prompt: `[场景裂变] ${result.prompt}`, - modelId: model.modelId, - }); - console.log(`[SceneFusion] ✅ Saved image to gallery: ${result.original.substring(0, 80)}...`); - } catch (dbError) { - console.error("[SceneFusion] ❌ Failed to save to gallery:", dbError); + // 如果不是第一张图,等待5秒 + if (i > 0) { + console.log(`[SceneFusion] Waiting 5 seconds before processing...`); + await new Promise((resolve) => setTimeout(resolve, 5000)); } - successCount++; - - // 立即发送完成的结果给前端 - controller.enqueue(encoder.encode(JSON.stringify({ - type: "result", - data: { - id: scene.id, - status: "completed" as const, - result: result.thumbnail, // 返回缩略图用于预览 - originalUrl: result.original, // 原图用于下载 - }, - }) + "\n")); - - } catch (error) { - console.error(`[SceneFusion] Failed to process scene ${scene.id}:`, error); - failCount++; - - // 立即发送失败的结果给前端 - controller.enqueue(encoder.encode(JSON.stringify({ - type: "result", - data: { - id: scene.id, - status: "failed" as const, - error: error instanceof Error ? error.message : "处理失败", - }, - }) + "\n")); + try { + // 使用新的通用处理函数,支持多种模型类型 + const result = await processSceneWithProvider( + productImageUrl, + scene.imageUrl, + productName, + scene.targetName, + scene.customPrompt, + config.defaultPrompt, + { type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey }, + model.modelId, + aspectRatio, + imageSize + ); + + // 将分辨率等级用于数据库存储 + const dbImageSize = imageSize || "2K"; + + // 保存到数据库(chatId 和 messageId 设为 null,因为场景裂变不关联聊天) + try { + await db.insert(generatedImage).values({ + userId: session.user.id, + chatId: null, // 场景裂变不关联聊天 + messageId: null, // 场景裂变不关联消息 + thumbnailUrl: result.thumbnail, + originalUrl: result.original, + imageSize: dbImageSize, + prompt: `[场景裂变] ${result.prompt}`, + modelId: model.modelId, + }); + console.log(`[SceneFusion] ✅ Saved image to gallery: ${result.original.substring(0, 80)}...`); + } catch (dbError) { + console.error("[SceneFusion] ❌ Failed to save to gallery:", dbError); + } + + successCount++; + + // 立即发送完成的结果给前端 + controller.enqueue(encoder.encode(JSON.stringify({ + type: "result", + data: { + id: scene.id, + status: "completed" as const, + result: result.thumbnail, // 返回缩略图用于预览 + originalUrl: result.original, // 原图用于下载 + }, + }) + "\n")); + + } catch (error) { + console.error(`[SceneFusion] Failed to process scene ${scene.id}:`, error); + failCount++; + + // 立即发送失败的结果给前端 + controller.enqueue(encoder.encode(JSON.stringify({ + type: "result", + data: { + id: scene.id, + status: "failed" as const, + error: error instanceof Error ? error.message : "处理失败", + }, + }) + "\n")); + } } + + // 发送完成消息 + console.log(`[SceneFusion] Completed: ${successCount} success, ${failCount} failed`); + controller.enqueue(encoder.encode(JSON.stringify({ + type: "done", + summary: { + total: processedScenes.length, + success: successCount, + failed: failCount, + }, + }) + "\n")); + + } catch (error) { + console.error("[SceneFusion] Stream error:", error); + // 发送错误消息 + controller.enqueue(encoder.encode(JSON.stringify({ + type: "error", + error: error instanceof Error ? error.message : "处理失败", + }) + "\n")); + } finally { + controller.close(); } - - // 发送完成消息 - console.log(`[SceneFusion] Completed: ${successCount} success, ${failCount} failed`); - controller.enqueue(encoder.encode(JSON.stringify({ - type: "done", - summary: { - total: processedScenes.length, - success: successCount, - failed: failCount, - }, - }) + "\n")); - - controller.close(); }, }); diff --git a/components/scene-fusion-view.tsx b/components/scene-fusion-view.tsx index 560f4a80e..eae839b5e 100644 --- a/components/scene-fusion-view.tsx +++ b/components/scene-fusion-view.tsx @@ -291,6 +291,13 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) { } else { toast.success(`批量处理完成:${summary.success} 张图片已生成`); } + } else if (message.type === 'error') { + console.error('[SceneFusion Client] Stream error:', message.error); + toast.error(`处理失败:${message.error || '未知错误'}`); + // 将所有处理中的场景标记为失败 + setScenes(prev => prev.map(s => + s.status === 'processing' ? { ...s, status: 'failed', error: message.error || '处理失败' } : s + )); } } catch (parseError) { console.error('[SceneFusion Client] Failed to parse message:', parseError, 'Line:', line);