fix: update CF AI image gen to use multipart for FLUX.2 models
Cloudflare Workers AI changed the API for flux-2-dev, flux-2-klein-4b,
and flux-2-klein-9b to require multipart/form-data (instead of JSON) and
now returns {"image":"<base64>"} instead of raw PNG bytes.
- Add requiresMultipart() helper for the three FLUX.2 models
- callImageAPI builds multipart body for those models, JSON for others
- Parse {"image":"<base64>"} JSON response; fall back to raw bytes for legacy models
- Use "steps" field name (not "num_steps") in multipart forms per CF docs
- Book page: capture and display actual backend error message instead of blank 'Error'
This commit is contained in:
@@ -164,6 +164,7 @@
|
||||
let coverPreview = $state<string | null>(null);
|
||||
let coverSaving = $state(false);
|
||||
let coverResult = $state<'saved' | 'error' | ''>('');
|
||||
let coverErrorMsg = $state('');
|
||||
let coverPromptOpen = $state(false);
|
||||
|
||||
function buildCoverPrompt(): string {
|
||||
@@ -186,6 +187,7 @@
|
||||
coverGenerating = true;
|
||||
coverPreview = null;
|
||||
coverResult = '';
|
||||
coverErrorMsg = '';
|
||||
const promptToUse = coverPrompt.trim() || buildCoverPrompt();
|
||||
try {
|
||||
let res: Response;
|
||||
@@ -210,22 +212,25 @@
|
||||
} else {
|
||||
res = await fetch('/api/admin/image-gen', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ slug, type: 'cover', prompt: promptToUse })
|
||||
});
|
||||
}
|
||||
if (res.ok) {
|
||||
const d = await res.json();
|
||||
coverPreview = d.image_b64 ? `data:${d.content_type ?? 'image/png'};base64,${d.image_b64}` : null;
|
||||
} else {
|
||||
coverResult = 'error';
|
||||
}
|
||||
} catch {
|
||||
coverResult = 'error';
|
||||
} finally {
|
||||
coverGenerating = false;
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ slug, type: 'cover', prompt: promptToUse })
|
||||
});
|
||||
}
|
||||
if (res.ok) {
|
||||
const d = await res.json();
|
||||
coverPreview = d.image_b64 ? `data:${d.content_type ?? 'image/png'};base64,${d.image_b64}` : null;
|
||||
} else {
|
||||
const d = await res.json().catch(() => ({}));
|
||||
coverErrorMsg = (d as any).error ?? '';
|
||||
coverResult = 'error';
|
||||
}
|
||||
} catch (e: any) {
|
||||
coverErrorMsg = e?.message ?? '';
|
||||
coverResult = 'error';
|
||||
} finally {
|
||||
coverGenerating = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCover() {
|
||||
const slug = data.book?.slug;
|
||||
@@ -258,6 +263,7 @@
|
||||
let chapterCoverGenerating = $state(false);
|
||||
let chapterCoverPreview = $state<string | null>(null);
|
||||
let chapterCoverResult = $state<'saved' | 'error' | ''>('');
|
||||
let chapterCoverErrorMsg = $state('');
|
||||
let chapterCoverSaving = $state(false);
|
||||
let chapterCoverPrompt = $state('');
|
||||
|
||||
@@ -269,6 +275,7 @@
|
||||
chapterCoverGenerating = true;
|
||||
chapterCoverPreview = null;
|
||||
chapterCoverResult = '';
|
||||
chapterCoverErrorMsg = '';
|
||||
const promptToUse = chapterCoverPrompt.trim() || `Chapter ${n} illustration for "${data.book?.title ?? slug}". Dramatic scene, vivid colors, detailed art, cinematic lighting.`;
|
||||
try {
|
||||
const res = await fetch('/api/admin/image-gen', {
|
||||
@@ -280,9 +287,12 @@
|
||||
const d = await res.json();
|
||||
chapterCoverPreview = d.image_b64 ? `data:${d.content_type ?? 'image/png'};base64,${d.image_b64}` : null;
|
||||
} else {
|
||||
const d = await res.json().catch(() => ({}));
|
||||
chapterCoverErrorMsg = (d as any).error ?? '';
|
||||
chapterCoverResult = 'error';
|
||||
}
|
||||
} catch {
|
||||
} catch (e: any) {
|
||||
chapterCoverErrorMsg = e?.message ?? '';
|
||||
chapterCoverResult = 'error';
|
||||
} finally {
|
||||
chapterCoverGenerating = false;
|
||||
@@ -1101,7 +1111,7 @@
|
||||
{m.book_detail_admin_generate()}{coverUseAsRef ? ' (img2img)' : ''}
|
||||
</button>
|
||||
{#if coverResult === 'error'}
|
||||
<span class="text-xs text-(--color-danger)">{m.common_error()}</span>
|
||||
<span class="text-xs text-(--color-danger)">{coverErrorMsg || m.common_error()}</span>
|
||||
{:else if coverResult === 'saved'}
|
||||
<span class="text-xs text-green-400">{m.book_detail_admin_saved()}</span>
|
||||
{/if}
|
||||
@@ -1159,7 +1169,7 @@
|
||||
{m.book_detail_admin_generate()}
|
||||
</button>
|
||||
{#if chapterCoverResult === 'error'}
|
||||
<span class="text-xs text-(--color-danger)">{m.common_error()}</span>
|
||||
<span class="text-xs text-(--color-danger)">{chapterCoverErrorMsg || m.common_error()}</span>
|
||||
{:else if chapterCoverResult === 'saved'}
|
||||
<span class="text-xs text-green-400">{m.book_detail_admin_saved()}</span>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user