Veu3 / index.html
Hamed744's picture
Update index.html
4b5c167 verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ساخت صدای اختصاصی</title>
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap" rel="stylesheet">
<style>
/* --- General & Background Styles --- */
:root {
--primary-color: #3d5afe;
--secondary-color: #00b0ff;
--text-color: #e0e0e0;
--bg-color-start: #1a2940;
--bg-color-end: #2b3b58;
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.2);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
body {
font-family: 'Vazirmatn', sans-serif;
background: linear-gradient(135deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow-x: hidden;
}
/* --- Delta Code Gate Styles (Glassmorphism) --- */
#delta-gate-container {
background: var(--glass-bg);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
border: 1px solid var(--glass-border);
padding: 40px;
border-radius: 20px;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
text-align: center;
max-width: 450px;
width: 90%;
transition: opacity 0.5s, transform 0.5s;
animation: fadeIn 0.8s ease-out;
}
#delta-gate-container.hidden {
opacity: 0;
transform: scale(0.9);
pointer-events: none;
}
.gate-icon {
width: 60px;
height: 60px;
margin-bottom: 20px;
opacity: 0.8;
}
#delta-gate-container h1 {
color: #fff;
margin-bottom: 15px;
font-size: 26px;
}
#delta-gate-container p {
color: var(--text-color);
margin-bottom: 30px;
font-size: 16px;
line-height: 1.6;
}
#delta-code-input {
width: 100%;
padding: 15px;
background: rgba(0, 0, 0, 0.2);
border: 1px solid var(--glass-border);
border-radius: 12px;
font-size: 16px;
text-align: center;
color: #fff;
box-sizing: border-box;
direction: ltr;
margin-bottom: 25px;
transition: all 0.3s ease;
}
#delta-code-input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
#delta-code-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 4px rgba(61, 90, 254, 0.3);
}
#verify-button {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
border: none;
border-radius: 12px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
#verify-button:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(61, 90, 254, 0.4);
}
#verify-button:disabled {
background: #555;
cursor: not-allowed;
}
.loader {
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
display: inline-block;
}
#error-message {
color: #ff8a80;
margin-top: 20px;
font-weight: bold;
min-height: 20px;
}
/* --- Main Content (Vevo API) Styles --- */
#main-content-container {
display: none;
width: 100%;
max-width: 650px;
}
#huggingface-vevo-api-container {
font-family: 'Vazirmatn', sans-serif;
margin: 20px auto;
background-color: #f7fafc;
color: #2d3748;
direction: rtl;
padding: 10px;
border-radius: 16px;
max-width: 600px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
animation: fadeIn 0.8s ease-out;
}
#huggingface-vevo-api-container .container {
padding: 20px;
background-color: white;
border-radius: 12px;
}
#huggingface-vevo-api-container h1 {
text-align: center; color: #2c5282; font-size: 24px;
}
#huggingface-vevo-api-container .file-input {
margin-bottom: 20px;
}
#huggingface-vevo-api-container label {
display: block; margin-bottom: 8px; font-weight: bold;
}
#huggingface-vevo-api-container .description {
font-size: 14px; color: #718096; margin-top: -5px; margin-bottom: 10px; font-weight: normal;
}
#huggingface-vevo-api-container input[type="file"] {
display: block; width: 100%; padding: 10px; border: 1px solid #cbd5e0; border-radius: 8px; box-sizing: border-box; background-color: #edf2f7; cursor: pointer;
}
#huggingface-vevo-api-container button {
display: block; width: 100%; padding: 14px; background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.3s;
}
#huggingface-vevo-api-container button:hover:not(:disabled) {
transform: translateY(-2px); box-shadow: 0 4px 15px rgba(61, 90, 254, 0.3);
}
#huggingface-vevo-api-container button:disabled {
background: #a0aec0; cursor: not-allowed;
}
#huggingface-vevo-api-container #status {
margin-top: 20px; padding: 12px; background-color: #e2e8f0; border-radius: 8px; text-align: center;
}
#huggingface-vevo-api-container #result {
margin-top: 20px; text-align: center;
}
#huggingface-vevo-api-container #result audio {
width: 100%; margin-top: 10px; border-radius: 8px;
}
#huggingface-vevo-api-container #result a {
color: var(--primary-color); text-decoration: none; display: inline-block; margin-top: 15px; font-weight: bold;
}
</style>
</head>
<body>
<!-- Gate Container: Asks for Delta Code -->
<div id="delta-gate-container">
<svg class="gate-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" color="#fff"><path d="M17 3.25H7C4.65279 3.25 2.75 5.15279 2.75 7.5V16.5C2.75 18.8472 4.65279 20.75 7 20.75H17C19.3472 20.75 21.25 18.8472 21.25 16.5V7.5C21.25 5.15279 19.3472 3.25 17 3.25ZM19.75 7.5V16.5C19.75 18.0376 18.5376 19.25 17 19.25H7C5.46243 19.25 4.25 18.0376 4.25 16.5V7.5C4.25 5.96243 5.46243 4.75 7 4.75H17C18.5376 4.75 19.75 5.96243 19.75 7.5ZM12 7.75C12.4142 7.75 12.75 8.08579 12.75 8.5V11H14.25C14.6642 11 15 11.3358 15 11.75V13.25C15 13.6642 14.6642 14 14.25 14H12.75V15.5C12.75 15.9142 12.4142 16.25 12 16.25C11.5858 16.25 11.25 15.9142 11.25 15.5V14H9.75C9.33579 14 9 13.6642 9 13.25V11.75C9 11.3358 9.33579 11 9.75 11H11.25V8.5C11.25 8.08579 11.5858 7.75 12 7.75Z"></path></svg>
<h1>ورود به پنل اختصاصی</h1>
<p>برای دسترسی به ابزار تبدیل صدا، لطفا کد دسترسی (کد دلتا) خود را وارد نمایید.</p>
<input type="text" id="delta-code-input" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
<button id="verify-button">تأیید و ورود</button>
<div id="error-message"></div>
</div>
<!-- Main Content: Your Voice Conversion Tool (Initially Hidden) -->
<div id="main-content-container">
<div id="huggingface-vevo-api-container">
<div class="container">
<h1>رابط کاربری API برای Vevo</h1>
<div class="file-input">
<label for="sourceAudio">فایل صوتی اصلی (Source Audio):</label>
<p class="description">این فایل صوتی حاوی کلامی است که می‌خواهید با صدای جدید خوانده شود (مثلاً صدای خودتان).</p>
<input type="file" id="sourceAudio" accept="audio/*" />
</div>
<div class="file-input">
<label for="timbreRef">فایل صوتی مرجع (Timbre Reference):</label>
<p class="description">این فایل صوتی، صدای مدلی است که می‌خواهید صدای اصلی به آن تبدیل شود.</p>
<input type="file" id="timbreRef" accept="audio/*" />
</div>
<button id="generateBtn">تبدیل صدا</button>
<div id="status">آماده برای شروع...</div>
<div id="result"></div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- Section 1: Gate Logic ---
const gateContainer = document.getElementById('delta-gate-container');
const mainContentContainer = document.getElementById('main-content-container');
const verifyButton = document.getElementById('verify-button');
const codeInput = document.getElementById('delta-code-input');
const errorMessage = document.getElementById('error-message');
const buttonOriginalText = verifyButton.innerHTML;
const showMainContent = () => {
gateContainer.classList.add('hidden');
setTimeout(() => {
gateContainer.style.display = 'none';
mainContentContainer.style.display = 'block';
}, 500);
};
if (localStorage.getItem('isDeltaCodeValidated') === 'true') {
showMainContent();
}
verifyButton.addEventListener('click', async () => {
const code = codeInput.value.trim();
errorMessage.textContent = '';
if (code.length < 38 || code.length > 44) {
errorMessage.textContent = 'کد دلتا نامعتبر است. طول کد باید بین ۳۸ تا ۴۴ کاراکتر باشد.';
return;
}
verifyButton.disabled = true;
verifyButton.innerHTML = '<div class="loader"></div>';
try {
// ** مهم: آدرس PHP API را اینجا وارد کنید **
// فرض می‌کنیم send_code_api.php در همان مسیر gemin.php است.
const response = await fetch('https://www.aisada.ir/app/send_code_api.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `delta_code=${encodeURIComponent(code)}`
});
const result = await response.json(); // سعی کنید پاسخ JSON را بخوانید
if (response.ok && result.status === 'success') {
localStorage.setItem('isDeltaCodeValidated', 'true');
showMainContent();
} else {
errorMessage.textContent = result.message || 'خطا در تأیید کد. لطفاً دوباره تلاش کنید.';
verifyButton.disabled = false;
verifyButton.innerHTML = buttonOriginalText;
}
} catch (error) {
errorMessage.textContent = 'خطای شبکه. از اتصال اینترنت خود مطمئن شوید یا بعداً تلاش کنید.';
verifyButton.disabled = false;
verifyButton.innerHTML = buttonOriginalText;
}
});
// --- Section 2: Your Vevo API Logic ---
const container = document.getElementById('huggingface-vevo-api-container');
if (container) {
const generateBtn = container.querySelector('#generateBtn');
const sourceAudioInput = container.querySelector('#sourceAudio');
const timbreRefInput = container.querySelector('#timbreRef');
const statusDiv = container.querySelector('#status');
const resultDiv = container.querySelector('#result');
const API_URL = "https://amphion-vevo.hf.space/gradio_api/";
const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
async function uploadFile(file) {
const formData = new FormData();
formData.append("files", file);
const response = await fetch(`${API_URL}upload`, { method: 'POST', body: formData });
const result = await response.json();
if (result && result.length > 0) return result[0];
throw new Error("آپلود فایل با خطا مواجه شد.");
}
async function startGeneration() {
const sourceFile = sourceAudioInput.files[0];
const timbreFile = timbreRefInput.files[0];
if (!sourceFile || !timbreFile) {
alert("لطفاً هر دو فایل صوتی را انتخاب کنید.");
return;
}
generateBtn.disabled = true;
statusDiv.textContent = 'در حال آپلود فایل اصلی...';
resultDiv.innerHTML = '';
const sessionHash = generateSessionHash();
try {
const sourcePath = await uploadFile(sourceFile);
statusDiv.textContent = 'در حال آپلود فایل مرجع...';
const timbrePath = await uploadFile(timbreFile);
statusDiv.textContent = 'ارسال درخواست برای پردازش...';
const joinPayload = {
"data": [
{ "path": sourcePath, "url": `${API_URL}file=${sourcePath}`, "orig_name": sourceFile.name, "meta": { "_type": "gradio.FileData" } },
{ "path": timbrePath, "url": `${API_URL}file=${timbrePath}`, "orig_name": timbreFile.name, "meta": { "_type": "gradio.FileData" } }
],
"fn_index": 0, "session_hash": sessionHash
};
await fetch(`${API_URL}queue/join?`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(joinPayload)
});
statusDiv.textContent = 'در صف پردازش. منتظر نتیجه بمانید...';
const eventSource = new EventSource(`${API_URL}queue/data?session_hash=${sessionHash}`);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.msg === 'process_starts') {
statusDiv.textContent = 'پردازش شروع شد...';
} else if (data.msg === 'process_completed') {
statusDiv.textContent = 'پردازش با موفقیت انجام شد!';
const resultPath = data.output.data[0].path;
const resultUrl = `${API_URL}file=${resultPath}`;
resultDiv.innerHTML = `<h3>نتیجه:</h3><audio controls src="${resultUrl}"></audio><br><a href="${resultUrl}" download="output_voice.wav">دانلود فایل</a>`;
eventSource.close();
generateBtn.disabled = false;
} else if (data.msg === 'queue_full') {
statusDiv.textContent = 'صف پر است. لطفاً چند لحظه دیگر دوباره تلاش کنید.';
eventSource.close();
generateBtn.disabled = false;
}
};
eventSource.onerror = () => {
statusDiv.textContent = 'خطا در ارتباط با سرور.';
eventSource.close();
generateBtn.disabled = false;
};
} catch (error) {
statusDiv.textContent = `خطا: ${error.message}`;
generateBtn.disabled = false;
}
}
generateBtn.addEventListener('click', startGeneration);
}
});
</script>
</body>
</html>