is in your HTML and loaded correctly. Check browser console for network errors or ad-blocker interference.");
throw new Error("Gemini AI SDK not loaded. Check browser console for script loading errors (e.g., network issues, ad-blockers).");
}
// Check API Key configuration (and warn about the public key)
if (GEMINI_API_KEY === "YOUR_GEMINI_API_KEY" || !GEMINI_API_KEY) {
throw new Error("Gemini API Key is not configured. Please set it in the script.");
} else if (GEMINI_API_KEY === "AIzaSyB-AX1KFYjavqGmnb2TUV_s29sDU-lZIis") {
console.warn("CRITICAL SECURITY WARNING: Using a publicly shared/compromised API key. This key should be revoked and replaced with a new, private key for any real use. This is for local testing ONLY.");
}
const { GoogleGenerativeAI } = genAI; // Destructure from the global 'genAI' object
const ai = new GoogleGenerativeAI(GEMINI_API_KEY);
// UI mentions Gemini 1.5 Flash. If you have access and intend to use it,
// change "gemini-pro" to "gemini-1.5-flash-latest" or your specific model name.
const model = ai.getGenerativeModel({ model: "gemini-pro" });
console.log("Sending prompt to Gemini SDK:", promptContent.substring(0, 100) + "..."); // Log snippet
const result = await model.generateContent(promptContent);
const response = result.response;
if (!response) {
// Attempt to get more details from the result if response is falsy
let feedbackInfo = "";
if (result && result.promptFeedback) {
feedbackInfo = ` Prompt Feedback: ${JSON.stringify(result.promptFeedback)}`;
}
throw new Error(`Gemini API returned an empty response structure.${feedbackInfo}`);
}
const rawText = response.text();
console.log("Gemini SDK extracted raw text:", rawText);
if (!rawText || rawText.trim() === "") {
let feedbackInfo = "";
if (response.promptFeedback) {
feedbackInfo = ` Prompt Feedback: ${JSON.stringify(response.promptFeedback)}`;
} else if (result && result.promptFeedback) {
feedbackInfo = ` Prompt Feedback: ${JSON.stringify(result.promptFeedback)}`;
}
throw new Error(`Gemini API returned empty text content.${feedbackInfo}`);
}
// Clean and parse JSON (remove potential markdown backticks)
const jsonString = rawText.replace(/```json/g, '').replace(/```/g, '').trim();
console.log("Gemini API cleaned JSON string:", jsonString);
let parsedJson;
try {
parsedJson = JSON.parse(jsonString);
} catch (jsonError) {
console.error("Failed to parse JSON from Gemini. Raw text:", rawText);
throw new Error(`Failed to parse JSON response from Gemini: ${jsonError.message}.`);
}
if (!parsedJson.prompt || !parsedJson.visual_description) {
console.warn("Gemini response might be missing expected fields. Parsed:", parsedJson);
throw new Error("Gemini response is missing required script details (e.g., 'prompt' or 'visual_description').");
}
return parsedJson;
} catch (error) {
console.error("Error during Gemini script generation:", error);
let errorMessage = `Error during Gemini script generation: ${error.message}`;
// Check if error object has response and promptFeedback for more specific Gemini errors
if (error.response && error.response.promptFeedback) {
console.error("Gemini Prompt Feedback:", error.response.promptFeedback);
errorMessage += ` (Safety/Feedback: ${JSON.stringify(error.response.promptFeedback)})`;
}
throw new Error(errorMessage);
}
}
/**
* Calls the Ginigen Flux API to generate the video based on the script.
* THIS IS CURRENTLY A MOCK/PLACEHOLDER.
* @param {Object} script - The detailed video script.
* @param {string} style - The selected video style.
* @param {number} duration - The selected video duration.
* @returns {Promise} A promise that resolves with the URL of the generated video.
* @throws {Error} If the Ginigen API call fails or returns no video data.
*/
async function generateVideoWithGinigen(script, style, duration) {
updateProgress(40);
// Simulate progress for demo purposes as the actual API call is mocked
for (let i = 41; i <= 90; i += Math.floor(Math.random() * 5) + 1) {
await new Promise(resolve => setTimeout(resolve, 200 + Math.random() * 300));
updateProgress(Math.min(i, 90));
}
try {
// --- THIS SECTION IS A MOCK ---
console.log("Simulating Ginigen Flux video generation...");
await new Promise(resolve => setTimeout(resolve, 2000)); // Simulate network delay
const placeholderVideos = [
"https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
];
currentVideoUrl = placeholderVideos[Math.floor(Math.random() * placeholderVideos.length)];
updateProgress(95);
return currentVideoUrl;
// --- END OF MOCK SECTION ---
} catch (error) {
console.error("Error during Ginigen video generation:", error);
throw new Error(`Error during Ginigen video generation: ${error.message}`);
}
}
/**
* Displays the generated video in the UI and updates button visibility.
* @param {string} videoUrl - The URL of the generated video.
*/
function showVideoResult(videoUrl) {
elements.generatedVideo.src = videoUrl;
elements.generatedVideo.classList.remove('hidden');
elements.placeholder.classList.add('hidden');
elements.downloadBtn.classList.remove('hidden');
elements.regenerateBtn.classList.remove('hidden');
elements.generatedVideo.load();
elements.generatedVideo.play().catch(e => console.warn("Video autoplay was prevented:", e));
}
/**
* Triggers the download of the current generated video.
*/
function downloadVideo() {
if (!currentVideoUrl) {
showError("No video to download.");
return;
}
const a = document.createElement('a');
a.href = currentVideoUrl;
const fileNameFromPrompt = elements.prompt.value.trim().toLowerCase().replace(/\s+/g, '-').substring(0, 30) || "ai-video";
a.download = `${fileNameFromPrompt}-${Date.now()}.mp4`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
if (currentVideoUrl.startsWith('blob:')) { // Only revoke if it's an object URL
URL.revokeObjectURL(a.href);
}
}
/**
* Sets the loading state for the UI elements (buttons, inputs, spinner).
* @param {boolean} isLoading - True to activate loading state, false to deactivate.
*/
function setLoadingState(isLoading) {
elements.generateBtn.disabled = isLoading;
elements.regenerateBtn.disabled = isLoading; // Disable regenerate during generation
elements.btnText.textContent = isLoading ? "Generating..." : "Generate Video";
elements.loadingSpinner.classList.toggle('hidden', !isLoading);
elements.prompt.disabled = isLoading;
elements.style.disabled = isLoading;
elements.duration.disabled = isLoading;
}
/**
* Displays an error message in the dedicated error area.
* @param {string} message - The error message text.
*/
function showError(message) {
elements.errorMessage.textContent = message;
elements.errorMessage.classList.remove('hidden');
}
/**
* Hides the error message from the display.
*/
function hideError() {
elements.errorMessage.classList.add('hidden');
elements.errorMessage.textContent = ""; // Clear previous message
}
/**
* Updates the progress bar width and percentage text.
* @param {number} percent - The progress percentage (0-100).
*/
function updateProgress(percent) {
const p = Math.max(0, Math.min(100, percent)); // Clamp between 0 and 100
elements.progressBar.style.width = `${p}%`;
elements.progressPercent.textContent = `${Math.round(p)}%`;
}
/**
* Updates the content and styling of the video placeholder.
* @param {string} message - The message to display in the placeholder.
* @param {boolean} isError - If true, styles the placeholder for an error message.
*/
function updatePlaceholder(message, isError = false) {
elements.placeholder.innerHTML = `
0 Comments
please do not enter any spam link in the comment box