অবশ্যই! আপনার প্রয়োজনীয়তা অনুসারে, Pollinations AI ব্যবহার করে একটি ইমেজ জেনারেটর অ্যাপ তৈরির জন্য ধাপে ধাপে একটি পূর্ণাঙ্গ টিউটোরিয়াল এবং সহজবোধ্য ব্লগ পোস্ট নিচে দেওয়া হলো।
ব্লগ পোস্ট: Pollinations AI এবং REST API দিয়ে বানান আপনার নিজের ইমেজ জারেটর অ্যাপ
আজকাল আমরা সবাই Midjourney বা DALL-E-এর মতো টেক্সট-টু-ইমেজ জেনারেটরের কথা শুনেছি। কেমন হয় যদি আপনি নিজেই এরকম একটি অ্যাপ্লিকেশন তৈরি করতে পারেন? আজ আমরা Pollinations AI-এর শক্তিশালী REST API ব্যবহার করে একটি ওয়েব অ্যাপ্লিকেশন তৈরি করব, যেখানে ব্যবহারকারীরা টেক্সট প্রম্পট দিয়ে ছবি তৈরি করতে পারবেন, ছবির সাইজ ও মডেল বেছে নিতে পারবেন এবং তৈরি করা ছবি ডাউনলোডও করতে পারবেন।
এই ব্লগ পোস্টে, আমরা পুরো প্রক্রিয়াটি ধাপে ধাপে সম্পন্ন করব। চলুন শুরু করা যাক!
আমাদের অ্যাপের ফিচারগুলো কী কী?
১. ইমেজ জেনারেশন: ব্যবহারকারী একটি টেক্সট ইনপুট দেবেন এবং এর উপর ভিত্তি করে ছবি তৈরি হবে। ২. অ্যাডভান্সড সেটিংস: ছবি তৈরির জন্য বিভিন্ন মডেল, ছবির উচ্চতা (Height) ও প্রস্থ (Width) এবং অ্যাসপেক্ট রেশিও (Aspect Ratio) সেট করার সুবিধা থাকবে। ৩. র্যান্ডম সীড (Seed): প্রতিটি ছবি তৈরির সময় একটি ইউনিক ও র্যান্ডম সীড ব্যবহার করা হবে, যাতে একই প্রম্পটেও নতুন নতুন ছবি পাওয়া যায়। ৪. ফলাফল প্রদর্শন: প্রতিবার সার্চে ৯টি ছবি গ্রিড আকারে দেখানো হবে। ৫. ডাউনলোড সুবিধা: ব্যবহারকারীরা তাদের পছন্দের ছবি ডাউনলোড করতে পারবেন। ৬. ডাউনলোড হিস্ট্রি: ডাউনলোড করা ছবিগুলো একটি আলাদা পেজে তালিকা আকারে দেখা যাবে। ৭. স্টেট ম্যানেজমেন্ট: লোডিং, এরর এবং ডেটা প্রদর্শনের জন্য স্টেট সঠিকভাবে ম্যানেজ করা হবে।
তাহলে আর দেরি না করে, চলুন মূল টিউটোরিয়ালে চলে যাই।
টিউটোরিয়াল: ধাপে ধাপে ইমেজ জেনারেটর অ্যাপ তৈরি
এই টিউটোরিয়ালটি অনুসরণ করার জন্য আপনার জাভাস্ক্রিপ্ট এবং React.js সম্পর্কে প্রাথমিক ধারণা থাকতে হবে। আমরা create-react-app
বা Vite
দিয়ে একটি নতুন রিয়্যাক্ট প্রজেক্ট তৈরি করে নেব।
ধাপ ১: প্রজেক্ট সেটআপ এবং UI তৈরি
প্রথমে, একটি নতুন রিয়্যাক্ট প্রজেক্ট তৈরি করুন। এরপর src
ফোল্ডারের ভেতরে components
নামে একটি ফোল্ডার তৈরি করুন এবং এর ভেতরে CreateImage.js
, Downloaded.js
, এবং Navbar.js
নামে তিনটি ফাইল তৈরি করুন।
CreateImage.js
ফাইলের প্রাথমিক কাঠামো:
আমাদের મુખ્ય UI এখানে থাকবে। এতে একটি প্রম্পট ইনপুট ফিল্ড, সাবমিট বাটন, অ্যাডভান্সড সেটিংস এবং ফলাফল দেখানোর জন্য একটি সেকশন থাকবে।
// CreateImage.js
import React, { useState, useEffect } from "react";
const CreateImage = () => {
// এখানে আমরা বিভিন্ন state রাখব
const [prompt, setPrompt] = useState("");
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = (e) => {
e.preventDefault();
// ইমেজ জেনারেট করার ফাংশন এখানে কল করা হবে
};
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold text-center mb-6">
Create Image with Pollinations AI
</h1>
{/* Prompt Input Form */}
<form
onSubmit={handleSubmit}
className="flex flex-col md:flex-row gap-4 mb-6"
>
<input
type="text"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Enter your prompt here, e.g., 'A cat sitting on a rainbow'"
className="flex-grow p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
<button
type="submit"
disabled={loading}
className="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-400"
>
{loading ? "Generating..." : "Generate"}
</button>
</form>
{/* Advanced Settings Section */}
{/* আমরা পরের ধাপে এটি যুক্ত করব */}
{/* Result Section */}
{loading && <div className="text-center">Loading images...</div>}
{error && <div className="text-center text-red-500">{error}</div>}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* এখানে জেনারেট হওয়া ইমেজগুলো দেখানো হবে */}
</div>
</div>
);
};
export default CreateImage;
ধাপ ২: মডেল লিস্ট আনা এবং অ্যাডভান্সড সেটিংস
Pollinations AI কোন কোন মডেল সাপোর্ট করে, তা আমাদের API থেকে নিয়ে আসতে হবে এবং Select Option-এ দেখাতে হবে।
CreateImage.js
-এ নিচের কোড যুক্ত করুন:
// ... আগের ইম্পোর্টগুলোর সাথে যুক্ত করুন
import { useState, useEffect } from "react";
// ... CreateImage 컴্পোনেন্টের ভেতরে
const [models, setModels] = useState([]);
const [selectedModel, setSelectedModel] = useState("turbo"); // Default model
const [width, setWidth] = useState(1024);
const [height, setHeight] = useState(1024);
useEffect(() => {
// Component মাউন্ট হওয়ার সময় মডেল লিস্ট ফেচ করবে
const fetchModels = async () => {
try {
const response = await fetch("https://image.pollinations.ai/models");
if (!response.ok) {
throw new Error("Failed to fetch models");
}
const data = await response.json();
setModels(Object.keys(data)); // API থেকে অবজেক্ট আসে, তাই কী গুলো নিচ্ছি
} catch (err) {
console.error(err);
// আপনি চাইলে এখানে একটি এরর মেসেজ দেখাতে পারেন
}
};
fetchModels();
}, []); // [] মানে এই ইফেক্টটি শুধু একবার চলবে
const handleRatioClick = (w, h) => {
setWidth(w);
setHeight(h);
};
// ... return স্টেটমেন্টের ভেতরে <form> এর পর অ্যাডভান্সড সেটিংস যোগ করুন
{
/* Advanced Settings Section */
}
<div className="bg-gray-100 p-4 rounded-lg mb-6">
<h2 className="text-xl font-semibold mb-3">Advanced Settings</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Model Selection */}
<div>
<label
htmlFor="model"
className="block text-sm font-medium text-gray-700"
>
Model
</label>
<select
id="model"
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
className="mt-1 block w-full p-2 border border-gray-300 rounded-md"
>
{models.map((model) => (
<option key={model} value={model}>
{model}
</option>
))}
</select>
</div>
{/* Width & Height */}
<div>
<label
htmlFor="width"
className="block text-sm font-medium text-gray-700"
>
Width
</label>
<input
type="number"
id="width"
value={width}
onChange={(e) => setWidth(e.target.value)}
className="mt-1 block w-full p-2 border border-gray-300 rounded-md"
/>
</div>
<div>
<label
htmlFor="height"
className="block text-sm font-medium text-gray-700"
>
Height
</label>
<input
type="number"
id="height"
value={height}
onChange={(e) => setHeight(e.target.value)}
className="mt-1 block w-full p-2 border border-gray-300 rounded-md"
/>
</div>
{/* Seed (Disabled) */}
<div>
<label htmlFor="seed" className="block text-sm font-medium text-gray-700">
Seed (Random)
</label>
<input
type="text"
id="seed"
placeholder="Generated randomly"
disabled
className="mt-1 block w-full p-2 border bg-gray-200 rounded-md"
/>
</div>
{/* Ratio Presets */}
<div className="md:col-span-2 flex items-end gap-2">
<button
type="button"
onClick={() => handleRatioClick(1024, 1024)}
className="bg-gray-300 px-4 py-2 rounded-md"
>
1:1
</button>
<button
type="button"
onClick={() => handleRatioClick(1920, 1080)}
className="bg-gray-300 px-4 py-2 rounded-md"
>
16:9
</button>
<button
type="button"
onClick={() => handleRatioClick(1080, 1920)}
className="bg-gray-300 px-4 py-2 rounded-md"
>
9:16
</button>
</div>
</div>
</div>;
ধাপ ৩: ইমেজ জেনারেট করা এবং ফলাফল দেখানো
এখন মূল কাজ। আমরা ফর্ম সাবমিট হলে Pollinations AI-এর API-তে রিকোয়েস্ট পাঠাব। আমাদের ৯টি ছবি দরকার, তাই আমরা ৯টি ভিন্ন ও র্যান্ডম সীডসহ ৯টি API কল একসাথে চালাব। এর জন্য Promise.allSettled
ব্যবহার করা সুবিধাজনক, কারণ এটি একটি কল ফেইল হলেও বাকিগুলোর জন্য অপেক্ষা করে।
CreateImage.js
এর handleSubmit
ফাংশনটি আপডেট করুন:
// CreateImage.js এর ভেতরে
const handleSubmit = async (e) => {
e.preventDefault();
if (!prompt) {
setError("Please enter a prompt.");
return;
}
setLoading(true);
setError(null);
setImages([]); // আগের ছবিগুলো মুছে ফেলি
const encodedPrompt = encodeURIComponent(prompt);
const imagePromises = [];
for (let i = 0; i < 9; i++) {
// প্রতি ছবির জন্য র্যান্ডম সীড তৈরি করি
const seed = Math.floor(Math.random() * 1000000000);
const url = `https://image.pollinations.ai/prompt/${encodedPrompt}?model=${selectedModel}&width=${width}&height=${height}&seed=${seed}&nologo=true`;
// Fetch Timeout যুক্ত করা
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // ৩০ সেকেন্ডের টাইমআউট
imagePromises.push(
fetch(url, { signal: controller.signal })
.then((response) => {
clearTimeout(timeoutId); // রেসপন্স পেলে টাইমআউট ক্লিয়ার করি
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.url; // আমরা সরাসরি ইমেজের URL রিটার্ন করব
})
.catch((err) => {
clearTimeout(timeoutId);
console.error(`Failed to load image for seed ${seed}:`, err);
return {
error: true,
message: err.name === "AbortError" ? "Timeout" : "Unable to Load",
};
})
);
}
const results = await Promise.allSettled(imagePromises);
const loadedImages = results.map((res) => {
if (res.status === "fulfilled") {
return res.value;
}
// এখানে ফেইল হওয়া প্রমিসের এরর অবজেক্টটি ব্যবহার করা যেত, তবে সরলতার জন্য একটি ডিফল্ট এরর অবজেক্ট দিচ্ছি
return { error: true, message: "Loading Failed" };
});
setImages(loadedImages);
setLoading(false);
};
// ... return স্টেটমেন্টের ভেতরে Result Section আপডেট করুন
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{images.map((img, index) => (
<div
key={index}
className="relative aspect-square border rounded-lg overflow-hidden group"
>
{img.error ? (
<div className="w-full h-full bg-gray-200 flex items-center justify-center">
<span className="text-red-500">{img.message}</span>
</div>
) : (
<>
<img
src={img}
alt={`Generated image ${index + 1}`}
className="w-full h-full object-cover"
/>
<div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-50 p-2 flex justify-center opacity-0 group-hover:opacity-100 transition-opacity">
{/* ডাউনলোড বাটন পরের ধাপে যুক্ত হবে */}
</div>
</>
)}
</div>
))}
</div>;
ধাপ ৪: ছবি ডাউনলোড এবং ডাউনলোড লিস্ট ম্যানেজ করা
এখন আমরা প্রতিটি ছবির জন্য একটি ডাউনলোড বাটন যোগ করব এবং কোন ছবিগুলো ডাউনলোড করা হয়েছে তার একটি তালিকা রাখব। এই তালিকাটি আমরা App.js
-এ একটি state
-এ রাখব এবং props
হিসেবে কম্পোনেন্টগুলোতে পাস করব।
App.js
ফাইল আপডেট করুন:
// App.js
import React, { useState } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import CreateImage from "./components/CreateImage";
import Downloaded from "./components/Downloaded";
import Navbar from "./components/Navbar";
function App() {
const [downloadedImages, setDownloadedImages] = useState([]);
const addDownloadedImage = (url) => {
// একই ছবি একাধিকবার যোগ করা থেকে বিরত থাকি
setDownloadedImages((prev) => {
if (prev.includes(url)) {
return prev;
}
return [...prev, url];
});
};
return (
<Router>
<Navbar />
<Routes>
<Route
path="/"
element={<CreateImage addDownloadedImage={addDownloadedImage} />}
/>
<Route
path="/downloaded"
element={<Downloaded downloadedImages={downloadedImages} />}
/>
</Routes>
</Router>
);
}
export default App;
CreateImage.js
-এ ডাউনলোড ফাংশনালিটি যোগ করুন:
// CreateImage.js
// কম্পোনেন্টের props হিসেবে addDownloadedImage নিন
const CreateImage = ({ addDownloadedImage }) => {
// ... বাকি কোড ...
const handleDownload = async (url) => {
try {
const response = await fetch(url);
const blob = await response.blob();
const objectUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = objectUrl;
link.download = `pollinations_image_${Date.now()}.jpg`; // ইউনিক ফাইল নাম
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(objectUrl); // মেমোরি ক্লিনের জন্য
addDownloadedImage(url); // ডাউনলোড লিস্টে যোগ করি
} catch (err) {
console.error("Download failed:", err);
alert("Failed to download image.");
}
};
// ... return স্টেটমেন্টের ভেতরে ইমেজ ম্যাপিং-এর সময় ডাউনলোড বাটন যুক্ত করুন ...
<button
onClick={() => handleDownload(img)}
className="text-white font-semibold"
>
Download
</button>;
};
ধাপ ৫: ডাউনলোড করা ছবির পেজ তৈরি করা
সবশেষে, আমরা /downloaded
রুটের জন্য একটি পেজ তৈরি করব যেখানে ডাউনলোড করা ছবিগুলো দেখা যাবে।
Downloaded.js
ফাইল তৈরি করুন:
// Downloaded.js
import React from "react";
const Downloaded = ({ downloadedImages }) => {
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold text-center mb-6">
Your Downloaded Images
</h1>
{downloadedImages.length === 0 ? (
<p className="text-center text-gray-500">
You haven't downloaded any images yet.
</p>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{downloadedImages.map((url, index) => (
<div key={index} className="border rounded-lg overflow-hidden">
<img
src={url}
alt={`Downloaded ${index + 1}`}
className="w-full h-auto object-cover"
/>
</div>
))}
</div>
)}
</div>
);
};
export default Downloaded;
Navbar.js
তৈরি করুন নেভিগেশনের জন্য:
// Navbar.js
import React from "react";
import { Link } from "react-router-dom";
const Navbar = () => {
return (
<nav className="bg-gray-800 text-white p-4">
<div className="container mx-auto flex justify-between">
<Link to="/" className="font-bold text-xl">
Image Generator
</Link>
<div>
<Link to="/" className="mr-4 hover:text-gray-300">
Create Image
</Link>
<Link to="/downloaded" className="hover:text-gray-300">
Downloaded
</Link>
</div>
</div>
</nav>
);
};
export default Navbar;
এবং এইভাবেই আপনার সম্পূর্ণ অ্যাপ্লিকেশনটি তৈরি হয়ে গেল! আপনি এখন npm start
বা yarn start
দিয়ে অ্যাপটি চালাতে পারবেন এবং নিজের তৈরি করা ইমেজ জেনারেটর ব্যবহার করতে পারবেন। এই টিউটোরিয়ালটি আপনাকে একটি বেসিক কাঠামো দিয়েছে, যার উপর ভিত্তি করে আপনি আরও অনেক ফিচার যোগ করতে পারেন, যেমন localStorage
ব্যবহার করে ডাউনলোড হিস্ট্রি সেভ করা। শুভ কোডিং!