+
- Model:
+ Provider:
- {modelOptions.find((m) => m.value === model)?.label}
+ {result.provider === "stability"
+ ? "Stability AI"
+ : "HuggingFace"}
Style:
-
- {styleOptions.find((s) => s.key === style)?.label}
+
+ {
+ styleOptions.find((s) => s.key === result.style)
+ ?.label
+ }
-
- Parameters:
-
-
- {result.parameters.steps} steps,{" "}
- {result.parameters.guidance_scale} guidance
+ Size:
+
+ {result.parameters.width}x{result.parameters.height}
-
- {/* Refinement Section */}
- {!hasRefined && (
-
-
-
- Refine Your Thumbnail
-
-
- Make one improvement to your thumbnail
-
-
-
- setRefinementPrompt(e.target.value)
- }
- maxLength={200}
- description={`${refinementPrompt.length}/200 characters`}
- classNames={{
- inputWrapper: [
- "bg-default-100",
- "border-1",
- "border-default-200",
- "hover:border-default-300",
- "focus:border-primary",
- ],
- }}
- />
-
-
- )}
-
- {hasRefined && (
-
-
- โ
- Thumbnail refined
-
-
- )}
-
-
๐จ
+
+ ๐จ
-
- Your generated thumbnail will appear here
+
+ Your thumbnail will appear here
)}
diff --git a/config/site.ts b/config/site.ts
index cd4a88b..196a0cf 100644
--- a/config/site.ts
+++ b/config/site.ts
@@ -34,7 +34,7 @@ export const siteConfig = {
links: {
github: "https://github.com/pycomet",
docs: "https://github.com/pycomet",
- sponsor: "https://patreon.com/jrgarciadev", // TODO: Add Patreon Account
+ sponsor: "https://patreon.com/pycomet",
},
searchTopics: [
"How to make your first $1000",
diff --git a/lib/ai/fal.ts b/lib/ai/fal.ts
deleted file mode 100644
index 3f7f630..0000000
--- a/lib/ai/fal.ts
+++ /dev/null
@@ -1,277 +0,0 @@
-// fal.ai Provider Implementation
-import {
- ThumbnailGenerationOptions,
- ThumbnailResult,
- GenerationParameters,
-} from "./providers";
-
-// Note: fal.ai client should be installed: npm install @fal-ai/client
-// For now, we'll use fetch directly to avoid adding dependencies
-
-const FAL_API_URL = "https://fal.run/fal-ai";
-
-// Style prompts optimized for fal.ai models
-const stylePrompts = {
- tech: {
- positive:
- "professional tech product, modern design, clean lighting, high quality, tech review thumbnail, sharp focus",
- negative: "blurry, low quality, amateur, watermark, text overlay",
- basePrompt: "tech product showcase",
- },
- gaming: {
- positive:
- "gaming setup, colorful RGB lighting, exciting atmosphere, gaming thumbnail, high energy, dramatic",
- negative: "boring, dull, poor lighting, low quality",
- basePrompt: "gaming content thumbnail",
- },
- tutorial: {
- positive:
- "educational content, clear presentation, professional layout, tutorial thumbnail, instructional, clean",
- negative: "confusing, cluttered, messy, poor quality",
- basePrompt: "tutorial content thumbnail",
- },
- lifestyle: {
- positive:
- "lifestyle photo, natural lighting, authentic, warm, lifestyle thumbnail, personal content, candid",
- negative: "artificial, fake, poor lighting, low quality",
- basePrompt: "lifestyle content thumbnail",
- },
-};
-
-const universalNegativePrompt =
- "low quality, blurry, amateur, watermark, text overlay, logos, copyright, distorted";
-
-// Model configurations for fal.ai
-const models = {
- "flux-schnell": {
- id: "flux/schnell",
- name: "FLUX.1 Schnell",
- steps: { fast: 1, balanced: 2, high: 4 },
- guidance: { fast: 3.0, balanced: 5.0, high: 7.0 },
- },
- "flux-dev": {
- id: "flux/dev",
- name: "FLUX.1 Dev",
- steps: { fast: 20, balanced: 30, high: 50 },
- guidance: { fast: 5.0, balanced: 7.0, high: 8.0 },
- },
- "hidream-fast": {
- id: "hidream-i1-fast",
- name: "HiDream I1 Fast",
- steps: { fast: 8, balanced: 16, high: 25 },
- guidance: { fast: 3.0, balanced: 5.0, high: 7.0 },
- },
-};
-
-export async function generateThumbnail(
- options: ThumbnailGenerationOptions
-): Promise
{
- const {
- prompt,
- style = "tech",
- model = "flux-schnell",
- quality = "balanced",
- refinementPrompt,
- } = options;
-
- // Validate API key
- if (!process.env.FAL_KEY) {
- throw new Error("FAL_KEY environment variable is not set");
- }
-
- // Get model configuration
- const selectedModel = models[model as keyof typeof models];
- if (!selectedModel) {
- throw new Error(`Model ${model} not found`);
- }
-
- const styleConfig = stylePrompts[style];
-
- // Build the prompt with optional refinement
- let finalPrompt = `${styleConfig.basePrompt}, ${prompt}, ${styleConfig.positive}`;
- if (refinementPrompt) {
- finalPrompt += `, ${refinementPrompt}`;
- }
-
- // Build negative prompt
- const negativePrompt = `${styleConfig.negative}, ${universalNegativePrompt}`;
-
- // Generation parameters
- const qualitySettings = selectedModel.steps[quality];
- const guidanceSettings = selectedModel.guidance[quality];
-
- const parameters: GenerationParameters = {
- model: selectedModel.id,
- width: 1024,
- height: 576, // 16:9 aspect ratio
- steps: qualitySettings,
- guidance_scale: guidanceSettings,
- negative_prompt: negativePrompt,
- };
-
- try {
- console.log("๐จ Generating with fal.ai:", {
- model: selectedModel.name,
- prompt: finalPrompt.substring(0, 80) + "...",
- quality,
- style,
- steps: parameters.steps,
- guidance: parameters.guidance_scale,
- dimensions: `${parameters.width}x${parameters.height}`,
- });
-
- // Prepare request body based on model
- const requestBody: any = {
- prompt: finalPrompt,
- negative_prompt: negativePrompt,
- image_size: {
- width: parameters.width,
- height: parameters.height,
- },
- num_images: 1,
- enable_safety_checker: true,
- output_format: "jpeg",
- };
-
- // Add model-specific parameters
- if (model === "flux-schnell") {
- requestBody.num_inference_steps = parameters.steps;
- } else if (model === "flux-dev") {
- requestBody.num_inference_steps = parameters.steps;
- requestBody.guidance_scale = parameters.guidance_scale;
- } else if (model === "hidream-fast") {
- requestBody.num_inference_steps = parameters.steps;
- requestBody.guidance_scale = parameters.guidance_scale;
- }
-
- const response = await fetch(`${FAL_API_URL}/${selectedModel.id}`, {
- method: "POST",
- headers: {
- Authorization: `Key ${process.env.FAL_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(requestBody),
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- console.error("fal.ai API error:", errorText);
-
- if (response.status === 401) {
- throw new Error("Invalid fal.ai API key");
- }
- if (response.status === 429) {
- throw new Error("fal.ai rate limit exceeded. Please try again later.");
- }
- if (response.status === 400) {
- throw new Error("Invalid request to fal.ai. Please check your prompt.");
- }
- if (response.status === 402) {
- throw new Error(
- "fal.ai payment required. Please add credits to your account."
- );
- }
-
- throw new Error(
- `fal.ai API error: ${response.status} ${response.statusText}`
- );
- }
-
- const data = await response.json();
-
- if (!data.images || data.images.length === 0) {
- throw new Error("No image generated by fal.ai");
- }
-
- // Download the image from the URL
- const imageUrl = data.images[0].url;
- const imageResponse = await fetch(imageUrl);
-
- if (!imageResponse.ok) {
- throw new Error("Failed to download generated image");
- }
-
- const imageBlob = await imageResponse.blob();
-
- console.log("โ
fal.ai generation successful");
-
- return {
- imageBlob,
- prompt: finalPrompt,
- style,
- model: selectedModel.name,
- provider: "fal",
- parameters,
- };
- } catch (error) {
- console.error("โ fal.ai error:", error);
-
- if (error instanceof Error) {
- throw error;
- }
-
- throw new Error(`Failed to generate thumbnail with fal.ai: ${error}`);
- }
-}
-
-export async function testConnection(): Promise {
- try {
- if (!process.env.FAL_KEY) {
- return false;
- }
-
- console.log("๐ Testing fal.ai connection...");
-
- // Test with a simple request to flux-schnell (fastest model)
- const response = await fetch(`${FAL_API_URL}/flux/schnell`, {
- method: "POST",
- headers: {
- Authorization: `Key ${process.env.FAL_KEY}`,
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- prompt: "test image",
- image_size: {
- width: 512,
- height: 512,
- },
- num_images: 1,
- num_inference_steps: 1,
- }),
- });
-
- if (response.ok) {
- console.log("โ
fal.ai connection test successful");
- return true;
- } else if (response.status === 402) {
- console.log("โ ๏ธ fal.ai connection OK but requires payment");
- return true; // Connection works, just needs credits
- } else {
- console.error("โ fal.ai connection test failed:", response.status);
- return false;
- }
- } catch (error) {
- console.error("โ fal.ai connection test failed:", error);
- return false;
- }
-}
-
-export async function testThumbnailGeneration(): Promise {
- console.log("๐งช Testing fal.ai thumbnail generation...");
-
- try {
- const result = await generateThumbnail({
- prompt: "iPhone review",
- style: "tech",
- model: "flux-schnell",
- quality: "fast",
- });
-
- console.log("โ
fal.ai test generation successful");
- console.log("Prompt used:", result.prompt);
- console.log("Image blob size:", result.imageBlob.size, "bytes");
- } catch (error) {
- console.error("โ fal.ai test generation failed:", error);
- throw error;
- }
-}
diff --git a/lib/ai/huggingface.ts b/lib/ai/huggingface.ts
index c90be30..30fd8b3 100644
--- a/lib/ai/huggingface.ts
+++ b/lib/ai/huggingface.ts
@@ -134,8 +134,8 @@ export async function generateThumbnail(
// Generation parameters optimized for free tier
const parameters: GenerationParameters = {
model: selectedModel.id,
- width: 768, // Smaller size for free tier
- height: 432, // 16:9 ratio
+ width: 768, // Standard size for free tier
+ height: 512, // Minimum required height
steps: qualitySettings.steps,
guidance_scale: qualitySettings.guidance_scale,
negative_prompt: negativePrompt,
diff --git a/lib/ai/index.ts b/lib/ai/index.ts
index e440ebc..b4b489a 100644
--- a/lib/ai/index.ts
+++ b/lib/ai/index.ts
@@ -93,7 +93,7 @@ export async function getBestAvailableProvider(): Promise {
const providers = getAvailableProviders();
// Test providers in order of preference
- const preferenceOrder = ["stability", "fal", "huggingface"];
+ const preferenceOrder = ["stability", "huggingface"];
for (const providerId of preferenceOrder) {
const provider = providers.find((p) => p.id === providerId);
diff --git a/lib/ai/providers.ts b/lib/ai/providers.ts
index c400c4b..ed4033d 100644
--- a/lib/ai/providers.ts
+++ b/lib/ai/providers.ts
@@ -59,30 +59,12 @@ export const AI_PROVIDERS: Record = {
pricing: "FREE",
supportsRefinement: true,
models: [
- {
- id: "sd-3.5-large",
- name: "Stable Diffusion 3.5 Large",
- description: "8B parameters, superior quality",
- icon: "๐ฏ",
- recommended: true,
- speed: "medium",
- quality: "excellent",
- },
- {
- id: "sd-3.5-turbo",
- name: "Stable Diffusion 3.5 Turbo",
- description: "4-step generation, ultra-fast",
- icon: "โก",
- recommended: false,
- speed: "fast",
- quality: "high",
- },
{
id: "sdxl",
name: "Stable Diffusion XL",
description: "Proven quality, reliable",
icon: "๐ผ๏ธ",
- recommended: false,
+ recommended: true,
speed: "medium",
quality: "high",
},
@@ -98,50 +80,7 @@ export const AI_PROVIDERS: Record = {
return stabilityTest();
},
},
- fal: {
- id: "fal",
- name: "fal.ai",
- description: "Pay-per-use, excellent value",
- pricing: "~$0.003/image",
- supportsRefinement: true,
- models: [
- {
- id: "flux-schnell",
- name: "FLUX.1 Schnell",
- description: "Ultra-fast, 333 images per $1",
- icon: "๐",
- recommended: true,
- speed: "fast",
- quality: "high",
- },
- {
- id: "flux-dev",
- name: "FLUX.1 Dev",
- description: "Premium quality, 40 images per $1",
- icon: "๐",
- recommended: false,
- speed: "medium",
- quality: "excellent",
- },
- {
- id: "hidream-fast",
- name: "HiDream I1 Fast",
- description: "New model, 16 steps",
- icon: "โจ",
- recommended: false,
- speed: "fast",
- quality: "high",
- },
- ],
- generateThumbnail: async (options: ThumbnailGenerationOptions) => {
- const { generateThumbnail: falGenerate } = await import("./fal");
- return falGenerate(options);
- },
- testConnection: async () => {
- const { testConnection: falTest } = await import("./fal");
- return falTest();
- },
- },
+
huggingface: {
id: "huggingface",
name: "HuggingFace",
diff --git a/lib/ai/stability.ts b/lib/ai/stability.ts
index 4dbe4ee..3b7d53f 100644
--- a/lib/ai/stability.ts
+++ b/lib/ai/stability.ts
@@ -38,20 +38,8 @@ const stylePrompts = {
const universalNegativePrompt =
"low quality, blurry, amateur, watermark, text overlay, logos, copyright";
-// Model configurations for Stability AI
+// Model configurations for Stability AI v1 (working models only)
const models = {
- "sd-3.5-large": {
- id: "stable-diffusion-3-5-large",
- name: "Stable Diffusion 3.5 Large",
- steps: { fast: 20, balanced: 30, high: 50 },
- guidance: { fast: 5.0, balanced: 7.0, high: 8.0 },
- },
- "sd-3.5-turbo": {
- id: "stable-diffusion-3-5-turbo",
- name: "Stable Diffusion 3.5 Turbo",
- steps: { fast: 4, balanced: 6, high: 8 },
- guidance: { fast: 3.0, balanced: 5.0, high: 7.0 },
- },
sdxl: {
id: "stable-diffusion-xl-1024-v1-0",
name: "Stable Diffusion XL",
@@ -66,7 +54,7 @@ export async function generateThumbnail(
const {
prompt,
style = "tech",
- model = "sd-3.5-large",
+ model = "sdxl",
quality = "balanced",
refinementPrompt,
} = options;
@@ -99,8 +87,8 @@ export async function generateThumbnail(
const parameters: GenerationParameters = {
model: selectedModel.id,
- width: 1024,
- height: 576, // 16:9 aspect ratio
+ width: 1344,
+ height: 768, // Closest to 16:9 ratio allowed by SDXL
steps: qualitySettings,
guidance_scale: guidanceSettings,
negative_prompt: negativePrompt,