<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>مولد صورة الأميرة الغائبة</title>
<!-- تضمين Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* خط عربي جميل */
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&display=swap');
body {
font-family: 'Cairo', sans-serif;
background-color: #f8f9fa;
}
.container-card {
background: linear-gradient(135deg, #ffffff, #f0f4f7);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
border-radius: 1.5rem;
}
.loading-spinner {
border-top-color: #3b82f6;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="min-h-screen flex items-center justify-center p-4">
<div id="app" class="container-card max-w-2xl w-full p-6 sm:p-8 text-center">
<h1 class="text-3xl sm:text-4xl font-bold text-gray-800 mb-4">طلعة الأميرة الحنساء</h1>
<p class="text-md sm:text-lg text-gray-600 mb-6">صورة مستوحاة من كلمات الشوق والحنين</p>
<!-- منطقة عرض الصورة -->
<div id="image-container" class="w-full aspect-square bg-gray-200 rounded-xl overflow-hidden mb-6 flex items-center justify-center border-4 border-dashed border-gray-400">
<p id="placeholder-text" class="text-gray-500 p-4">انقر على الزر أدناه لتوليد صورة فنية تعكس جمال الانتظار والشوق.</p>
<div id="loading-indicator" class="hidden flex-col items-center">
<div class="loading-spinner w-12 h-12 border-4 rounded-full border-gray-300"></div>
<p class="mt-3 text-blue-600">جاري توليد الصورة... قد يستغرق الأمر بضع لحظات.</p>
</div>
<img id="generated-image" src="" alt="صورة فنية مولدة" class="w-full h-full object-cover hidden">
</div>
<!-- زر توليد الصورة -->
<button id="generate-btn" class="w-full px-6 py-3 bg-blue-600 text-white font-semibold text-lg rounded-full shadow-lg hover:bg-blue-700 transition duration-300 transform hover:scale-[1.02] focus:outline-none focus:ring-4 focus:ring-blue-300 disabled:opacity-50" onclick="generateImage()">
توليد صورة العشق والانتظار
</button>
<p id="error-message" class="text-red-500 mt-4 hidden"></p>
</div>
<script type="module">
const apiKey = ""; // يتم توفيره تلقائيًا بواسطة البيئة
const apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/imagen-4.0-generate-001:predict";
const imageContainer = document.getElementById('image-container');
const placeholderText = document.getElementById('placeholder-text');
const loadingIndicator = document.getElementById('loading-indicator');
const generatedImage = document.getElementById('generated-image');
const generateBtn = document.getElementById('generate-btn');
const errorMessage = document.getElementById('error-message');
const imagePrompt = "A cinematic and emotionally charged image: A lonely male lover in traditional attire (Moroccan style) stands in a narrow, melancholic alley of an old town, looking towards an empty doorway bathed in bright, hopeful light, symbolizing the return of the 'Missing Princess' (Al-Hansaa). The atmosphere should convey deep longing, waiting, sorrow, and ultimate hope. Dramatic chiaroscuro lighting. Highly detailed, artistic photography.";
// دالة تحويل Base64 إلى ArrayBuffer للعب الفيديوهات أو الصوتيات (غير مستخدمة هنا، ولكن مفيدة لمهام أخرى)
function base64ToArrayBuffer(base64) {
const binary_string = window.atob(base64);
const len = binary_string.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
// دالة تنفيذ طلب API مع آلية تكرار (Exponential Backoff)
async function fetchWithRetry(url, options, maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
// إذا كان الخطأ 429 (Too Many Requests)، انتظر وكرر
if (response.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000 + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw new Error(`API request failed with status ${response.status}: ${await response.text()}`);
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000 + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
window.generateImage = async function() {
generateBtn.disabled = true;
placeholderText.classList.add('hidden');
generatedImage.classList.add('hidden');
errorMessage.classList.add('hidden');
loadingIndicator.classList.remove('hidden');
const payload = {
instances: [{ prompt: imagePrompt }],
parameters: { "sampleCount": 1 }
};
try {
const response = await fetchWithRetry(`${apiUrl}?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json();
if (result.predictions && result.predictions.length > 0 && result.predictions[0].bytesBase64Encoded) {
const base64Data = result.predictions[0].bytesBase64Encoded;
const imageUrl = `data:image/png;base64,${base64Data}`;
generatedImage.src = imageUrl;
generatedImage.classList.remove('hidden');
imageContainer.classList.remove('border-dashed', 'border-gray-400');
imageContainer.classList.add('border-transparent');
} else {
const errorDetail = result.error ? result.error.message : 'لم يتم العثور على بيانات الصورة في الاستجابة.';
errorMessage.textContent = `خطأ في التوليد: ${errorDetail}`;
errorMessage.classList.remove('hidden');
placeholderText.classList.remove('hidden');
}
} catch (error) {
errorMessage.textContent = `فشل الاتصال أو خطأ غير متوقع: ${error.message}`;
errorMessage.classList.remove('hidden');
placeholderText.classList.remove('hidden');
} finally {
loadingIndicator.classList.add('hidden');
generateBtn.disabled = false;
}
};
</script>
</body>
</html>
