fix: scene fusion retry now correctly handles streaming response and batch download works for all images
This commit is contained in:
parent
ee4bb14736
commit
0d69e8d9fa
@ -253,25 +253,76 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(data.error || '处理失败');
|
// 尝试解析错误信息
|
||||||
|
try {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.error || '处理失败');
|
||||||
|
} catch {
|
||||||
|
throw new Error(`请求失败: ${response.status}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = data.results?.[0];
|
// 处理流式响应
|
||||||
if (result?.status === 'completed') {
|
const reader = response.body?.getReader();
|
||||||
setScenes(prev => prev.map(s =>
|
if (!reader) {
|
||||||
s.id === id ? {
|
throw new Error('无法读取响应流');
|
||||||
...s,
|
}
|
||||||
status: 'completed',
|
|
||||||
result: result.result,
|
const decoder = new TextDecoder();
|
||||||
originalUrl: result.originalUrl,
|
let buffer = '';
|
||||||
} : s
|
let success = false;
|
||||||
));
|
|
||||||
toast.success('重新生成成功');
|
while (true) {
|
||||||
} else {
|
const { done, value } = await reader.read();
|
||||||
throw new Error(result?.error || '生成失败');
|
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
|
const lines = buffer.split('\n');
|
||||||
|
buffer = lines.pop() || '';
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const message = JSON.parse(line);
|
||||||
|
console.log('[SceneFusion] Regenerate message:', message.type);
|
||||||
|
|
||||||
|
if (message.type === 'result') {
|
||||||
|
const result = message.data;
|
||||||
|
if (result.id === id) {
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
setScenes(prev => prev.map(s =>
|
||||||
|
s.id === id ? {
|
||||||
|
...s,
|
||||||
|
status: 'completed',
|
||||||
|
result: result.result,
|
||||||
|
originalUrl: result.originalUrl,
|
||||||
|
} : s
|
||||||
|
));
|
||||||
|
toast.success('重新生成成功');
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.error || '生成失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (message.type === 'error') {
|
||||||
|
throw new Error(message.error || '处理失败');
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
if (parseError instanceof SyntaxError) {
|
||||||
|
console.error('[SceneFusion] Failed to parse message:', line);
|
||||||
|
} else {
|
||||||
|
throw parseError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error('未收到有效的生成结果');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SceneFusion] Regenerate error:', error);
|
console.error('[SceneFusion] Regenerate error:', error);
|
||||||
@ -282,17 +333,30 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 下载图片
|
// 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
|
||||||
const downloadImage = (url: string, filename: string) => {
|
const downloadImage = async (url: string, filename: string) => {
|
||||||
const link = document.createElement('a');
|
try {
|
||||||
link.href = url;
|
const response = await fetch(url);
|
||||||
link.download = filename;
|
const blob = await response.blob();
|
||||||
document.body.appendChild(link);
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
const link = document.createElement('a');
|
||||||
|
link.href = blobUrl;
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
// 释放 Blob URL
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载图片失败:', error);
|
||||||
|
// 降级方案:直接打开链接
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全部下载
|
// 全部下载 - 顺序下载所有图片
|
||||||
const downloadAll = async () => {
|
const downloadAll = async () => {
|
||||||
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
||||||
if (completedScenes.length === 0) {
|
if (completedScenes.length === 0) {
|
||||||
@ -300,16 +364,22 @@ export function SceneFusionDialog({ open, onOpenChange }: SceneFusionDialogProps
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.info(`正在下载 ${completedScenes.length} 张图片...`);
|
toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
|
||||||
|
|
||||||
|
// 顺序下载,每张图片下载完成后再下载下一张
|
||||||
for (let i = 0; i < completedScenes.length; i++) {
|
for (let i = 0; i < completedScenes.length; i++) {
|
||||||
const scene = completedScenes[i];
|
const scene = completedScenes[i];
|
||||||
const url = scene.originalUrl || scene.result!;
|
const url = scene.originalUrl || scene.result!;
|
||||||
// 延迟下载避免浏览器阻止
|
try {
|
||||||
setTimeout(() => {
|
await downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||||
downloadImage(url, `scene-fusion-${i + 1}.png`);
|
// 添加小延迟避免浏览器阻止
|
||||||
}, i * 300);
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`下载第 ${i + 1} 张图片失败:`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toast.success(`已下载 ${completedScenes.length} 张图片`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -337,8 +337,8 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
productImage,
|
productImage,
|
||||||
productName: productName || undefined,
|
productName: productName || undefined,
|
||||||
aspectRatio, // 图片比例
|
aspectRatio,
|
||||||
imageSize, // 图片分辨率
|
imageSize,
|
||||||
scenes: [{
|
scenes: [{
|
||||||
id: scene.id,
|
id: scene.id,
|
||||||
image: scene.image,
|
image: scene.image,
|
||||||
@ -348,25 +348,76 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(data.error || '处理失败');
|
// 尝试解析错误信息
|
||||||
|
try {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(errorData.error || '处理失败');
|
||||||
|
} catch {
|
||||||
|
throw new Error(`请求失败: ${response.status}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = data.results?.[0];
|
// 处理流式响应
|
||||||
if (result?.status === 'completed') {
|
const reader = response.body?.getReader();
|
||||||
setScenes(prev => prev.map(s =>
|
if (!reader) {
|
||||||
s.id === id ? {
|
throw new Error('无法读取响应流');
|
||||||
...s,
|
}
|
||||||
status: 'completed',
|
|
||||||
result: result.result,
|
const decoder = new TextDecoder();
|
||||||
originalUrl: result.originalUrl,
|
let buffer = '';
|
||||||
} : s
|
let success = false;
|
||||||
));
|
|
||||||
toast.success('重新生成成功');
|
while (true) {
|
||||||
} else {
|
const { done, value } = await reader.read();
|
||||||
throw new Error(result?.error || '生成失败');
|
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
|
||||||
|
const lines = buffer.split('\n');
|
||||||
|
buffer = lines.pop() || '';
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const message = JSON.parse(line);
|
||||||
|
console.log('[SceneFusion] Regenerate message:', message.type);
|
||||||
|
|
||||||
|
if (message.type === 'result') {
|
||||||
|
const result = message.data;
|
||||||
|
if (result.id === id) {
|
||||||
|
if (result.status === 'completed') {
|
||||||
|
setScenes(prev => prev.map(s =>
|
||||||
|
s.id === id ? {
|
||||||
|
...s,
|
||||||
|
status: 'completed',
|
||||||
|
result: result.result,
|
||||||
|
originalUrl: result.originalUrl,
|
||||||
|
} : s
|
||||||
|
));
|
||||||
|
toast.success('重新生成成功');
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.error || '生成失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (message.type === 'error') {
|
||||||
|
throw new Error(message.error || '处理失败');
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
if (parseError instanceof SyntaxError) {
|
||||||
|
console.error('[SceneFusion] Failed to parse message:', line);
|
||||||
|
} else {
|
||||||
|
throw parseError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error('未收到有效的生成结果');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SceneFusion] Regenerate error:', error);
|
console.error('[SceneFusion] Regenerate error:', error);
|
||||||
@ -377,17 +428,30 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 下载图片
|
// 下载图片 - 使用 fetch 获取图片数据后下载,解决跨域问题
|
||||||
const downloadImage = (url: string, filename: string) => {
|
const downloadImage = async (url: string, filename: string) => {
|
||||||
const link = document.createElement('a');
|
try {
|
||||||
link.href = url;
|
const response = await fetch(url);
|
||||||
link.download = filename;
|
const blob = await response.blob();
|
||||||
document.body.appendChild(link);
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
const link = document.createElement('a');
|
||||||
|
link.href = blobUrl;
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
// 释放 Blob URL
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载图片失败:', error);
|
||||||
|
// 降级方案:直接打开链接
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 全部下载
|
// 全部下载 - 顺序下载所有图片
|
||||||
const downloadAll = async () => {
|
const downloadAll = async () => {
|
||||||
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
const completedScenes = scenes.filter(s => s.status === 'completed' && (s.originalUrl || s.result));
|
||||||
if (completedScenes.length === 0) {
|
if (completedScenes.length === 0) {
|
||||||
@ -395,16 +459,22 @@ export function SceneFusionView({ onBack }: SceneFusionViewProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.info(`正在下载 ${completedScenes.length} 张图片...`);
|
toast.info(`正在下载 ${completedScenes.length} 张图片,请稍候...`);
|
||||||
|
|
||||||
|
// 顺序下载,每张图片下载完成后再下载下一张
|
||||||
for (let i = 0; i < completedScenes.length; i++) {
|
for (let i = 0; i < completedScenes.length; i++) {
|
||||||
const scene = completedScenes[i];
|
const scene = completedScenes[i];
|
||||||
const url = scene.originalUrl || scene.result!;
|
const url = scene.originalUrl || scene.result!;
|
||||||
// 延迟下载避免浏览器阻止
|
try {
|
||||||
setTimeout(() => {
|
await downloadImage(url, `scene-fusion-${i + 1}.png`);
|
||||||
downloadImage(url, `scene-fusion-${i + 1}.png`);
|
// 添加小延迟避免浏览器阻止
|
||||||
}, i * 300);
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`下载第 ${i + 1} 张图片失败:`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toast.success(`已下载 ${completedScenes.length} 张图片`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user